diff --git a/README.md b/README.md index 5bfe350..7e9a4f1 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Packagist](https://img.shields.io/packagist/v/vimeo/mill.svg)](https://packagist.org/packages/vimeo/mill) [![Travis CI](http://img.shields.io/travis/vimeo/mill.svg?style=flat)](https://travis-ci.org/vimeo/mill) -Mill is a PHP library for documenting a REST API with a small annotation DSL. +Mill is an annotation-based DSL for documenting a REST API. It was built for automatically generating the [Vimeo API](https://developer.vimeo.com/api/endpoints) documentation microsite, and can be compiled down into [API Blueprint](https://apiblueprint.org/) files. diff --git a/bin/mill b/bin/mill index 791c47e..7d6a5e7 100755 --- a/bin/mill +++ b/bin/mill @@ -4,6 +4,8 @@ // Ignore any user application issues with classes that they have that aren't strictly handled. error_reporting(E_ALL & ~E_STRICT & ~E_WARNING); +ini_set('memory_limit', '4096M'); + foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) { if (file_exists($file)) { require $file; @@ -17,7 +19,7 @@ $version = PackageVersions\Versions::getVersion('ocramius/package-versions'); $application = new Application('Mill', $version); $application->add(new Mill\Command\Changelog); +$application->add(new Mill\Command\Compile); $application->add(new Mill\Command\ErrorMap); -$application->add(new Mill\Command\Generate); $application->run(); diff --git a/composer.json b/composer.json index 9562046..57a8be1 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "vimeo/mill", - "description": "A PHP library for documenting a REST API with a small annotation DSL.", + "description": "☴ An annotation-based DSL for documenting a REST API.", "license": "MIT", "authors": [ { @@ -12,7 +12,7 @@ "email": "jon@ursenba.ch" } ], - "minimum-stability": "stable", + "minimum-stability": "RC", "bin": ["bin/mill"], "require": { "php": ">=7.1.0", @@ -24,13 +24,15 @@ "gossi/docblock": "^1.5", "nicmart/string-template": "^0.1.1", "ocramius/package-versions": "^1.1", - "symfony/console": "^3.2 || ^4.0" + "symfony/console": "^3.2 || ^4.0", + "symfony/yaml": "^2.0", + "cocur/slugify": "^3.1" }, "require-dev": { "league/flysystem-memory": "^1.0", "squizlabs/php_codesniffer": "^3.0", - "vimeo/psalm": "^1.0", - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^7.0", + "vimeo/psalm": "^2.0" }, "suggest": { "ext-xml": "Required for config file processing." @@ -51,46 +53,50 @@ } }, "scripts": { - "build-blueprints": [ - "rm -rf resources/examples/Showtimes/blueprints", - "mkdir resources/examples/Showtimes/blueprints", - "./bin/mill generate --config=resources/examples/mill.xml resources/examples/Showtimes/blueprints/" + "build-docs-apiblueprint": [ + "./bin/mill compile --config=resources/examples/mill.xml --format=apiblueprint resources/examples/Showtimes/compiled/" + ], + "build-docs-openapi": [ + "./bin/mill compile --config=resources/examples/mill.xml --format=openapi resources/examples/Showtimes/compiled/" ], "build-changelogs": [ - "./bin/mill changelog --config=resources/examples/mill.xml --private=false resources/examples/Showtimes/blueprints/", - "mv resources/examples/Showtimes/blueprints/changelog.md resources/examples/Showtimes/blueprints/changelog-public-only-all-capabilities.md", + "./bin/mill changelog --config=resources/examples/mill.xml --private=false resources/examples/Showtimes/compiled/", + "mv resources/examples/Showtimes/compiled/changelog.md resources/examples/Showtimes/compiled/changelog-public-only-all-vendor-tags.md", - "./bin/mill changelog --config=resources/examples/mill.xml --private=false --capability=BUY_TICKETS --capability=FEATURE_FLAG resources/examples/Showtimes/blueprints/", - "mv resources/examples/Showtimes/blueprints/changelog.md resources/examples/Showtimes/blueprints/changelog-public-only-matched-with-tickets-and-feature-capabilities.md", + "./bin/mill changelog --config=resources/examples/mill.xml --private=false --vendor_tag='tag:BUY_TICKETS' --vendor_tag='tag:FEATURE_FLAG' resources/examples/Showtimes/compiled/", + "mv resources/examples/Showtimes/compiled/changelog.md resources/examples/Showtimes/compiled/changelog-public-only-matched-with-tickets-and-feature-vendor-tags.md", - "./bin/mill changelog --config=resources/examples/mill.xml --private=false --capability=DELETE_CONTENT resources/examples/Showtimes/blueprints/", - "mv resources/examples/Showtimes/blueprints/changelog.md resources/examples/Showtimes/blueprints/changelog-public-only-matched-with-delete-capabilities.md", + "./bin/mill changelog --config=resources/examples/mill.xml --private=false --vendor_tag='tag:DELETE_CONTENT' resources/examples/Showtimes/compiled/", + "mv resources/examples/Showtimes/compiled/changelog.md resources/examples/Showtimes/compiled/changelog-public-only-matched-with-delete-vendor-tags.md", - "./bin/mill changelog --config=resources/examples/mill.xml resources/examples/Showtimes/blueprints/" + "./bin/mill changelog --config=resources/examples/mill.xml resources/examples/Showtimes/compiled/" ], "build-errors": [ - "./bin/mill errors --config=resources/examples/mill.xml --private=false resources/examples/Showtimes/blueprints/", - "mv resources/examples/Showtimes/blueprints/1.0/errors.md resources/examples/Showtimes/blueprints/1.0/errors-public-only-all-capabilities.md", - "mv resources/examples/Showtimes/blueprints/1.1/errors.md resources/examples/Showtimes/blueprints/1.1/errors-public-only-all-capabilities.md", - "mv resources/examples/Showtimes/blueprints/1.1.1/errors.md resources/examples/Showtimes/blueprints/1.1.1/errors-public-only-all-capabilities.md", - "mv resources/examples/Showtimes/blueprints/1.1.3/errors.md resources/examples/Showtimes/blueprints/1.1.3/errors-public-only-all-capabilities.md", + "./bin/mill errors --config=resources/examples/mill.xml --private=false resources/examples/Showtimes/compiled/", + "mv resources/examples/Showtimes/compiled/1.0/errors.md resources/examples/Showtimes/compiled/1.0/errors-public-only-all-vendor-tags.md", + "mv resources/examples/Showtimes/compiled/1.1/errors.md resources/examples/Showtimes/compiled/1.1/errors-public-only-all-vendor-tags.md", + "mv resources/examples/Showtimes/compiled/1.1.1/errors.md resources/examples/Showtimes/compiled/1.1.1/errors-public-only-all-vendor-tags.md", + "mv resources/examples/Showtimes/compiled/1.1.3/errors.md resources/examples/Showtimes/compiled/1.1.3/errors-public-only-all-vendor-tags.md", - "./bin/mill errors --config=resources/examples/mill.xml --private=false --capability=BUY_TICKETS --capability=FEATURE_FLAG resources/examples/Showtimes/blueprints/", - "mv resources/examples/Showtimes/blueprints/1.0/errors.md resources/examples/Showtimes/blueprints/1.0/errors-public-only-unmatched-capabilities.md", - "mv resources/examples/Showtimes/blueprints/1.1/errors.md resources/examples/Showtimes/blueprints/1.1/errors-public-only-unmatched-capabilities.md", - "mv resources/examples/Showtimes/blueprints/1.1.1/errors.md resources/examples/Showtimes/blueprints/1.1.1/errors-public-only-unmatched-capabilities.md", - "mv resources/examples/Showtimes/blueprints/1.1.3/errors.md resources/examples/Showtimes/blueprints/1.1.3/errors-public-only-unmatched-capabilities.md", + "./bin/mill errors --config=resources/examples/mill.xml --private=false --vendor_tag='tag:BUY_TICKETS' --vendor_tag='tag:FEATURE_FLAG' resources/examples/Showtimes/compiled/", + "mv resources/examples/Showtimes/compiled/1.0/errors.md resources/examples/Showtimes/compiled/1.0/errors-public-only-unmatched-vendor-tags.md", + "mv resources/examples/Showtimes/compiled/1.1/errors.md resources/examples/Showtimes/compiled/1.1/errors-public-only-unmatched-vendor-tags.md", + "mv resources/examples/Showtimes/compiled/1.1.1/errors.md resources/examples/Showtimes/compiled/1.1.1/errors-public-only-unmatched-vendor-tags.md", + "mv resources/examples/Showtimes/compiled/1.1.3/errors.md resources/examples/Showtimes/compiled/1.1.3/errors-public-only-unmatched-vendor-tags.md", - "./bin/mill errors --config=resources/examples/mill.xml --private=false --capability=DELETE_CONTENT resources/examples/Showtimes/blueprints/", - "mv resources/examples/Showtimes/blueprints/1.0/errors.md resources/examples/Showtimes/blueprints/1.0/errors-public-only-matched-capabilities.md", - "mv resources/examples/Showtimes/blueprints/1.1/errors.md resources/examples/Showtimes/blueprints/1.1/errors-public-only-matched-capabilities.md", - "mv resources/examples/Showtimes/blueprints/1.1.1/errors.md resources/examples/Showtimes/blueprints/1.1.1/errors-public-only-matched-capabilities.md", - "mv resources/examples/Showtimes/blueprints/1.1.3/errors.md resources/examples/Showtimes/blueprints/1.1.3/errors-public-only-matched-capabilities.md", + "./bin/mill errors --config=resources/examples/mill.xml --private=false --vendor_tag='tag:DELETE_CONTENT' resources/examples/Showtimes/compiled/", + "mv resources/examples/Showtimes/compiled/1.0/errors.md resources/examples/Showtimes/compiled/1.0/errors-public-only-matched-vendor-tags.md", + "mv resources/examples/Showtimes/compiled/1.1/errors.md resources/examples/Showtimes/compiled/1.1/errors-public-only-matched-vendor-tags.md", + "mv resources/examples/Showtimes/compiled/1.1.1/errors.md resources/examples/Showtimes/compiled/1.1.1/errors-public-only-matched-vendor-tags.md", + "mv resources/examples/Showtimes/compiled/1.1.3/errors.md resources/examples/Showtimes/compiled/1.1.3/errors-public-only-matched-vendor-tags.md", - "./bin/mill errors --config=resources/examples/mill.xml resources/examples/Showtimes/blueprints/" + "./bin/mill errors --config=resources/examples/mill.xml resources/examples/Showtimes/compiled/" ], "build-resources": [ - "composer build-blueprints", + "rm -rf resources/examples/Showtimes/compiled", + "mkdir resources/examples/Showtimes/compiled", + "composer build-docs-apiblueprint", + "composer build-docs-openapi", "composer build-changelogs", "composer build-errors" ], diff --git a/config.xsd b/config.xsd index bc11302..9510b86 100644 --- a/config.xsd +++ b/config.xsd @@ -1,59 +1,100 @@ - + - + - + - - + + + + + + + + - - + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + - - - + - - - - - - - + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + - + - + @@ -62,121 +103,179 @@ - - - - - + - - - - - - - - + + - + + + + + + + + + + + + + + + + - - + - - - - - + - + - - - + + + + + + + + + + + + + + + + + + - + + - - + + + + + + + + + - - + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - + - + + + + + + + + + + + - - - - - + - - - - - + + + + + + + + + + + + + + - - - - - + - - + + - - + - + - - + + diff --git a/docs/compile-documentation.md b/docs/compile-documentation.md new file mode 100644 index 0000000..8e2664a --- /dev/null +++ b/docs/compile-documentation.md @@ -0,0 +1,135 @@ +--- +id: compile-documentation +title: Compiling documentation +--- + +Mill includes a `mill` command line application for doing various tasks on your API, including compiling it down into versioned [OpenAPI](https://swagger.io/) or [API Blueprint](https://apiblueprint.org/) specifications. + +## OpenAPI +Mill will compile your documentation into an [OpenAPI 3.0](https://swagger.io/) specification. + +```shell +$ ./vendor/bin/mill compile docs/ +Compiling controllers and representations... +Compiling OpenAPI files... + - API version: 1.0 + - API version: 1.1 + - API version: 1.1.1 + - API version: 1.1.2 + - API version: 1.1.3 + +Done! +``` + +If we look at the versioned directories that it created, `docs/1.1`, we'll see the compiled specification for v1.1 of our API. + +```shell +$ ls specs/1.1 +api.yaml + +$ cat specs/1.1/api.yaml | less +openapi: 3.0.0 +info: + title: 'Mill unit test API, Showtimes' + version: '1.1' +tags: + - + name: Movies + - + name: Theaters +paths: + '/movie/{id}': + get: + summary: 'Get a single movie.' +``` + +## API Blueprint +Mill will compile your documentation into an [API Blueprint](https://apiblueprint.org/) specification. + +```shell +$ ./vendor/bin/mill compile --format=apiblueprint docs/ +Compiling controllers and representations... +Compiling OpenAPI files... + - API version: 1.0 + - API version: 1.1 + - API version: 1.1.1 + - API version: 1.1.2 + - API version: 1.1.3 + +Done! +``` + +If we look at the versioned directories that it created, `docs/1.1`, we'll see the compiled specification for v1.1 of our API. + +```shell +$ ls specs/1.1 +api.apib representations resources + +$ cat specs/1.1/api.apib | less +FORMAT: 1A + +# Mill unit test API, Showtimes +This is the API Blueprint file for Mill unit test API, Showtimes. + +It was automatically generated by [Mill](https://github.com/vimeo/mill) on 2017-03-20 10:55:39. + +# Group Movies +## Movies [/movies/{id}] +Information on a specific movie. + +### Get a single movie. [GET] ++ Parameters + + `id` (integer, required) - Movie ID ++ Response 200 (application/json) + + Attributes (Movie) +``` + +Mill also generates individual parts of your documentation for you. This is helpful if you have people working on API design work, but just want to work on the API Blueprint files (and have someone else do the backend work). + +```shell +$ cat specs/1.1/resources/Movies.apib | less +# Group Movies +## Movies [/movies/{id}] +Information on a specific movie. + +### Get a single movie. [GET] ++ Parameters + + `id` (integer, required) - Movie ID ++ Response 200 (application/json) + + Attributes (Movie) ++ Response 304 (application/json) ++ Response 404 (application/json) + + Attributes (Error) + +### Update a movie. [PATCH] +This action requires a bearer token with `edit` scope. + ++ Parameters + + `id` (integer, required) - Movie ID ++ Request + + Attributes + - `cast` (array) - Array of names of the cast. + - `content_rating` (enum[string]) - MPAA rating + + Members + + `G` +``` + +```shell +$ cat specs/1.1/representations/Movie.apib | less +## Movie +- `cast` (array[Person]) - Cast +- `content_rating` (enum[string]) - MPAA rating + + Members + + `G` + + `PG` + + `PG-13` + + `R` + + `NC-17` + + `X` + + `NR` + + `UR` +- `description` (string) - Description +- `director` (Person) - Director +- `external_urls` (object) - External URLs + - `imdb` (string) - IMDB URL +``` diff --git a/docs/configuration.md b/docs/configuration.md index 6ba4de2..0e73b58 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -3,8 +3,7 @@ id: configuration title: Configuration --- -In order to instruct Mill on where to look for documentation, and any constraints you may have, Mill requires the use -of an XML configuration file: `mill.xml` +In order to instruct Mill on where to look for documentation, and any constraints you may have, Mill requires the use of an XML configuration file: `mill.xml` ```xml @@ -12,6 +11,24 @@ of an XML configuration file: `mill.xml` name="Movie showtimes API" bootstrap="vendor/autoload.php" > + + + + + + + + + + + + + + + @@ -46,18 +63,62 @@ of an XML configuration file: `mill.xml` ## Options | Option | Optional |Description | | :--- | :--- | :--- | -| name | ✓ | This is the canonical name of your API. When you generate API Blueprint files, this will be the header declaration. | +| name | ✓ | This is the canonical name of your API. | | bootstrap | × | Relative path to a PHP bootstrap file that will get loaded before Mill does any work. This is usually a [Composer](https://getcomposer.org/) `vendor/autoload.php` file. This is necessary so Mill can access, and parse your API classes for documentation. | ## Settings > All directory paths should be relative to the location of your `mill.xml` configuration file. -> If you specify a controller, representation, capability, or scope in your documentation that hasn't been configured here, documentation generation will fail with errors. +> If you specify a controller, representation, vendor tag, or authentication scope in your documentation that hasn't been configured here, documentation compiling will fail with errors. -### Versions -The `` setting lets you inform Mill on the various version of your API that exist. From here, Mill will then know what versions to compile documentation for. +### Authentication +The `` element lets you configure specific authentication flows and OAuth 2 scopes for your API. -To set a "default" API version, use the `default="true"` attribute. You **must** have a default version set, and there can only be one. +```xml + + + + + + + + + + + + + + + + + +``` + +#### Flows +Currently supported authentication flows are `bearer` and `oauth2`. + +#### Scopes +If your API has an authentication system that requires a specific scope(s) for using an API endpoint, use this to document those. + +You can find usage details for scopes in the [`@api-scope`](reference-api-scope.md) documentation. + +### Compilers +The `` element lets you control the documentation compilers that Mill supports from the [`compile`](compile-documentation.md) command. + +#### Excludes +* Use `` elements to specify a resource group that should be excluded from compiled specifications. + * Make sure to add a `group` attribute so Mill knows what group you're excluding. + +Example: + +```xml + + + + + + +``` ### Controllers The `` setting lets you inform Mill on where your API controllers live. @@ -66,6 +127,65 @@ The `` setting lets you inform Mill on where your API controllers l * Specify a `` element for a specific, fully-qualified class name. * Add in an `` block, with `` elements for excluding specific controllers from being parsed. +```xml + + + + + +``` + +### Info +The `` element allows you to configure some information about your API: + +* ``: A terms of service URL. +* ``: Contact information. `name` and `email` are optional. +* ``: External API documentation you may want to surface to the end-user. + +```xml + + + + + + + + + +``` + +### Parameter Tokens +Parameter tokens allow you to create an [`@api-param`](reference-api-param.md) or [`@api-queryparam`](reference-api-queryparam.md) shortcode to save time for common elements in your API (like paging or sorting). + +Example: + +```xml + + page (integer, optional) - The page number to show. + per_page (integer, optional) - Number of items to show on each page. Max 100. + filter (string, optional) - Filter to apply to the results. + +``` + +You can find usage details for parameter tokens in the [`@api-param`](reference-api-param.md#tokens) and [`@api-queryparam`](reference-api-queryparam.md#tokens) documentation. + +### Path Parameters +#### Translations +The path parameters translations section allows you to set up translation elements for [`@api-pathparam`](reference-api-pathparam.md) annotations. Say, in your code, the route for a video is at `/videos/+video_id`, but in your documentation, you want it to just say `/videos/+id`, this is the place to do that. + +Example: + +```xml + + + + + +``` + ### Representations The ` setting lets you inform Mill on where your API data representations (the content that your controllers return), live. @@ -75,8 +195,27 @@ The ` setting lets you inform Mill on where your API data repr * If the representation doesn't have a method, or documentation, you should add it to the `excludes` block. * Add in an `` block, with `` elements for excluding specific controllers from being parsed. +```xml + + + + + + + + + + + + + + + + +``` + #### Errors -The representation `` setting lets you tell Mill where your error representations are (the content that is returned from [`@api-throws`](reference-api-throws.md) annotations. Here you can specify a `` with a fully-qualified class name. +The representation `` setting lets you tell Mill where your error representations are (the content that is returned from [`@api-error`](reference-api-error.md) annotations. Here you can specify a `` with a fully-qualified class name. Required attributes for the `` element are: @@ -87,8 +226,8 @@ Required attributes for the `` element are: /** * … * - * @api-throws:public {403} \ErrorRepresentation (\AppError::USER_NOT_ALLOWED) - * If the user isn't allowed to do something. + * @api-error:public 403 (\ErrorRepresentation<7701>) - If the user isn't + * allowed to do something. */ public function PATCH() { @@ -98,83 +237,40 @@ public function PATCH() Here, `\ErrorRepresentation` would have `needsErrorCode="true"`. -### Capabilities -If your API has a capability-backed permission system for granting certain endpoints, or data in representations, to specific users, you should use this to document that. - -```xml - - - - - -``` - -You can find usage details for capabilities in the [`@api-capability`](reference-api-capability.md), [`@api-param`](reference-api-param.md), [`@api-return`](reference-api-return.md), and [`@api-throws`](reference-api-throws.md) documentation. - -### Scopes -If your API has an authentication system that requires a specific scope(s) for using an API endpoint, use this to document those. - -Example: - -```xml - - - - - - -``` - -You can find usage details for scopes in the [`@api-scope`](reference-api-scope.md) documentation. - -### Parameter Tokens -Parameter tokens allow you to create a [`@api-param`](reference-api-param.md) shortcode to save time for common elements in your API (like paging or sorting). - -Example: +### Servers +Configure your API servers with the `` element. ```xml - - page (integer, optional) - The page number to show. - per_page (integer, optional) - Number of items to show on each page. Max 100. - filter (string, optional) - Filter to apply to the results. - + + + + ``` -You can find usage details for parameter tokens in the [`@api-param`](reference-api-param.md#tokens) documentation. - -### uriSegments -#### translations -The URI segment translations section allows you to set up translation elements for [`@api-uriSegment`](reference-api-urisegment.md) annotations. Say, in your code, the route for a video is at `/videos/+video_id`, but in your documentation, you want it to just say `/videos/+id`, this is the place to do that. - -Example: +### Vendor tags +If you'd like to add additional metadata (that you can eventually filter your documentation against), you should use vendor tags to document those. ```xml - - - - - + + + + + ``` -### Generators -These settings let you control the documentation generators that Mill supports from the `./bin/mill generate` command. +You can find usage details for vendor tags in the [`@api-vendortag`](reference-api-vendortag.md), [`@api-param`](reference-api-param.md), [`@api-queryparam`](reference-api-queryparam.md), [`@api-return`](reference-api-return.md), and [`@api-error`](reference-api-error.md) documentation. -#### API Blueprint -##### Excludes -* Use `` elements to specify a resource namespace that should be excluded from API Blueprint generation and compilation. - * Make sure to add a `namespace` attribute so Mill knows what namespace you're excluding.. +### Versions +The `` setting lets you inform Mill on the various version of your API that exist. From here, Mill will then know what versions to compile documentation for. -Example: +To set a "default" API version, use the `default="true"` attribute. You **must** have a default version set, and there can only be one. ```xml - - - - - - - - + + + + + ``` ## XML Schema Definition diff --git a/docs/generate-changelogs.md b/docs/generate-changelogs.md index c72ffe0..1a9b641 100644 --- a/docs/generate-changelogs.md +++ b/docs/generate-changelogs.md @@ -89,5 +89,5 @@ Mill wraps important pieces of content in the JSON-encoded changelog that can th | Representation field | `mill-changelog_field` | `data-mill-field` | | Resource action method | `mill-changelog_method` | `data-mill-method` | | Resource action parameter| `mill-changelog_parameter` | `data-mill-parameter` | -| Resource action URI | `mill-changelog_uri` | `data-mill-uri` | -| Resource namespaces | `mill-changelog_resource_namespace` | `data-mill-resource-namespace` | +| Resource action path | `mill-changelog_path` | `data-mill-path` | +| Resource groups | `mill-changelog_resource_group` | `data-mill-resource-group` | diff --git a/docs/generate-documentation.md b/docs/generate-documentation.md deleted file mode 100644 index afda404..0000000 --- a/docs/generate-documentation.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -id: generate-documentation -title: Generating documentation ---- - -Mill includes a `mill` command line application for doing various tasks on your API, including compiling it down into versioned [API Blueprint](https://apiblueprint.org/) specifications. - -```shell -$ ./vendor/bin/mill generate --help -Usage: - generate [options] [--] - -Arguments: - output Directory to output your generated `.apib` files in. - -Options: - --config[=CONFIG] Path to your `mill.xml` config file. [default: "mill.xml"] - --constraint[=CONSTRAINT] Version constraint to compile documentation for. eg. "3.*", "3.1 - 3.2" - --default[=DEFAULT] Generate just the configured default API version documentation. - `defaultApiVersion` in your `mill.xml` file. [default: false] - --dry-run[=DRY-RUN] Execute a dry run (do not save any files). [default: false] - -h, --help Display this help message - -q, --quiet Do not output any message - -V, --version Display this application version - --ansi Force ANSI output - --no-ansi Disable ANSI output - -n, --no-interaction Do not ask any interactive question - -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose - output and 3 for debug - -Help: - Generate API Blueprint documentation. -``` - -Example usage: - -```shell -$ ./vendor/bin/mill generate blueprints/ -Compiling controllers and representations… -Generating API Blueprint files… - - API version: 1.0 - 7/7 [============================] - - API version: 1.1 - 7/7 [============================] - - API version: 1.1.1 - 7/7 [============================] - -Done! -``` - -This will compile the configured documentation for my API (versions 1.0 through 1.1.1) into the `blueprints/` directory. - -If we look at one of the versioned directories that it created, `blueprints/1.1`, we can see that we've got API Blueprint specs! - -```shell -$ ls blueprints/1.1 -api.apib representations resources -``` - -```shell -$ cat blueprints/1.1/api.apib -FORMAT: 1A - -# Mill unit test API, Showtimes -This is the API Blueprint file for Mill unit test API, Showtimes. - -It was automatically generated by [Mill](https://github.com/vimeo/mill) on 2017-03-20 10:55:39. - -# Group Movies -## Movies [/movies/{id}] -Information on a specific movie. - -### Get a single movie. [GET] -+ Parameters - + `id` (integer, required) - Movie ID -+ Response 200 (application/json) - + Attributes (Movie) -… -``` - -Mill also generates individual parts of your documentation for you. - -```shell -$ cat blueprints/1.1/resources/Movies.apib -# Group Movies -## Movies [/movies/{id}] -Information on a specific movie. - -### Get a single movie. [GET] -+ Parameters - + `id` (integer, required) - Movie ID -+ Response 200 (application/json) - + Attributes (Movie) -+ Response 304 (application/json) -+ Response 404 (application/json) - + Attributes (Error) - -### Update a movie. [PATCH] -This action requires a bearer token with `edit` scope. - -+ Parameters - + `id` (integer, required) - Movie ID -+ Request - + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating - + Members - + `G` -… -``` - -```shell -$ cat blueprints/1.1/representations/Movie.apib -## Movie -- `cast` (array[Person]) - Cast -- `content_rating` (enum[string]) - MPAA rating - + Members - + `G` - + `PG` - + `PG-13` - + `R` - + `NC-17` - + `X` - + `NR` - + `UR` -- `description` (string) - Description -- `director` (Person) - Director -- `external_urls` (object) - External URLs - - `imdb` (string) - IMDB URL -… -``` diff --git a/docs/installation.md b/docs/installation.md index 484cab9..88a7c79 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -3,7 +3,7 @@ id: installation title: Installation --- -Mill was built for automatically generating the [Vimeo API](https://developer.vimeo.com/api/endpoints) documentation microsite. It can compile your API documentation down into a [API Blueprint](https://apiblueprint.org) specification, or automatically generated [changelogs](https://developer.vimeo.com/api/changelog). +Mill was built for automatically generating the [Vimeo API](https://developer.vimeo.com/api/endpoints) documentation microsite. It can compile your API documentation down into an [OpenAPI 3.0](https://swagger.io/), [API Blueprint](https://apiblueprint.org), specification, and also automatically generated [changelogs](https://developer.vimeo.com/api/changelog). As it was built specifically for the Vimeo API, it may come off as opinionated, and may not be a good fit for all types of APIs, however we've put a lot of effort to make it as generalized as possible. diff --git a/docs/reference-api-capability.md b/docs/reference-api-capability.md deleted file mode 100644 index 7fc4440..0000000 --- a/docs/reference-api-capability.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -id: api-capability -title: "@api-capability" ---- - -This defines a required capability in your API that the developer's application needs to possess in order to execute the resource action, or get the representation field in the endpoint response. - -## Syntax -```php -@api-capability capability -``` - -## Requirements - -| Required? | Needs a visibility | Supports versioning | Supports deprecation | -| :--- | :--- | :--- | :--- | -| × | × | × | × | - -## Breakdown - -| Tag | Optional | Description | -| :--- | :--- | :--- | -| capability | × | Required capability that an API application must posses. | - -## Examples -On a resource action: - -```php -/** - * … - * @api-capability SomeApplicationCapability - * … - */ -public function PATCH() -{ - … -} -``` - -On a representation field: - -```php -$representation = [ - … - - /** - * @api-data download (boolean, SomeApplicationCapability) - Download - * permission setting - */ - 'download' => true, - - … -]; -``` diff --git a/docs/reference-api-data.md b/docs/reference-api-data.md index f6a24b1..1fa7c58 100644 --- a/docs/reference-api-data.md +++ b/docs/reference-api-data.md @@ -7,7 +7,7 @@ This describes a piece of data within a representation that a resource action ca ## Syntax ```php -@api-data fieldName `sampleData` (type, required|optional, nullable, capabilityName) - Description +@api-data fieldName `sampleData` (type, required|optional, nullable, vendor:tagName) - Description + Members - `option` - Option description ``` @@ -23,17 +23,17 @@ This describes a piece of data within a representation that a resource action ca | Tag | Optional | Description | | :--- | :--- | :--- | | fieldName | × | The data in a representation that you're documenting. So if your representation has a `link`, `field_name` would be `link`. If you're documenting nested objects, you should use dot-notation to map out the field name. So say you have a `metadata[connections][albums]` array in your representation, the `field_name` would then be `metadata.connections.albums`. | -| sampleData | ✓ | This is a sample of what the contents of the representation data can contain. For example, if this returns a number, this can be "50". | +| sampleData | ✓ | This is a sample of what the contents of the representation data can contain. For example, this returns a number, this can be "50". | | type | × | This describes the type of data that a representation data field contains. | | required|optional | ✓ | A flag that indicates that the data is, well, optional. If nothing is supplied, it defaults to being `optional`. | | nullable | ✓ | A flag that indicates that the data is nullable. If nothing is supplied, it defaults to being non-nullable. | -| capabilityName | ✓ | Defined capability that the developers application should possess. | +| vendor:tagName | ✓ | Defined vendor tag. See the [`@api-vendortag`](reference/api-vendortag.md) documentation for more information. There is no limit to the amount of vendor tags you can specify on a parameter. | | Description | × | A description for that this data is. | | Members | ✓ | If this data has acceptable values (like in the case of an `enum` type), you can document those values here along with a description for what the value is, or means. | ### Supported Types -| Type | API Blueprint conversion | +| Type | Specification representation | | :--- | :--- | | array | array | | boolean | boolean | @@ -61,7 +61,7 @@ If your dataset is a Mill representation, you can document that by setting the t #### Subtypes Mill allows you, if necessary, to define a single subtype for a dataset. For example, if you have an array that contains objects, you can set the `type` as `array`. Alternatively, if you have an array of representations, you can do the same with `array<\RepresentationFQN>`. -Currently, only `array` types are allowed to contain subtypes. To define subtypes of objects, use a `@api-data` annotation for each property. +Currently, only `array` types are allowed to contain subtypes. To define subtypes of objects, use an `@api-data` annotation for each property. ## Examples Common uses: diff --git a/docs/reference-api-error.md b/docs/reference-api-error.md new file mode 100644 index 0000000..cae8921 --- /dev/null +++ b/docs/reference-api-error.md @@ -0,0 +1,90 @@ +--- +id: "api-error" +title: "@api-error" +--- + +This represents an exception that may be returned on a resource action. + +## Syntax +```php +@api-error:visibility httpCode (\Representation, vendor:tagName) - description +``` + +## Requirements + +| Required? | Needs a visibility | Supports versioning | Supports deprecation | +| :--- | :--- | :--- | :--- | +| × | ✓ | ✓ | × | + +## Breakdown + +| Tag | Optional | Description | +| :--- | :--- | :--- | +| :visibility | ✓ | [Visibility decorator](reference-visibility.md) | +| httpCode | × | The HTTP code that will be returned. Example: `404`, `403`, etc. | +| \Representation | × | The fully qualified class name for a representation that will be returned. | +| error code | ✓ | An optional error code, if your application supports unique error codes, that this error returns. This can either be a numerical code (ex. `(1234)`), or a fully qualified static accessor (ex. (`\Some\Exception::NOT_ALLOWED`). | +| vendor:tagName | ✓ | Defined vendor tag. See the [`@api-vendortag`](reference-api-vendortag.md) documentation for more information. There is no limit to the amount of vendor tags you can specify on a parameter. | +| description | ✓ | A short description describing why, or what, this error is. | + +## Types and subtypes +In addition to supporting straight descriptions, the [`@api-error`](reference-api-error.md) annotation also supports the concept of "types" and "subtypes". For example: + +```php +@api-error:public 404 (\ErrorRepresentation) - {user} +``` + +In this case, this exception will be thrown when the `{user}` passed into the route (usually via the URI) is not found. The generated error message for this becomes: "If the user cannot be found." + +There also exist the concept of a subtype, represented as: + +```php +@api-error:public 404 (\ErrorRepresentation) - {user,group} +``` + +This means that if the supplied group could not be found for the supplied user, an exception will be thrown. The generated error message for this is: "If the user cannot be found in the group." + +## Examples +Usage with a vendor tag and description type: + +```php +/** + * … + * + * @api-error:public 404 (\ErrorRepresentation, needs:SomeApplicationFeature) - {user} + */ +public function PATCH() +{ + … +} +``` + +With an error code: + +```php +/** + * … + * + * @api-error:public 403 (\ErrorRepresentation<7701>) If the user isn't + * allowed to do something. + */ +public function PATCH() +{ + … +} +``` + +Standard usage: + +```php +/** + * … + * + * @api-error:public 404 (\ErrorRepresentation) - If the user isn't allowed + * to do something. + */ +public function PATCH() +{ + … +} +``` diff --git a/docs/reference-api-group.md b/docs/reference-api-group.md new file mode 100644 index 0000000..1ea7926 --- /dev/null +++ b/docs/reference-api-group.md @@ -0,0 +1,41 @@ +--- +id: "api-group" +title: "@api-group" +--- + +Name of the group that this action should be grouped under when generating documentation. + +## Syntax +```php +@api-group groupName +``` + +## Requirements + +| Required? | Needs a visibility | Supports versioning | Supports deprecation | +| :--- | :--- | :--- | :--- | +| ✓ | × | × | × | + +## Breakdown + +| Tag | Optional | Description | +| :--- | :--- | :--- | +| groupName | × | Group name | + +## Examples +On a resource action: + +```php +/** + * @api-label Update data on a group of videos. + * @api-group Users + * + * @api-path:public /users + * + * … + */ +public function PATCH() +{ + … +} +``` diff --git a/docs/reference-api-maxversion.md b/docs/reference-api-maxversion.md new file mode 100644 index 0000000..fde9286 --- /dev/null +++ b/docs/reference-api-maxversion.md @@ -0,0 +1,43 @@ +--- +id: api-maxversion +title: "@api-maxversion" +--- + +This allows you to denote the maximum API version required for a resource action. + +## Syntax +```php +@api-maxversion version +``` + +## Requirements + +| Required? | Needs a visibility | Supports versioning | Supports deprecation | +| :--- | :--- | :--- | :--- | +| × | × | × | × | + +## Breakdown + +| Tag | Optional | Description | +| :--- | :--- | :--- | +| version | × | A specific, maximum API version required for a resource action. | + +## Examples +```php +/** + * @api-label Update a movie. + * @api-group Movies + * + * @api-path:public /movies/+id + * @api-pathparam id (integer) - Movie ID + * + * @api-maxversion 3.2 + * + * @api-error:private 403 (\Some\ErrorErrorRepresentation) - If the user isn't + * allowed to do something. + */ +public function PATCH() +{ + … +} +``` diff --git a/docs/reference-api-minversion.md b/docs/reference-api-minversion.md index 2fafdb7..27812f8 100644 --- a/docs/reference-api-minversion.md +++ b/docs/reference-api-minversion.md @@ -25,12 +25,15 @@ This allows you to denote the minimum API version required for a resource action ## Examples ```php /** - * @api-uri:public {Movies} /movies/+id - * @api-urisegment {/movies/+id} id (integer) - Movie ID + * @api-label Update a movie. + * @api-group Movies + * + * @api-path:public /movies/+id + * @api-pathparam id (integer) - Movie ID * * @api-minversion 3.2 * - * @api-throws:private {403} \Some\ErrorErrorRepresentation If the user isn't + * @api-error:private 403 (\Some\ErrorErrorRepresentation) - If the user isn't * allowed to do something. */ public function PATCH() diff --git a/docs/reference-api-param.md b/docs/reference-api-param.md index cc9a584..bc0513d 100644 --- a/docs/reference-api-param.md +++ b/docs/reference-api-param.md @@ -3,11 +3,13 @@ id: api-param title: "@api-param" --- -A request parameter that can be supplied to a resource action. +A request parameter that can be supplied to a resource action via a body payload. + +> If you need to describe a parameter that can be used within a query string, use the [`@api-queryparam`](reference-api-queryparam.md) annotation. ## Syntax ```php -@api-param:visibility fieldName `sampleData` (type, required|optional, nullable, capabilityName) - Description +@api-param:visibility fieldName `sampleData` (type, required|optional, nullable, vendor:tagName) - Description + Members - `option` - Option description ``` @@ -28,13 +30,13 @@ A request parameter that can be supplied to a resource action. | type | × | This can be a reference to the type of variable that is being passed in (string, boolean, array, etc.), or can be one of the [tokens](#tokens) that are configured for your API. | | required|optional | ✓ | A flag that indicates that the parameter is, well, optional. If nothing is supplied, it defaults to being `optional`. | | nullable | ✓ | A flag that indicates that the parameter is nullable. If nothing is supplied, it defaults to being non-nullable. | -| capabilityName | ✓ | Defined capability that the developers application should possess. | +| vendor:tagName | ✓ | Defined vendor tag. See the [`@api-vendortag`](reference-api-vendortag.md) documentation for more information. There is no limit to the amount of vendor tags you can specify on a parameter. | | Description | × | Description for what the parameter is for. | | Members | ✓ | If this parameter has acceptable values (like in the case of an `enum` type), you can document those values here along with a description for what the value is, or means. | ### Supported Types -| Type | API Blueprint conversion | +| Type | Specification representation | | :--- | :--- | | array | array | | boolean | boolean | @@ -49,6 +51,11 @@ A request parameter that can be supplied to a resource action. | timestamp | string | | uri | string | +#### Subtypes +Mill allows you, if necessary, to define a single subtype for a parameter. For example, if you have a parameter that is an array of objects, you can set the `type` as `array`. + +Currently only `array` types are allowed to contain subtypes. To define subtypes of objects, use an `@api-param` annotation for each child parameter. + ## Tokens Because writing out the same parameter for a large number of endpoints can get tiring, we have a system in place that allows you to configure tokens, which act as kind of a short-code for a parameter: @@ -93,11 +100,10 @@ Using a token with available values: `playable` ``` -With a capability: +With a vendor tag: ```php -@api-param:public locked_down (string, AnotherRequiredCapability) - This is a - cool thing. +@api-param:public locked_down (string, needs:SomeApplicationFeature) - This is a cool thing. ``` Normal usage with acceptable values: diff --git a/docs/reference-api-path.md b/docs/reference-api-path.md new file mode 100644 index 0000000..4951168 --- /dev/null +++ b/docs/reference-api-path.md @@ -0,0 +1,38 @@ +--- +id: api-path +title: "@api-path" +--- + +This allows you to describe the path(s) that a resource action services. + +## Syntax +```php +@api-path:visibility path +``` + +## Requirements + +| Required? | Needs a visibility | Supports versioning | Supports deprecation | +| :--- | :--- | :--- | :--- | +| ✓ | ✓ | × | ✓ | + +## Breakdown + +| Tag | Optional | Description | +| :--- | :--- | :--- | +| path | × | This is the path that the resource action services. It's recommended that these conform to [RFC 3986](https://tools.ietf.org/html/rfc3986) and [RFC 6570](https://tools.ietf.org/html/rfc6570). | + +## Examples +```php +/** + * … + * @api-path:private /movies + * + * @api-path:private:deprecated /theaters/+id/movies + * … + */ +public function PATCH() +{ + … +} +``` diff --git a/docs/reference-api-pathparam.md b/docs/reference-api-pathparam.md new file mode 100644 index 0000000..1434f9c --- /dev/null +++ b/docs/reference-api-pathparam.md @@ -0,0 +1,62 @@ +--- +id: api-pathparam +title: "@api-pathparam" +--- + +This allows you to describe the parameters of a coupled resource action path. + +## Syntax +```php +@api-pathparam paramName `sampleData` (type) - Description + + Members + - `option` - Value description +``` + +## Requirements + +| Required? | Needs a visibility | Supports versioning | Supports deprecation | +| :--- | :--- | :--- | :--- | +| × | × | × | × | + +## Breakdown + +| Tag | Optional | Description | +| :--- | :--- | :--- | +| paramName | × | This is the name of the parameter that is used within the path. | +| sampleData | ✓ | This is a sample of what the contents of the parameter should be. For example, if you're passing in a number, this can be "50". | +| type | × | This can be a reference to the type of variable that is being passed in (string, boolean, array, etc.), into the coupled path. | +| description | × | Description of what the parameter is for. | +| Members | ✓ | If this path parameter has acceptable values (like in the case of an `enum` type), you can document those values here along with a description for what the value is, or means. | + +### Supported Types + +| Type | Specification representation | +| :--- | :--- | +| array | array | +| boolean | boolean | +| date | string | +| datetime | string | +| float | number | +| enum | enum | +| integer | number | +| number | number | +| object | object | +| string | string | +| timestamp | string | +| uri | string | + +## Examples +```php +/** + * … + * + * @api-path:private:deprecated /movies/+id + * @api-pathparam id `1234` (integer) - Movie ID + * + * … + */ +public function PATCH() +{ + … +} +``` diff --git a/docs/reference-api-queryparam.md b/docs/reference-api-queryparam.md new file mode 100644 index 0000000..99d35fc --- /dev/null +++ b/docs/reference-api-queryparam.md @@ -0,0 +1,41 @@ +--- +id: api-queryparam +title: "@api-queryparam" +--- + +A request parameter that can be supplied to a resource action via the query string. + +> If you need to describe a parameter that can be used within a body payload, use the [`@api-param`](reference-api-param.md) annotation. + +The syntax for this is exactly the same as [`@api-param`](reference-api-param.md). + +## Examples +Using a token: + +```php +@api-queryparam:public {page} +``` + +Using a token with available values: + +```php +@api-queryparam:public {filter} + + Members + `embeddable` + `playable` +``` + +With a vendor tag: + +```php +@api-queryparam:public locked_down (string, needs:SomeApplicationFeature) - This is a cool thing. +``` + +Normal usage with acceptable values: + +```php +@api-queryparam:private __testing (string) - This does a thing. + + Members + - `true` + - `false` +``` diff --git a/docs/reference-api-return.md b/docs/reference-api-return.md index 1890f1c..300b526 100644 --- a/docs/reference-api-return.md +++ b/docs/reference-api-return.md @@ -48,7 +48,7 @@ Say if this is a user data action, it might return a `\UserRepresentation`. | | updated | | 304 Not Modified | notmodified | -> **Note:** `@api-return` does not support returning 400 or 500 error codes. If you need those, use [`@api-throws`](reference-api-throws.md) instead. +> **Note:** `@api-return` does not support returning 400 or 500 error codes. If you need those, use [`@api-error`](reference-api-error.md) instead. ## Examples ```php diff --git a/docs/reference-api-scope.md b/docs/reference-api-scope.md index 1785ee3..3597aa2 100644 --- a/docs/reference-api-scope.md +++ b/docs/reference-api-scope.md @@ -26,12 +26,15 @@ This corresponds to an available user authentication token scope (ex. "create", ## Examples ```php /** - * @api-uri:public {Movies} /movies/+id - * @api-urisegment {/movies/+id} id (integer) - Movie ID + * @api-label Update a movie + * @api-group Movies + * + * @api-path:public /movies/+id + * @api-pathparam id (integer) - Movie ID * * @api-scope edit * - * @api-throws:private {403} \Some\ErrorErrorRepresentation If the user isn't + * @api-error:private 403 (\Some\ErrorErrorRepresentation) - If the user isn't * allowed to do something. */ public function PATCH() diff --git a/docs/reference-api-see.md b/docs/reference-api-see.md index 064d087..8698665 100644 --- a/docs/reference-api-see.md +++ b/docs/reference-api-see.md @@ -50,4 +50,5 @@ private function getPrivacy($object, $request) } ``` -From here, `download` will be imported into the representation documentation as `privacy.download`. +From here, `download` will be imported into the representation documentation as +`privacy.download`. diff --git a/docs/reference-api-throws.md b/docs/reference-api-throws.md deleted file mode 100644 index 973c4dc..0000000 --- a/docs/reference-api-throws.md +++ /dev/null @@ -1,89 +0,0 @@ ---- -id: api-throws -title: "@api-throws" ---- - -This represents an exception that may be thrown inside of a resource action. - -## Syntax -```php -@api-throws:visibility {http code} \Representation (error code) +capability+ description -``` - -## Requirements - -| Required? | Needs a visibility | Supports versioning | Supports deprecation | -| :--- | :--- | :--- | :--- | -| × | ✓ | ✓ | × | - -## Breakdown - -| Tag | Optional | Description | -| :--- | :--- | :--- | -| :visibility | ✓ | [Visibility decorator](reference-visibility.md) | -| {http code} | × | The HTTP code that will be returned. Example: `{404}`, `{403}`, etc. | -| \Representation | × | The fully qualified class name for a representation that will be returned. | -| (error code` | ✓ | An optional error code, if your application supports unique error codes, that this error returns. This can either be a numerical code (ex. `(1234)`), or a fully qualified static accessor (ex. (`\Some\Exception::NOT_ALLOWED`). | -| description | ✓ | A short description describing why, or what, this error is. | - -## Types and subtypes -In addition to supporting straight descriptions, the [`@api-throws`](reference-api-throws.md) annotation also supports the concept of "types" and "subtypes". For example: - -```php -@api-throws:public {404} \ErrorRepresentation {user} -``` - -In this case, this exception will be thrown when the `{user}` passed into the route (usually via the URI) is not found. The generated error message for this becomes: "If the user cannot be found." - -There also exist the concept of a subtype, represented as: - -```php -@api-throws:public {404} \ErrorRepresentation {user,group} -``` - -This means that if the supplied group could not be found for the supplied user, an exception will be thrown. The generated error message for this is: "If the user cannot be found in the group." - -## Examples -Usage with a capability and description type: - -```php -/** - * … - * - * @api-throws:public {404} \ErrorRepresentation +SomeCapability+ {user} - */ -public function PATCH() -{ - … -} -``` - -With an error code: - -```php -/** - * … - * - * @api-throws:public {403} \ErrorRepresentation (\AppError::USER_NOT_ALLOWED) - * If the user isn't allowed to do something. - */ -public function PATCH() -{ - … -} -``` - -Standard usage: - -```php -/** - * … - * - * @api-throws:public {404} \ErrorRepresentation If the user isn't allowed to do - * something. - */ -public function PATCH() -{ - … -} -``` diff --git a/docs/reference-api-uri.md b/docs/reference-api-uri.md deleted file mode 100644 index 613e463..0000000 --- a/docs/reference-api-uri.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -id: api-uri -title: "@api-uri" ---- - -This allows you to describe the URI(s) that a resource action services. - -## Syntax -```php -@api-uri:visibility {namespace} uri -``` - -## Requirements - -| Required? | Needs a visibility | Supports versioning | Supports deprecation | -| :--- | :--- | :--- | :--- | -| ✓ | ✓ | × | ✓ | - -## Breakdown - -| Tag | Optional | Description | -| :--- | :--- | :--- | -| {namespace} | × | A namespace which this action lives under. This is used for grouping your documentation into like groups (like having your user actions grouped under `User`). It also allows you to do sub-grouping with `Namespace\Secondary Namespace`, if you wish. There is no limit to the amount of depths a URI can be in. | -| uri | × | This is the URI that the resource action services. It's recommended that these conform to [RFC 3986](https://tools.ietf.org/html/rfc3986) and [RFC 6570](https://tools.ietf.org/html/rfc6570). | - -## Examples -```php -/** - * … - * @api-uri:private {Movies} /movies - * - * @api-uri:private:deprecated {Theaters\Movies} /theaters/+id/movies - * … - */ -public function PATCH() -{ - … -} -``` diff --git a/docs/reference-api-urisegment.md b/docs/reference-api-urisegment.md deleted file mode 100644 index ca6d39f..0000000 --- a/docs/reference-api-urisegment.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -id: api-urisegment -title: "@api-urisegment" ---- - -This allows you to describe the segments, or parameters, of a particular resource action URI. - -## Syntax -```php -@api-urisegment {uri} segmentName (type) - Description - + Members - - `option` - Value description -``` - -## Requirements - -| Required? | Needs a visibility | Supports versioning | Supports deprecation | -| :--- | :--- | :--- | :--- | -| × | × | × | × | - -## Breakdown - -| Tag | Optional | Description | -| :--- | :--- | :--- | -| uri | × | This is the corresponding [`@api-uri`](reference-api-uri.md) URI that this segment/parameter exists on. | -| segmentName | × | This is the name of the parameter that is used within the URI. | -| type | × | This can be a reference to the type of variable that is being passed in (string, boolean, array, etc.), into the accompanying URI. | -| description | × | Description of what the parameter is for. | -| Members | ✓ | If this URI segment has acceptable values (like in the case of an `enum` type), you can document those values here along with a description for what the value is, or means. | - -### Supported Types - -| Type | API Blueprint conversion | -| :--- | :--- | -| array | array | -| boolean | boolean | -| date | string | -| datetime | string | -| float | number | -| enum | enum | -| integer | number | -| number | number | -| object | object | -| string | string | -| timestamp | string | -| uri | string | - -## Examples -```php -/** - * … - * - * @api-uri:private:deprecated {Movies} /movies/+id - * @api-urisegment {/movies/+id} id (integer) - Movie ID - * - * … - */ -public function PATCH() -{ - … -} -``` diff --git a/docs/reference-api-vendortag.md b/docs/reference-api-vendortag.md new file mode 100644 index 0000000..b604202 --- /dev/null +++ b/docs/reference-api-vendortag.md @@ -0,0 +1,57 @@ +--- +id: "api-vendortag" +title: "@api-vendortag" +--- + +This defines a vendor tag in your API. With these, you can specify additional metadata (requirements specific to your API, notes like "requiresAUser", or anything else you can think of) on your resource actions or representation fields. + +With Mill's generator commands, you can also later filter down your documentation to only those that have specific vendor tags. + +## Syntax +```php +@api-vendortag vendorTagName +``` + +## Requirements + +| Required? | Needs a visibility | Supports versioning | Supports deprecation | +| :--- | :--- | :--- | :--- | +| × | × | × | × | + +## Breakdown + +| Tag | Optional | Description | +| :--- | :--- | :--- | +| vendortag | × | Name of the vendor tag | + +## Examples +On a resource action: + +```php +/** + * … + * @api-vendortag needs:SomeApplicationFeature + * … + */ +public function PATCH() +{ + … +} +``` + +On a representation field: + +```php +$representation = [ + … + + /** + * @api-data download (boolean, needs:SomeApplicationFeature) - Download + * permission setting + */ + 'download' => true, + + … +]; +``` + diff --git a/docs/reference-deprecation.md b/docs/reference-deprecation.md index 5b891a8..e0b8a8d 100644 --- a/docs/reference-deprecation.md +++ b/docs/reference-deprecation.md @@ -3,30 +3,28 @@ id: deprecation title: "Deprecation" --- -> **Note:** Deprecated status is not currently being used when generating documentation, however there are [plans](https://github.com/vimeo/mill/milestones) to hook it up to the internal generator system to make it available in your compiled API Blueprints and manual Mill API usages. +> **Note:** Deprecated status is not currently being used when compiling documentation, however there are [plans](https://github.com/vimeo/mill/milestones) to make this available. ## Usage -You might have instances where you need to deprecate a resource action request parameter or URI, you can use the -`:deprecated` "decorator". +You might have instances where you need to deprecate a resource action request parameter or path, you can use the `:deprecated` "decorator". ```php /** * Return information on a specific movie. * * @api-label Get a single movie. + * @api-group Movies * - * @api-uri:public {Movies} /movies/+id - * @api-urisegment {/movies/+id} id (integer) - Movie ID - * - * @api-uri:private:deprecated {Films} /films/+id - * @api-urisegment {/films/+id} id (integer) - Film ID + * @api-path:public /movies/+id + * @api-path:private:deprecated /films/+id + * @api-pathparam id (integer) - Movie ID * * @api-contenttype application/json * @api-scope public * * @api-return:public {object} \MyApplication\Representations\Movie * - * @api-throws:public {404} \MyApplicationRepresentations\Error If the movie + * @api-error:public 404 (\MyApplicationRepresentations\Error) - If the movie * could not be found. */ public function GET() @@ -35,4 +33,4 @@ public function GET() } ``` -Deprecated decorators are only available on [`@api-param`](reference-api-param.md) and [`@api-uri`](reference-api-uri.md). +Deprecated decorators are only available on [`@api-param`](reference-api-param.md), [`@api-queryparam`](reference-api-queryparam.md) and [`@api-path`](reference-api-path.md). diff --git a/docs/reference-resource-actions.md b/docs/reference-resource-actions.md index ba43000..72a967b 100644 --- a/docs/reference-resource-actions.md +++ b/docs/reference-resource-actions.md @@ -5,13 +5,16 @@ title: "Resource Actions" | @annotation | Description | | :--- | :--- | -| [`@api-capability`](reference-api-capability.md) | Denotes a required API capability for the action. | | [`@api-contenttype`](reference-api-contenttype.md) | Denotes the `Content-Type` returned. | +| [`@api-error`](reference-api-error.md) | An exception or error that may be returned on the action. | +| [`@api-group`](reference-api-group.md) | Name of the group that the action should be grouped under when generating documentation. | | [`@api-label`](reference-api-label.md) | Short description of what the resource action handles. | +| [`@api-maxversion`](reference-api-maxversion.md) | Maximum version required for this action. | | [`@api-minversion`](reference-api-minversion.md) | Minimum version required for this action. | -| [`@api-param`](reference-api-param.md) | A request parameter for this action. | +| [`@api-param`](reference-api-param.md) | A request parameter for this action that can be supplied in a body payload. | +| [`@api-path`](reference-api-path.md) | Denotes a path that this action services. | +| [`@api-pathparam`](reference-api-pathparam.md) | Describes parameters for the path. | +| [`@api-queryparam`](reference-api-queryparam.md) | A request parameter for this action that can be supplied in a query string. | | [`@api-return`](reference-api-return.md) | A representation that is returned in a request. | | [`@api-scope`](reference-api-scope.md) | Required authentication token scope necessary for the action. | -| [`@api-throws`](reference-api-throws.md) | An exception or error that may be thrown in the action. | -| [`@api-uri`](reference-api-uri.md) | Denotes a URI that this action services. | -| [`@api-urisegment`](reference-api-urisegment.md) | Describes parameters for the URI. | +| [`@api-vendortag`](reference-api-vendortag.md) | Vendor tag for specifying additional metadata on the action. | diff --git a/docs/reference-versioning.md b/docs/reference-versioning.md index 2323054..9121862 100644 --- a/docs/reference-versioning.md +++ b/docs/reference-versioning.md @@ -25,7 +25,7 @@ On resource actions, `@api-version` is a block-level annotation. This means that * Anything below `@api-version >=3.4` will have `version = >=3.4`. * And anything that doesn't follow am `@api-version` annotation will be treated as being available across all versions. -Versioning is currently only supported on [`@api-param`](reference-api-param.md), [`@api-return`](reference-api-return.md), and [`@api-throws`](reference-api-throws.md). +Versioning is currently only supported on [`@api-param`](reference-api-param.md), [`@api-return`](reference-api-return.md), and [`@api-error`](reference-api-error.md). ### Representations In representations, the [`@api-version`](reference-api-version.md) annotation works as any other annotation. diff --git a/docs/reference-visibility.md b/docs/reference-visibility.md index e2897f7..44391b7 100644 --- a/docs/reference-visibility.md +++ b/docs/reference-visibility.md @@ -3,7 +3,7 @@ id: visibility title: "Visibility" --- -> **Note:** Visibility status is not currently being used when generating API Blueprint specifications, however support is on the Mill [roadmap](https://github.com/vimeo/mill/milestones). +> **Note:** Visibility status is not currently being used when compiling specifications, however support is on the Mill [roadmap](https://github.com/vimeo/mill/milestones). We have the concept of an annotation visibility "decorator" that allows you to set certain annotations as private, or explicitly public. With this additional metadata, you can do cool things like only show certain endpoints or parameters to privileged developers in your documentation. @@ -12,13 +12,13 @@ To choose what visibility your annotation should have, suffix your annotation wi ```php /** - * @api-uri:public {Movies} /movies/+id - * @api-urisegment {/movies/+id} id (integer) - Movie ID - * - * @api-uri:private {Films} /films/+id - * @api-urisegment {/films/+id} id (integer) - Film ID + * @api-group Movies + + * @api-path:public /movies/+id + * @api-path:private /films/+id + * @api-pathparam id (integer) - Movie ID * - * @api-throws:private {403} \Some\ErrorErrorRepresentation If the user isn't + * @api-error:private 403 (\Some\ErrorErrorRepresentation) - If the user isn't * allowed to do something. */ public function PATCH() @@ -27,4 +27,4 @@ public function PATCH() } ``` -Visibility decorators are required on [`@api-param`](reference-api-param.md), [`@api-return`](reference-api-return.md), [`@api-throws`](reference-api-throws.md), and [`@api-uri`](reference-api-uri.md). +Visibility decorators are required on [`@api-param`](reference-api-param.md), [`@api-queryparam`](reference-api-queryparam.md), [`@api-return`](reference-api-return.md), [`@api-error`](reference-api-error.md), and [`@api-path`](reference-api-path.md). diff --git a/docs/writing-documentation.md b/docs/writing-documentation.md index 4f89ace..6d3087e 100644 --- a/docs/writing-documentation.md +++ b/docs/writing-documentation.md @@ -65,19 +65,20 @@ class UsersController extends \MyApplication\Controller * Search for users. * * @api-label Search + * @api-group Users * - * @api-uri:public {Users} /users + * @api-path:public /users * - * @api-contentType application/json + * @api-contenttype application/json * - * @api-param:public page (integer) - The page number to show. - * @api-param:public per_page (integer) - Number of items to show - * on each page. Max 100. - * @api-param:public query (string, required) - Search query. + * @api-queryparam:public page (integer) - The page number to show. + * @api-queryparam:public per_page (integer) - Number of items to show on + * each page. Max 100. + * @api-queryparam:public query (string, required) - Search query. * * @api-return:public {collection} \MyApplication\Representation\User * - * @api-throws:public {503} \MyApplication\Representation\Error If search + * @api-error:public {503} \MyApplication\Representation\Error If search * is disabled. */ public function GET() diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0da6f89 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1743 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@cloudflare/json-schema-walker": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@cloudflare/json-schema-walker/-/json-schema-walker-0.1.1.tgz", + "integrity": "sha512-P3n0hEgk1m6uKWgL4Yb1owzXVG4pM70G4kRnDQxZXiVvfCRtaqiHu+ZRiRPzmwGBiLTO1LWc2yR1M8oz0YkXww==", + "dev": true + }, + "@types/chai": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.3.tgz", + "integrity": "sha512-f5dXGzOJycyzSMdaXVhiBhauL4dYydXwVpavfQ1mVCaGjR56a9QfklXObUxlIY9bGTmCPHEEZ04I16BZ/8w5ww==", + "dev": true + }, + "@types/tapable": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.2.tgz", + "integrity": "sha512-42zEJkBpNfMEAvWR5WlwtTH22oDzcMjFsL9gDGExwF8X8WvAiw7Vwop7hPw03QT8TKfec83LwbHj6SvpqM4ELQ==", + "dev": true + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "dev": true, + "requires": { + "mime-types": "2.1.18", + "negotiator": "0.6.1" + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "api-blueprint-validator-module": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/api-blueprint-validator-module/-/api-blueprint-validator-module-1.2.2.tgz", + "integrity": "sha1-P5mJPGKq3NBCB7T24nGr0VWHAvU=", + "dev": true, + "requires": { + "async": "1.5.2", + "colors": "1.1.2", + "drafter.js": "2.5.1", + "glob": "7.1.1", + "lodash": "4.17.4", + "tv4": "1.3.0" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "drafter.js": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/drafter.js/-/drafter.js-2.5.1.tgz", + "integrity": "sha1-h66djN9PLl7MkjyHF3p5iFhL3aI=", + "dev": true + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + }, + "dependencies": { + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + }, + "dependencies": { + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "dev": true, + "requires": { + "brace-expansion": "1.1.7" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", + "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", + "dev": true, + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + } + } + } + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + }, + "dependencies": { + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + } + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + }, + "tv4": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", + "integrity": "sha1-0CDIRvrdUMhVq7JeuuzGj8EPeWM=", + "dev": true + } + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "content-type": "1.0.4", + "debug": "2.6.9", + "depd": "1.1.2", + "http-errors": "1.6.3", + "iconv-lite": "0.4.19", + "on-finished": "2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "1.6.16" + } + }, + "buffer": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz", + "integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==", + "dev": true, + "requires": { + "base64-js": "1.3.0", + "ieee754": "1.1.12" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==", + "dev": true + }, + "clipboard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.1.tgz", + "integrity": "sha512-7yhQBmtN+uYZmfRjjVjKa0dZdWuabzpSKGtyQZN+9C8xlC788SSJjOHWh7tzurfwTqTD5UDYAhIv5fRJg3sHjQ==", + "dev": true, + "optional": true, + "requires": { + "good-listener": "1.2.2", + "select": "1.1.2", + "tiny-emitter": "2.0.2" + } + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "dev": true + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=", + "dev": true + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "4.1.3", + "shebang-command": "1.2.0", + "which": "1.3.1" + } + }, + "css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=", + "dev": true + }, + "css-to-react-native": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.2.0.tgz", + "integrity": "sha512-SWG8+tsVRBHpxn1cSDmx7B95DJCiKwUecBbboGpm2znDCnJDMGkcoYR73w1p2IZMab6iNqVms8VC+4TrSqoFeQ==", + "dev": true, + "requires": { + "css-color-keywords": "1.0.0", + "fbjs": "0.8.17", + "postcss-value-parser": "3.3.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decko": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decko/-/decko-1.2.0.tgz", + "integrity": "sha1-/UPHNelnuAEzBohKVvvmZZlraBc=", + "dev": true + }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", + "dev": true, + "optional": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=", + "dev": true + }, + "dompurify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-1.0.4.tgz", + "integrity": "sha512-Y/HFiY5NACdpMs8DJQhNCjF8Kj6msjQRLW5fgD628gBk6a2tjZcVN57SF/PvEgogxsrBPXOF0f3d6qNiAdIYoA==", + "dev": true + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "ejs": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", + "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "dev": true, + "requires": { + "iconv-lite": "0.4.19" + } + }, + "es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "dev": true + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + } + }, + "express": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", + "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "dev": true, + "requires": { + "accepts": "1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "1.1.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "2.0.3", + "qs": "6.5.1", + "range-parser": "1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "1.4.0", + "type-is": "1.6.16", + "utils-merge": "1.0.1", + "vary": "1.1.2" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "dev": true, + "requires": { + "core-js": "1.2.7", + "isomorphic-fetch": "2.2.1", + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "promise": "7.3.1", + "setimmediate": "1.0.5", + "ua-parser-js": "0.7.18" + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.4.0", + "unpipe": "1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "2.0.0" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "format-util": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.3.tgz", + "integrity": "sha1-Ay3KShFiYqEsQ/TD7IVmQWxbLZU=", + "dev": true + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "dev": true, + "requires": { + "min-document": "2.19.0", + "process": "0.5.2" + } + }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "dev": true, + "optional": true, + "requires": { + "delegate": "3.2.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "hoist-non-react-statics": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.4.tgz", + "integrity": "sha512-yklXtcYj0Pt5Dz9No8xUh7d+/7fy5XRIm+r7U/BXgwJ/VsD75EfXA8t4p9tIL0jykzo5A/sGzt1xV6oqd/gP0w==", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": "1.4.0" + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "dev": true + }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "ipaddr.js": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", + "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "3.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "dev": true, + "requires": { + "node-fetch": "1.7.3", + "whatwg-fetch": "2.0.4" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.0" + } + }, + "json-pointer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/json-pointer/-/json-pointer-0.6.0.tgz", + "integrity": "sha1-jlAFUKaqxUZKRzN32leqbMIoKNc=", + "dev": true, + "requires": { + "foreach": "2.0.5" + } + }, + "json-schema-ref-parser": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-5.0.3.tgz", + "integrity": "sha512-RCZE9RpvMG2zY+dSSQkqHxsCYaqBYlh/u2PWWRdWMvtaH718YqR73lYu4IW4cGNxJIkUnIgTMuJtp+A1tIn4+w==", + "dev": true, + "requires": { + "call-me-maybe": "1.0.1", + "debug": "3.1.0", + "js-yaml": "3.12.0", + "ono": "4.0.5" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "json-schema-to-openapi-schema": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json-schema-to-openapi-schema/-/json-schema-to-openapi-schema-0.2.0.tgz", + "integrity": "sha512-QL/8QBl2ZkNit+jZUTvWKJPK2UIC//n78+xx/JFyfRkyAF+vyX02wD3cxwtybaN4nZAkIbFIPLJ4OM2qSOdGZA==", + "dev": true, + "requires": { + "@cloudflare/json-schema-walker": "0.1.1" + } + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "1.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + } + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "dev": true, + "requires": { + "js-tokens": "3.0.2" + } + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "lunr": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.2.1.tgz", + "integrity": "sha1-YsYpR7+DYZRQ6Uq/JgYfHOO6qkc=", + "dev": true + }, + "mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha1-GA8fnr74sOY45BZq1S24eb6y/8U=", + "dev": true + }, + "marked": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.18.tgz", + "integrity": "sha512-49i2QYhfULqaXzNZpxC808PisuCTGT2fgG0zrzdCI9N3rIfAWfW0nggvbXr6zvpynZdOG5+9xNxdzP0kwZnERw==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "1.2.0" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, + "requires": { + "mime-db": "1.33.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "dev": true, + "requires": { + "dom-walk": "0.1.1" + } + }, + "mobx": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-4.3.1.tgz", + "integrity": "sha1-M05aq0kWsdQ/D682BaZLG0s8y40=", + "dev": true + }, + "mobx-react": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.2.3.tgz", + "integrity": "sha512-OuSlF2nJEa1PGookZcZnINbvEK4iWNNYiqUh6aebk2AkWxj3sG8OafDOQMcMYApQALTHRsrBIjOx/K8TFxcz7w==", + "dev": true, + "requires": { + "hoist-non-react-statics": "2.5.4", + "react-lifecycles-compat": "3.0.4" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "dev": true, + "requires": { + "encoding": "0.1.12", + "is-stream": "1.1.0" + } + }, + "node-readfiles": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", + "integrity": "sha1-271K8SE04uY1wkXvk//Pb2BnOl0=", + "dev": true, + "requires": { + "es6-promise": "3.3.1" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "2.0.1" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "ono": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/ono/-/ono-4.0.5.tgz", + "integrity": "sha512-ZVNuV9kJbr/2tWs83I2snrYo+WIS0DISF/xUfX9p9b6GyDD6F5N9PzHjW+p/dep6IGwSYylf1HCub5I/nM0R5Q==", + "dev": true, + "requires": { + "format-util": "1.0.3" + } + }, + "openapi-sampler": { + "version": "1.0.0-beta.12", + "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.0.0-beta.12.tgz", + "integrity": "sha512-/6DK47sOfOjjOs0VDp2tkX35fJSRpi2e5FfGSXT42+cCBTbJ3mKX27tkuZM3V9Q6ot0wM0/7x7CCzlNMSFlGyA==", + "dev": true, + "requires": { + "json-pointer": "0.6.0" + } + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "1.3.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "perfect-scrollbar": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.4.0.tgz", + "integrity": "sha512-/2Sk/khljhdrsamjJYS5NjrH+GKEHEwh7zFSiYyxROyYKagkE4kSn2zDQDRTOMo8mpT2jikxx6yI1dG7lNP/hw==", + "dev": true + }, + "polished": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/polished/-/polished-1.9.2.tgz", + "integrity": "sha512-mPocQrVUSiqQdHNZFGL1iHJmsR/etiv05Nf2oZUbya+GMsQkZVEBl5wonN+Sr/e9zQBEhT6yrMjxAUJ06eyocQ==", + "dev": true + }, + "postcss-value-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", + "dev": true + }, + "prismjs": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.14.0.tgz", + "integrity": "sha512-sa2s4m60bXs+kU3jcuUwx3ZCrUH7o0kuqnOOINbODqlRrDB7KY8SRx+xR/D7nHLlgfDdG7zXbRO8wJ+su5Ls0A==", + "dev": true, + "requires": { + "clipboard": "2.0.1" + } + }, + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "requires": { + "asap": "2.0.6" + } + }, + "prop-types": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz", + "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==", + "dev": true, + "requires": { + "fbjs": "0.8.17", + "loose-envify": "1.3.1", + "object-assign": "4.1.1" + } + }, + "proxy-addr": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", + "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", + "dev": true, + "requires": { + "forwarded": "0.1.2", + "ipaddr.js": "1.6.0" + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "dev": true + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "dev": true + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "dev": true, + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.4.0" + } + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", + "dev": true + } + } + }, + "react-dropdown": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/react-dropdown/-/react-dropdown-1.5.0.tgz", + "integrity": "sha512-rRv3a7NiP++yC1rzdjzkviC5ujq759i4SRa0M3C0Cr7loYT4Z3+JhSPekv1/04JiZNXX46cV3/g6A9kS7rkI4Q==", + "dev": true, + "requires": { + "classnames": "2.2.6" + } + }, + "react-hot-loader": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.3.2.tgz", + "integrity": "sha512-om51vlWYUrymKk130b5DndbP4mJWyLdrM65wncoBkqd/ydOS2RxMMnQ8yPi8kcP4nuNXnzrlfx3IVyRhBVQ+Pg==", + "dev": true, + "requires": { + "fast-levenshtein": "2.0.6", + "global": "4.3.2", + "hoist-non-react-statics": "2.5.4", + "prop-types": "15.6.1", + "react-lifecycles-compat": "3.0.4", + "shallowequal": "1.0.2" + } + }, + "react-is": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.4.1.tgz", + "integrity": "sha512-xpb0PpALlFWNw/q13A+1aHeyJyLYCg0/cCHPUA43zYluZuIPHaHL3k8OBsTgQtxqW0FhyDEMvi8fZ/+7+r4OSQ==", + "dev": true + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "dev": true + }, + "react-tabs": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-2.2.2.tgz", + "integrity": "sha512-jZGAAoq1yknr/XF60/2kH5X3UJdR1X8ItcsLZ1mJnHfoniKcCr3shT2TK5wiS2sO0LOmqwX2BtJQX3snKd50sg==", + "dev": true, + "requires": { + "classnames": "2.2.6", + "prop-types": "15.6.1" + } + }, + "redoc": { + "version": "2.0.0-alpha.24", + "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-alpha.24.tgz", + "integrity": "sha512-c7qN2O7laHSQB4MD3D0OTfgVsnSfu6NogejLE1wBC1wyAqEcQdVL3um1RzbMdEGezzUvfh3s/jAjandr5tQ5oQ==", + "dev": true, + "requires": { + "@types/chai": "4.1.3", + "@types/tapable": "1.0.2", + "classnames": "2.2.6", + "decko": "1.2.0", + "dompurify": "1.0.4", + "eventemitter3": "3.1.0", + "json-pointer": "0.6.0", + "json-schema-ref-parser": "5.0.3", + "lunr": "2.2.1", + "mark.js": "8.11.1", + "marked": "0.3.18", + "mobx": "4.3.1", + "mobx-react": "5.2.3", + "openapi-sampler": "1.0.0-beta.12", + "perfect-scrollbar": "1.4.0", + "polished": "1.9.2", + "prismjs": "1.14.0", + "prop-types": "15.6.1", + "react-dropdown": "1.5.0", + "react-hot-loader": "4.3.2", + "react-tabs": "2.2.2", + "slugify": "1.3.0", + "stickyfill": "1.1.1", + "styled-components": "3.3.2", + "swagger2openapi": "2.11.16", + "tslib": "1.9.2" + } + }, + "reftools": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/reftools/-/reftools-0.0.20.tgz", + "integrity": "sha512-xP04tVBgU6IEfVN3AD+jKg0tuDFjzbpAVulI8vFp9jJ6avejnq6BaTj4aneY0sNseXJbAykG6uMDuFJaun1Q9w==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", + "dev": true, + "optional": true + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", + "fresh": "0.5.2", + "http-errors": "1.6.3", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.4.0" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dev": true, + "requires": { + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.2", + "send": "0.16.2" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "shallowequal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.0.2.tgz", + "integrity": "sha512-zlVXeVUKvo+HEv1e2KQF/csyeMKx2oHvatQ9l6XjCUj3agvC8XGf6R9HvIPDSmp8FNPvx7b5kaEJTRi7CqxtEw==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "should": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/should/-/should-13.2.1.tgz", + "integrity": "sha512-l+/NwEMO+DcstsHEwPHRHzC9j4UOE3VQwJGcMWSsD/vqpqHbnQ+1iSHy64Ihmmjx1uiRPD9pFadTSc3MJtXAgw==", + "dev": true, + "requires": { + "should-equal": "2.0.0", + "should-format": "3.0.3", + "should-type": "1.4.0", + "should-type-adaptors": "1.1.0", + "should-util": "1.0.0" + } + }, + "should-equal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", + "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", + "dev": true, + "requires": { + "should-type": "1.4.0" + } + }, + "should-format": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", + "dev": true, + "requires": { + "should-type": "1.4.0", + "should-type-adaptors": "1.1.0" + } + }, + "should-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", + "dev": true + }, + "should-type-adaptors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", + "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", + "dev": true, + "requires": { + "should-type": "1.4.0", + "should-util": "1.0.0" + } + }, + "should-util": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.0.tgz", + "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slugify": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.3.0.tgz", + "integrity": "sha512-vvz+9ANt7CtdTHwJpfrsHOnGkgxky+CUPnvtzDZBZYFo/H/CdZkd5lJL7z7RqtH/x9QW/ItYYfHlcGf38CBK1w==", + "dev": true + }, + "speccy": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/speccy/-/speccy-0.7.2.tgz", + "integrity": "sha512-sTNXKjBWiGJJEhBXLtI1U8yZ/eG0z6NoOqvwZLSgCSjM5vaJrTJ+g3S7POUwpjS7B0GoXE6Is5SIfeav/w+AyQ==", + "dev": true, + "requires": { + "ajv": "5.5.2", + "commander": "2.15.1", + "ejs": "2.6.1", + "express": "4.16.3", + "js-yaml": "3.12.0", + "json-schema-to-openapi-schema": "0.2.0", + "node-fetch": "1.7.3", + "node-readfiles": "0.2.0", + "redoc": "2.0.0-alpha.24", + "reftools": "0.0.20", + "should": "13.2.1" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + }, + "stickyfill": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stickyfill/-/stickyfill-1.1.1.tgz", + "integrity": "sha1-OUE/7p0CXHSn5ZzuyyN4TMDxfwI=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "styled-components": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-3.3.2.tgz", + "integrity": "sha1-CHuWgw7j1g2ai17xfBMrTynMcd8=", + "dev": true, + "requires": { + "buffer": "5.1.0", + "css-to-react-native": "2.2.0", + "fbjs": "0.8.17", + "hoist-non-react-statics": "2.5.4", + "is-plain-object": "2.0.4", + "prop-types": "15.6.1", + "react-is": "16.4.1", + "stylis": "3.5.0", + "stylis-rule-sheet": "0.0.10", + "supports-color": "3.2.3" + } + }, + "stylis": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.0.tgz", + "integrity": "sha512-pP7yXN6dwMzAR29Q0mBrabPCe0/mNO1MSr93bhay+hcZondvMMTpeGyd8nbhYJdyperNT2DRxONQuUGcJr5iPw==", + "dev": true + }, + "stylis-rule-sheet": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz", + "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + }, + "swagger2openapi": { + "version": "2.11.16", + "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-2.11.16.tgz", + "integrity": "sha512-5Pv20whg9Bn1dqKDuGatH0r+7mAEU8+tHJU1PdT9ufGeZNseqJBs9Y8AOd8EMEHKRuAgAzYYWhINvgtodAlZRA==", + "dev": true, + "requires": { + "ajv": "5.5.2", + "call-me-maybe": "1.0.1", + "co": "4.6.0", + "js-yaml": "3.12.0", + "node-fetch": "2.1.2", + "node-readfiles": "0.2.0", + "reftools": "0.0.20", + "should": "13.2.1", + "yargs": "11.0.0" + }, + "dependencies": { + "node-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz", + "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=", + "dev": true + } + } + }, + "tiny-emitter": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz", + "integrity": "sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==", + "dev": true, + "optional": true + }, + "tslib": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz", + "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw==", + "dev": true + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.18" + } + }, + "ua-parser-js": { + "version": "0.7.18", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz", + "integrity": "sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "whatwg-fetch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + } + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz", + "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", + "dev": true, + "requires": { + "cliui": "4.1.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "9.0.2" + } + }, + "yargs-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "dev": true, + "requires": { + "camelcase": "4.1.0" + } + } + } +} diff --git a/package.json b/package.json index db6c1a9..bbee8e1 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,11 @@ { "scripts": { - "test": "find resources/examples/Showtimes/blueprints/ -name 'api.apib' -exec api-blueprint-validator-module {} true \\;" + "test": "npm run test-openapi; npm run test-apiblueprint", + "test-openapi": "find resources/examples/Showtimes/compiled -name 'api.yaml' -exec speccy lint {} true \\;", + "test-apiblueprint": "find resources/examples/Showtimes/compiled/ -name 'api.apib' -exec api-blueprint-validator-module {} true \\;" }, "devDependencies": { - "api-blueprint-validator-module": "^1.2.2" + "api-blueprint-validator-module": "^1.2.2", + "speccy": "^0.7.2" } } diff --git a/psalm.xml b/psalm.xml index 7d3c92e..cf9c2df 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,8 +1,5 @@ - + @@ -20,6 +17,8 @@ + + diff --git a/resources/examples/Showtimes/Controllers/Movie.php b/resources/examples/Showtimes/Controllers/Movie.php index 1a417e5..8889f2f 100644 --- a/resources/examples/Showtimes/Controllers/Movie.php +++ b/resources/examples/Showtimes/Controllers/Movie.php @@ -25,27 +25,26 @@ class Movie * ``` * * @api-label Get a single movie. + * @api-group Movies * - * @api-uri:private:alias {Movies} /movie/+id - * @api-uriSegment {/movie/+id} id (integer) - Movie ID - * - * @api-uri:public {Movies} /movies/+id - * @api-uriSegment {/movies/+id} id (integer) - Movie ID + * @api-path:private:alias /movie/+movie_id + * @api-path:public /movies/+movie_id + * @api-pathparam movie_id `1234` (integer) - Movie ID * * @api-return:public {object} \Mill\Examples\Showtimes\Representations\Movie * @api-return:public {notmodified} If no content has been modified since the supplied Last-Modified header. * - * @api-throws:public {404} \Mill\Examples\Showtimes\Representations\Error If the movie could not be found. + * @api-error:public 404 (\Mill\Examples\Showtimes\Representations\Error) - If the movie could not be found. * * @api-version >=1.1.2 - * @api-contentType application/mill.example.movie + * @api-contenttype application/mill.example.movie+json * * @api-version <1.1.2 - * @api-contentType application/json + * @api-contenttype application/json * * @api-version >=1.1.3 - * @api-throws:public {404} \Mill\Examples\Showtimes\Representations\Error For no reason. - * @api-throws:public {404} \Mill\Examples\Showtimes\Representations\Error For some other reason. + * @api-error:public 404 (\Mill\Examples\Showtimes\Representations\Error) - For no reason. + * @api-error:public 404 (\Mill\Examples\Showtimes\Representations\Error) - For some other reason. */ public function GET() { @@ -56,57 +55,59 @@ public function GET() * Update a movies data. * * @api-label Update a movie. + * @api-group Movies * - * @api-uri:public {Movies} /movies/+id - * @api-uriSegment {/movies/+id} id (integer) - Movie ID + * @api-path:public /movies/+id + * @api-pathparam id `1234` (integer) - Movie ID * * @api-scope edit - * @api-minVersion 1.1 + * @api-minversion 1.1 * - * @api-param:public name (string, required) - Name of the movie. + * @api-param:public name `Demons` (string, required) - Name of the movie. * @api-param:public description (string, required) - Description, or tagline, for the movie. - * @api-param:public runtime (string, optional) - Movie runtime, in `HHhr MMmin` format. - * @api-param:public content_rating (enum, optional) - MPAA rating + * @api-param:public runtime `1hr 20min` (string, optional) - Movie runtime, in `HHhr MMmin` format. + * @api-param:public content_rating `NR` (enum, optional) - MPAA rating * + Members - * - `G` - * - `PG` - * - `PG-13` - * - `R` - * - `NC-17` - * - `X` - * - `NR` - * - `UR` + * - `G` - Rated G + * - `PG` - Rated PG + * - `PG-13` - Rated PG-13 + * - `R` - Rated R + * - `NC-17` - Rated NC-17 + * - `X` - Rated X + * - `NR` - Not rated + * - `UR` - Unrated * @api-param:public genres (array, optional) - Array of movie genres. - * @api-param:public trailer (string, optional, nullable) - Trailer URL - * @api-param:public director (string, optional) - Name of the director. - * @api-param:public cast (array, optional) - Array of names of the cast. + * @api-param:public trailer `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, optional, nullable) - Trailer + * URL + * @api-param:public director `Lamberto Bava` (string, optional) - Name of the director. + * @api-param:public cast (array, optional) - Array of cast members. + * @api-param:public cast.name `Natasha Hovey` (string, optional) - Cast member name. + * @api-param:public cast.role `Cheryl` (string, optional) - Cast member role. * @api-param:public is_kid_friendly (boolean, optional) - Is this movie kid friendly? - * @api-param:public rotten_tomatoes_score (integer, optional) - Rotten Tomatoes score + * @api-param:public rotten_tomatoes_score `56` (integer, optional) - Rotten Tomatoes score * * @api-return:public {object} \Mill\Examples\Showtimes\Representations\Movie * - * @api-throws:public {400} \Mill\Examples\Showtimes\Representations\Error If there is a problem with the - * request. - * @api-throws:public {400} \Mill\Examples\Showtimes\Representations\Error If the IMDB URL could not be validated. - * @api-throws:public {404} \Mill\Examples\Showtimes\Representations\Error If the movie could not be found. + * @api-error:public 400 (\Mill\Examples\Showtimes\Representations\Error) - If there is a problem with the request. + * @api-error:public 400 (\Mill\Examples\Showtimes\Representations\Error) - If the IMDB URL could not be validated. + * @api-error:public 404 (\Mill\Examples\Showtimes\Representations\Error) - If the movie could not be found. * * @api-version >=1.1.2 - * @api-contentType application/mill.example.movie + * @api-contenttype application/mill.example.movie+json * * @api-version <1.1.2 - * @api-contentType application/json + * @api-contenttype application/json * * @api-version >=1.1.1 - * @api-param:public imdb (string, optional) - IMDB URL + * @api-param:public imdb `https://www.imdb.com/title/tt0089013/` (string, optional) - IMDB URL * * @api-version >=1.1.3 * @api-return:public {accepted} \Mill\Examples\Showtimes\Representations\Movie - * @api-throws:public {404} \Mill\Examples\Showtimes\Representations\Error If the trailer URL could not be + * @api-error:public 404 (\Mill\Examples\Showtimes\Representations\Error) - If the trailer URL could not be * validated. - * @api-throws:private {403} \Mill\Examples\Showtimes\Representations\CodedError (1337) If something cool happened. - * @api-throws:public {403} \Mill\Examples\Showtimes\Representations\CodedError - * (Mill\Examples\Showtimes\Representations\CodedError::DISALLOWED) If the user is not allowed to edit that - * movie. + * @api-error:private 403 (\Mill\Examples\Showtimes\Representations\CodedError<1337>) - If something cool happened. + * @api-error:public 403 (\Mill\Examples\Showtimes\Representations\CodedError<666>) - If the user is not allowed to + * edit that movie. */ public function PATCH() { @@ -117,18 +118,20 @@ public function PATCH() * Delete a movie. * * @api-label Delete a movie. + * @api-group Movies * - * @api-uri:private {Movies} /movies/+id - * @api-uriSegment {/movies/+id} id (integer) - Movie ID + * @api-path:private /movies/+id + * @api-pathparam id `1234` (integer) - Movie ID * - * @api-contentType application/json - * @api-capability DELETE_CONTENT + * @api-contenttype application/json + * @api-vendortag tag:DELETE_CONTENT * @api-scope delete * @api-minVersion 1.1 + * @api-maxVersion 1.1.2 * * @api-return:private {deleted} * - * @api-throws:private {404} \Mill\Examples\Showtimes\Representations\Error If the movie could not be found. + * @api-error:private 404 (\Mill\Examples\Showtimes\Representations\Error) - If the movie could not be found. */ public function DELETE() { diff --git a/resources/examples/Showtimes/Controllers/Movies.php b/resources/examples/Showtimes/Controllers/Movies.php index c99c3bd..e6ee6ee 100644 --- a/resources/examples/Showtimes/Controllers/Movies.php +++ b/resources/examples/Showtimes/Controllers/Movies.php @@ -10,23 +10,24 @@ class Movies * Returns all movies for a specific location. * * @api-label Get movies. + * @api-group Movies * - * @api-uri:public {Movies} /movies + * @api-path:public /movies * - * @api-param:public location (string, required) - Location you want movies for. + * @api-queryparam:public location (string, required) - Location you want movies for. * * @api-return:public {collection} \Mill\Examples\Showtimes\Representations\Movie * - * @api-throws:public {400} \Mill\Examples\Showtimes\Representations\Error If the location is invalid. + * @api-error:public 400 (\Mill\Examples\Showtimes\Representations\Error) - If the location is invalid. * * @api-version >=1.1.2 - * @api-contentType application/mill.example.movie + * @api-contentType application/mill.example.movie+json * * @api-version <1.1.2 * @api-contentType application/json * * @api-version >=1.1 - * @api-param:public page (integer, optional) - Page of results to pull. + * @api-queryparam:public page (integer, optional) - Page of results to pull. */ public function GET() { @@ -37,45 +38,48 @@ public function GET() * Create a new movie. * * @api-label Create a movie. + * @api-group Movies * - * @api-uri:public {Movies} /movies + * @api-path:public /movies * * @api-scope create * - * @api-param:public name (string, required) - Name of the movie. + * @api-param:public name `Demons` (string, required) - Name of the movie. * @api-param:public description (string, required) - Description, or tagline, for the movie. - * @api-param:public runtime (string, optional) - Movie runtime, in `HHhr MMmin` format. - * @api-param:public content_rating (string, optional) - MPAA rating + * @api-param:public runtime `1hr 20min` (string, optional) - Movie runtime, in `HHhr MMmin` format. + * @api-param:public content_rating `NR` (enum, optional) - MPAA rating * + Members - * - `G` - * - `PG` - * - `PG-13` - * - `R` - * - `NC-17` - * - `X` - * - `NR` - * - `UR` + * - `G` - Rated G + * - `PG` - Rated PG + * - `PG-13` - Rated PG-13 + * - `R` - Rated R + * - `NC-17` - Rated NC-17 + * - `X` - Rated X + * - `NR` - Not rated + * - `UR` - Unrated * @api-param:public genres (array, optional) - Array of movie genres. - * @api-param:public director (string, optional) - Name of the director. - * @api-param:public cast (array, optional) - Array of names of the cast. + * @api-param:public director `Lamberto Bava` (string, optional) - Name of the director. + * @api-param:public cast (array, optional) - Array of cast members. + * @api-param:public cast.name `Natasha Hovey` (string, optional) - Cast member name. + * @api-param:public cast.role `Cheryl` (string, optional) - Cast member role. * @api-param:public is_kid_friendly (boolean, optional) - Is this movie kid friendly? - * @api-param:public rotten_tomatoes_score (integer, optional) - Rotten Tomatoes score + * @api-param:public rotten_tomatoes_score `56` (integer, optional) - Rotten Tomatoes score * * @api-return:public {object} \Mill\Examples\Showtimes\Representations\Movie * - * @api-throws:public {400} \Mill\Examples\Showtimes\Representations\Error If there is a problem with the - * request. - * @api-throws:public {400} \Mill\Examples\Showtimes\Representations\Error If the IMDB URL could not be validated. + * @api-error:public 400 (\Mill\Examples\Showtimes\Representations\Error) - If there is a problem with the request. + * @api-error:public 400 (\Mill\Examples\Showtimes\Representations\Error) - If the IMDB URL could not be validated. * * @api-version >=1.1.2 - * @api-contentType application/mill.example.movie + * @api-contenttype application/mill.example.movie+json * * @api-version <1.1.2 - * @api-contentType application/json + * @api-contenttype application/json * * @api-version >=1.1 - * @api-param:public imdb (string, optional) - IMDB URL - * @api-param:public trailer (string, optional, nullable) - Trailer URL + * @api-param:public imdb `https://www.imdb.com/title/tt0089013/` (string, optional) - IMDB URL + * @api-param:public trailer `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, optional, nullable) - Trailer + * URL * * @api-version >=1.1.3 * @api-return:public {created} diff --git a/resources/examples/Showtimes/Controllers/Theater.php b/resources/examples/Showtimes/Controllers/Theater.php index 00dd96a..ccaea0f 100644 --- a/resources/examples/Showtimes/Controllers/Theater.php +++ b/resources/examples/Showtimes/Controllers/Theater.php @@ -14,21 +14,21 @@ class Theater * Return information on a specific movie theater. * * @api-label Get a single movie theater. + * @api-group Theaters * - * @api-uri:public {Theaters} /theaters/+id - * @api-uriSegment {/theaters/+id} id (integer) - Theater ID + * @api-path:public /theaters/+id + * @api-pathparam id `1234` (integer) - Theater ID * * @api-return:public {object} \Mill\Examples\Showtimes\Representations\Theater * @api-return:public {notmodified} If no content has been modified since the supplied Last-Modified header. * - * @api-throws:public {404} \Mill\Examples\Showtimes\Representations\Error If the movie theater could not be - * found. + * @api-error:public 404 (\Mill\Examples\Showtimes\Representations\Error) - If the movie theater could not be found. * * @api-version >=1.1.2 - * @api-contentType application/mill.example.theater + * @api-contenttype application/mill.example.theater+json * * @api-version <1.1.2 - * @api-contentType application/json + * @api-contenttype application/json */ public function GET() { @@ -39,29 +39,28 @@ public function GET() * Update a movie theaters' data. * * @api-label Update a movie theater. + * @api-group Theaters * - * @api-uri:public {Theaters} /theaters/+id - * @api-uriSegment {/theaters/+id} id (integer) - Theater ID + * @api-path:public /theaters/+id + * @api-pathparam id `1234` (integer) - Theater ID * * @api-scope create * - * @api-param:public name (string, required) - Name of the theater. - * @api-param:public address (string, required) - Theater address - * @api-param:public phone_number (string, required) - Theater phone number + * @api-param:public name `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + * @api-param:public address `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + * @api-param:public phone_number `(914) 226-3082` (string, required) - Theater phone number * * @api-return:public {object} \Mill\Examples\Showtimes\Representations\Theater * - * @api-throws:public {400} \Mill\Examples\Showtimes\Representations\Error If there is a problem with the - * request. - * @api-throws:public {404} \Mill\Examples\Showtimes\Representations\Error If the movie movie could not be - * found. + * @api-error:public 400 (\Mill\Examples\Showtimes\Representations\Error) - If there is a problem with the request. + * @api-error:public 404 (\Mill\Examples\Showtimes\Representations\Error) - If the movie movie could not be found. * * @api-version >=1.1.2 - * @api-contentType application/mill.example.theater + * @api-contenttype application/mill.example.theater+json * * @api-version <1.1.2 - * @api-contentType application/json - * @api-throws:public {403} \Mill\Examples\Showtimes\Representations\CodedError (1337) If something cool happened. + * @api-contenttype application/json + * @api-error:public 403 (\Mill\Examples\Showtimes\Representations\CodedError<1337>) - If something cool happened. */ public function PATCH() { @@ -72,16 +71,17 @@ public function PATCH() * Delete a movie theater. * * @api-label Delete a movie theater. + * @api-group Theaters * - * @api-uri:private {Theaters} /theaters/+id - * @api-uriSegment {/theaters/+id} id (integer) - Theater ID + * @api-path:private /theaters/+id + * @api-pathparam id `1234` (integer) - Theater ID * - * @api-contentType application/json + * @api-contenttype application/json * @api-scope delete * * @api-return:private {deleted} * - * @api-throws:private {404} \Mill\Examples\Showtimes\Representations\Error If the movie theater could not be + * @api-error:private 404 (\Mill\Examples\Showtimes\Representations\Error) - If the movie theater could not be * found. */ public function DELETE() diff --git a/resources/examples/Showtimes/Controllers/Theaters.php b/resources/examples/Showtimes/Controllers/Theaters.php index 20bbf3a..6a1c3f1 100644 --- a/resources/examples/Showtimes/Controllers/Theaters.php +++ b/resources/examples/Showtimes/Controllers/Theaters.php @@ -10,20 +10,21 @@ class Theaters * Returns all movie theatres for a specific location. * * @api-label Get movie theaters. + * @api-group Theaters * - * @api-uri:public {Theaters} /theaters + * @api-path:public /theaters * - * @api-param:public location (string, required) - Location you want theaters in. + * @api-queryparam:public location (string, required) - Location you want theaters in. * * @api-return:public {collection} \Mill\Examples\Showtimes\Representations\Theater * - * @api-throws:public {400} \Mill\Examples\Showtimes\Representations\Error If the location is invalid. + * @api-error:public 400 (\Mill\Examples\Showtimes\Representations\Error) - If the location is invalid. * * @api-version >=1.1.2 - * @api-contentType application/mill.example.theater + * @api-contenttype application/mill.example.theater+json * * @api-version <1.1.2 - * @api-contentType application/json + * @api-contenttype application/json */ public function GET() { @@ -34,25 +35,25 @@ public function GET() * Create a new movie theater. * * @api-label Create a movie theater. + * @api-group Theaters * - * @api-uri:public {Theaters} /theaters + * @api-path:public /theaters * * @api-scope create * - * @api-param:public name (string, required) - Name of the theater. - * @api-param:public address (string, required) - Theater address - * @api-param:public phone_number (string, required) - Theater phone number + * @api-param:public name `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + * @api-param:public address `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + * @api-param:public phone_number `(914) 226-3082` (string, required) - Theater phone number * * @api-return:public {object} \Mill\Examples\Showtimes\Representations\Theater * - * @api-throws:public {400} \Mill\Examples\Showtimes\Representations\Error If there is a problem with the - * request. + * @api-error:public 400 (\Mill\Examples\Showtimes\Representations\Error) - If there is a problem with the request. * * @api-version >=1.1.2 - * @api-contentType application/mill.example.theater + * @api-contenttype application/mill.example.theater+json * * @api-version <1.1.2 - * @api-contentType application/json + * @api-contenttype application/json */ public function POST() { diff --git a/resources/examples/Showtimes/Representations/Movie.php b/resources/examples/Showtimes/Representations/Movie.php index df2f803..df7d3fc 100644 --- a/resources/examples/Showtimes/Representations/Movie.php +++ b/resources/examples/Showtimes/Representations/Movie.php @@ -53,7 +53,7 @@ public function create() 'rating' => $this->movie->rating, /** - * @api-data genres (array) - Genres + * @api-data genres (array) - Genres */ 'genres' => $this->movie->getGenres(), @@ -86,12 +86,18 @@ public function create() 'showtimes' => $this->getShowtimes(), /** - * @api-data external_urls (object) - External URLs + * @api-scope public + * @api-data external_urls (array) - External URLs * @api-version >=1.1 * @api-see self::getExternalUrls external_urls */ 'external_urls' => $this->getExternalUrls(), + /** + * @api-data external_urls.imdb (string) - IMDB URL + */ + 'imdb' => $this->movie->imdb, + /** * @api-data rotten_tomatoes_score (number) - Rotten Tomatoes score */ @@ -112,18 +118,13 @@ public function create() private function getExternalUrls() { return [ - /** - * @api-data imdb (string) - IMDB URL - */ - 'imdb' => $this->movie->imdb, - /** * @api-data trailer (string) - Trailer URL */ 'trailer' => $this->movie->trailer, /** - * @api-data tickets (string, BUY_TICKETS) - Tickets URL + * @api-data tickets (string, tag:BUY_TICKETS) - Tickets URL * @api-version <1.1.3 */ 'tickets' => $this->movie->tickets_url diff --git a/resources/examples/Showtimes/blueprints/1.1.1/errors-public-only-all-capabilities.md b/resources/examples/Showtimes/blueprints/1.1.1/errors-public-only-all-capabilities.md deleted file mode 100644 index 2e55e09..0000000 --- a/resources/examples/Showtimes/blueprints/1.1.1/errors-public-only-all-capabilities.md +++ /dev/null @@ -1,6 +0,0 @@ -# Errors: Mill unit test API, Showtimes - -## Theaters -| Error Code | URI | Method | HTTP Code | Description | -| :--- | :--- | :--- | :--- | :--- | -| 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1.1/errors-public-only-matched-capabilities.md b/resources/examples/Showtimes/blueprints/1.1.1/errors-public-only-matched-capabilities.md deleted file mode 100644 index 2e55e09..0000000 --- a/resources/examples/Showtimes/blueprints/1.1.1/errors-public-only-matched-capabilities.md +++ /dev/null @@ -1,6 +0,0 @@ -# Errors: Mill unit test API, Showtimes - -## Theaters -| Error Code | URI | Method | HTTP Code | Description | -| :--- | :--- | :--- | :--- | :--- | -| 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1.1/errors-public-only-unmatched-capabilities.md b/resources/examples/Showtimes/blueprints/1.1.1/errors-public-only-unmatched-capabilities.md deleted file mode 100644 index 2e55e09..0000000 --- a/resources/examples/Showtimes/blueprints/1.1.1/errors-public-only-unmatched-capabilities.md +++ /dev/null @@ -1,6 +0,0 @@ -# Errors: Mill unit test API, Showtimes - -## Theaters -| Error Code | URI | Method | HTTP Code | Description | -| :--- | :--- | :--- | :--- | :--- | -| 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1/errors-public-only-all-capabilities.md b/resources/examples/Showtimes/blueprints/1.1/errors-public-only-all-capabilities.md deleted file mode 100644 index 2e55e09..0000000 --- a/resources/examples/Showtimes/blueprints/1.1/errors-public-only-all-capabilities.md +++ /dev/null @@ -1,6 +0,0 @@ -# Errors: Mill unit test API, Showtimes - -## Theaters -| Error Code | URI | Method | HTTP Code | Description | -| :--- | :--- | :--- | :--- | :--- | -| 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1/errors-public-only-matched-capabilities.md b/resources/examples/Showtimes/blueprints/1.1/errors-public-only-matched-capabilities.md deleted file mode 100644 index 2e55e09..0000000 --- a/resources/examples/Showtimes/blueprints/1.1/errors-public-only-matched-capabilities.md +++ /dev/null @@ -1,6 +0,0 @@ -# Errors: Mill unit test API, Showtimes - -## Theaters -| Error Code | URI | Method | HTTP Code | Description | -| :--- | :--- | :--- | :--- | :--- | -| 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1/errors-public-only-unmatched-capabilities.md b/resources/examples/Showtimes/blueprints/1.1/errors-public-only-unmatched-capabilities.md deleted file mode 100644 index 2e55e09..0000000 --- a/resources/examples/Showtimes/blueprints/1.1/errors-public-only-unmatched-capabilities.md +++ /dev/null @@ -1,6 +0,0 @@ -# Errors: Mill unit test API, Showtimes - -## Theaters -| Error Code | URI | Method | HTTP Code | Description | -| :--- | :--- | :--- | :--- | :--- | -| 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1/representations/Movie.apib b/resources/examples/Showtimes/blueprints/1.1/representations/Movie.apib deleted file mode 100644 index eaf3141..0000000 --- a/resources/examples/Showtimes/blueprints/1.1/representations/Movie.apib +++ /dev/null @@ -1,29 +0,0 @@ -## Movie -- `cast` (array[Person]) - Cast. This data requires a bearer token with the `public` scope. -- `content_rating`: `G` (enum[string]) - MPAA rating - + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` -- `description` (string) - Description -- `director` (Person) - Director. This data requires a bearer token with the `public` scope. -- `external_urls` (object) - External URLs - - `imdb` (string) - IMDB URL - - `tickets` (string) - Tickets URL - - `trailer` (string) - Trailer URL -- `genres` (array) - Genres -- `id` (number) - Unique ID -- `kid_friendly`: `false` (boolean) - Kid friendly? -- `name` (string) - Name -- `purchase` (object) - - `url` (string) - URL to purchase the film. -- `rotten_tomatoes_score` (number) - Rotten Tomatoes score -- `runtime` (string) - Runtime -- `showtimes` (array) - Non-theater specific showtimes -- `theaters` (array[Theater]) - Theaters the movie is currently showing in -- `uri` (string) - Movie URI \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.0/api.apib b/resources/examples/Showtimes/compiled/1.0/api.apib similarity index 81% rename from resources/examples/Showtimes/blueprints/1.0/api.apib rename to resources/examples/Showtimes/compiled/1.0/api.apib index 92c0546..c0e7603 100644 --- a/resources/examples/Showtimes/blueprints/1.0/api.apib +++ b/resources/examples/Showtimes/compiled/1.0/api.apib @@ -81,24 +81,27 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + Response 200 (application/json) + Attributes (Movie) + Response 400 (application/json) @@ -133,9 +136,9 @@ This action requires a bearer token with the `create` scope. - `id` (number, required) - Theater ID + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number + Response 200 (application/json) + Attributes (Theater) + Response 400 (application/json) @@ -179,9 +182,9 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number + Response 200 (application/json) + Attributes (Theater) + Response 400 (application/json) @@ -209,7 +212,7 @@ This action requires a bearer token with the `create` scope. + `X` - `description` (string) - Description - `director` (Person) - Director. This data requires a bearer token with the `public` scope. -- `genres` (array) - Genres +- `genres` (array[string]) - Genres - `id` (number) - Unique ID - `kid_friendly`: `false` (boolean) - Kid friendly? - `name` (string) - Name diff --git a/resources/examples/Showtimes/compiled/1.0/api.yaml b/resources/examples/Showtimes/compiled/1.0/api.yaml new file mode 100644 index 0000000..2c1983b --- /dev/null +++ b/resources/examples/Showtimes/compiled/1.0/api.yaml @@ -0,0 +1,566 @@ +openapi: 3.0.0 +info: + title: 'Mill unit test API, Showtimes' + version: '1.0' + contact: + name: 'Get help!' + email: support@example.com + url: 'https://developer.example.com/help' +tags: + - + name: Movies + - + name: Theaters +servers: + - + url: 'https://api.example.com' + description: Production + - + url: 'https://api.example.local' + description: Development +paths: + '/movie/{id}': + get: + summary: 'Get a single movie.' + description: "Return information on a specific movie.\n\nDonec id elit non mi porta gravida at eget metus. Cras mattis consectetur purus sit amet fermentum. Lorem\nipsum dolor sit amet, consectetur adipiscing elit. Etiam porta sem malesuada magna mollis euismod. Duis\nmollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Etiam porta\nsem malesuada magna mollis euismod.\n\n```\n[\n {\"id\": \"fizzbuzz\"}\n]\n```" + operationId: getMovieId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/movie' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: 'If the movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + '/movies/{id}': + get: + summary: 'Get a single movie.' + description: "Return information on a specific movie.\n\nDonec id elit non mi porta gravida at eget metus. Cras mattis consectetur purus sit amet fermentum. Lorem\nipsum dolor sit amet, consectetur adipiscing elit. Etiam porta sem malesuada magna mollis euismod. Duis\nmollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Etiam porta\nsem malesuada magna mollis euismod.\n\n```\n[\n {\"id\": \"fizzbuzz\"}\n]\n```" + operationId: getMoviesId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/movie' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: 'If the movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + /movies: + get: + summary: 'Get movies.' + description: 'Returns all movies for a specific location.' + operationId: getMovies + tags: + - Movies + parameters: + - + name: location + in: query + description: 'Location you want movies for.' + required: true + schema: + type: string + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/movie' + 400: + description: 'If the location is invalid.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + post: + summary: 'Create a movie.' + description: 'Create a new movie.' + operationId: postMovies + tags: + - Movies + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + cast: + description: 'Array of cast members.' + type: array + items: + type: object + properties: + name: + description: 'Cast member name.' + type: string + example: 'Natasha Hovey' + role: + description: 'Cast member role.' + type: string + example: Cheryl + content_rating: + description: "MPAA rating\n\nOption descriptions:\n * `G` - Rated G\n * `NC-17` - Rated NC-17\n * `NR` - Not rated\n * `PG` - Rated PG\n * `PG-13` - Rated PG-13\n * `R` - Rated R\n * `UR` - Unrated\n * `X` - Rated X\n" + type: string + example: NR + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: 'Description, or tagline, for the movie.' + type: string + director: + description: 'Name of the director.' + type: string + example: 'Lamberto Bava' + genres: + description: 'Array of movie genres.' + type: array + items: + type: string + is_kid_friendly: + description: 'Is this movie kid friendly?' + type: boolean + name: + description: 'Name of the movie.' + type: string + example: Demons + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + example: '56' + runtime: + description: 'Movie runtime, in `HHhr MMmin` format.' + type: string + example: '1hr 20min' + required: + - description + - name + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/movie' + 400: + description: "There are two ways that this status code can be encountered:\n * If there is a problem with the request.\n * If the IMDB URL could not be validated." + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - create + x-mill-visibility-private: true + '/theaters/{id}': + get: + summary: 'Get a single movie theater.' + description: 'Return information on a specific movie theater.' + operationId: getTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/theater' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: 'If the movie theater could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + patch: + summary: 'Update a movie theater.' + description: 'Update a movie theaters'' data.' + operationId: patchTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + address: + description: 'Theater address' + type: string + example: '2548 Central Park Ave, Yonkers, NY 10710' + name: + description: 'Name of the theater.' + type: string + example: 'Alamo Drafthouse Cinema - Yonkers' + phone_number: + description: 'Theater phone number' + type: string + example: '(914) 226-3082' + required: + - address + - name + - phone_number + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/theater' + 400: + description: 'If there is a problem with the request.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + 404: + description: 'If the movie movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + 403: + description: 'If something cool happened. Returns a unique error code of `1337`.' + content: + application/json: + schema: + $ref: '#/components/schemas/coded-error' + security: + - + oauth2: + - create + x-mill-visibility-private: true + delete: + summary: 'Delete a movie theater.' + description: 'Delete a movie theater.' + operationId: deleteTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + responses: + 204: + description: 'Standard request.' + 404: + description: 'If the movie theater could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - delete + /theaters: + get: + summary: 'Get movie theaters.' + description: 'Returns all movie theatres for a specific location.' + operationId: getTheaters + tags: + - Theaters + parameters: + - + name: location + in: query + description: 'Location you want theaters in.' + required: true + schema: + type: string + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/theater' + 400: + description: 'If the location is invalid.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + post: + summary: 'Create a movie theater.' + description: 'Create a new movie theater.' + operationId: postTheaters + tags: + - Theaters + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + address: + description: 'Theater address' + type: string + example: '2548 Central Park Ave, Yonkers, NY 10710' + name: + description: 'Name of the theater.' + type: string + example: 'Alamo Drafthouse Cinema - Yonkers' + phone_number: + description: 'Theater phone number' + type: string + example: '(914) 226-3082' + required: + - address + - name + - phone_number + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/theater' + 400: + description: 'If there is a problem with the request.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - create + x-mill-visibility-private: true +components: + securitySchemes: + bearer: + type: http + scheme: bearer + bearerFormat: bearer + oauth2: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: /oauth/authorize + tokenUrl: /oauth/access_token + scopes: + create: Create + delete: Delete + edit: Edit + public: Public + clientCredentials: + tokenUrl: /oauth/authorize/client + scopes: + create: Create + delete: Delete + edit: Edit + public: Public + schemas: + coded-error: + properties: + error: + description: 'User-friendly error message' + type: string + error_code: + description: 'Error code' + type: number + error: + properties: + error: + description: 'User-friendly error message' + type: string + movie: + properties: + cast: + description: 'Cast. This data requires a bearer token with the `public` scope.' + type: array + items: + type: string + content_rating: + description: 'MPAA rating' + type: string + example: G + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: Description + type: string + director: + description: 'Director. This data requires a bearer token with the `public` scope.' + allOf: + - + $ref: '#/components/schemas/person' + genres: + description: Genres + type: array + items: + type: string + id: + description: 'Unique ID' + type: number + kid_friendly: + description: 'Kid friendly?' + type: boolean + example: 'false' + name: + description: Name + type: string + purchase: + type: object + properties: + url: + description: 'URL to purchase the film.' + type: string + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + runtime: + description: Runtime + type: string + showtimes: + description: 'Non-theater specific showtimes' + type: array + items: + type: string + theaters: + description: 'Theaters the movie is currently showing in' + type: array + items: + type: string + uri: + description: 'Movie URI' + type: string + person: + properties: + id: + description: 'Unique ID' + type: number + imdb: + description: 'IMDB URL' + type: string + name: + description: Name + type: string + uri: + description: 'Person URI' + type: string + theater: + properties: + address: + description: Address + type: string + id: + description: 'Unique ID' + type: number + movies: + description: 'Movies currently playing' + type: array + items: + type: string + name: + description: Name + type: string + phone_number: + description: 'Phone number' + type: string + showtimes: + description: 'Non-movie specific showtimes' + type: array + items: + type: string + uri: + description: 'Theater URI' + type: string + website: + description: Website + type: string +security: + - + oauth2: + - create + - delete + - edit + - public diff --git a/resources/examples/Showtimes/blueprints/1.0/errors-public-only-all-capabilities.md b/resources/examples/Showtimes/compiled/1.0/errors-public-only-all-vendor-tags.md similarity index 74% rename from resources/examples/Showtimes/blueprints/1.0/errors-public-only-all-capabilities.md rename to resources/examples/Showtimes/compiled/1.0/errors-public-only-all-vendor-tags.md index 2e55e09..b0b3d7e 100644 --- a/resources/examples/Showtimes/blueprints/1.0/errors-public-only-all-capabilities.md +++ b/resources/examples/Showtimes/compiled/1.0/errors-public-only-all-vendor-tags.md @@ -1,6 +1,6 @@ # Errors: Mill unit test API, Showtimes ## Theaters -| Error Code | URI | Method | HTTP Code | Description | +| Error Code | Path | Method | HTTP Code | Description | | :--- | :--- | :--- | :--- | :--- | | 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.0/errors-public-only-matched-capabilities.md b/resources/examples/Showtimes/compiled/1.0/errors-public-only-matched-vendor-tags.md similarity index 74% rename from resources/examples/Showtimes/blueprints/1.0/errors-public-only-matched-capabilities.md rename to resources/examples/Showtimes/compiled/1.0/errors-public-only-matched-vendor-tags.md index 2e55e09..b0b3d7e 100644 --- a/resources/examples/Showtimes/blueprints/1.0/errors-public-only-matched-capabilities.md +++ b/resources/examples/Showtimes/compiled/1.0/errors-public-only-matched-vendor-tags.md @@ -1,6 +1,6 @@ # Errors: Mill unit test API, Showtimes ## Theaters -| Error Code | URI | Method | HTTP Code | Description | +| Error Code | Path | Method | HTTP Code | Description | | :--- | :--- | :--- | :--- | :--- | | 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.0/errors-public-only-unmatched-capabilities.md b/resources/examples/Showtimes/compiled/1.0/errors-public-only-unmatched-vendor-tags.md similarity index 74% rename from resources/examples/Showtimes/blueprints/1.0/errors-public-only-unmatched-capabilities.md rename to resources/examples/Showtimes/compiled/1.0/errors-public-only-unmatched-vendor-tags.md index 2e55e09..b0b3d7e 100644 --- a/resources/examples/Showtimes/blueprints/1.0/errors-public-only-unmatched-capabilities.md +++ b/resources/examples/Showtimes/compiled/1.0/errors-public-only-unmatched-vendor-tags.md @@ -1,6 +1,6 @@ # Errors: Mill unit test API, Showtimes ## Theaters -| Error Code | URI | Method | HTTP Code | Description | +| Error Code | Path | Method | HTTP Code | Description | | :--- | :--- | :--- | :--- | :--- | | 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1.1/errors.md b/resources/examples/Showtimes/compiled/1.0/errors.md similarity index 74% rename from resources/examples/Showtimes/blueprints/1.1.1/errors.md rename to resources/examples/Showtimes/compiled/1.0/errors.md index 2e55e09..b0b3d7e 100644 --- a/resources/examples/Showtimes/blueprints/1.1.1/errors.md +++ b/resources/examples/Showtimes/compiled/1.0/errors.md @@ -1,6 +1,6 @@ # Errors: Mill unit test API, Showtimes ## Theaters -| Error Code | URI | Method | HTTP Code | Description | +| Error Code | Path | Method | HTTP Code | Description | | :--- | :--- | :--- | :--- | :--- | | 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.0/representations/Coded error.apib b/resources/examples/Showtimes/compiled/1.0/representations/Coded error.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.0/representations/Coded error.apib rename to resources/examples/Showtimes/compiled/1.0/representations/Coded error.apib diff --git a/resources/examples/Showtimes/blueprints/1.0/representations/Error.apib b/resources/examples/Showtimes/compiled/1.0/representations/Error.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.0/representations/Error.apib rename to resources/examples/Showtimes/compiled/1.0/representations/Error.apib diff --git a/resources/examples/Showtimes/blueprints/1.0/representations/Movie.apib b/resources/examples/Showtimes/compiled/1.0/representations/Movie.apib similarity index 95% rename from resources/examples/Showtimes/blueprints/1.0/representations/Movie.apib rename to resources/examples/Showtimes/compiled/1.0/representations/Movie.apib index 7ea8d18..6348d83 100644 --- a/resources/examples/Showtimes/blueprints/1.0/representations/Movie.apib +++ b/resources/examples/Showtimes/compiled/1.0/representations/Movie.apib @@ -12,7 +12,7 @@ + `X` - `description` (string) - Description - `director` (Person) - Director. This data requires a bearer token with the `public` scope. -- `genres` (array) - Genres +- `genres` (array[string]) - Genres - `id` (number) - Unique ID - `kid_friendly`: `false` (boolean) - Kid friendly? - `name` (string) - Name diff --git a/resources/examples/Showtimes/blueprints/1.0/representations/Person.apib b/resources/examples/Showtimes/compiled/1.0/representations/Person.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.0/representations/Person.apib rename to resources/examples/Showtimes/compiled/1.0/representations/Person.apib diff --git a/resources/examples/Showtimes/blueprints/1.0/representations/Theater.apib b/resources/examples/Showtimes/compiled/1.0/representations/Theater.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.0/representations/Theater.apib rename to resources/examples/Showtimes/compiled/1.0/representations/Theater.apib diff --git a/resources/examples/Showtimes/blueprints/1.0/resources/Movies.apib b/resources/examples/Showtimes/compiled/1.0/resources/Movies.apib similarity index 75% rename from resources/examples/Showtimes/blueprints/1.0/resources/Movies.apib rename to resources/examples/Showtimes/compiled/1.0/resources/Movies.apib index 14dac64..636fd20 100644 --- a/resources/examples/Showtimes/blueprints/1.0/resources/Movies.apib +++ b/resources/examples/Showtimes/compiled/1.0/resources/Movies.apib @@ -76,24 +76,27 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + Response 200 (application/json) + Attributes (Movie) + Response 400 (application/json) diff --git a/resources/examples/Showtimes/blueprints/1.1/resources/Theaters.apib b/resources/examples/Showtimes/compiled/1.0/resources/Theaters.apib similarity index 77% rename from resources/examples/Showtimes/blueprints/1.1/resources/Theaters.apib rename to resources/examples/Showtimes/compiled/1.0/resources/Theaters.apib index 4269a5b..f90eeae 100644 --- a/resources/examples/Showtimes/blueprints/1.1/resources/Theaters.apib +++ b/resources/examples/Showtimes/compiled/1.0/resources/Theaters.apib @@ -24,9 +24,9 @@ This action requires a bearer token with the `create` scope. - `id` (number, required) - Theater ID + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number + Response 200 (application/json) + Attributes (Theater) + Response 400 (application/json) @@ -70,9 +70,9 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number + Response 200 (application/json) + Attributes (Theater) + Response 400 (application/json) diff --git a/resources/examples/Showtimes/blueprints/1.1.1/api.apib b/resources/examples/Showtimes/compiled/1.1.1/api.apib similarity index 70% rename from resources/examples/Showtimes/blueprints/1.1.1/api.apib rename to resources/examples/Showtimes/compiled/1.1.1/api.apib index 1c17a60..b22f1e3 100644 --- a/resources/examples/Showtimes/blueprints/1.1.1/api.apib +++ b/resources/examples/Showtimes/compiled/1.1.1/api.apib @@ -67,26 +67,29 @@ This action requires a bearer token with the `edit` scope. - `id` (number, required) - Movie ID + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL + Response 200 (application/json) + Attributes (Movie) + Response 400 (application/json) @@ -132,26 +135,29 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL + Response 200 (application/json) + Attributes (Movie) + Response 400 (application/json) @@ -186,9 +192,9 @@ This action requires a bearer token with the `create` scope. - `id` (number, required) - Theater ID + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number + Response 200 (application/json) + Attributes (Theater) + Response 400 (application/json) @@ -232,9 +238,9 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number + Response 200 (application/json) + Attributes (Theater) + Response 400 (application/json) @@ -262,11 +268,12 @@ This action requires a bearer token with the `create` scope. + `X` - `description` (string) - Description - `director` (Person) - Director. This data requires a bearer token with the `public` scope. -- `external_urls` (object) - External URLs - - `imdb` (string) - IMDB URL - - `tickets` (string) - Tickets URL - - `trailer` (string) - Trailer URL -- `genres` (array) - Genres +- `external_urls` (array) - External URLs. This data requires a bearer token with the `public` scope. + - (object) + - `imdb` (string) - IMDB URL. This data requires a bearer token with the `public` scope. + - `tickets` (string) - Tickets URL. This data requires a bearer token with the `public` scope. + - `trailer` (string) - Trailer URL. This data requires a bearer token with the `public` scope. +- `genres` (array[string]) - Genres - `id` (number) - Unique ID - `kid_friendly`: `false` (boolean) - Kid friendly? - `name` (string) - Name diff --git a/resources/examples/Showtimes/compiled/1.1.1/api.yaml b/resources/examples/Showtimes/compiled/1.1.1/api.yaml new file mode 100644 index 0000000..9423380 --- /dev/null +++ b/resources/examples/Showtimes/compiled/1.1.1/api.yaml @@ -0,0 +1,736 @@ +openapi: 3.0.0 +info: + title: 'Mill unit test API, Showtimes' + version: 1.1.1 + contact: + name: 'Get help!' + email: support@example.com + url: 'https://developer.example.com/help' +tags: + - + name: Movies + - + name: Theaters +servers: + - + url: 'https://api.example.com' + description: Production + - + url: 'https://api.example.local' + description: Development +paths: + '/movie/{id}': + get: + summary: 'Get a single movie.' + description: "Return information on a specific movie.\n\nDonec id elit non mi porta gravida at eget metus. Cras mattis consectetur purus sit amet fermentum. Lorem\nipsum dolor sit amet, consectetur adipiscing elit. Etiam porta sem malesuada magna mollis euismod. Duis\nmollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Etiam porta\nsem malesuada magna mollis euismod.\n\n```\n[\n {\"id\": \"fizzbuzz\"}\n]\n```" + operationId: getMovieId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/movie' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: 'If the movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + '/movies/{id}': + get: + summary: 'Get a single movie.' + description: "Return information on a specific movie.\n\nDonec id elit non mi porta gravida at eget metus. Cras mattis consectetur purus sit amet fermentum. Lorem\nipsum dolor sit amet, consectetur adipiscing elit. Etiam porta sem malesuada magna mollis euismod. Duis\nmollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Etiam porta\nsem malesuada magna mollis euismod.\n\n```\n[\n {\"id\": \"fizzbuzz\"}\n]\n```" + operationId: getMoviesId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/movie' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: 'If the movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + patch: + summary: 'Update a movie.' + description: 'Update a movies data.' + operationId: patchMoviesId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + cast: + description: 'Array of cast members.' + type: array + items: + type: object + properties: + name: + description: 'Cast member name.' + type: string + example: 'Natasha Hovey' + role: + description: 'Cast member role.' + type: string + example: Cheryl + content_rating: + description: "MPAA rating\n\nOption descriptions:\n * `G` - Rated G\n * `NC-17` - Rated NC-17\n * `NR` - Not rated\n * `PG` - Rated PG\n * `PG-13` - Rated PG-13\n * `R` - Rated R\n * `UR` - Unrated\n * `X` - Rated X\n" + type: string + example: NR + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: 'Description, or tagline, for the movie.' + type: string + director: + description: 'Name of the director.' + type: string + example: 'Lamberto Bava' + genres: + description: 'Array of movie genres.' + type: array + items: + type: string + imdb: + description: 'IMDB URL' + type: string + example: 'https://www.imdb.com/title/tt0089013/' + is_kid_friendly: + description: 'Is this movie kid friendly?' + type: boolean + name: + description: 'Name of the movie.' + type: string + example: Demons + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + example: '56' + runtime: + description: 'Movie runtime, in `HHhr MMmin` format.' + type: string + example: '1hr 20min' + trailer: + description: 'Trailer URL' + type: string + example: 'https://www.youtube.com/watch?v=_cNjTdFHL8E' + nullable: true + required: + - description + - name + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/movie' + 400: + description: "There are two ways that this status code can be encountered:\n * If there is a problem with the request.\n * If the IMDB URL could not be validated." + content: + application/json: + schema: + $ref: '#/components/schemas/error' + 404: + description: 'If the movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - edit + x-mill-visibility-private: true + delete: + summary: 'Delete a movie.' + description: 'Delete a movie.' + operationId: deleteMoviesId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + responses: + 204: + description: 'Standard request.' + 404: + description: 'If the movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - delete + x-mill-vendortags: + - 'tag:DELETE_CONTENT' + /movies: + get: + summary: 'Get movies.' + description: 'Returns all movies for a specific location.' + operationId: getMovies + tags: + - Movies + parameters: + - + name: location + in: query + description: 'Location you want movies for.' + required: true + schema: + type: string + - + name: page + in: query + description: 'Page of results to pull.' + required: false + schema: + type: number + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/movie' + 400: + description: 'If the location is invalid.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + post: + summary: 'Create a movie.' + description: 'Create a new movie.' + operationId: postMovies + tags: + - Movies + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + cast: + description: 'Array of cast members.' + type: array + items: + type: object + properties: + name: + description: 'Cast member name.' + type: string + example: 'Natasha Hovey' + role: + description: 'Cast member role.' + type: string + example: Cheryl + content_rating: + description: "MPAA rating\n\nOption descriptions:\n * `G` - Rated G\n * `NC-17` - Rated NC-17\n * `NR` - Not rated\n * `PG` - Rated PG\n * `PG-13` - Rated PG-13\n * `R` - Rated R\n * `UR` - Unrated\n * `X` - Rated X\n" + type: string + example: NR + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: 'Description, or tagline, for the movie.' + type: string + director: + description: 'Name of the director.' + type: string + example: 'Lamberto Bava' + genres: + description: 'Array of movie genres.' + type: array + items: + type: string + imdb: + description: 'IMDB URL' + type: string + example: 'https://www.imdb.com/title/tt0089013/' + is_kid_friendly: + description: 'Is this movie kid friendly?' + type: boolean + name: + description: 'Name of the movie.' + type: string + example: Demons + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + example: '56' + runtime: + description: 'Movie runtime, in `HHhr MMmin` format.' + type: string + example: '1hr 20min' + trailer: + description: 'Trailer URL' + type: string + example: 'https://www.youtube.com/watch?v=_cNjTdFHL8E' + nullable: true + required: + - description + - name + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/movie' + 400: + description: "There are two ways that this status code can be encountered:\n * If there is a problem with the request.\n * If the IMDB URL could not be validated." + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - create + x-mill-visibility-private: true + '/theaters/{id}': + get: + summary: 'Get a single movie theater.' + description: 'Return information on a specific movie theater.' + operationId: getTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/theater' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: 'If the movie theater could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + patch: + summary: 'Update a movie theater.' + description: 'Update a movie theaters'' data.' + operationId: patchTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + address: + description: 'Theater address' + type: string + example: '2548 Central Park Ave, Yonkers, NY 10710' + name: + description: 'Name of the theater.' + type: string + example: 'Alamo Drafthouse Cinema - Yonkers' + phone_number: + description: 'Theater phone number' + type: string + example: '(914) 226-3082' + required: + - address + - name + - phone_number + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/theater' + 400: + description: 'If there is a problem with the request.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + 404: + description: 'If the movie movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + 403: + description: 'If something cool happened. Returns a unique error code of `1337`.' + content: + application/json: + schema: + $ref: '#/components/schemas/coded-error' + security: + - + oauth2: + - create + x-mill-visibility-private: true + delete: + summary: 'Delete a movie theater.' + description: 'Delete a movie theater.' + operationId: deleteTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + responses: + 204: + description: 'Standard request.' + 404: + description: 'If the movie theater could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - delete + /theaters: + get: + summary: 'Get movie theaters.' + description: 'Returns all movie theatres for a specific location.' + operationId: getTheaters + tags: + - Theaters + parameters: + - + name: location + in: query + description: 'Location you want theaters in.' + required: true + schema: + type: string + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/theater' + 400: + description: 'If the location is invalid.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + post: + summary: 'Create a movie theater.' + description: 'Create a new movie theater.' + operationId: postTheaters + tags: + - Theaters + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + address: + description: 'Theater address' + type: string + example: '2548 Central Park Ave, Yonkers, NY 10710' + name: + description: 'Name of the theater.' + type: string + example: 'Alamo Drafthouse Cinema - Yonkers' + phone_number: + description: 'Theater phone number' + type: string + example: '(914) 226-3082' + required: + - address + - name + - phone_number + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/theater' + 400: + description: 'If there is a problem with the request.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - create + x-mill-visibility-private: true +components: + securitySchemes: + bearer: + type: http + scheme: bearer + bearerFormat: bearer + oauth2: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: /oauth/authorize + tokenUrl: /oauth/access_token + scopes: + create: Create + delete: Delete + edit: Edit + public: Public + clientCredentials: + tokenUrl: /oauth/authorize/client + scopes: + create: Create + delete: Delete + edit: Edit + public: Public + schemas: + coded-error: + properties: + error: + description: 'User-friendly error message' + type: string + error_code: + description: 'Error code' + type: number + error: + properties: + error: + description: 'User-friendly error message' + type: string + movie: + properties: + cast: + description: 'Cast. This data requires a bearer token with the `public` scope.' + type: array + items: + type: string + content_rating: + description: 'MPAA rating' + type: string + example: G + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: Description + type: string + director: + description: 'Director. This data requires a bearer token with the `public` scope.' + allOf: + - + $ref: '#/components/schemas/person' + external_urls: + description: 'External URLs. This data requires a bearer token with the `public` scope.' + type: array + items: + type: object + properties: + imdb: + description: 'IMDB URL. This data requires a bearer token with the `public` scope.' + type: string + tickets: + description: 'Tickets URL. This data requires a bearer token with the `public` scope.' + type: string + trailer: + description: 'Trailer URL. This data requires a bearer token with the `public` scope.' + type: string + genres: + description: Genres + type: array + items: + type: string + id: + description: 'Unique ID' + type: number + kid_friendly: + description: 'Kid friendly?' + type: boolean + example: 'false' + name: + description: Name + type: string + purchase: + type: object + properties: + url: + description: 'URL to purchase the film.' + type: string + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + runtime: + description: Runtime + type: string + showtimes: + description: 'Non-theater specific showtimes' + type: array + items: + type: string + theaters: + description: 'Theaters the movie is currently showing in' + type: array + items: + type: string + uri: + description: 'Movie URI' + type: string + person: + properties: + id: + description: 'Unique ID' + type: number + imdb: + description: 'IMDB URL' + type: string + name: + description: Name + type: string + uri: + description: 'Person URI' + type: string + theater: + properties: + address: + description: Address + type: string + id: + description: 'Unique ID' + type: number + movies: + description: 'Movies currently playing' + type: array + items: + type: string + name: + description: Name + type: string + phone_number: + description: 'Phone number' + type: string + showtimes: + description: 'Non-movie specific showtimes' + type: array + items: + type: string + uri: + description: 'Theater URI' + type: string +security: + - + oauth2: + - create + - delete + - edit + - public diff --git a/resources/examples/Showtimes/compiled/1.1.1/errors-public-only-all-vendor-tags.md b/resources/examples/Showtimes/compiled/1.1.1/errors-public-only-all-vendor-tags.md new file mode 100644 index 0000000..b0b3d7e --- /dev/null +++ b/resources/examples/Showtimes/compiled/1.1.1/errors-public-only-all-vendor-tags.md @@ -0,0 +1,6 @@ +# Errors: Mill unit test API, Showtimes + +## Theaters +| Error Code | Path | Method | HTTP Code | Description | +| :--- | :--- | :--- | :--- | :--- | +| 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/compiled/1.1.1/errors-public-only-matched-vendor-tags.md b/resources/examples/Showtimes/compiled/1.1.1/errors-public-only-matched-vendor-tags.md new file mode 100644 index 0000000..b0b3d7e --- /dev/null +++ b/resources/examples/Showtimes/compiled/1.1.1/errors-public-only-matched-vendor-tags.md @@ -0,0 +1,6 @@ +# Errors: Mill unit test API, Showtimes + +## Theaters +| Error Code | Path | Method | HTTP Code | Description | +| :--- | :--- | :--- | :--- | :--- | +| 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/compiled/1.1.1/errors-public-only-unmatched-vendor-tags.md b/resources/examples/Showtimes/compiled/1.1.1/errors-public-only-unmatched-vendor-tags.md new file mode 100644 index 0000000..b0b3d7e --- /dev/null +++ b/resources/examples/Showtimes/compiled/1.1.1/errors-public-only-unmatched-vendor-tags.md @@ -0,0 +1,6 @@ +# Errors: Mill unit test API, Showtimes + +## Theaters +| Error Code | Path | Method | HTTP Code | Description | +| :--- | :--- | :--- | :--- | :--- | +| 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1/errors.md b/resources/examples/Showtimes/compiled/1.1.1/errors.md similarity index 74% rename from resources/examples/Showtimes/blueprints/1.1/errors.md rename to resources/examples/Showtimes/compiled/1.1.1/errors.md index 2e55e09..b0b3d7e 100644 --- a/resources/examples/Showtimes/blueprints/1.1/errors.md +++ b/resources/examples/Showtimes/compiled/1.1.1/errors.md @@ -1,6 +1,6 @@ # Errors: Mill unit test API, Showtimes ## Theaters -| Error Code | URI | Method | HTTP Code | Description | +| Error Code | Path | Method | HTTP Code | Description | | :--- | :--- | :--- | :--- | :--- | | 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1.1/representations/Coded error.apib b/resources/examples/Showtimes/compiled/1.1.1/representations/Coded error.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1.1/representations/Coded error.apib rename to resources/examples/Showtimes/compiled/1.1.1/representations/Coded error.apib diff --git a/resources/examples/Showtimes/blueprints/1.1.1/representations/Error.apib b/resources/examples/Showtimes/compiled/1.1.1/representations/Error.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1.1/representations/Error.apib rename to resources/examples/Showtimes/compiled/1.1.1/representations/Error.apib diff --git a/resources/examples/Showtimes/blueprints/1.1.1/representations/Movie.apib b/resources/examples/Showtimes/compiled/1.1.1/representations/Movie.apib similarity index 64% rename from resources/examples/Showtimes/blueprints/1.1.1/representations/Movie.apib rename to resources/examples/Showtimes/compiled/1.1.1/representations/Movie.apib index eaf3141..4b4a780 100644 --- a/resources/examples/Showtimes/blueprints/1.1.1/representations/Movie.apib +++ b/resources/examples/Showtimes/compiled/1.1.1/representations/Movie.apib @@ -12,11 +12,12 @@ + `X` - `description` (string) - Description - `director` (Person) - Director. This data requires a bearer token with the `public` scope. -- `external_urls` (object) - External URLs - - `imdb` (string) - IMDB URL - - `tickets` (string) - Tickets URL - - `trailer` (string) - Trailer URL -- `genres` (array) - Genres +- `external_urls` (array) - External URLs. This data requires a bearer token with the `public` scope. + - (object) + - `imdb` (string) - IMDB URL. This data requires a bearer token with the `public` scope. + - `tickets` (string) - Tickets URL. This data requires a bearer token with the `public` scope. + - `trailer` (string) - Trailer URL. This data requires a bearer token with the `public` scope. +- `genres` (array[string]) - Genres - `id` (number) - Unique ID - `kid_friendly`: `false` (boolean) - Kid friendly? - `name` (string) - Name diff --git a/resources/examples/Showtimes/blueprints/1.1.1/representations/Person.apib b/resources/examples/Showtimes/compiled/1.1.1/representations/Person.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1.1/representations/Person.apib rename to resources/examples/Showtimes/compiled/1.1.1/representations/Person.apib diff --git a/resources/examples/Showtimes/blueprints/1.1.1/representations/Theater.apib b/resources/examples/Showtimes/compiled/1.1.1/representations/Theater.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1.1/representations/Theater.apib rename to resources/examples/Showtimes/compiled/1.1.1/representations/Theater.apib diff --git a/resources/examples/Showtimes/blueprints/1.1/resources/Movies.apib b/resources/examples/Showtimes/compiled/1.1.1/resources/Movies.apib similarity index 64% rename from resources/examples/Showtimes/blueprints/1.1/resources/Movies.apib rename to resources/examples/Showtimes/compiled/1.1.1/resources/Movies.apib index 3cc966b..59ce577 100644 --- a/resources/examples/Showtimes/blueprints/1.1/resources/Movies.apib +++ b/resources/examples/Showtimes/compiled/1.1.1/resources/Movies.apib @@ -62,25 +62,29 @@ This action requires a bearer token with the `edit` scope. - `id` (number, required) - Movie ID + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL + Response 200 (application/json) + Attributes (Movie) + Response 400 (application/json) @@ -126,26 +130,29 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL + Response 200 (application/json) + Attributes (Movie) + Response 400 (application/json) diff --git a/resources/examples/Showtimes/blueprints/1.0/resources/Theaters.apib b/resources/examples/Showtimes/compiled/1.1.1/resources/Theaters.apib similarity index 77% rename from resources/examples/Showtimes/blueprints/1.0/resources/Theaters.apib rename to resources/examples/Showtimes/compiled/1.1.1/resources/Theaters.apib index 4269a5b..f90eeae 100644 --- a/resources/examples/Showtimes/blueprints/1.0/resources/Theaters.apib +++ b/resources/examples/Showtimes/compiled/1.1.1/resources/Theaters.apib @@ -24,9 +24,9 @@ This action requires a bearer token with the `create` scope. - `id` (number, required) - Theater ID + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number + Response 200 (application/json) + Attributes (Theater) + Response 400 (application/json) @@ -70,9 +70,9 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number + Response 200 (application/json) + Attributes (Theater) + Response 400 (application/json) diff --git a/resources/examples/Showtimes/blueprints/1.1.2/api.apib b/resources/examples/Showtimes/compiled/1.1.2/api.apib similarity index 59% rename from resources/examples/Showtimes/blueprints/1.1.2/api.apib rename to resources/examples/Showtimes/compiled/1.1.2/api.apib index 47f503b..69e3092 100644 --- a/resources/examples/Showtimes/blueprints/1.1.2/api.apib +++ b/resources/examples/Showtimes/compiled/1.1.2/api.apib @@ -25,10 +25,10 @@ sem malesuada magna mollis euismod. + Parameters - `id` (number, required) - Movie ID -+ Response 200 (application/mill.example.movie) ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 304 (application/mill.example.movie) -+ Response 404 (application/mill.example.movie) ++ Response 304 (application/mill.example.movie+json) ++ Response 404 (application/mill.example.movie+json) + Attributes (Error) ## Movies [/movies/{id}] @@ -52,10 +52,10 @@ sem malesuada magna mollis euismod. + Parameters - `id` (number, required) - Movie ID -+ Response 200 (application/mill.example.movie) ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 304 (application/mill.example.movie) -+ Response 404 (application/mill.example.movie) ++ Response 304 (application/mill.example.movie+json) ++ Response 404 (application/mill.example.movie+json) + Attributes (Error) ### Update a movie. [PATCH] @@ -67,34 +67,37 @@ This action requires a bearer token with the `edit` scope. - `id` (number, required) - Movie ID + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL -+ Response 200 (application/mill.example.movie) + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 400 (application/mill.example.movie) ++ Response 400 (application/mill.example.movie+json) There are two ways that this status code can be encountered: * If there is a problem with the request. * If the IMDB URL could not be validated. + Attributes (Error) -+ Response 404 (application/mill.example.movie) ++ Response 404 (application/mill.example.movie+json) + Attributes (Error) ### Delete a movie. [DELETE] @@ -120,9 +123,9 @@ Returns all movies for a specific location. + Attributes - `location` (string, required) - Location you want movies for. - `page` (number) - Page of results to pull. -+ Response 200 (application/mill.example.movie) ++ Response 200 (application/mill.example.movie+json) + Attributes (array[Movie]) -+ Response 400 (application/mill.example.movie) ++ Response 400 (application/mill.example.movie+json) + Attributes (Error) ### Create a movie. [POST] @@ -132,29 +135,32 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL -+ Response 200 (application/mill.example.movie) + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 400 (application/mill.example.movie) ++ Response 400 (application/mill.example.movie+json) There are two ways that this status code can be encountered: * If there is a problem with the request. * If the IMDB URL could not be validated. @@ -171,10 +177,10 @@ Return information on a specific movie theater. + Parameters - `id` (number, required) - Theater ID -+ Response 200 (application/mill.example.theater) ++ Response 200 (application/mill.example.theater+json) + Attributes (Theater) -+ Response 304 (application/mill.example.theater) -+ Response 404 (application/mill.example.theater) ++ Response 304 (application/mill.example.theater+json) ++ Response 404 (application/mill.example.theater+json) + Attributes (Error) ### Update a movie theater. [PATCH] @@ -186,14 +192,14 @@ This action requires a bearer token with the `create` scope. - `id` (number, required) - Theater ID + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number -+ Response 200 (application/mill.example.theater) + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number ++ Response 200 (application/mill.example.theater+json) + Attributes (Theater) -+ Response 400 (application/mill.example.theater) ++ Response 400 (application/mill.example.theater+json) + Attributes (Error) -+ Response 404 (application/mill.example.theater) ++ Response 404 (application/mill.example.theater+json) + Attributes (Error) ### Delete a movie theater. [DELETE] @@ -218,9 +224,9 @@ Returns all movie theatres for a specific location. + Request + Attributes - `location` (string, required) - Location you want theaters in. -+ Response 200 (application/mill.example.theater) ++ Response 200 (application/mill.example.theater+json) + Attributes (array[Theater]) -+ Response 400 (application/mill.example.theater) ++ Response 400 (application/mill.example.theater+json) + Attributes (Error) ### Create a movie theater. [POST] @@ -230,12 +236,12 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number -+ Response 200 (application/mill.example.theater) + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number ++ Response 200 (application/mill.example.theater+json) + Attributes (Theater) -+ Response 400 (application/mill.example.theater) ++ Response 400 (application/mill.example.theater+json) + Attributes (Error) # Data Structures @@ -260,11 +266,12 @@ This action requires a bearer token with the `create` scope. + `X` - `description` (string) - Description - `director` (Person) - Director. This data requires a bearer token with the `public` scope. -- `external_urls` (object) - External URLs - - `imdb` (string) - IMDB URL - - `tickets` (string) - Tickets URL - - `trailer` (string) - Trailer URL -- `genres` (array) - Genres +- `external_urls` (array) - External URLs. This data requires a bearer token with the `public` scope. + - (object) + - `imdb` (string) - IMDB URL. This data requires a bearer token with the `public` scope. + - `tickets` (string) - Tickets URL. This data requires a bearer token with the `public` scope. + - `trailer` (string) - Trailer URL. This data requires a bearer token with the `public` scope. +- `genres` (array[string]) - Genres - `id` (number) - Unique ID - `kid_friendly`: `false` (boolean) - Kid friendly? - `name` (string) - Name diff --git a/resources/examples/Showtimes/compiled/1.1.2/api.yaml b/resources/examples/Showtimes/compiled/1.1.2/api.yaml new file mode 100644 index 0000000..e98c25b --- /dev/null +++ b/resources/examples/Showtimes/compiled/1.1.2/api.yaml @@ -0,0 +1,730 @@ +openapi: 3.0.0 +info: + title: 'Mill unit test API, Showtimes' + version: 1.1.2 + contact: + name: 'Get help!' + email: support@example.com + url: 'https://developer.example.com/help' +tags: + - + name: Movies + - + name: Theaters +servers: + - + url: 'https://api.example.com' + description: Production + - + url: 'https://api.example.local' + description: Development +paths: + '/movie/{id}': + get: + summary: 'Get a single movie.' + description: "Return information on a specific movie.\n\nDonec id elit non mi porta gravida at eget metus. Cras mattis consectetur purus sit amet fermentum. Lorem\nipsum dolor sit amet, consectetur adipiscing elit. Etiam porta sem malesuada magna mollis euismod. Duis\nmollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Etiam porta\nsem malesuada magna mollis euismod.\n\n```\n[\n {\"id\": \"fizzbuzz\"}\n]\n```" + operationId: getMovieId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/movie' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: 'If the movie could not be found.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/error' + '/movies/{id}': + get: + summary: 'Get a single movie.' + description: "Return information on a specific movie.\n\nDonec id elit non mi porta gravida at eget metus. Cras mattis consectetur purus sit amet fermentum. Lorem\nipsum dolor sit amet, consectetur adipiscing elit. Etiam porta sem malesuada magna mollis euismod. Duis\nmollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Etiam porta\nsem malesuada magna mollis euismod.\n\n```\n[\n {\"id\": \"fizzbuzz\"}\n]\n```" + operationId: getMoviesId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/movie' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: 'If the movie could not be found.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + patch: + summary: 'Update a movie.' + description: 'Update a movies data.' + operationId: patchMoviesId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + requestBody: + required: true + content: + application/mill.example.movie+json: + schema: + type: object + properties: + cast: + description: 'Array of cast members.' + type: array + items: + type: object + properties: + name: + description: 'Cast member name.' + type: string + example: 'Natasha Hovey' + role: + description: 'Cast member role.' + type: string + example: Cheryl + content_rating: + description: "MPAA rating\n\nOption descriptions:\n * `G` - Rated G\n * `NC-17` - Rated NC-17\n * `NR` - Not rated\n * `PG` - Rated PG\n * `PG-13` - Rated PG-13\n * `R` - Rated R\n * `UR` - Unrated\n * `X` - Rated X\n" + type: string + example: NR + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: 'Description, or tagline, for the movie.' + type: string + director: + description: 'Name of the director.' + type: string + example: 'Lamberto Bava' + genres: + description: 'Array of movie genres.' + type: array + items: + type: string + imdb: + description: 'IMDB URL' + type: string + example: 'https://www.imdb.com/title/tt0089013/' + is_kid_friendly: + description: 'Is this movie kid friendly?' + type: boolean + name: + description: 'Name of the movie.' + type: string + example: Demons + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + example: '56' + runtime: + description: 'Movie runtime, in `HHhr MMmin` format.' + type: string + example: '1hr 20min' + trailer: + description: 'Trailer URL' + type: string + example: 'https://www.youtube.com/watch?v=_cNjTdFHL8E' + nullable: true + required: + - description + - name + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/movie' + 400: + description: "There are two ways that this status code can be encountered:\n * If there is a problem with the request.\n * If the IMDB URL could not be validated." + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/error' + 404: + description: 'If the movie could not be found.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - edit + x-mill-visibility-private: true + delete: + summary: 'Delete a movie.' + description: 'Delete a movie.' + operationId: deleteMoviesId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + responses: + 204: + description: 'Standard request.' + 404: + description: 'If the movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - delete + x-mill-vendortags: + - 'tag:DELETE_CONTENT' + /movies: + get: + summary: 'Get movies.' + description: 'Returns all movies for a specific location.' + operationId: getMovies + tags: + - Movies + parameters: + - + name: location + in: query + description: 'Location you want movies for.' + required: true + schema: + type: string + - + name: page + in: query + description: 'Page of results to pull.' + required: false + schema: + type: number + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.movie+json: + schema: + type: array + items: + $ref: '#/components/schemas/movie' + 400: + description: 'If the location is invalid.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + post: + summary: 'Create a movie.' + description: 'Create a new movie.' + operationId: postMovies + tags: + - Movies + requestBody: + required: true + content: + application/mill.example.movie+json: + schema: + type: object + properties: + cast: + description: 'Array of cast members.' + type: array + items: + type: object + properties: + name: + description: 'Cast member name.' + type: string + example: 'Natasha Hovey' + role: + description: 'Cast member role.' + type: string + example: Cheryl + content_rating: + description: "MPAA rating\n\nOption descriptions:\n * `G` - Rated G\n * `NC-17` - Rated NC-17\n * `NR` - Not rated\n * `PG` - Rated PG\n * `PG-13` - Rated PG-13\n * `R` - Rated R\n * `UR` - Unrated\n * `X` - Rated X\n" + type: string + example: NR + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: 'Description, or tagline, for the movie.' + type: string + director: + description: 'Name of the director.' + type: string + example: 'Lamberto Bava' + genres: + description: 'Array of movie genres.' + type: array + items: + type: string + imdb: + description: 'IMDB URL' + type: string + example: 'https://www.imdb.com/title/tt0089013/' + is_kid_friendly: + description: 'Is this movie kid friendly?' + type: boolean + name: + description: 'Name of the movie.' + type: string + example: Demons + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + example: '56' + runtime: + description: 'Movie runtime, in `HHhr MMmin` format.' + type: string + example: '1hr 20min' + trailer: + description: 'Trailer URL' + type: string + example: 'https://www.youtube.com/watch?v=_cNjTdFHL8E' + nullable: true + required: + - description + - name + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/movie' + 400: + description: "There are two ways that this status code can be encountered:\n * If there is a problem with the request.\n * If the IMDB URL could not be validated." + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - create + x-mill-visibility-private: true + '/theaters/{id}': + get: + summary: 'Get a single movie theater.' + description: 'Return information on a specific movie theater.' + operationId: getTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/theater' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: 'If the movie theater could not be found.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + patch: + summary: 'Update a movie theater.' + description: 'Update a movie theaters'' data.' + operationId: patchTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + requestBody: + required: true + content: + application/mill.example.theater+json: + schema: + type: object + properties: + address: + description: 'Theater address' + type: string + example: '2548 Central Park Ave, Yonkers, NY 10710' + name: + description: 'Name of the theater.' + type: string + example: 'Alamo Drafthouse Cinema - Yonkers' + phone_number: + description: 'Theater phone number' + type: string + example: '(914) 226-3082' + required: + - address + - name + - phone_number + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/theater' + 400: + description: 'If there is a problem with the request.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/error' + 404: + description: 'If the movie movie could not be found.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - create + x-mill-visibility-private: true + delete: + summary: 'Delete a movie theater.' + description: 'Delete a movie theater.' + operationId: deleteTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + responses: + 204: + description: 'Standard request.' + 404: + description: 'If the movie theater could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - delete + /theaters: + get: + summary: 'Get movie theaters.' + description: 'Returns all movie theatres for a specific location.' + operationId: getTheaters + tags: + - Theaters + parameters: + - + name: location + in: query + description: 'Location you want theaters in.' + required: true + schema: + type: string + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.theater+json: + schema: + type: array + items: + $ref: '#/components/schemas/theater' + 400: + description: 'If the location is invalid.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + post: + summary: 'Create a movie theater.' + description: 'Create a new movie theater.' + operationId: postTheaters + tags: + - Theaters + requestBody: + required: true + content: + application/mill.example.theater+json: + schema: + type: object + properties: + address: + description: 'Theater address' + type: string + example: '2548 Central Park Ave, Yonkers, NY 10710' + name: + description: 'Name of the theater.' + type: string + example: 'Alamo Drafthouse Cinema - Yonkers' + phone_number: + description: 'Theater phone number' + type: string + example: '(914) 226-3082' + required: + - address + - name + - phone_number + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/theater' + 400: + description: 'If there is a problem with the request.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - create + x-mill-visibility-private: true +components: + securitySchemes: + bearer: + type: http + scheme: bearer + bearerFormat: bearer + oauth2: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: /oauth/authorize + tokenUrl: /oauth/access_token + scopes: + create: Create + delete: Delete + edit: Edit + public: Public + clientCredentials: + tokenUrl: /oauth/authorize/client + scopes: + create: Create + delete: Delete + edit: Edit + public: Public + schemas: + coded-error: + properties: + error: + description: 'User-friendly error message' + type: string + error_code: + description: 'Error code' + type: number + error: + properties: + error: + description: 'User-friendly error message' + type: string + movie: + properties: + cast: + description: 'Cast. This data requires a bearer token with the `public` scope.' + type: array + items: + type: string + content_rating: + description: 'MPAA rating' + type: string + example: G + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: Description + type: string + director: + description: 'Director. This data requires a bearer token with the `public` scope.' + allOf: + - + $ref: '#/components/schemas/person' + external_urls: + description: 'External URLs. This data requires a bearer token with the `public` scope.' + type: array + items: + type: object + properties: + imdb: + description: 'IMDB URL. This data requires a bearer token with the `public` scope.' + type: string + tickets: + description: 'Tickets URL. This data requires a bearer token with the `public` scope.' + type: string + trailer: + description: 'Trailer URL. This data requires a bearer token with the `public` scope.' + type: string + genres: + description: Genres + type: array + items: + type: string + id: + description: 'Unique ID' + type: number + kid_friendly: + description: 'Kid friendly?' + type: boolean + example: 'false' + name: + description: Name + type: string + purchase: + type: object + properties: + url: + description: 'URL to purchase the film.' + type: string + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + runtime: + description: Runtime + type: string + showtimes: + description: 'Non-theater specific showtimes' + type: array + items: + type: string + theaters: + description: 'Theaters the movie is currently showing in' + type: array + items: + type: string + uri: + description: 'Movie URI' + type: string + person: + properties: + id: + description: 'Unique ID' + type: number + imdb: + description: 'IMDB URL' + type: string + name: + description: Name + type: string + uri: + description: 'Person URI' + type: string + theater: + properties: + address: + description: Address + type: string + id: + description: 'Unique ID' + type: number + movies: + description: 'Movies currently playing' + type: array + items: + type: string + name: + description: Name + type: string + phone_number: + description: 'Phone number' + type: string + showtimes: + description: 'Non-movie specific showtimes' + type: array + items: + type: string + uri: + description: 'Theater URI' + type: string +security: + - + oauth2: + - create + - delete + - edit + - public diff --git a/resources/examples/Showtimes/blueprints/1.1.2/representations/Coded error.apib b/resources/examples/Showtimes/compiled/1.1.2/representations/Coded error.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1.2/representations/Coded error.apib rename to resources/examples/Showtimes/compiled/1.1.2/representations/Coded error.apib diff --git a/resources/examples/Showtimes/blueprints/1.1.2/representations/Error.apib b/resources/examples/Showtimes/compiled/1.1.2/representations/Error.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1.2/representations/Error.apib rename to resources/examples/Showtimes/compiled/1.1.2/representations/Error.apib diff --git a/resources/examples/Showtimes/blueprints/1.1.2/representations/Movie.apib b/resources/examples/Showtimes/compiled/1.1.2/representations/Movie.apib similarity index 64% rename from resources/examples/Showtimes/blueprints/1.1.2/representations/Movie.apib rename to resources/examples/Showtimes/compiled/1.1.2/representations/Movie.apib index eaf3141..4b4a780 100644 --- a/resources/examples/Showtimes/blueprints/1.1.2/representations/Movie.apib +++ b/resources/examples/Showtimes/compiled/1.1.2/representations/Movie.apib @@ -12,11 +12,12 @@ + `X` - `description` (string) - Description - `director` (Person) - Director. This data requires a bearer token with the `public` scope. -- `external_urls` (object) - External URLs - - `imdb` (string) - IMDB URL - - `tickets` (string) - Tickets URL - - `trailer` (string) - Trailer URL -- `genres` (array) - Genres +- `external_urls` (array) - External URLs. This data requires a bearer token with the `public` scope. + - (object) + - `imdb` (string) - IMDB URL. This data requires a bearer token with the `public` scope. + - `tickets` (string) - Tickets URL. This data requires a bearer token with the `public` scope. + - `trailer` (string) - Trailer URL. This data requires a bearer token with the `public` scope. +- `genres` (array[string]) - Genres - `id` (number) - Unique ID - `kid_friendly`: `false` (boolean) - Kid friendly? - `name` (string) - Name diff --git a/resources/examples/Showtimes/blueprints/1.1.2/representations/Person.apib b/resources/examples/Showtimes/compiled/1.1.2/representations/Person.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1.2/representations/Person.apib rename to resources/examples/Showtimes/compiled/1.1.2/representations/Person.apib diff --git a/resources/examples/Showtimes/blueprints/1.1.2/representations/Theater.apib b/resources/examples/Showtimes/compiled/1.1.2/representations/Theater.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1.2/representations/Theater.apib rename to resources/examples/Showtimes/compiled/1.1.2/representations/Theater.apib diff --git a/resources/examples/Showtimes/blueprints/1.1.2/resources/Movies.apib b/resources/examples/Showtimes/compiled/1.1.2/resources/Movies.apib similarity index 54% rename from resources/examples/Showtimes/blueprints/1.1.2/resources/Movies.apib rename to resources/examples/Showtimes/compiled/1.1.2/resources/Movies.apib index dc5d8fa..a486a26 100644 --- a/resources/examples/Showtimes/blueprints/1.1.2/resources/Movies.apib +++ b/resources/examples/Showtimes/compiled/1.1.2/resources/Movies.apib @@ -20,10 +20,10 @@ sem malesuada magna mollis euismod. + Parameters - `id` (number, required) - Movie ID -+ Response 200 (application/mill.example.movie) ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 304 (application/mill.example.movie) -+ Response 404 (application/mill.example.movie) ++ Response 304 (application/mill.example.movie+json) ++ Response 404 (application/mill.example.movie+json) + Attributes (Error) ## Movies [/movies/{id}] @@ -47,10 +47,10 @@ sem malesuada magna mollis euismod. + Parameters - `id` (number, required) - Movie ID -+ Response 200 (application/mill.example.movie) ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 304 (application/mill.example.movie) -+ Response 404 (application/mill.example.movie) ++ Response 304 (application/mill.example.movie+json) ++ Response 404 (application/mill.example.movie+json) + Attributes (Error) ### Update a movie. [PATCH] @@ -62,34 +62,37 @@ This action requires a bearer token with the `edit` scope. - `id` (number, required) - Movie ID + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL -+ Response 200 (application/mill.example.movie) + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 400 (application/mill.example.movie) ++ Response 400 (application/mill.example.movie+json) There are two ways that this status code can be encountered: * If there is a problem with the request. * If the IMDB URL could not be validated. + Attributes (Error) -+ Response 404 (application/mill.example.movie) ++ Response 404 (application/mill.example.movie+json) + Attributes (Error) ### Delete a movie. [DELETE] @@ -115,9 +118,9 @@ Returns all movies for a specific location. + Attributes - `location` (string, required) - Location you want movies for. - `page` (number) - Page of results to pull. -+ Response 200 (application/mill.example.movie) ++ Response 200 (application/mill.example.movie+json) + Attributes (array[Movie]) -+ Response 400 (application/mill.example.movie) ++ Response 400 (application/mill.example.movie+json) + Attributes (Error) ### Create a movie. [POST] @@ -127,29 +130,32 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL -+ Response 200 (application/mill.example.movie) + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 400 (application/mill.example.movie) ++ Response 400 (application/mill.example.movie+json) There are two ways that this status code can be encountered: * If there is a problem with the request. * If the IMDB URL could not be validated. diff --git a/resources/examples/Showtimes/blueprints/1.1.2/resources/Theaters.apib b/resources/examples/Showtimes/compiled/1.1.2/resources/Theaters.apib similarity index 57% rename from resources/examples/Showtimes/blueprints/1.1.2/resources/Theaters.apib rename to resources/examples/Showtimes/compiled/1.1.2/resources/Theaters.apib index e2b9e2f..d66956e 100644 --- a/resources/examples/Showtimes/blueprints/1.1.2/resources/Theaters.apib +++ b/resources/examples/Showtimes/compiled/1.1.2/resources/Theaters.apib @@ -9,10 +9,10 @@ Return information on a specific movie theater. + Parameters - `id` (number, required) - Theater ID -+ Response 200 (application/mill.example.theater) ++ Response 200 (application/mill.example.theater+json) + Attributes (Theater) -+ Response 304 (application/mill.example.theater) -+ Response 404 (application/mill.example.theater) ++ Response 304 (application/mill.example.theater+json) ++ Response 404 (application/mill.example.theater+json) + Attributes (Error) ### Update a movie theater. [PATCH] @@ -24,14 +24,14 @@ This action requires a bearer token with the `create` scope. - `id` (number, required) - Theater ID + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number -+ Response 200 (application/mill.example.theater) + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number ++ Response 200 (application/mill.example.theater+json) + Attributes (Theater) -+ Response 400 (application/mill.example.theater) ++ Response 400 (application/mill.example.theater+json) + Attributes (Error) -+ Response 404 (application/mill.example.theater) ++ Response 404 (application/mill.example.theater+json) + Attributes (Error) ### Delete a movie theater. [DELETE] @@ -56,9 +56,9 @@ Returns all movie theatres for a specific location. + Request + Attributes - `location` (string, required) - Location you want theaters in. -+ Response 200 (application/mill.example.theater) ++ Response 200 (application/mill.example.theater+json) + Attributes (array[Theater]) -+ Response 400 (application/mill.example.theater) ++ Response 400 (application/mill.example.theater+json) + Attributes (Error) ### Create a movie theater. [POST] @@ -68,10 +68,10 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number -+ Response 200 (application/mill.example.theater) + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number ++ Response 200 (application/mill.example.theater+json) + Attributes (Theater) -+ Response 400 (application/mill.example.theater) ++ Response 400 (application/mill.example.theater+json) + Attributes (Error) \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1.3/api.apib b/resources/examples/Showtimes/compiled/1.1.3/api.apib similarity index 61% rename from resources/examples/Showtimes/blueprints/1.1.3/api.apib rename to resources/examples/Showtimes/compiled/1.1.3/api.apib index 5226663..390a3fd 100644 --- a/resources/examples/Showtimes/blueprints/1.1.3/api.apib +++ b/resources/examples/Showtimes/compiled/1.1.3/api.apib @@ -25,10 +25,10 @@ sem malesuada magna mollis euismod. + Parameters - `id` (number, required) - Movie ID -+ Response 200 (application/mill.example.movie) ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 304 (application/mill.example.movie) -+ Response 404 (application/mill.example.movie) ++ Response 304 (application/mill.example.movie+json) ++ Response 404 (application/mill.example.movie+json) There are three ways that this status code can be encountered: * If the movie could not be found. * For no reason. @@ -56,10 +56,10 @@ sem malesuada magna mollis euismod. + Parameters - `id` (number, required) - Movie ID -+ Response 200 (application/mill.example.movie) ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 304 (application/mill.example.movie) -+ Response 404 (application/mill.example.movie) ++ Response 304 (application/mill.example.movie+json) ++ Response 404 (application/mill.example.movie+json) There are three ways that this status code can be encountered: * If the movie could not be found. * For no reason. @@ -75,57 +75,49 @@ This action requires a bearer token with the `edit` scope. - `id` (number, required) - Movie ID + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL -+ Response 200 (application/mill.example.movie) + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 202 (application/mill.example.movie) ++ Response 202 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 400 (application/mill.example.movie) ++ Response 400 (application/mill.example.movie+json) There are two ways that this status code can be encountered: * If there is a problem with the request. * If the IMDB URL could not be validated. + Attributes (Error) -+ Response 403 (application/mill.example.movie) ++ Response 403 (application/mill.example.movie+json) There are two ways that this status code can be encountered: * If something cool happened. Unique error code: 1337 * If the user is not allowed to edit that movie. Unique error code: 666 + Attributes (Coded error) -+ Response 404 (application/mill.example.movie) ++ Response 404 (application/mill.example.movie+json) There are two ways that this status code can be encountered: * If the movie could not be found. * If the trailer URL could not be validated. + Attributes (Error) -### Delete a movie. [DELETE] -Delete a movie. - -This action requires a bearer token with the `delete` scope. - -+ Parameters - - `id` (number, required) - Movie ID -+ Response 204 (application/json) -+ Response 404 (application/json) - + Attributes (Error) - ## Movies [/movies] Information on a specific movie. @@ -138,9 +130,9 @@ Returns all movies for a specific location. + Attributes - `location` (string, required) - Location you want movies for. - `page` (number) - Page of results to pull. -+ Response 200 (application/mill.example.movie) ++ Response 200 (application/mill.example.movie+json) + Attributes (array[Movie]) -+ Response 400 (application/mill.example.movie) ++ Response 400 (application/mill.example.movie+json) + Attributes (Error) ### Create a movie. [POST] @@ -150,30 +142,33 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL -+ Response 200 (application/mill.example.movie) + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 201 (application/mill.example.movie) -+ Response 400 (application/mill.example.movie) ++ Response 201 (application/mill.example.movie+json) ++ Response 400 (application/mill.example.movie+json) There are two ways that this status code can be encountered: * If there is a problem with the request. * If the IMDB URL could not be validated. @@ -190,10 +185,10 @@ Return information on a specific movie theater. + Parameters - `id` (number, required) - Theater ID -+ Response 200 (application/mill.example.theater) ++ Response 200 (application/mill.example.theater+json) + Attributes (Theater) -+ Response 304 (application/mill.example.theater) -+ Response 404 (application/mill.example.theater) ++ Response 304 (application/mill.example.theater+json) ++ Response 404 (application/mill.example.theater+json) + Attributes (Error) ### Update a movie theater. [PATCH] @@ -205,14 +200,14 @@ This action requires a bearer token with the `create` scope. - `id` (number, required) - Theater ID + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number -+ Response 200 (application/mill.example.theater) + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number ++ Response 200 (application/mill.example.theater+json) + Attributes (Theater) -+ Response 400 (application/mill.example.theater) ++ Response 400 (application/mill.example.theater+json) + Attributes (Error) -+ Response 404 (application/mill.example.theater) ++ Response 404 (application/mill.example.theater+json) + Attributes (Error) ### Delete a movie theater. [DELETE] @@ -237,9 +232,9 @@ Returns all movie theatres for a specific location. + Request + Attributes - `location` (string, required) - Location you want theaters in. -+ Response 200 (application/mill.example.theater) ++ Response 200 (application/mill.example.theater+json) + Attributes (array[Theater]) -+ Response 400 (application/mill.example.theater) ++ Response 400 (application/mill.example.theater+json) + Attributes (Error) ### Create a movie theater. [POST] @@ -249,12 +244,12 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number -+ Response 200 (application/mill.example.theater) + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number ++ Response 200 (application/mill.example.theater+json) + Attributes (Theater) -+ Response 400 (application/mill.example.theater) ++ Response 400 (application/mill.example.theater+json) + Attributes (Error) # Data Structures @@ -279,10 +274,11 @@ This action requires a bearer token with the `create` scope. + `X` - `description` (string) - Description - `director` (Person) - Director. This data requires a bearer token with the `public` scope. -- `external_urls` (object) - External URLs - - `imdb` (string) - IMDB URL - - `trailer` (string) - Trailer URL -- `genres` (array) - Genres +- `external_urls` (array) - External URLs. This data requires a bearer token with the `public` scope. + - (object) + - `imdb` (string) - IMDB URL. This data requires a bearer token with the `public` scope. + - `trailer` (string) - Trailer URL. This data requires a bearer token with the `public` scope. +- `genres` (array[string]) - Genres - `id` (number) - Unique ID - `kid_friendly`: `false` (boolean) - Kid friendly? - `name` (string) - Name diff --git a/resources/examples/Showtimes/compiled/1.1.3/api.yaml b/resources/examples/Showtimes/compiled/1.1.3/api.yaml new file mode 100644 index 0000000..e952fcc --- /dev/null +++ b/resources/examples/Showtimes/compiled/1.1.3/api.yaml @@ -0,0 +1,711 @@ +openapi: 3.0.0 +info: + title: 'Mill unit test API, Showtimes' + version: 1.1.3 + contact: + name: 'Get help!' + email: support@example.com + url: 'https://developer.example.com/help' +tags: + - + name: Movies + - + name: Theaters +servers: + - + url: 'https://api.example.com' + description: Production + - + url: 'https://api.example.local' + description: Development +paths: + '/movie/{id}': + get: + summary: 'Get a single movie.' + description: "Return information on a specific movie.\n\nDonec id elit non mi porta gravida at eget metus. Cras mattis consectetur purus sit amet fermentum. Lorem\nipsum dolor sit amet, consectetur adipiscing elit. Etiam porta sem malesuada magna mollis euismod. Duis\nmollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Etiam porta\nsem malesuada magna mollis euismod.\n\n```\n[\n {\"id\": \"fizzbuzz\"}\n]\n```" + operationId: getMovieId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/movie' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: "There are three ways that this status code can be encountered:\n * If the movie could not be found.\n * For no reason.\n * For some other reason." + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/error' + '/movies/{id}': + get: + summary: 'Get a single movie.' + description: "Return information on a specific movie.\n\nDonec id elit non mi porta gravida at eget metus. Cras mattis consectetur purus sit amet fermentum. Lorem\nipsum dolor sit amet, consectetur adipiscing elit. Etiam porta sem malesuada magna mollis euismod. Duis\nmollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Etiam porta\nsem malesuada magna mollis euismod.\n\n```\n[\n {\"id\": \"fizzbuzz\"}\n]\n```" + operationId: getMoviesId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/movie' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: "There are three ways that this status code can be encountered:\n * If the movie could not be found.\n * For no reason.\n * For some other reason." + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + patch: + summary: 'Update a movie.' + description: 'Update a movies data.' + operationId: patchMoviesId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + requestBody: + required: true + content: + application/mill.example.movie+json: + schema: + type: object + properties: + cast: + description: 'Array of cast members.' + type: array + items: + type: object + properties: + name: + description: 'Cast member name.' + type: string + example: 'Natasha Hovey' + role: + description: 'Cast member role.' + type: string + example: Cheryl + content_rating: + description: "MPAA rating\n\nOption descriptions:\n * `G` - Rated G\n * `NC-17` - Rated NC-17\n * `NR` - Not rated\n * `PG` - Rated PG\n * `PG-13` - Rated PG-13\n * `R` - Rated R\n * `UR` - Unrated\n * `X` - Rated X\n" + type: string + example: NR + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: 'Description, or tagline, for the movie.' + type: string + director: + description: 'Name of the director.' + type: string + example: 'Lamberto Bava' + genres: + description: 'Array of movie genres.' + type: array + items: + type: string + imdb: + description: 'IMDB URL' + type: string + example: 'https://www.imdb.com/title/tt0089013/' + is_kid_friendly: + description: 'Is this movie kid friendly?' + type: boolean + name: + description: 'Name of the movie.' + type: string + example: Demons + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + example: '56' + runtime: + description: 'Movie runtime, in `HHhr MMmin` format.' + type: string + example: '1hr 20min' + trailer: + description: 'Trailer URL' + type: string + example: 'https://www.youtube.com/watch?v=_cNjTdFHL8E' + nullable: true + required: + - description + - name + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/movie' + 202: + description: 'Standard request.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/movie' + 400: + description: "There are two ways that this status code can be encountered:\n * If there is a problem with the request.\n * If the IMDB URL could not be validated." + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/error' + 404: + description: "There are two ways that this status code can be encountered:\n * If the movie could not be found.\n * If the trailer URL could not be validated." + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/error' + 403: + description: "There are two ways that this status code can be encountered:\n * If something cool happened. Returns a unique error code of `1337`.\n * If the user is not allowed to edit that movie. Returns a unique error code of `666`." + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/coded-error' + security: + - + oauth2: + - edit + x-mill-visibility-private: true + /movies: + get: + summary: 'Get movies.' + description: 'Returns all movies for a specific location.' + operationId: getMovies + tags: + - Movies + parameters: + - + name: location + in: query + description: 'Location you want movies for.' + required: true + schema: + type: string + - + name: page + in: query + description: 'Page of results to pull.' + required: false + schema: + type: number + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.movie+json: + schema: + type: array + items: + $ref: '#/components/schemas/movie' + 400: + description: 'If the location is invalid.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + post: + summary: 'Create a movie.' + description: 'Create a new movie.' + operationId: postMovies + tags: + - Movies + requestBody: + required: true + content: + application/mill.example.movie+json: + schema: + type: object + properties: + cast: + description: 'Array of cast members.' + type: array + items: + type: object + properties: + name: + description: 'Cast member name.' + type: string + example: 'Natasha Hovey' + role: + description: 'Cast member role.' + type: string + example: Cheryl + content_rating: + description: "MPAA rating\n\nOption descriptions:\n * `G` - Rated G\n * `NC-17` - Rated NC-17\n * `NR` - Not rated\n * `PG` - Rated PG\n * `PG-13` - Rated PG-13\n * `R` - Rated R\n * `UR` - Unrated\n * `X` - Rated X\n" + type: string + example: NR + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: 'Description, or tagline, for the movie.' + type: string + director: + description: 'Name of the director.' + type: string + example: 'Lamberto Bava' + genres: + description: 'Array of movie genres.' + type: array + items: + type: string + imdb: + description: 'IMDB URL' + type: string + example: 'https://www.imdb.com/title/tt0089013/' + is_kid_friendly: + description: 'Is this movie kid friendly?' + type: boolean + name: + description: 'Name of the movie.' + type: string + example: Demons + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + example: '56' + runtime: + description: 'Movie runtime, in `HHhr MMmin` format.' + type: string + example: '1hr 20min' + trailer: + description: 'Trailer URL' + type: string + example: 'https://www.youtube.com/watch?v=_cNjTdFHL8E' + nullable: true + required: + - description + - name + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/movie' + 201: + description: 'Standard request.' + 400: + description: "There are two ways that this status code can be encountered:\n * If there is a problem with the request.\n * If the IMDB URL could not be validated." + content: + application/mill.example.movie+json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - create + x-mill-visibility-private: true + '/theaters/{id}': + get: + summary: 'Get a single movie theater.' + description: 'Return information on a specific movie theater.' + operationId: getTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/theater' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: 'If the movie theater could not be found.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + patch: + summary: 'Update a movie theater.' + description: 'Update a movie theaters'' data.' + operationId: patchTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + requestBody: + required: true + content: + application/mill.example.theater+json: + schema: + type: object + properties: + address: + description: 'Theater address' + type: string + example: '2548 Central Park Ave, Yonkers, NY 10710' + name: + description: 'Name of the theater.' + type: string + example: 'Alamo Drafthouse Cinema - Yonkers' + phone_number: + description: 'Theater phone number' + type: string + example: '(914) 226-3082' + required: + - address + - name + - phone_number + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/theater' + 400: + description: 'If there is a problem with the request.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/error' + 404: + description: 'If the movie movie could not be found.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - create + x-mill-visibility-private: true + delete: + summary: 'Delete a movie theater.' + description: 'Delete a movie theater.' + operationId: deleteTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + responses: + 204: + description: 'Standard request.' + 404: + description: 'If the movie theater could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - delete + /theaters: + get: + summary: 'Get movie theaters.' + description: 'Returns all movie theatres for a specific location.' + operationId: getTheaters + tags: + - Theaters + parameters: + - + name: location + in: query + description: 'Location you want theaters in.' + required: true + schema: + type: string + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.theater+json: + schema: + type: array + items: + $ref: '#/components/schemas/theater' + 400: + description: 'If the location is invalid.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + post: + summary: 'Create a movie theater.' + description: 'Create a new movie theater.' + operationId: postTheaters + tags: + - Theaters + requestBody: + required: true + content: + application/mill.example.theater+json: + schema: + type: object + properties: + address: + description: 'Theater address' + type: string + example: '2548 Central Park Ave, Yonkers, NY 10710' + name: + description: 'Name of the theater.' + type: string + example: 'Alamo Drafthouse Cinema - Yonkers' + phone_number: + description: 'Theater phone number' + type: string + example: '(914) 226-3082' + required: + - address + - name + - phone_number + responses: + 200: + description: 'Standard request.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/theater' + 400: + description: 'If there is a problem with the request.' + content: + application/mill.example.theater+json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - create + x-mill-visibility-private: true +components: + securitySchemes: + bearer: + type: http + scheme: bearer + bearerFormat: bearer + oauth2: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: /oauth/authorize + tokenUrl: /oauth/access_token + scopes: + create: Create + delete: Delete + edit: Edit + public: Public + clientCredentials: + tokenUrl: /oauth/authorize/client + scopes: + create: Create + delete: Delete + edit: Edit + public: Public + schemas: + coded-error: + properties: + error: + description: 'User-friendly error message' + type: string + error_code: + description: 'Error code' + type: number + error: + properties: + error: + description: 'User-friendly error message' + type: string + movie: + properties: + cast: + description: 'Cast. This data requires a bearer token with the `public` scope.' + type: array + items: + type: string + content_rating: + description: 'MPAA rating' + type: string + example: G + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: Description + type: string + director: + description: 'Director. This data requires a bearer token with the `public` scope.' + allOf: + - + $ref: '#/components/schemas/person' + external_urls: + description: 'External URLs. This data requires a bearer token with the `public` scope.' + type: array + items: + type: object + properties: + imdb: + description: 'IMDB URL. This data requires a bearer token with the `public` scope.' + type: string + trailer: + description: 'Trailer URL. This data requires a bearer token with the `public` scope.' + type: string + genres: + description: Genres + type: array + items: + type: string + id: + description: 'Unique ID' + type: number + kid_friendly: + description: 'Kid friendly?' + type: boolean + example: 'false' + name: + description: Name + type: string + purchase: + type: object + properties: + url: + description: 'URL to purchase the film.' + type: string + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + runtime: + description: Runtime + type: string + showtimes: + description: 'Non-theater specific showtimes' + type: array + items: + type: string + theaters: + description: 'Theaters the movie is currently showing in' + type: array + items: + type: string + uri: + description: 'Movie URI' + type: string + person: + properties: + id: + description: 'Unique ID' + type: number + imdb: + description: 'IMDB URL' + type: string + name: + description: Name + type: string + uri: + description: 'Person URI' + type: string + theater: + properties: + address: + description: Address + type: string + id: + description: 'Unique ID' + type: number + movies: + description: 'Movies currently playing' + type: array + items: + type: string + name: + description: Name + type: string + phone_number: + description: 'Phone number' + type: string + showtimes: + description: 'Non-movie specific showtimes' + type: array + items: + type: string + uri: + description: 'Theater URI' + type: string +security: + - + oauth2: + - create + - delete + - edit + - public diff --git a/resources/examples/Showtimes/blueprints/1.1.3/errors-public-only-all-capabilities.md b/resources/examples/Showtimes/compiled/1.1.3/errors-public-only-all-vendor-tags.md similarity index 76% rename from resources/examples/Showtimes/blueprints/1.1.3/errors-public-only-all-capabilities.md rename to resources/examples/Showtimes/compiled/1.1.3/errors-public-only-all-vendor-tags.md index 1134cf0..9bd792f 100644 --- a/resources/examples/Showtimes/blueprints/1.1.3/errors-public-only-all-capabilities.md +++ b/resources/examples/Showtimes/compiled/1.1.3/errors-public-only-all-vendor-tags.md @@ -1,6 +1,6 @@ # Errors: Mill unit test API, Showtimes ## Movies -| Error Code | URI | Method | HTTP Code | Description | +| Error Code | Path | Method | HTTP Code | Description | | :--- | :--- | :--- | :--- | :--- | | 666 | /movies/{id} | PATCH | 403 Forbidden | If the user is not allowed to edit that movie. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1.3/errors-public-only-matched-capabilities.md b/resources/examples/Showtimes/compiled/1.1.3/errors-public-only-matched-vendor-tags.md similarity index 76% rename from resources/examples/Showtimes/blueprints/1.1.3/errors-public-only-matched-capabilities.md rename to resources/examples/Showtimes/compiled/1.1.3/errors-public-only-matched-vendor-tags.md index 1134cf0..9bd792f 100644 --- a/resources/examples/Showtimes/blueprints/1.1.3/errors-public-only-matched-capabilities.md +++ b/resources/examples/Showtimes/compiled/1.1.3/errors-public-only-matched-vendor-tags.md @@ -1,6 +1,6 @@ # Errors: Mill unit test API, Showtimes ## Movies -| Error Code | URI | Method | HTTP Code | Description | +| Error Code | Path | Method | HTTP Code | Description | | :--- | :--- | :--- | :--- | :--- | | 666 | /movies/{id} | PATCH | 403 Forbidden | If the user is not allowed to edit that movie. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1.3/errors-public-only-unmatched-capabilities.md b/resources/examples/Showtimes/compiled/1.1.3/errors-public-only-unmatched-vendor-tags.md similarity index 76% rename from resources/examples/Showtimes/blueprints/1.1.3/errors-public-only-unmatched-capabilities.md rename to resources/examples/Showtimes/compiled/1.1.3/errors-public-only-unmatched-vendor-tags.md index 1134cf0..9bd792f 100644 --- a/resources/examples/Showtimes/blueprints/1.1.3/errors-public-only-unmatched-capabilities.md +++ b/resources/examples/Showtimes/compiled/1.1.3/errors-public-only-unmatched-vendor-tags.md @@ -1,6 +1,6 @@ # Errors: Mill unit test API, Showtimes ## Movies -| Error Code | URI | Method | HTTP Code | Description | +| Error Code | Path | Method | HTTP Code | Description | | :--- | :--- | :--- | :--- | :--- | | 666 | /movies/{id} | PATCH | 403 Forbidden | If the user is not allowed to edit that movie. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1.3/errors.md b/resources/examples/Showtimes/compiled/1.1.3/errors.md similarity index 82% rename from resources/examples/Showtimes/blueprints/1.1.3/errors.md rename to resources/examples/Showtimes/compiled/1.1.3/errors.md index 844af81..253dade 100644 --- a/resources/examples/Showtimes/blueprints/1.1.3/errors.md +++ b/resources/examples/Showtimes/compiled/1.1.3/errors.md @@ -1,7 +1,7 @@ # Errors: Mill unit test API, Showtimes ## Movies -| Error Code | URI | Method | HTTP Code | Description | +| Error Code | Path | Method | HTTP Code | Description | | :--- | :--- | :--- | :--- | :--- | | 666 | /movies/{id} | PATCH | 403 Forbidden | If the user is not allowed to edit that movie. | | 1337 | /movies/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1.3/representations/Coded error.apib b/resources/examples/Showtimes/compiled/1.1.3/representations/Coded error.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1.3/representations/Coded error.apib rename to resources/examples/Showtimes/compiled/1.1.3/representations/Coded error.apib diff --git a/resources/examples/Showtimes/blueprints/1.1.3/representations/Error.apib b/resources/examples/Showtimes/compiled/1.1.3/representations/Error.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1.3/representations/Error.apib rename to resources/examples/Showtimes/compiled/1.1.3/representations/Error.apib diff --git a/resources/examples/Showtimes/blueprints/1.1.3/representations/Movie.apib b/resources/examples/Showtimes/compiled/1.1.3/representations/Movie.apib similarity index 70% rename from resources/examples/Showtimes/blueprints/1.1.3/representations/Movie.apib rename to resources/examples/Showtimes/compiled/1.1.3/representations/Movie.apib index b605709..4e6006b 100644 --- a/resources/examples/Showtimes/blueprints/1.1.3/representations/Movie.apib +++ b/resources/examples/Showtimes/compiled/1.1.3/representations/Movie.apib @@ -12,10 +12,11 @@ + `X` - `description` (string) - Description - `director` (Person) - Director. This data requires a bearer token with the `public` scope. -- `external_urls` (object) - External URLs - - `imdb` (string) - IMDB URL - - `trailer` (string) - Trailer URL -- `genres` (array) - Genres +- `external_urls` (array) - External URLs. This data requires a bearer token with the `public` scope. + - (object) + - `imdb` (string) - IMDB URL. This data requires a bearer token with the `public` scope. + - `trailer` (string) - Trailer URL. This data requires a bearer token with the `public` scope. +- `genres` (array[string]) - Genres - `id` (number) - Unique ID - `kid_friendly`: `false` (boolean) - Kid friendly? - `name` (string) - Name diff --git a/resources/examples/Showtimes/blueprints/1.1.3/representations/Person.apib b/resources/examples/Showtimes/compiled/1.1.3/representations/Person.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1.3/representations/Person.apib rename to resources/examples/Showtimes/compiled/1.1.3/representations/Person.apib diff --git a/resources/examples/Showtimes/blueprints/1.1.3/representations/Theater.apib b/resources/examples/Showtimes/compiled/1.1.3/representations/Theater.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1.3/representations/Theater.apib rename to resources/examples/Showtimes/compiled/1.1.3/representations/Theater.apib diff --git a/resources/examples/Showtimes/blueprints/1.1.3/resources/Movies.apib b/resources/examples/Showtimes/compiled/1.1.3/resources/Movies.apib similarity index 56% rename from resources/examples/Showtimes/blueprints/1.1.3/resources/Movies.apib rename to resources/examples/Showtimes/compiled/1.1.3/resources/Movies.apib index 8ce0ace..97e9920 100644 --- a/resources/examples/Showtimes/blueprints/1.1.3/resources/Movies.apib +++ b/resources/examples/Showtimes/compiled/1.1.3/resources/Movies.apib @@ -20,10 +20,10 @@ sem malesuada magna mollis euismod. + Parameters - `id` (number, required) - Movie ID -+ Response 200 (application/mill.example.movie) ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 304 (application/mill.example.movie) -+ Response 404 (application/mill.example.movie) ++ Response 304 (application/mill.example.movie+json) ++ Response 404 (application/mill.example.movie+json) There are three ways that this status code can be encountered: * If the movie could not be found. * For no reason. @@ -51,10 +51,10 @@ sem malesuada magna mollis euismod. + Parameters - `id` (number, required) - Movie ID -+ Response 200 (application/mill.example.movie) ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 304 (application/mill.example.movie) -+ Response 404 (application/mill.example.movie) ++ Response 304 (application/mill.example.movie+json) ++ Response 404 (application/mill.example.movie+json) There are three ways that this status code can be encountered: * If the movie could not be found. * For no reason. @@ -70,57 +70,49 @@ This action requires a bearer token with the `edit` scope. - `id` (number, required) - Movie ID + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL -+ Response 200 (application/mill.example.movie) + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 202 (application/mill.example.movie) ++ Response 202 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 400 (application/mill.example.movie) ++ Response 400 (application/mill.example.movie+json) There are two ways that this status code can be encountered: * If there is a problem with the request. * If the IMDB URL could not be validated. + Attributes (Error) -+ Response 403 (application/mill.example.movie) ++ Response 403 (application/mill.example.movie+json) There are two ways that this status code can be encountered: * If something cool happened. Unique error code: 1337 * If the user is not allowed to edit that movie. Unique error code: 666 + Attributes (Coded error) -+ Response 404 (application/mill.example.movie) ++ Response 404 (application/mill.example.movie+json) There are two ways that this status code can be encountered: * If the movie could not be found. * If the trailer URL could not be validated. + Attributes (Error) -### Delete a movie. [DELETE] -Delete a movie. - -This action requires a bearer token with the `delete` scope. - -+ Parameters - - `id` (number, required) - Movie ID -+ Response 204 (application/json) -+ Response 404 (application/json) - + Attributes (Error) - ## Movies [/movies] Information on a specific movie. @@ -133,9 +125,9 @@ Returns all movies for a specific location. + Attributes - `location` (string, required) - Location you want movies for. - `page` (number) - Page of results to pull. -+ Response 200 (application/mill.example.movie) ++ Response 200 (application/mill.example.movie+json) + Attributes (array[Movie]) -+ Response 400 (application/mill.example.movie) ++ Response 400 (application/mill.example.movie+json) + Attributes (Error) ### Create a movie. [POST] @@ -145,30 +137,33 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL -+ Response 200 (application/mill.example.movie) + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL ++ Response 200 (application/mill.example.movie+json) + Attributes (Movie) -+ Response 201 (application/mill.example.movie) -+ Response 400 (application/mill.example.movie) ++ Response 201 (application/mill.example.movie+json) ++ Response 400 (application/mill.example.movie+json) There are two ways that this status code can be encountered: * If there is a problem with the request. * If the IMDB URL could not be validated. diff --git a/resources/examples/Showtimes/blueprints/1.1.3/resources/Theaters.apib b/resources/examples/Showtimes/compiled/1.1.3/resources/Theaters.apib similarity index 57% rename from resources/examples/Showtimes/blueprints/1.1.3/resources/Theaters.apib rename to resources/examples/Showtimes/compiled/1.1.3/resources/Theaters.apib index e2b9e2f..d66956e 100644 --- a/resources/examples/Showtimes/blueprints/1.1.3/resources/Theaters.apib +++ b/resources/examples/Showtimes/compiled/1.1.3/resources/Theaters.apib @@ -9,10 +9,10 @@ Return information on a specific movie theater. + Parameters - `id` (number, required) - Theater ID -+ Response 200 (application/mill.example.theater) ++ Response 200 (application/mill.example.theater+json) + Attributes (Theater) -+ Response 304 (application/mill.example.theater) -+ Response 404 (application/mill.example.theater) ++ Response 304 (application/mill.example.theater+json) ++ Response 404 (application/mill.example.theater+json) + Attributes (Error) ### Update a movie theater. [PATCH] @@ -24,14 +24,14 @@ This action requires a bearer token with the `create` scope. - `id` (number, required) - Theater ID + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number -+ Response 200 (application/mill.example.theater) + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number ++ Response 200 (application/mill.example.theater+json) + Attributes (Theater) -+ Response 400 (application/mill.example.theater) ++ Response 400 (application/mill.example.theater+json) + Attributes (Error) -+ Response 404 (application/mill.example.theater) ++ Response 404 (application/mill.example.theater+json) + Attributes (Error) ### Delete a movie theater. [DELETE] @@ -56,9 +56,9 @@ Returns all movie theatres for a specific location. + Request + Attributes - `location` (string, required) - Location you want theaters in. -+ Response 200 (application/mill.example.theater) ++ Response 200 (application/mill.example.theater+json) + Attributes (array[Theater]) -+ Response 400 (application/mill.example.theater) ++ Response 400 (application/mill.example.theater+json) + Attributes (Error) ### Create a movie theater. [POST] @@ -68,10 +68,10 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number -+ Response 200 (application/mill.example.theater) + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number ++ Response 200 (application/mill.example.theater+json) + Attributes (Theater) -+ Response 400 (application/mill.example.theater) ++ Response 400 (application/mill.example.theater+json) + Attributes (Error) \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1/api.apib b/resources/examples/Showtimes/compiled/1.1/api.apib similarity index 70% rename from resources/examples/Showtimes/blueprints/1.1/api.apib rename to resources/examples/Showtimes/compiled/1.1/api.apib index 8a2a89a..5ca6218 100644 --- a/resources/examples/Showtimes/blueprints/1.1/api.apib +++ b/resources/examples/Showtimes/compiled/1.1/api.apib @@ -67,25 +67,28 @@ This action requires a bearer token with the `edit` scope. - `id` (number, required) - Movie ID + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL + Response 200 (application/json) + Attributes (Movie) + Response 400 (application/json) @@ -131,26 +134,29 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL + Response 200 (application/json) + Attributes (Movie) + Response 400 (application/json) @@ -185,9 +191,9 @@ This action requires a bearer token with the `create` scope. - `id` (number, required) - Theater ID + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number + Response 200 (application/json) + Attributes (Theater) + Response 400 (application/json) @@ -231,9 +237,9 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number + Response 200 (application/json) + Attributes (Theater) + Response 400 (application/json) @@ -261,11 +267,12 @@ This action requires a bearer token with the `create` scope. + `X` - `description` (string) - Description - `director` (Person) - Director. This data requires a bearer token with the `public` scope. -- `external_urls` (object) - External URLs - - `imdb` (string) - IMDB URL - - `tickets` (string) - Tickets URL - - `trailer` (string) - Trailer URL -- `genres` (array) - Genres +- `external_urls` (array) - External URLs. This data requires a bearer token with the `public` scope. + - (object) + - `imdb` (string) - IMDB URL. This data requires a bearer token with the `public` scope. + - `tickets` (string) - Tickets URL. This data requires a bearer token with the `public` scope. + - `trailer` (string) - Trailer URL. This data requires a bearer token with the `public` scope. +- `genres` (array[string]) - Genres - `id` (number) - Unique ID - `kid_friendly`: `false` (boolean) - Kid friendly? - `name` (string) - Name diff --git a/resources/examples/Showtimes/compiled/1.1/api.yaml b/resources/examples/Showtimes/compiled/1.1/api.yaml new file mode 100644 index 0000000..f2d6ab7 --- /dev/null +++ b/resources/examples/Showtimes/compiled/1.1/api.yaml @@ -0,0 +1,732 @@ +openapi: 3.0.0 +info: + title: 'Mill unit test API, Showtimes' + version: '1.1' + contact: + name: 'Get help!' + email: support@example.com + url: 'https://developer.example.com/help' +tags: + - + name: Movies + - + name: Theaters +servers: + - + url: 'https://api.example.com' + description: Production + - + url: 'https://api.example.local' + description: Development +paths: + '/movie/{id}': + get: + summary: 'Get a single movie.' + description: "Return information on a specific movie.\n\nDonec id elit non mi porta gravida at eget metus. Cras mattis consectetur purus sit amet fermentum. Lorem\nipsum dolor sit amet, consectetur adipiscing elit. Etiam porta sem malesuada magna mollis euismod. Duis\nmollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Etiam porta\nsem malesuada magna mollis euismod.\n\n```\n[\n {\"id\": \"fizzbuzz\"}\n]\n```" + operationId: getMovieId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/movie' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: 'If the movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + '/movies/{id}': + get: + summary: 'Get a single movie.' + description: "Return information on a specific movie.\n\nDonec id elit non mi porta gravida at eget metus. Cras mattis consectetur purus sit amet fermentum. Lorem\nipsum dolor sit amet, consectetur adipiscing elit. Etiam porta sem malesuada magna mollis euismod. Duis\nmollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Etiam porta\nsem malesuada magna mollis euismod.\n\n```\n[\n {\"id\": \"fizzbuzz\"}\n]\n```" + operationId: getMoviesId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/movie' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: 'If the movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + patch: + summary: 'Update a movie.' + description: 'Update a movies data.' + operationId: patchMoviesId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + cast: + description: 'Array of cast members.' + type: array + items: + type: object + properties: + name: + description: 'Cast member name.' + type: string + example: 'Natasha Hovey' + role: + description: 'Cast member role.' + type: string + example: Cheryl + content_rating: + description: "MPAA rating\n\nOption descriptions:\n * `G` - Rated G\n * `NC-17` - Rated NC-17\n * `NR` - Not rated\n * `PG` - Rated PG\n * `PG-13` - Rated PG-13\n * `R` - Rated R\n * `UR` - Unrated\n * `X` - Rated X\n" + type: string + example: NR + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: 'Description, or tagline, for the movie.' + type: string + director: + description: 'Name of the director.' + type: string + example: 'Lamberto Bava' + genres: + description: 'Array of movie genres.' + type: array + items: + type: string + is_kid_friendly: + description: 'Is this movie kid friendly?' + type: boolean + name: + description: 'Name of the movie.' + type: string + example: Demons + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + example: '56' + runtime: + description: 'Movie runtime, in `HHhr MMmin` format.' + type: string + example: '1hr 20min' + trailer: + description: 'Trailer URL' + type: string + example: 'https://www.youtube.com/watch?v=_cNjTdFHL8E' + nullable: true + required: + - description + - name + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/movie' + 400: + description: "There are two ways that this status code can be encountered:\n * If there is a problem with the request.\n * If the IMDB URL could not be validated." + content: + application/json: + schema: + $ref: '#/components/schemas/error' + 404: + description: 'If the movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - edit + x-mill-visibility-private: true + delete: + summary: 'Delete a movie.' + description: 'Delete a movie.' + operationId: deleteMoviesId + tags: + - Movies + parameters: + - + name: id + in: path + description: 'Movie ID' + required: true + schema: + type: number + example: '1234' + responses: + 204: + description: 'Standard request.' + 404: + description: 'If the movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - delete + x-mill-vendortags: + - 'tag:DELETE_CONTENT' + /movies: + get: + summary: 'Get movies.' + description: 'Returns all movies for a specific location.' + operationId: getMovies + tags: + - Movies + parameters: + - + name: location + in: query + description: 'Location you want movies for.' + required: true + schema: + type: string + - + name: page + in: query + description: 'Page of results to pull.' + required: false + schema: + type: number + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/movie' + 400: + description: 'If the location is invalid.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + post: + summary: 'Create a movie.' + description: 'Create a new movie.' + operationId: postMovies + tags: + - Movies + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + cast: + description: 'Array of cast members.' + type: array + items: + type: object + properties: + name: + description: 'Cast member name.' + type: string + example: 'Natasha Hovey' + role: + description: 'Cast member role.' + type: string + example: Cheryl + content_rating: + description: "MPAA rating\n\nOption descriptions:\n * `G` - Rated G\n * `NC-17` - Rated NC-17\n * `NR` - Not rated\n * `PG` - Rated PG\n * `PG-13` - Rated PG-13\n * `R` - Rated R\n * `UR` - Unrated\n * `X` - Rated X\n" + type: string + example: NR + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: 'Description, or tagline, for the movie.' + type: string + director: + description: 'Name of the director.' + type: string + example: 'Lamberto Bava' + genres: + description: 'Array of movie genres.' + type: array + items: + type: string + imdb: + description: 'IMDB URL' + type: string + example: 'https://www.imdb.com/title/tt0089013/' + is_kid_friendly: + description: 'Is this movie kid friendly?' + type: boolean + name: + description: 'Name of the movie.' + type: string + example: Demons + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + example: '56' + runtime: + description: 'Movie runtime, in `HHhr MMmin` format.' + type: string + example: '1hr 20min' + trailer: + description: 'Trailer URL' + type: string + example: 'https://www.youtube.com/watch?v=_cNjTdFHL8E' + nullable: true + required: + - description + - name + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/movie' + 400: + description: "There are two ways that this status code can be encountered:\n * If there is a problem with the request.\n * If the IMDB URL could not be validated." + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - create + x-mill-visibility-private: true + '/theaters/{id}': + get: + summary: 'Get a single movie theater.' + description: 'Return information on a specific movie theater.' + operationId: getTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/theater' + 304: + description: 'If no content has been modified since the supplied Last-Modified header.' + 404: + description: 'If the movie theater could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + patch: + summary: 'Update a movie theater.' + description: 'Update a movie theaters'' data.' + operationId: patchTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + address: + description: 'Theater address' + type: string + example: '2548 Central Park Ave, Yonkers, NY 10710' + name: + description: 'Name of the theater.' + type: string + example: 'Alamo Drafthouse Cinema - Yonkers' + phone_number: + description: 'Theater phone number' + type: string + example: '(914) 226-3082' + required: + - address + - name + - phone_number + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/theater' + 400: + description: 'If there is a problem with the request.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + 404: + description: 'If the movie movie could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + 403: + description: 'If something cool happened. Returns a unique error code of `1337`.' + content: + application/json: + schema: + $ref: '#/components/schemas/coded-error' + security: + - + oauth2: + - create + x-mill-visibility-private: true + delete: + summary: 'Delete a movie theater.' + description: 'Delete a movie theater.' + operationId: deleteTheatersId + tags: + - Theaters + parameters: + - + name: id + in: path + description: 'Theater ID' + required: true + schema: + type: number + example: '1234' + responses: + 204: + description: 'Standard request.' + 404: + description: 'If the movie theater could not be found.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - delete + /theaters: + get: + summary: 'Get movie theaters.' + description: 'Returns all movie theatres for a specific location.' + operationId: getTheaters + tags: + - Theaters + parameters: + - + name: location + in: query + description: 'Location you want theaters in.' + required: true + schema: + type: string + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/theater' + 400: + description: 'If the location is invalid.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + x-mill-visibility-private: true + post: + summary: 'Create a movie theater.' + description: 'Create a new movie theater.' + operationId: postTheaters + tags: + - Theaters + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + address: + description: 'Theater address' + type: string + example: '2548 Central Park Ave, Yonkers, NY 10710' + name: + description: 'Name of the theater.' + type: string + example: 'Alamo Drafthouse Cinema - Yonkers' + phone_number: + description: 'Theater phone number' + type: string + example: '(914) 226-3082' + required: + - address + - name + - phone_number + responses: + 200: + description: 'Standard request.' + content: + application/json: + schema: + $ref: '#/components/schemas/theater' + 400: + description: 'If there is a problem with the request.' + content: + application/json: + schema: + $ref: '#/components/schemas/error' + security: + - + oauth2: + - create + x-mill-visibility-private: true +components: + securitySchemes: + bearer: + type: http + scheme: bearer + bearerFormat: bearer + oauth2: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: /oauth/authorize + tokenUrl: /oauth/access_token + scopes: + create: Create + delete: Delete + edit: Edit + public: Public + clientCredentials: + tokenUrl: /oauth/authorize/client + scopes: + create: Create + delete: Delete + edit: Edit + public: Public + schemas: + coded-error: + properties: + error: + description: 'User-friendly error message' + type: string + error_code: + description: 'Error code' + type: number + error: + properties: + error: + description: 'User-friendly error message' + type: string + movie: + properties: + cast: + description: 'Cast. This data requires a bearer token with the `public` scope.' + type: array + items: + type: string + content_rating: + description: 'MPAA rating' + type: string + example: G + enum: + - G + - NC-17 + - NR + - PG + - PG-13 + - R + - UR + - X + description: + description: Description + type: string + director: + description: 'Director. This data requires a bearer token with the `public` scope.' + allOf: + - + $ref: '#/components/schemas/person' + external_urls: + description: 'External URLs. This data requires a bearer token with the `public` scope.' + type: array + items: + type: object + properties: + imdb: + description: 'IMDB URL. This data requires a bearer token with the `public` scope.' + type: string + tickets: + description: 'Tickets URL. This data requires a bearer token with the `public` scope.' + type: string + trailer: + description: 'Trailer URL. This data requires a bearer token with the `public` scope.' + type: string + genres: + description: Genres + type: array + items: + type: string + id: + description: 'Unique ID' + type: number + kid_friendly: + description: 'Kid friendly?' + type: boolean + example: 'false' + name: + description: Name + type: string + purchase: + type: object + properties: + url: + description: 'URL to purchase the film.' + type: string + rotten_tomatoes_score: + description: 'Rotten Tomatoes score' + type: number + runtime: + description: Runtime + type: string + showtimes: + description: 'Non-theater specific showtimes' + type: array + items: + type: string + theaters: + description: 'Theaters the movie is currently showing in' + type: array + items: + type: string + uri: + description: 'Movie URI' + type: string + person: + properties: + id: + description: 'Unique ID' + type: number + imdb: + description: 'IMDB URL' + type: string + name: + description: Name + type: string + uri: + description: 'Person URI' + type: string + theater: + properties: + address: + description: Address + type: string + id: + description: 'Unique ID' + type: number + movies: + description: 'Movies currently playing' + type: array + items: + type: string + name: + description: Name + type: string + phone_number: + description: 'Phone number' + type: string + showtimes: + description: 'Non-movie specific showtimes' + type: array + items: + type: string + uri: + description: 'Theater URI' + type: string +security: + - + oauth2: + - create + - delete + - edit + - public diff --git a/resources/examples/Showtimes/compiled/1.1/errors-public-only-all-vendor-tags.md b/resources/examples/Showtimes/compiled/1.1/errors-public-only-all-vendor-tags.md new file mode 100644 index 0000000..b0b3d7e --- /dev/null +++ b/resources/examples/Showtimes/compiled/1.1/errors-public-only-all-vendor-tags.md @@ -0,0 +1,6 @@ +# Errors: Mill unit test API, Showtimes + +## Theaters +| Error Code | Path | Method | HTTP Code | Description | +| :--- | :--- | :--- | :--- | :--- | +| 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/compiled/1.1/errors-public-only-matched-vendor-tags.md b/resources/examples/Showtimes/compiled/1.1/errors-public-only-matched-vendor-tags.md new file mode 100644 index 0000000..b0b3d7e --- /dev/null +++ b/resources/examples/Showtimes/compiled/1.1/errors-public-only-matched-vendor-tags.md @@ -0,0 +1,6 @@ +# Errors: Mill unit test API, Showtimes + +## Theaters +| Error Code | Path | Method | HTTP Code | Description | +| :--- | :--- | :--- | :--- | :--- | +| 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/compiled/1.1/errors-public-only-unmatched-vendor-tags.md b/resources/examples/Showtimes/compiled/1.1/errors-public-only-unmatched-vendor-tags.md new file mode 100644 index 0000000..b0b3d7e --- /dev/null +++ b/resources/examples/Showtimes/compiled/1.1/errors-public-only-unmatched-vendor-tags.md @@ -0,0 +1,6 @@ +# Errors: Mill unit test API, Showtimes + +## Theaters +| Error Code | Path | Method | HTTP Code | Description | +| :--- | :--- | :--- | :--- | :--- | +| 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.0/errors.md b/resources/examples/Showtimes/compiled/1.1/errors.md similarity index 74% rename from resources/examples/Showtimes/blueprints/1.0/errors.md rename to resources/examples/Showtimes/compiled/1.1/errors.md index 2e55e09..b0b3d7e 100644 --- a/resources/examples/Showtimes/blueprints/1.0/errors.md +++ b/resources/examples/Showtimes/compiled/1.1/errors.md @@ -1,6 +1,6 @@ # Errors: Mill unit test API, Showtimes ## Theaters -| Error Code | URI | Method | HTTP Code | Description | +| Error Code | Path | Method | HTTP Code | Description | | :--- | :--- | :--- | :--- | :--- | | 1337 | /theaters/{id} | PATCH | 403 Forbidden | If something cool happened. | \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1/representations/Coded error.apib b/resources/examples/Showtimes/compiled/1.1/representations/Coded error.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1/representations/Coded error.apib rename to resources/examples/Showtimes/compiled/1.1/representations/Coded error.apib diff --git a/resources/examples/Showtimes/blueprints/1.1/representations/Error.apib b/resources/examples/Showtimes/compiled/1.1/representations/Error.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1/representations/Error.apib rename to resources/examples/Showtimes/compiled/1.1/representations/Error.apib diff --git a/resources/examples/Showtimes/compiled/1.1/representations/Movie.apib b/resources/examples/Showtimes/compiled/1.1/representations/Movie.apib new file mode 100644 index 0000000..4b4a780 --- /dev/null +++ b/resources/examples/Showtimes/compiled/1.1/representations/Movie.apib @@ -0,0 +1,30 @@ +## Movie +- `cast` (array[Person]) - Cast. This data requires a bearer token with the `public` scope. +- `content_rating`: `G` (enum[string]) - MPAA rating + + Members + + `G` + + `NC-17` + + `NR` + + `PG` + + `PG-13` + + `R` + + `UR` + + `X` +- `description` (string) - Description +- `director` (Person) - Director. This data requires a bearer token with the `public` scope. +- `external_urls` (array) - External URLs. This data requires a bearer token with the `public` scope. + - (object) + - `imdb` (string) - IMDB URL. This data requires a bearer token with the `public` scope. + - `tickets` (string) - Tickets URL. This data requires a bearer token with the `public` scope. + - `trailer` (string) - Trailer URL. This data requires a bearer token with the `public` scope. +- `genres` (array[string]) - Genres +- `id` (number) - Unique ID +- `kid_friendly`: `false` (boolean) - Kid friendly? +- `name` (string) - Name +- `purchase` (object) + - `url` (string) - URL to purchase the film. +- `rotten_tomatoes_score` (number) - Rotten Tomatoes score +- `runtime` (string) - Runtime +- `showtimes` (array) - Non-theater specific showtimes +- `theaters` (array[Theater]) - Theaters the movie is currently showing in +- `uri` (string) - Movie URI \ No newline at end of file diff --git a/resources/examples/Showtimes/blueprints/1.1/representations/Person.apib b/resources/examples/Showtimes/compiled/1.1/representations/Person.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1/representations/Person.apib rename to resources/examples/Showtimes/compiled/1.1/representations/Person.apib diff --git a/resources/examples/Showtimes/blueprints/1.1/representations/Theater.apib b/resources/examples/Showtimes/compiled/1.1/representations/Theater.apib similarity index 100% rename from resources/examples/Showtimes/blueprints/1.1/representations/Theater.apib rename to resources/examples/Showtimes/compiled/1.1/representations/Theater.apib diff --git a/resources/examples/Showtimes/blueprints/1.1.1/resources/Movies.apib b/resources/examples/Showtimes/compiled/1.1/resources/Movies.apib similarity index 65% rename from resources/examples/Showtimes/blueprints/1.1.1/resources/Movies.apib rename to resources/examples/Showtimes/compiled/1.1/resources/Movies.apib index 4d43ff0..6fd016a 100644 --- a/resources/examples/Showtimes/blueprints/1.1.1/resources/Movies.apib +++ b/resources/examples/Showtimes/compiled/1.1/resources/Movies.apib @@ -62,26 +62,28 @@ This action requires a bearer token with the `edit` scope. - `id` (number, required) - Movie ID + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL + Response 200 (application/json) + Attributes (Movie) + Response 400 (application/json) @@ -127,26 +129,29 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `cast` (array) - Array of names of the cast. - - `content_rating` (enum[string]) - MPAA rating + - `cast` (array) - Array of cast members. + - (object) + - `name`: `Natasha Hovey` (string) - Cast member name. + - `role`: `Cheryl` (string) - Cast member role. + - `content_rating`: `NR` (enum[string]) - MPAA rating + Members - + `G` - + `NC-17` - + `NR` - + `PG` - + `PG-13` - + `R` - + `UR` - + `X` + + `G` - Rated G + + `NC-17` - Rated NC-17 + + `NR` - Not rated + + `PG` - Rated PG + + `PG-13` - Rated PG-13 + + `R` - Rated R + + `UR` - Unrated + + `X` - Rated X - `description` (string, required) - Description, or tagline, for the movie. - - `director` (string) - Name of the director. + - `director`: `Lamberto Bava` (string) - Name of the director. - `genres` (array) - Array of movie genres. - - `imdb` (string) - IMDB URL + - `imdb`: `https://www.imdb.com/title/tt0089013/` (string) - IMDB URL - `is_kid_friendly` (boolean) - Is this movie kid friendly? - - `name` (string, required) - Name of the movie. - - `rotten_tomatoes_score` (number) - Rotten Tomatoes score - - `runtime` (string) - Movie runtime, in `HHhr MMmin` format. - - `trailer` (string, nullable) - Trailer URL + - `name`: `Demons` (string, required) - Name of the movie. + - `rotten_tomatoes_score`: `56` (number) - Rotten Tomatoes score + - `runtime`: `1hr 20min` (string) - Movie runtime, in `HHhr MMmin` format. + - `trailer`: `https://www.youtube.com/watch?v=_cNjTdFHL8E` (string, nullable) - Trailer URL + Response 200 (application/json) + Attributes (Movie) + Response 400 (application/json) diff --git a/resources/examples/Showtimes/blueprints/1.1.1/resources/Theaters.apib b/resources/examples/Showtimes/compiled/1.1/resources/Theaters.apib similarity index 77% rename from resources/examples/Showtimes/blueprints/1.1.1/resources/Theaters.apib rename to resources/examples/Showtimes/compiled/1.1/resources/Theaters.apib index 4269a5b..f90eeae 100644 --- a/resources/examples/Showtimes/blueprints/1.1.1/resources/Theaters.apib +++ b/resources/examples/Showtimes/compiled/1.1/resources/Theaters.apib @@ -24,9 +24,9 @@ This action requires a bearer token with the `create` scope. - `id` (number, required) - Theater ID + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number + Response 200 (application/json) + Attributes (Theater) + Response 400 (application/json) @@ -70,9 +70,9 @@ This action requires a bearer token with the `create` scope. + Request + Attributes - - `address` (string, required) - Theater address - - `name` (string, required) - Name of the theater. - - `phone_number` (string, required) - Theater phone number + - `address`: `2548 Central Park Ave, Yonkers, NY 10710` (string, required) - Theater address + - `name`: `Alamo Drafthouse Cinema - Yonkers` (string, required) - Name of the theater. + - `phone_number`: `(914) 226-3082` (string, required) - Theater phone number + Response 200 (application/json) + Attributes (Theater) + Response 400 (application/json) diff --git a/resources/examples/Showtimes/blueprints/changelog-public-only-all-capabilities.md b/resources/examples/Showtimes/compiled/changelog-public-only-all-vendor-tags.md similarity index 82% rename from resources/examples/Showtimes/blueprints/changelog-public-only-all-capabilities.md rename to resources/examples/Showtimes/compiled/changelog-public-only-all-vendor-tags.md index e255c58..03b83b7 100644 --- a/resources/examples/Showtimes/blueprints/changelog-public-only-all-capabilities.md +++ b/resources/examples/Showtimes/compiled/changelog-public-only-all-vendor-tags.md @@ -7,10 +7,10 @@ Changed up the responses for `/movie/{id}`, `/movies/{id}` and `/movies`. #### Added ##### Resources - The following Movies resources have added: - - `/movies/{id}` now throws the following errors on `GET` requests: + - `/movies/{id}` now returns the following errors on `GET` requests: - `404 Not Found` with a `Error` representation: For no reason. - `404 Not Found` with a `Error` representation: For some other reason. - - `/movies/{id}` now throws the following errors on `PATCH` requests: + - `/movies/{id}` now returns the following errors on `PATCH` requests: - `404 Not Found` with a `Error` representation: If the trailer URL could not be validated. - `403 Forbidden` with a `Coded error` representation: If the user is not allowed to edit that movie. - On `/movies/{id}`, `PATCH` requests now return a `202 Accepted` with a `Movie` representation. @@ -25,15 +25,15 @@ Changed up the responses for `/movie/{id}`, `/movies/{id}` and `/movies`. #### Changed ##### Resources - The following Movies resources have changed: - - On `/movies/{id}`, `GET` requests now return a `application/mill.example.movie` Content-Type header. - - On `/movies/{id}`, `PATCH` requests now return a `application/mill.example.movie` Content-Type header. - - On `/movies`, `GET` requests now return a `application/mill.example.movie` Content-Type header. - - On `/movies`, `POST` requests now return a `application/mill.example.movie` Content-Type header. + - On `/movies/{id}`, `GET` requests now return a `application/mill.example.movie+json` Content-Type header. + - On `/movies/{id}`, `PATCH` requests now return a `application/mill.example.movie+json` Content-Type header. + - On `/movies`, `GET` requests now return a `application/mill.example.movie+json` Content-Type header. + - On `/movies`, `POST` requests now return a `application/mill.example.movie+json` Content-Type header. - The following Theaters resources have changed: - - On `/theaters/{id}`, `GET` requests now return a `application/mill.example.theater` Content-Type header. - - On `/theaters/{id}`, `PATCH` requests now return a `application/mill.example.theater` Content-Type header. - - On `/theaters`, `GET` requests now return a `application/mill.example.theater` Content-Type header. - - On `/theaters`, `POST` requests now return a `application/mill.example.theater` Content-Type header. + - On `/theaters/{id}`, `GET` requests now return a `application/mill.example.theater+json` Content-Type header. + - On `/theaters/{id}`, `PATCH` requests now return a `application/mill.example.theater+json` Content-Type header. + - On `/theaters`, `GET` requests now return a `application/mill.example.theater+json` Content-Type header. + - On `/theaters`, `POST` requests now return a `application/mill.example.theater+json` Content-Type header. #### Removed ##### Resources diff --git a/resources/examples/Showtimes/blueprints/changelog-public-only-matched-with-delete-capabilities.md b/resources/examples/Showtimes/compiled/changelog-public-only-matched-with-delete-vendor-tags.md similarity index 82% rename from resources/examples/Showtimes/blueprints/changelog-public-only-matched-with-delete-capabilities.md rename to resources/examples/Showtimes/compiled/changelog-public-only-matched-with-delete-vendor-tags.md index 4ff66c6..af260cd 100644 --- a/resources/examples/Showtimes/blueprints/changelog-public-only-matched-with-delete-capabilities.md +++ b/resources/examples/Showtimes/compiled/changelog-public-only-matched-with-delete-vendor-tags.md @@ -7,10 +7,10 @@ Changed up the responses for `/movie/{id}`, `/movies/{id}` and `/movies`. #### Added ##### Resources - The following Movies resources have added: - - `/movies/{id}` now throws the following errors on `GET` requests: + - `/movies/{id}` now returns the following errors on `GET` requests: - `404 Not Found` with a `Error` representation: For no reason. - `404 Not Found` with a `Error` representation: For some other reason. - - `/movies/{id}` now throws the following errors on `PATCH` requests: + - `/movies/{id}` now returns the following errors on `PATCH` requests: - `404 Not Found` with a `Error` representation: If the trailer URL could not be validated. - `403 Forbidden` with a `Coded error` representation: If the user is not allowed to edit that movie. - On `/movies/{id}`, `PATCH` requests now return a `202 Accepted` with a `Movie` representation. @@ -21,15 +21,15 @@ Changed up the responses for `/movie/{id}`, `/movies/{id}` and `/movies`. #### Changed ##### Resources - The following Movies resources have changed: - - On `/movies/{id}`, `GET` requests now return a `application/mill.example.movie` Content-Type header. - - On `/movies/{id}`, `PATCH` requests now return a `application/mill.example.movie` Content-Type header. - - On `/movies`, `GET` requests now return a `application/mill.example.movie` Content-Type header. - - On `/movies`, `POST` requests now return a `application/mill.example.movie` Content-Type header. + - On `/movies/{id}`, `GET` requests now return a `application/mill.example.movie+json` Content-Type header. + - On `/movies/{id}`, `PATCH` requests now return a `application/mill.example.movie+json` Content-Type header. + - On `/movies`, `GET` requests now return a `application/mill.example.movie+json` Content-Type header. + - On `/movies`, `POST` requests now return a `application/mill.example.movie+json` Content-Type header. - The following Theaters resources have changed: - - On `/theaters/{id}`, `GET` requests now return a `application/mill.example.theater` Content-Type header. - - On `/theaters/{id}`, `PATCH` requests now return a `application/mill.example.theater` Content-Type header. - - On `/theaters`, `GET` requests now return a `application/mill.example.theater` Content-Type header. - - On `/theaters`, `POST` requests now return a `application/mill.example.theater` Content-Type header. + - On `/theaters/{id}`, `GET` requests now return a `application/mill.example.theater+json` Content-Type header. + - On `/theaters/{id}`, `PATCH` requests now return a `application/mill.example.theater+json` Content-Type header. + - On `/theaters`, `GET` requests now return a `application/mill.example.theater+json` Content-Type header. + - On `/theaters`, `POST` requests now return a `application/mill.example.theater+json` Content-Type header. #### Removed ##### Resources diff --git a/resources/examples/Showtimes/blueprints/changelog-public-only-matched-with-tickets-and-feature-capabilities.md b/resources/examples/Showtimes/compiled/changelog-public-only-matched-with-tickets-and-feature-vendor-tags.md similarity index 82% rename from resources/examples/Showtimes/blueprints/changelog-public-only-matched-with-tickets-and-feature-capabilities.md rename to resources/examples/Showtimes/compiled/changelog-public-only-matched-with-tickets-and-feature-vendor-tags.md index e255c58..03b83b7 100644 --- a/resources/examples/Showtimes/blueprints/changelog-public-only-matched-with-tickets-and-feature-capabilities.md +++ b/resources/examples/Showtimes/compiled/changelog-public-only-matched-with-tickets-and-feature-vendor-tags.md @@ -7,10 +7,10 @@ Changed up the responses for `/movie/{id}`, `/movies/{id}` and `/movies`. #### Added ##### Resources - The following Movies resources have added: - - `/movies/{id}` now throws the following errors on `GET` requests: + - `/movies/{id}` now returns the following errors on `GET` requests: - `404 Not Found` with a `Error` representation: For no reason. - `404 Not Found` with a `Error` representation: For some other reason. - - `/movies/{id}` now throws the following errors on `PATCH` requests: + - `/movies/{id}` now returns the following errors on `PATCH` requests: - `404 Not Found` with a `Error` representation: If the trailer URL could not be validated. - `403 Forbidden` with a `Coded error` representation: If the user is not allowed to edit that movie. - On `/movies/{id}`, `PATCH` requests now return a `202 Accepted` with a `Movie` representation. @@ -25,15 +25,15 @@ Changed up the responses for `/movie/{id}`, `/movies/{id}` and `/movies`. #### Changed ##### Resources - The following Movies resources have changed: - - On `/movies/{id}`, `GET` requests now return a `application/mill.example.movie` Content-Type header. - - On `/movies/{id}`, `PATCH` requests now return a `application/mill.example.movie` Content-Type header. - - On `/movies`, `GET` requests now return a `application/mill.example.movie` Content-Type header. - - On `/movies`, `POST` requests now return a `application/mill.example.movie` Content-Type header. + - On `/movies/{id}`, `GET` requests now return a `application/mill.example.movie+json` Content-Type header. + - On `/movies/{id}`, `PATCH` requests now return a `application/mill.example.movie+json` Content-Type header. + - On `/movies`, `GET` requests now return a `application/mill.example.movie+json` Content-Type header. + - On `/movies`, `POST` requests now return a `application/mill.example.movie+json` Content-Type header. - The following Theaters resources have changed: - - On `/theaters/{id}`, `GET` requests now return a `application/mill.example.theater` Content-Type header. - - On `/theaters/{id}`, `PATCH` requests now return a `application/mill.example.theater` Content-Type header. - - On `/theaters`, `GET` requests now return a `application/mill.example.theater` Content-Type header. - - On `/theaters`, `POST` requests now return a `application/mill.example.theater` Content-Type header. + - On `/theaters/{id}`, `GET` requests now return a `application/mill.example.theater+json` Content-Type header. + - On `/theaters/{id}`, `PATCH` requests now return a `application/mill.example.theater+json` Content-Type header. + - On `/theaters`, `GET` requests now return a `application/mill.example.theater+json` Content-Type header. + - On `/theaters`, `POST` requests now return a `application/mill.example.theater+json` Content-Type header. #### Removed ##### Resources diff --git a/resources/examples/Showtimes/blueprints/changelog.md b/resources/examples/Showtimes/compiled/changelog.md similarity index 82% rename from resources/examples/Showtimes/blueprints/changelog.md rename to resources/examples/Showtimes/compiled/changelog.md index 8604bf7..1e51c73 100644 --- a/resources/examples/Showtimes/blueprints/changelog.md +++ b/resources/examples/Showtimes/compiled/changelog.md @@ -7,13 +7,13 @@ Changed up the responses for `/movie/{id}`, `/movies/{id}` and `/movies`. #### Added ##### Resources - The following Movies resources have added: - - `/movie/{id}` now throws the following errors on `GET` requests: + - `/movie/{id}` now returns the following errors on `GET` requests: - `404 Not Found` with a `Error` representation: For no reason. - `404 Not Found` with a `Error` representation: For some other reason. - - `/movies/{id}` now throws the following errors on `GET` requests: + - `/movies/{id}` now returns the following errors on `GET` requests: - `404 Not Found` with a `Error` representation: For no reason. - `404 Not Found` with a `Error` representation: For some other reason. - - `/movies/{id}` now throws the following errors on `PATCH` requests: + - `/movies/{id}` now returns the following errors on `PATCH` requests: - `404 Not Found` with a `Error` representation: If the trailer URL could not be validated. - `403 Forbidden` with a `Coded error` representation: If something cool happened. - `403 Forbidden` with a `Coded error` representation: If the user is not allowed to edit that movie. @@ -29,16 +29,16 @@ Changed up the responses for `/movie/{id}`, `/movies/{id}` and `/movies`. #### Changed ##### Resources - The following Movies resources have changed: - - On `/movie/{id}`, `GET` requests now return a `application/mill.example.movie` Content-Type header. - - On `/movies/{id}`, `GET` requests now return a `application/mill.example.movie` Content-Type header. - - On `/movies/{id}`, `PATCH` requests now return a `application/mill.example.movie` Content-Type header. - - On `/movies`, `GET` requests now return a `application/mill.example.movie` Content-Type header. - - On `/movies`, `POST` requests now return a `application/mill.example.movie` Content-Type header. + - On `/movie/{id}`, `GET` requests now return a `application/mill.example.movie+json` Content-Type header. + - On `/movies/{id}`, `GET` requests now return a `application/mill.example.movie+json` Content-Type header. + - On `/movies/{id}`, `PATCH` requests now return a `application/mill.example.movie+json` Content-Type header. + - On `/movies`, `GET` requests now return a `application/mill.example.movie+json` Content-Type header. + - On `/movies`, `POST` requests now return a `application/mill.example.movie+json` Content-Type header. - The following Theaters resources have changed: - - On `/theaters/{id}`, `GET` requests now return a `application/mill.example.theater` Content-Type header. - - On `/theaters/{id}`, `PATCH` requests now return a `application/mill.example.theater` Content-Type header. - - On `/theaters`, `GET` requests now return a `application/mill.example.theater` Content-Type header. - - On `/theaters`, `POST` requests now return a `application/mill.example.theater` Content-Type header. + - On `/theaters/{id}`, `GET` requests now return a `application/mill.example.theater+json` Content-Type header. + - On `/theaters/{id}`, `PATCH` requests now return a `application/mill.example.theater+json` Content-Type header. + - On `/theaters`, `GET` requests now return a `application/mill.example.theater+json` Content-Type header. + - On `/theaters`, `POST` requests now return a `application/mill.example.theater+json` Content-Type header. #### Removed ##### Resources diff --git a/resources/examples/mill.xml b/resources/examples/mill.xml index cd5019c..774c19d 100644 --- a/resources/examples/mill.xml +++ b/resources/examples/mill.xml @@ -3,6 +3,24 @@ name="Mill unit test API, Showtimes" bootstrap="../../vendor/autoload.php" > + + + + + + + + + + + + + + + @@ -36,17 +54,35 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Application.php b/src/Application.php index a2c91e2..1afc25b 100644 --- a/src/Application.php +++ b/src/Application.php @@ -9,11 +9,23 @@ class Application extends Command { - const DS = DIRECTORY_SEPARATOR; + /** + * When building out dot-notation annotation keys for compiling documentation we use this key to designate the + * content of an annotations' data. + * + * @var string + */ + const DOT_NOTATION_ANNOTATION_DATA_KEY = '__NESTED_DATA__'; /** - * @var \Pimple\Container + * When building out dot-notation annotation keys for compiling documentation we use this key to designate the + * type of parameter that it is. + * + * @var string */ + const DOT_NOTATION_ANNOTATION_PARAMETER_TYPE_KEY = '__PARAMETER_TYPE__'; + + /** @var \Pimple\Container */ protected $container; /** diff --git a/src/Command/Changelog.php b/src/Command/Changelog.php index 4698fc8..c596331 100644 --- a/src/Command/Changelog.php +++ b/src/Command/Changelog.php @@ -3,16 +3,12 @@ use Mill\Application; use Mill\Config; -use Mill\Generator; +use Mill\Compiler; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -/** - * Generate command for a changelog off your API documentation. - * - */ class Changelog extends Application { /** @@ -28,20 +24,20 @@ protected function configure() 'private', null, InputOption::VALUE_OPTIONAL, - 'Flag designating if you want to generate a changelog that includes private documentation.', + 'Flag designating if you want to compile a changelog that includes private documentation.', true ) ->addOption( - 'capability', + 'vendor_tag', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, - 'The name of a capability if you want to generate a changelog that includes capability-locked ' . + 'The name of a vendor tag if you want to compile a changelog that includes vendor tag-bound ' . 'documentation.' ) ->addArgument( 'output', InputArgument::REQUIRED, - 'Directory to output your generated `changelog.md` file in.' + 'Directory to output your compiled `changelog.md` file in.' ); } @@ -49,17 +45,20 @@ protected function configure() * @param InputInterface $input * @param OutputInterface $output * @return int + * @throws \Mill\Exceptions\Annotations\MultipleAnnotationsException + * @throws \Mill\Exceptions\Annotations\RequiredAnnotationException + * @throws \Mill\Exceptions\Resource\NoAnnotationsException */ protected function execute(InputInterface $input, OutputInterface $output) { parent::execute($input, $output); $private_docs = $input->getOption('private'); - $capabilities = $input->getOption('capability'); + $vendor_tags = $input->getOption('vendor_tag'); $output_dir = realpath($input->getArgument('output')); $private_docs = ($private_docs === true || strtolower($private_docs) == 'true') ? true : false; - $capabilities = (!empty($capabilities)) ? $capabilities : null; + $vendor_tags = (!empty($vendor_tags)) ? $vendor_tags : null; /** @var Config $config */ $config = $this->container['config']; @@ -67,17 +66,14 @@ protected function execute(InputInterface $input, OutputInterface $output) /** @var \League\Flysystem\Filesystem $filesystem */ $filesystem = $this->container['filesystem']; - $output->writeln('Generating a changelog…'); + $output->writeln('Compiling a changelog...'); - $changelog = new Generator\Changelog($config); + $changelog = new Compiler\Changelog($config); $changelog->setLoadPrivateDocs($private_docs); - $changelog->setLoadCapabilityDocs($capabilities); - $markdown = $changelog->generateMarkdown(); + $changelog->setLoadVendorTagDocs($vendor_tags); + $markdown = $changelog->toMarkdown(); - $filesystem->put( - $output_dir . self::DS . 'changelog.md', - trim($markdown) - ); + $filesystem->put($output_dir . DIRECTORY_SEPARATOR . 'changelog.md', trim($markdown)); $output->writeln(['', 'Done!']); diff --git a/src/Command/Compile.php b/src/Command/Compile.php new file mode 100644 index 0000000..88df429 --- /dev/null +++ b/src/Command/Compile.php @@ -0,0 +1,193 @@ +setName('compile') + ->setDescription('Compile API documentation.') + ->addOption( + 'format', + null, + InputOption::VALUE_OPTIONAL, + 'API specification format to compile documentation into. Available formats: ' . implode( + ', ', + array_map(function (string $format): string { + return '`' . $format . '`'; + }, self::FORMATS) + ), + self::FORMAT_OPENAPI + ) + ->addOption( + 'constraint', + null, + InputOption::VALUE_OPTIONAL, + 'Version constraint to compile documentation for. eg. "3.*", "3.1 - 3.2"', + null + ) + ->addOption( + 'default', + null, + InputOption::VALUE_OPTIONAL, + 'Compile just the configured default API version documentation. `defaultApiVersion` in your ' . + '`mill.xml` file.', + false + ) + ->addArgument('output', InputArgument::REQUIRED, 'Directory to output your compiled documentation in.'); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int + * @throws \Exception + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + parent::execute($input, $output); + + $output_dir = realpath($input->getArgument('output')); + $format = strtolower($input->getOption('format')); + $version = $input->getOption('constraint'); + + if (!in_array($format, ['apiblueprint', 'openapi'])) { + $output->writeLn('`' . $format . '` is an unknown compilation format.'); + return 1; + } + + if ($input->getOption('default')) { + $version = $this->container['config']->getDefaultApiVersion(); + } + + // Validate the current version constraint. + if (!empty($version)) { + try { + $version = new Version($version, __CLASS__, __METHOD__); + } catch (UnrecognizedSchemaException $e) { + $output->writeLn('' . $e->getValidationMessage() . ''); + return 1; + } + } + + /** @var Config $config */ + $config = $this->container['config']; + $this->filesystem = $this->container['filesystem']; + + $output->writeln('Compiling controllers and representations...'); + if ($format === self::FORMAT_API_BLUEPRINT) { + $compiler = new ApiBlueprint($config, $version); + } else { + $compiler = new OpenApi($config, $version); + } + + $output->writeln( + sprintf( + 'Compiling %s files...', + ($format === self::FORMAT_API_BLUEPRINT) ? 'API Blueprint' : 'OpenAPI' + ) + ); + + $compiled = $compiler->compile(); + foreach ($compiled as $version => $spec) { + $version_dir = $output_dir . DIRECTORY_SEPARATOR . $version . DIRECTORY_SEPARATOR; + + $output->writeLn(' - API version: ' . $version . ''); + + switch ($format) { + case self::FORMAT_API_BLUEPRINT: + $this->saveApiBlueprint($output, $version_dir, $spec); + break; + + case self::FORMAT_OPENAPI: + $this->saveOpenApi($output, $version_dir, $spec); + break; + } + } + + $output->writeln(['', 'Done!']); + + return 0; + } + + /** + * @param OutputInterface $output + * @param string $version_dir + * @param array $spec + */ + private function saveApiBlueprint(OutputInterface $output, string $version_dir, array $spec): void + { + // Process resource groups. + if (isset($spec['groups'])) { + foreach ($spec['groups'] as $group => $markdown) { + // Convert any nested groups, like `Me\Videos`, into a proper directory structure: `Me/Videos`. + $group = str_replace('\\', DIRECTORY_SEPARATOR, $group); + + $this->filesystem->put( + $version_dir . 'resources' . DIRECTORY_SEPARATOR . $group . '.apib', + trim($markdown) + ); + } + } + + // Process data structures. + if (isset($spec['structures'])) { + foreach ($spec['structures'] as $structure => $markdown) { + // Sanitize any structure names with forward slashes to avoid them from being nested in directories + // by Flysystem. + $structure = str_replace('/', '-', $structure); + + $this->filesystem->put( + $version_dir . 'representations' . DIRECTORY_SEPARATOR . $structure . '.apib', + trim($markdown) + ); + } + } + + // Save a, single, combined API Blueprint file. + $this->filesystem->put($version_dir . 'api.apib', $spec['combined']); + } + + /** + * @param OutputInterface $output + * @param string $version_dir + * @param array $spec + */ + private function saveOpenApi(OutputInterface $output, string $version_dir, array $spec): void + { + $this->filesystem->put( + $version_dir . 'api.yaml', + Yaml::dump($spec, PHP_INT_MAX, 2, true) + ); + } +} diff --git a/src/Command/ErrorMap.php b/src/Command/ErrorMap.php index aaa1c5a..7dab4c9 100644 --- a/src/Command/ErrorMap.php +++ b/src/Command/ErrorMap.php @@ -3,18 +3,14 @@ use Mill\Application; use Mill\Config; +use Mill\Compiler; use Mill\Exceptions\Version\UnrecognizedSchemaException; -use Mill\Generator; use Mill\Parser\Version; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -/** - * Generate command for an error map off documented API errors. - * - */ class ErrorMap extends Application { /** @@ -37,66 +33,50 @@ protected function configure() 'default', null, InputOption::VALUE_OPTIONAL, - 'Generate just the configured default API version documentation. `defaultApiVersion` in your ' . + 'Compile just the configured default API version documentation. `defaultApiVersion` in your ' . '`mill.xml` file.', false ) - ->addOption( - 'dry-run', - null, - InputOption::VALUE_OPTIONAL, - 'Execute a dry run (do not save any files).', - false - ) ->addOption( 'private', null, InputOption::VALUE_OPTIONAL, - 'Flag designating if you want to generate an error map that includes private error documentation.', + 'Flag designating if you want to compile an error map that includes private error documentation.', true ) ->addOption( - 'capability', + 'vendor_tag', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, - 'The name of a capability if you want to generate an error map that includes capability-locked error' . + 'The name of a vendor tag if you want to compile an error map that includes vendor tag-bound error' . 'documentation.' ) - ->addArgument( - 'output', - InputArgument::REQUIRED, - 'Directory to output your generated `errors.md` file in.' - ); + ->addArgument('output', InputArgument::REQUIRED, 'Directory to output your compiled `errors.md` file in.'); } /** * @param InputInterface $input * @param OutputInterface $output * @return int + * @throws \Exception */ protected function execute(InputInterface $input, OutputInterface $output) { parent::execute($input, $output); $private_docs = $input->getOption('private'); - $capabilities = $input->getOption('capability'); + $vendor_tags = $input->getOption('vendor_tag'); $output_dir = realpath($input->getArgument('output')); $version = $input->getOption('constraint'); - $dry_run = $input->getOption('dry-run'); $private_docs = ($private_docs === true || strtolower($private_docs) == 'true') ? true : false; - $capabilities = (!empty($capabilities)) ? $capabilities : null; - - // Generate! - if ($dry_run) { - $output->writeln('Running a dry run…'); - } + $vendor_tags = (!empty($vendor_tags)) ? $vendor_tags : null; if ($input->getOption('default')) { $version = $this->container['config']->getDefaultApiVersion(); } - // Validate the current version generation constraint. + // Validate the current version constraint. if (!empty($version)) { try { $version = new Version($version, __CLASS__, __METHOD__); @@ -112,22 +92,20 @@ protected function execute(InputInterface $input, OutputInterface $output) /** @var \League\Flysystem\Filesystem $filesystem */ $filesystem = $this->container['filesystem']; - $output->writeln('Generating an error map…'); + $output->writeln('Compiling an error map...'); - $error_map = new Generator\ErrorMap($config, $version); + $error_map = new Compiler\ErrorMap($config, $version); $error_map->setLoadPrivateDocs($private_docs); - $error_map->setLoadCapabilityDocs($capabilities); - $markdown = $error_map->generateMarkdown(); + $error_map->setLoadVendorTagDocs($vendor_tags); + $markdown = $error_map->toMarkdown(); foreach ($markdown as $version => $content) { $output->writeLn(' - API version: ' . $version . ''); - if (!$dry_run) { - $filesystem->put( - $output_dir . self::DS . $version . self::DS . 'errors.md', - trim($content) - ); - } + $filesystem->put( + $output_dir . DIRECTORY_SEPARATOR . $version . DIRECTORY_SEPARATOR . 'errors.md', + trim($content) + ); } $output->writeln(['', 'Done!']); diff --git a/src/Command/Generate.php b/src/Command/Generate.php deleted file mode 100644 index 2909067..0000000 --- a/src/Command/Generate.php +++ /dev/null @@ -1,172 +0,0 @@ -setName('generate') - ->setDescription('Generate API Blueprint documentation.') - ->addOption( - 'constraint', - null, - InputOption::VALUE_OPTIONAL, - 'Version constraint to compile documentation for. eg. "3.*", "3.1 - 3.2"', - null - ) - ->addOption( - 'default', - null, - InputOption::VALUE_OPTIONAL, - 'Generate just the configured default API version documentation. `defaultApiVersion` in your ' . - '`mill.xml` file.', - false - ) - ->addOption( - 'dry-run', - null, - InputOption::VALUE_OPTIONAL, - 'Execute a dry run (do not save any files).', - false - ) - ->addArgument('output', InputArgument::REQUIRED, 'Directory to output your generated `.apib` files in.'); - } - - /** - * @param InputInterface $input - * @param OutputInterface $output - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - parent::execute($input, $output); - - $output_dir = realpath($input->getArgument('output')); - $version = $input->getOption('constraint'); - $dry_run = $input->getOption('dry-run'); - - // Generate! - if ($dry_run) { - $output->writeln('Running a dry run…'); - } - - if ($input->getOption('default')) { - $version = $this->container['config']->getDefaultApiVersion(); - } - - // Validate the current version generation constraint. - if (!empty($version)) { - try { - $version = new Version($version, __CLASS__, __METHOD__); - } catch (UnrecognizedSchemaException $e) { - $output->writeLn('' . $e->getValidationMessage() . ''); - return 1; - } - } - - /** @var Config $config */ - $config = $this->container['config']; - - /** @var \League\Flysystem\Filesystem $filesystem */ - $filesystem = $this->container['filesystem']; - - $output->writeln('Compiling controllers and representations…'); - $generator = new Blueprint($config, $version); - - $output->writeln('Generating API Blueprint files…'); - $blueprints = $generator->generate(); - - foreach ($blueprints as $version => $section) { - $version_dir = $output_dir . self::DS . $version . self::DS; - - $output->writeLn(' - API version: ' . $version . ''); - - $total_work = (isset($section['groups'])) ? count($section['groups']) : 0; - $total_work += (isset($section['structures'])) ? count($section['structures']) : 0; - - $progress = new ProgressBar($output, $total_work); - $progress->setFormatDefinition('custom', ' %current%/%max% [%bar%] %message%'); - $progress->setFormat('custom'); - $progress->start(); - - // Process resource groups. - if (isset($section['groups'])) { - $progress->setMessage('Processing resources…'); - foreach ($section['groups'] as $namespace => $markdown) { - $progress->advance(); - - if ($dry_run) { - continue; - } - - // Convert any nested namespaces, like `Me\Videos`, into a proper directory structure: `Me/Videos`. - $namespace = str_replace('\\', self::DS, $namespace); - - $filesystem->put( - $version_dir . 'resources' . self::DS . $namespace . '.apib', - trim($markdown) - ); - } - } - - // Process data structures. - if (isset($section['structures'])) { - $progress->setMessage('Processing representations…'); - foreach ($section['structures'] as $structure => $markdown) { - $progress->advance(); - - if ($dry_run) { - continue; - } - - // Sanitize any structure names with forward slashes to avoid them from being nested in directories - // by Flysystem. - $structure = str_replace('/', '-', $structure); - - $filesystem->put( - $version_dir . 'representations' . self::DS . $structure . '.apib', - trim($markdown) - ); - } - } - - // Save a, single, combined API Blueprint file. - if (!$dry_run) { - $filesystem->put( - $version_dir . 'api.apib', - $section['combined'] - ); - } - - $progress->setMessage(''); - $progress->finish(); - $output->writeLn(''); - } - - $output->writeln(['', 'Done!']); - - return 0; - } -} diff --git a/src/Generator.php b/src/Compiler.php similarity index 57% rename from src/Generator.php rename to src/Compiler.php index c29b713..80b4f54 100644 --- a/src/Generator.php +++ b/src/Compiler.php @@ -1,44 +1,31 @@ [], 'resources' => [] ]; /** - * Setting to generate documentation for documentation that's been marked, through a `:private` decorator, as + * A setting to compile documentation for documentation that's been marked, through a `:private` decorator, as * private. * * @var bool @@ -46,13 +33,13 @@ class Generator protected $load_private_docs = true; /** - * Capabilities to generate documentation against. If this array contains capabilities, only public and - * documentation that have that capability will be generated. If this is null, this will be disregarded, and - * everything will be generated. + * Vendor tags to compile documentation against. If this array contains vendor tags, only public and documentation + * that have a matched tag will be compiled. If this is null, this will be disregarded and everything will be + * compiled. * * @var array|null */ - protected $load_capability_docs = null; + protected $load_vendor_tag_docs = null; /** * @param Config $config @@ -71,33 +58,30 @@ public function __construct(Config $config, Version $version = null) * * @return array */ - public function generate(): array + public function compile(): array { - // Generate resources. $resources = $this->compileResources($this->parseResources()); - foreach ($resources as $version => $namespaces) { - // Alphabetize the versioned resource namespaces! + foreach ($resources as $version => $groups) { ksort($resources[$version]); } - $this->compiled['resources'] = $resources; - - // Generate representations. $representations = $this->compileRepresentations($this->parseRepresentations()); foreach ($representations as $version => $data) { - // Alphabetize the versioned representations! ksort($representations[$version]); } + $this->compiled['resources'] = $resources; $this->compiled['representations'] = $representations; return $this->compiled; } /** - * Run through configured controllers, parse them, and generate a collection of resource action documentation. + * Run through configured controllers, parse them, and compile a collection of resource action documentation. * * @return array + * @throws Exceptions\Annotations\MultipleAnnotationsException + * @throws Exceptions\Annotations\RequiredAnnotationException */ protected function parseResources(): array { @@ -108,45 +92,49 @@ protected function parseResources(): array /** @var \Mill\Parser\Resource\Action\Documentation $method */ foreach ($docs->getMethods() as $method) { - /** @var \Mill\Parser\Annotations\UriAnnotation $uri */ - foreach ($method->getUris() as $uri) { - $uri_data = $uri->toArray(); - $namespace = $uri_data['namespace']; + $group = $method->getGroup(); - // Are we generating documentation for a private or protected resource? - if (!$this->shouldParseUri($method, $uri)) { + /** @var \Mill\Parser\Annotations\PathAnnotation $path */ + foreach ($method->getPaths() as $path) { + // Are we compiling documentation for a private or protected resource? + if (!$this->shouldParsePath($method, $path)) { continue; } + // We're always going to be compiling documentation for this path, regardless if it's an alias or + // not, so let's strip any awareness of that. + $path->setAliased(false); + $path->setAliases([]); + $resource_label = $annotations['label']; - if (!isset($resources[$namespace]['resources'][$resource_label])) { - $resources[$namespace]['resources'][$resource_label] = [ + if (!isset($resources[$group]['resources'][$resource_label])) { + $resources[$group]['resources'][$resource_label] = [ 'label' => $annotations['label'], 'description' => $annotations['description'], 'actions' => [] ]; } - // Set any segments that belong to this URI on onto this action. - $segments = []; + // Set any params that belong to this path on onto this action. + $params = []; - /** @var \Mill\Parser\Annotations\UriSegmentAnnotation $segment */ - foreach ($method->getUriSegments() as $segment) { - if ($segment->getUri() === $uri_data['path']) { - $segments[] = $segment; + /** @var \Mill\Parser\Annotations\PathParamAnnotation $param */ + foreach ($method->getPathParameters() as $param) { + if ($path->doesPathHaveParam($param)) { + $params[$param->getField()] = $param; } } - // Set the lone URI that this action and namespace run under. + // Set the lone path that this action and group run under. $action = clone $method; - $action->setUri($uri); - $action->setUriSegments($segments); - $action->filterAnnotationsForVisibility($this->load_private_docs, $this->load_capability_docs); + $action->setPath($path); + $action->setPathParams($params); + $action->filterAnnotationsForVisibility($this->load_private_docs, $this->load_vendor_tag_docs); // Hash the action so we don't happen to double up and end up with dupes. - $identifier = $action->getUri()->getPath() . '::' . $action->getMethod(); + $identifier = $action->getPath()->getPath() . '::' . $action->getMethod(); - $resources[$namespace]['resources'][$resource_label]['actions'][$identifier] = $action; + $resources[$group]['resources'][$resource_label]['actions'][$identifier] = $action; } } } @@ -157,49 +145,54 @@ protected function parseResources(): array /** * Compile parsed resources into a versioned collection. * - * @psalm-suppress EmptyArrayAccess Psalm thinks that `$resources[$version][$namespace]['resources']` is an empty + * @psalm-suppress EmptyArrayAccess Psalm thinks that `$resources[$version][$group]['resources']` is an empty * value array. It is not. * @param array $parsed * @return array + * @throws \Exception */ private function compileResources(array $parsed = []): array { $resources = []; - foreach ($parsed as $namespace => $namespace_data) { - foreach ($namespace_data['resources'] as $resource_label => $resource) { + foreach ($parsed as $group => $group_data) { + foreach ($group_data['resources'] as $resource_label => $resource) { /** @var Resource\Action\Documentation $action */ foreach ($resource['actions'] as $identifier => $action) { // Run through every supported API version and flatten out documentation for it. foreach ($this->supported_versions as $supported_version) { $version = $supported_version['version']; - // If we're generating documentation for a specific version range, and this doesn't fall in + // If we're compiling documentation for a specific version range, and this doesn't fall in // that, then skip it. if ($this->version && !$this->version->matches($version)) { continue; } - // If this method has a minimum version specified, and we aren't generating for that, skip it. + // If this method has either a minimum or maximum version specified, and we aren't compiling + // an acceptable version, skip it. $min_version = $action->getMinimumVersion(); - if ($min_version && $min_version->getMinimumVersion() > $version) { + $max_version = $action->getMaximumVersion(); + if (($min_version && $min_version->getMinimumVersion() > $version) || + ($max_version && $max_version->getMaximumVersion() < $version) + ) { continue; } if (!isset($resources[$version])) { $resources[$version] = []; - } elseif (!isset($resources[$version][$namespace])) { - $resources[$version][$namespace] = [ + } elseif (!isset($resources[$version][$group])) { + $resources[$version][$group] = [ 'resources' => [] ]; } // Filter down the annotations on this action for just those of the current version we're - // generating documentation for. + // compiling documentation for. $cloned = clone $action; $cloned->filterAnnotationsForVersion($version); - if (!isset($resources[$version][$namespace]['resources'][$resource_label])) { - $resources[$version][$namespace]['resources'][$resource_label] = [ + if (!isset($resources[$version][$group]['resources'][$resource_label])) { + $resources[$version][$group]['resources'][$resource_label] = [ 'label' => $resource['label'], 'description' => $resource['description'], 'actions' => [] @@ -208,10 +201,9 @@ private function compileResources(array $parsed = []): array // Hash the action so we don't happen to double up and end up with dupes, and then remove the // currently non-hash index from the action array. - $identifier = $cloned->getUri()->getPath() . '::' . $cloned->getMethod(); + $identifier = $cloned->getPath()->getPath() . '::' . $cloned->getMethod(); - $resources[$version][$namespace]['resources'][$resource_label]['actions'][$identifier] = - $cloned; + $resources[$version][$group]['resources'][$resource_label]['actions'][$identifier] = $cloned; } } } @@ -221,9 +213,12 @@ private function compileResources(array $parsed = []): array } /** - * Run through configured representations, parse them, and generate a collection of representation documentation. + * Run through configured representations, parse them, and compile a collection of representation documentation. * * @return array + * @throws Exceptions\Annotations\MultipleAnnotationsException + * @throws Exceptions\Annotations\RequiredAnnotationException + * @throws Exceptions\Resource\NoAnnotationsException */ protected function parseRepresentations(): array { @@ -242,7 +237,7 @@ protected function parseRepresentations(): array } $parsed = (new Representation\Documentation($class, $representation['method']))->parse(); - $parsed->filterAnnotationsForVisibility($this->load_capability_docs); + $parsed->filterAnnotationsForVisibility($this->load_vendor_tag_docs); $representations[$class] = $parsed; } @@ -265,14 +260,14 @@ private function compileRepresentations(array $parsed = []): array foreach ($this->supported_versions as $supported_version) { $version = $supported_version['version']; - // If we're generating documentation for a specific version range, and this doesn't fall in - // that, then skip it. + // If we're compiling documentation for a specific version range, and this doesn't fall in that, then + // skip it. if ($this->version && !$this->version->matches($version)) { continue; } - // Filter down the annotations on this action for just those of the current version we're - // generating documentation for. + // Filter down the annotations on this action for just those of the current version we're compiling + // documentation for. $cloned = clone $representation; $cloned->filterRepresentationForVersion($version); @@ -321,7 +316,7 @@ public function getResources(string $version = null): array * Set if we'll be loading documentation that's been marked as being private. * * @param bool $load_private_docs - * @return self + * @return Compiler */ public function setLoadPrivateDocs(bool $load_private_docs = true): self { @@ -330,49 +325,49 @@ public function setLoadPrivateDocs(bool $load_private_docs = true): self } /** - * Set an array of capabilities that we'll be generating documentation against. + * Set an array of vendor tags that we'll be compiling documentation against. * - * If you want all documentation, even that that is behind a capability, supply `null`. If you want documentation - * that either has no capability, or specific ones, supply an array with those capability names. + * If you want all documentation, even that which has a vendor tag, supply `null`. If you want documentation that + * either has no vendor tag, or specific ones, supply an array with those vendor tag names. * - * @param array|null $capabilities - * @return self + * @param array|null $vendor_tags + * @return Compiler */ - public function setLoadCapabilityDocs(array $capabilities = null): self + public function setLoadVendorTagDocs(?array $vendor_tags): self { - $this->load_capability_docs = $capabilities; + $this->load_vendor_tag_docs = $vendor_tags; return $this; } /** - * With the rules set up on the Generator, should we parse a supplied URI? + * With the rules set up on the compiler, should we parse a supplied path? * * @param Resource\Action\Documentation $method - * @param UriAnnotation $uri + * @param PathAnnotation $path * @return bool */ - private function shouldParseUri(Resource\Action\Documentation $method, UriAnnotation $uri): bool + private function shouldParsePath(Resource\Action\Documentation $method, PathAnnotation $path): bool { - $uri_data = $uri->toArray(); - $capabilities = $method->getCapabilities(); + $path_data = $path->toArray(); + $vendor_tags = $method->getVendorTags(); - // Should we generate documentation that is locked behind a capability? - if (!empty($capabilities) && !is_null($this->load_capability_docs)) { - // We don't have any configured capabilities to pull documentation for, so this URI shouldn't be parsed. - if (empty($this->load_capability_docs)) { + // Should we compile documentation that has a vendor tag? + if (!empty($vendor_tags) && !is_null($this->load_vendor_tag_docs)) { + // We don't have any configured vendor tags to pull documentation for, so this path shouldn't be parsed. + if (empty($this->load_vendor_tag_docs)) { return false; } $all_found = true; - /** @var CapabilityAnnotation $capability */ - foreach ($capabilities as $capability) { - if (!in_array($capability->getCapability(), $this->load_capability_docs)) { + /** @var VendorTagAnnotation $vendor_tag */ + foreach ($vendor_tags as $vendor_tag) { + if (!in_array($vendor_tag->getVendorTag(), $this->load_vendor_tag_docs)) { $all_found = false; } } - // This URI should only be parsed if it has every capability we're looking for. + // This path should only be parsed if it has every vendor tag we're looking for. if ($all_found) { return true; } @@ -380,8 +375,8 @@ private function shouldParseUri(Resource\Action\Documentation $method, UriAnnota return false; } - // If we aren't generating docs for private resource, but this URI is private, we shouldn't parse it. - if (!$this->load_private_docs && !$uri_data['visible']) { + // If we aren't compiling docs for private resource, but this path is private, we shouldn't parse it. + if (!$this->load_private_docs && !$path_data['visible']) { return false; } diff --git a/src/Generator/Changelog.php b/src/Compiler/Changelog.php similarity index 78% rename from src/Generator/Changelog.php rename to src/Compiler/Changelog.php index 39d6f06..7ca6ef0 100644 --- a/src/Generator/Changelog.php +++ b/src/Compiler/Changelog.php @@ -1,24 +1,24 @@ [], 'resources' => [] ]; /** - * Take compiled API documentation and generate a changelog over the life of the API. + * Take compiled API documentation and convert it into a changelog over the life of the API. * * @return array + * @throws \Mill\Exceptions\Annotations\MultipleAnnotationsException + * @throws \Mill\Exceptions\Annotations\RequiredAnnotationException + * @throws \Mill\Exceptions\Resource\NoAnnotationsException */ - public function generate(): array + public function compile(): array { $this->parsed['representations'] = $this->parseRepresentations(); $this->parsed['resources'] = $this->parseResources(); @@ -59,7 +54,6 @@ public function generate(): array $this->buildRepresentationChangelog($this->parsed['representations']); $this->buildResourceChangelog($this->parsed['resources']); - // Keep things tidy foreach ($this->changelog as $version => $changes) { $version_data = $this->config->getApiVersion($version); $this->changelog[$version]['_details'] = [ @@ -85,29 +79,37 @@ public function generate(): array } /** - * Take compiled API documentation and generate a JSON-encoded changelog over the life of the API. + * Take compiled API documentation and convert it into a JSON-encoded changelog over the life of the API. * * @return string + * @throws \Mill\Exceptions\Annotations\MultipleAnnotationsException + * @throws \Mill\Exceptions\Annotations\RequiredAnnotationException + * @throws \Mill\Exceptions\Resource\NoAnnotationsException */ - public function generateJson(): string + public function toJson(): string { $json = new Json($this->config); - $json->setChangelog($this->generate()); - $generated = $json->generate(); - return array_shift($generated); + $json->setChangelog($this->compile()); + $compiled = $json->compile(); + + return array_shift($compiled); } /** - * Take compiled API documentation and generate a Markdown-based changelog over the life of the API. + * Take compiled API documentation and convert it into a Markdown-based changelog over the life of the API. * * @return string + * @throws \Mill\Exceptions\Annotations\MultipleAnnotationsException + * @throws \Mill\Exceptions\Annotations\RequiredAnnotationException + * @throws \Mill\Exceptions\Resource\NoAnnotationsException */ - public function generateMarkdown(): string + public function toMarkdown(): string { $markdown = new Markdown($this->config); - $markdown->setChangelog($this->generate()); - $generated = $markdown->generate(); - return array_shift($generated); + $markdown->setChangelog($this->compile()); + $compiled = $markdown->compile(); + + return array_shift($compiled); } /** @@ -159,10 +161,11 @@ private function buildRepresentationChangelog(array $representations = []): void * Compile a changelog for a parsed set of resources. * * @param array $resources + * @throws \Exception */ private function buildResourceChangelog(array $resources = []): void { - foreach ($resources as $namespace => $data) { + foreach ($resources as $group => $data) { foreach ($data['resources'] as $resource_name => $resource) { /** @var Action\Documentation $action */ foreach ($resource['actions'] as $identifier => $action) { @@ -174,11 +177,11 @@ private function buildResourceChangelog(array $resources = []): void self::DEFINITION_ADDED, $min_version, self::CHANGESET_TYPE_ACTION, - $namespace, + $group, [ - 'resource_namespace' => $namespace, + 'resource_group' => $group, 'method' => $action->getMethod(), - 'uri' => $action->getUri()->getCleanPath() + 'path' => $action->getPath()->getCleanPath() ] ); } @@ -192,18 +195,18 @@ private function buildResourceChangelog(array $resources = []): void self::DEFINITION_CHANGED, $introduced, self::CHANGESET_TYPE_CONTENT_TYPE, - $namespace, + $group, [ - 'resource_namespace' => $namespace, + 'resource_group' => $group, 'method' => $action->getMethod(), - 'uri' => $action->getUri()->getCleanPath(), + 'path' => $action->getPath()->getCleanPath(), 'content_type' => $content_type->getContentType() ] ); } } - // Diff action `param`, `return` and `throws` annotations. + // Diff action `param`, `return` and `error` annotations. foreach ($action->getAnnotations() as $annotation_name => $annotations) { /** @var Annotation $annotation */ foreach ($annotations as $annotation) { @@ -218,9 +221,9 @@ private function buildResourceChangelog(array $resources = []): void } $data = [ - 'resource_namespace' => $namespace, + 'resource_group' => $group, 'method' => $action->getMethod(), - 'uri' => $action->getUri()->getCleanPath() + 'path' => $action->getPath()->getCleanPath() ]; if ($annotation instanceof ParamAnnotation) { @@ -244,39 +247,27 @@ private function buildResourceChangelog(array $resources = []): void } else { $data['representation'] = false; } - } elseif ($annotation instanceof ThrowsAnnotation) { - $change_type = self::CHANGESET_TYPE_ACTION_THROWS; + } elseif ($annotation instanceof ErrorAnnotation) { + $change_type = self::CHANGESET_TYPE_ACTION_ERROR; /** @var Documentation $representation */ $representation = $this->parsed['representations'][$annotation->getRepresentation()]; - /** @var ThrowsAnnotation $annotation */ + /** @var ErrorAnnotation $annotation */ $data['http_code'] = $annotation->getHttpCode(); $data['representation'] = $representation->getLabel(); $data['description'] = $annotation->getDescription(); } else { - // This annotation isn't yet supported in changelog generation. + // This annotation isn't yet supported in changelog compilation. continue; } if ($introduced) { - $this->record( - self::DEFINITION_ADDED, - $introduced, - $change_type, - $namespace, - $data - ); + $this->record(self::DEFINITION_ADDED, $introduced, $change_type, $group, $data); } if ($removed) { - $this->record( - self::DEFINITION_REMOVED, - $removed, - $change_type, - $namespace, - $data - ); + $this->record(self::DEFINITION_REMOVED, $removed, $change_type, $group, $data); } } } @@ -291,30 +282,29 @@ private function buildResourceChangelog(array $resources = []): void * @param string $definition * @param false|string $version * @param string $change_type - * @param null|string $namespace + * @param string|null $group * @param array $data - * @return self + * @return Changelog */ private function record( string $definition, $version, string $change_type, - string $namespace = null, + string $group = null, array $data = [] ): self { - // Since namespaces can be nested, let's throw changes under the top-level namespace. - if (!is_null($namespace)) { - $namespace = explode('\\', $namespace); - $namespace = array_shift($namespace); + // Since groups can be nested, let's throw changes under the top-level group. + if (!is_null($group)) { + $group = explode('\\', $group); + $group = array_shift($group); } $hash = $this->hashChangeset($change_type, $data); if ($change_type === self::CHANGESET_TYPE_REPRESENTATION_DATA) { - $this->changelog[$version][$definition]['representations'][$namespace][$change_type][$hash][] = $data; + $this->changelog[$version][$definition]['representations'][$group][$change_type][$hash][] = $data; } else { - $this->changelog[$version][$definition]['resources'][$namespace][$data['uri']][$change_type][$hash][] = - $data; + $this->changelog[$version][$definition]['resources'][$group][$data['path']][$change_type][$hash][] = $data; } return $this; @@ -405,26 +395,26 @@ private function hashChangeset(string $change_type, array $data): string $hash_data = []; // For changesets, the hash is grouped based on an indexed piece of content. Here we're excluding those indexes - // from the to-be-generated hashes so we can get like-hashes across multiple pieces of data. Without this, we + // from the to-be-compiled hashes so we can get like-hashes across multiple pieces of data. Without this, we // wouldn't be able to do proper duplicate detection. switch ($change_type) { case self::CHANGESET_TYPE_ACTION: - $hash_data['uri'] = $data['uri']; + $hash_data['path'] = $data['path']; break; case self::CHANGESET_TYPE_ACTION_PARAM: $hash_data['method'] = $data['method']; - $hash_data['uri'] = $data['uri']; + $hash_data['path'] = $data['path']; break; case self::CHANGESET_TYPE_ACTION_RETURN: $hash_data['method'] = $data['method']; - $hash_data['uri'] = $data['uri']; + $hash_data['path'] = $data['path']; break; - case self::CHANGESET_TYPE_ACTION_THROWS: + case self::CHANGESET_TYPE_ACTION_ERROR: $hash_data['method'] = $data['method']; - $hash_data['uri'] = $data['uri']; + $hash_data['path'] = $data['path']; break; case self::CHANGESET_TYPE_CONTENT_TYPE: @@ -437,7 +427,7 @@ private function hashChangeset(string $change_type, array $data): string default: $hash_data = $data; - unset($hash_data['uri']); + unset($hash_data['path']); } return substr(sha1(serialize($hash_data)), 0, 10); diff --git a/src/Generator/Changelog/Changeset.php b/src/Compiler/Changelog/Changeset.php similarity index 87% rename from src/Generator/Changelog/Changeset.php rename to src/Compiler/Changelog/Changeset.php index 4cdb8a4..04896ad 100644 --- a/src/Generator/Changelog/Changeset.php +++ b/src/Compiler/Changelog/Changeset.php @@ -1,14 +1,14 @@ [ - Changelog::DEFINITION_ADDED => '{uri} has been added with support for the following HTTP methods:' + Changelog::DEFINITION_ADDED => '{path} has been added with support for the following HTTP methods:' ], 'singular' => [ - Changelog::DEFINITION_ADDED => '{method} on {uri} was added.' + Changelog::DEFINITION_ADDED => '{method} on {path} was added.' ] ]; } @@ -42,10 +42,10 @@ public function compileAddedOrRemovedChangeset(string $definition, array $change $template = $templates['plural'][$definition]; return [ [ - // Changes are grouped by URIs so it's safe to just pull the first URI here. + // Changes are grouped by paths so it's safe to just pull the first path here. $this->renderText($template, [ - 'resource_namespace' => $changes[0]['resource_namespace'], - 'uri' => $changes[0]['uri'] + 'resource_group' => $changes[0]['resource_group'], + 'path' => $changes[0]['path'] ]), $methods ] diff --git a/src/Generator/Changelog/Changesets/ActionThrows.php b/src/Compiler/Changelog/Changesets/ActionError.php similarity index 72% rename from src/Generator/Changelog/Changesets/ActionThrows.php rename to src/Compiler/Changelog/Changesets/ActionError.php index 6b89e0e..60839e4 100644 --- a/src/Generator/Changelog/Changesets/ActionThrows.php +++ b/src/Compiler/Changelog/Changesets/ActionError.php @@ -1,10 +1,10 @@ [ - Changelog::DEFINITION_ADDED => '{uri} now throws the following errors on {method} requests:', - Changelog::DEFINITION_REMOVED => '{uri} no longer throws the following errors on {method} requests:' + Changelog::DEFINITION_ADDED => '{path} now returns the following errors on {method} requests:', + Changelog::DEFINITION_REMOVED => '{path} no longer returns the following errors on {method} requests:' ], 'singular' => [ - Changelog::DEFINITION_ADDED => 'On {method} requests to {uri}, a {http_code} with a {representation} ' . - 'representation is now returned: {description}', - Changelog::DEFINITION_REMOVED => '{method} requests to {uri} no longer returns a {http_code} with a ' . + Changelog::DEFINITION_ADDED => 'On {method} requests to {path}, a {http_code} with a ' . + '{representation} representation is now returned: {description}', + Changelog::DEFINITION_REMOVED => '{method} requests to {path} no longer returns a {http_code} with a ' . '{representation} representation: {description}' ] ]; @@ -59,9 +59,9 @@ public function compileAddedOrRemovedChangeset(string $definition, array $change $template = $templates['plural'][$definition]; $entries[] = [ $this->renderText($template, [ - 'resource_namespace' => $change['resource_namespace'], + 'resource_group' => $change['resource_group'], 'method' => $method, - 'uri' => $change['uri'] + 'path' => $change['path'] ]), array_unique($errors) ]; @@ -81,6 +81,6 @@ public function compileAddedOrRemovedChangeset(string $definition, array $change */ public function compileChangedChangeset(string $definition, array $changes = []) { - throw new \Exception($definition . ' action throws changes are not yet supported.'); + throw new \Exception($definition . ' action error changes are not yet supported.'); } } diff --git a/src/Generator/Changelog/Changesets/ActionParam.php b/src/Compiler/Changelog/Changesets/ActionParam.php similarity index 75% rename from src/Generator/Changelog/Changesets/ActionParam.php rename to src/Compiler/Changelog/Changesets/ActionParam.php index bd1fea1..099c5a1 100644 --- a/src/Generator/Changelog/Changesets/ActionParam.php +++ b/src/Compiler/Changelog/Changesets/ActionParam.php @@ -1,8 +1,8 @@ [ - Changelog::DEFINITION_ADDED => 'The following parameters have been added to {method} on {uri}:', - Changelog::DEFINITION_REMOVED => 'The following parameters have been removed from {method} on {uri}:' + Changelog::DEFINITION_ADDED => 'The following parameters have been added to {method} on {path}:', + Changelog::DEFINITION_REMOVED => 'The following parameters have been removed from {method} on {path}:' ], 'singular' => [ - Changelog::DEFINITION_ADDED => 'A {parameter} request parameter was added to {method} on {uri}.', + Changelog::DEFINITION_ADDED => 'A {parameter} request parameter was added to {method} on {path}.', Changelog::DEFINITION_REMOVED => 'The {parameter} request parameter has been removed from {method} ' . - 'requests on {uri}.' + 'requests on {path}.' ] ]; } @@ -49,16 +49,19 @@ public function compileAddedOrRemovedChangeset(string $definition, array $change // array_map call, but you can't pass `$this` into closures. foreach ($params as $k => $param) { $params[$k] = $this->renderText('{parameter}', [ - 'parameter' => $param + 'parameter' => $param, + 'resource_group' => $changes[0]['resource_group'], + 'method' => $method, + 'path' => $changes[0]['path'] ]); } $template = $templates['plural'][$definition]; $entry[] = [ $this->renderText($template, [ - 'resource_namespace' => $changes[0]['resource_namespace'], + 'resource_group' => $changes[0]['resource_group'], 'method' => $method, - 'uri' => $changes[0]['uri'] + 'path' => $changes[0]['path'] ]), $params ]; @@ -68,10 +71,10 @@ public function compileAddedOrRemovedChangeset(string $definition, array $change $template = $templates['singular'][$definition]; $entry[] = $this->renderText($template, [ - 'resource_namespace' => $changes[0]['resource_namespace'], + 'resource_group' => $changes[0]['resource_group'], 'parameter' => array_shift($params), 'method' => $method, - 'uri' => $changes[0]['uri'] + 'path' => $changes[0]['path'] ]); } diff --git a/src/Generator/Changelog/Changesets/ActionReturn.php b/src/Compiler/Changelog/Changesets/ActionReturn.php similarity index 81% rename from src/Generator/Changelog/Changesets/ActionReturn.php rename to src/Compiler/Changelog/Changesets/ActionReturn.php index e156418..7a0cea2 100644 --- a/src/Generator/Changelog/Changesets/ActionReturn.php +++ b/src/Compiler/Changelog/Changesets/ActionReturn.php @@ -1,8 +1,8 @@ [ - Changelog::DEFINITION_ADDED => 'The {method} on {uri} now returns the following responses:', - Changelog::DEFINITION_REMOVED => 'The {method} on {uri} no longer returns the following responses:' + Changelog::DEFINITION_ADDED => 'The {method} on {path} now returns the following responses:', + Changelog::DEFINITION_REMOVED => 'The {method} on {path} no longer returns the following responses:' ], 'singular' => [ - Changelog::DEFINITION_ADDED => 'On {uri}, {method} requests now return a {http_code} with a ' . + Changelog::DEFINITION_ADDED => 'On {path}, {method} requests now return a {http_code} with a ' . '{representation} representation.', - Changelog::DEFINITION_REMOVED => 'On {uri}, {method} requests no longer return a {http_code} with a ' . + Changelog::DEFINITION_REMOVED => 'On {path}, {method} requests no longer return a {http_code} with a ' . '{representation} representation.', // Representations are optional on returns, so we need special strings for those cases. 'no_representation' => [ - Changelog::DEFINITION_ADDED => '{method} on {uri} now returns a {http_code}.', - Changelog::DEFINITION_REMOVED => '{method} on {uri} no longer returns a {http_code}.' + Changelog::DEFINITION_ADDED => '{method} on {path} now returns a {http_code}.', + Changelog::DEFINITION_REMOVED => '{method} on {path} no longer returns a {http_code}.' ] ] ]; @@ -72,9 +72,9 @@ public function compileAddedOrRemovedChangeset(string $definition, array $change $template = $templates['plural'][$definition]; $entries[] = [ $this->renderText($template, [ - 'resource_namespace' => $changes[0]['resource_namespace'], + 'resource_group' => $changes[0]['resource_group'], 'method' => $method, - 'uri' => $changes[0]['uri'] + 'path' => $changes[0]['path'] ]), $returns ]; diff --git a/src/Generator/Changelog/Changesets/ContentType.php b/src/Compiler/Changelog/Changesets/ContentType.php similarity index 71% rename from src/Generator/Changelog/Changesets/ContentType.php rename to src/Compiler/Changelog/Changesets/ContentType.php index ea75e5c..acf91b4 100644 --- a/src/Generator/Changelog/Changesets/ContentType.php +++ b/src/Compiler/Changelog/Changesets/ContentType.php @@ -1,8 +1,8 @@ [ - Changelog::DEFINITION_CHANGED => 'On {uri}, {method} requests now return a {content_type} ' . + Changelog::DEFINITION_CHANGED => 'On {path}, {method} requests now return a {content_type} ' . 'Content-Type header.' ] ]; @@ -35,14 +35,14 @@ public function compileChangedChangeset(string $definition, array $changes = []) $templates = $this->getTemplates(); if (count($changes) > 1) { - $uris = array_map(function (array $change): string { - return $change['uri']; + $paths = array_map(function (array $change): string { + return $change['path']; }, $changes); - // Changes are hashed and grouped by their hashes (sans URI), so it's safe to just pass along this change + // Changes are hashed and grouped by their hashes (sans path), so it's safe to just pass along this change // into the template engine to build a string. $change = array_shift($changes); - $change['uri'] = $uris; + $change['path'] = $paths; } else { $change = array_shift($changes); } diff --git a/src/Generator/Changelog/Changesets/RepresentationData.php b/src/Compiler/Changelog/Changesets/RepresentationData.php similarity index 93% rename from src/Generator/Changelog/Changesets/RepresentationData.php rename to src/Compiler/Changelog/Changesets/RepresentationData.php index 1fc9d6d..6692a03 100644 --- a/src/Generator/Changelog/Changesets/RepresentationData.php +++ b/src/Compiler/Changelog/Changesets/RepresentationData.php @@ -1,8 +1,8 @@ $data) { - $namespace_entry = [ - $this->renderText('The following {resource_namespace} resources have ' . $definition . ':', [ - 'resource_namespace' => $namespace + foreach ($changesets as $group => $data) { + $group_entry = [ + $this->renderText('The following {resource_group} resources have ' . $definition . ':', [ + 'resource_group' => $group ]), - [] // Namespace-related entries will be nested here. + [] // Group-related entries will be nested here. ]; - foreach ($data as $uri => $change_types) { + foreach ($data as $path => $change_types) { foreach ($change_types as $change_type => $hashes) { foreach ($hashes as $hash => $changes) { if (in_array($definition, [ @@ -131,12 +130,12 @@ private function parseResourceChangesets(string $definition, array $changesets = $entry = array_shift($entry); } - $namespace_entry[1][] = $entry; + $group_entry[1][] = $entry; } } } - $entries[] = $namespace_entry; + $entries[] = $group_entry; } return $entries; @@ -166,8 +165,8 @@ private function getAddedOrRemovedChangesetFactory(string $definition, string $c $changeset = new Changelog\Changesets\ActionReturn; break; - case Changelog::CHANGESET_TYPE_ACTION_THROWS: - $changeset = new Changelog\Changesets\ActionThrows; + case Changelog::CHANGESET_TYPE_ACTION_ERROR: + $changeset = new Changelog\Changesets\ActionError; break; case Changelog::CHANGESET_TYPE_REPRESENTATION_DATA: @@ -193,8 +192,8 @@ private function getAddedOrRemovedChangesetFactory(string $definition, string $c */ private function getChangedChangesetFactory(string $definition, string $change_type, array $changes) { - // Due to versioning restrictions in the Mill syntax (that will be fixed), only `@api-contentType` annotations - // will generate a "changed" entry in the changelog. + // Due to versioning restrictions in the Mill syntax (that will be fixed), only `@api-contenttype` annotations + // will create a "changed" entry in the changelog. switch ($change_type) { case Changelog::CHANGESET_TYPE_CONTENT_TYPE: $changeset = new Changelog\Changesets\ContentType; diff --git a/src/Generator/Changelog/Formats/Markdown.php b/src/Compiler/Changelog/Formats/Markdown.php similarity index 90% rename from src/Generator/Changelog/Formats/Markdown.php rename to src/Compiler/Changelog/Formats/Markdown.php index 44f1ba7..d9d7e9d 100644 --- a/src/Generator/Changelog/Formats/Markdown.php +++ b/src/Compiler/Changelog/Formats/Markdown.php @@ -1,24 +1,22 @@ line(2); } - $changelog = parent::generate(); + $changelog = parent::compile(); $changelog = array_shift($changelog); $changelog = json_decode($changelog, true); foreach ($changelog as $version => $data) { diff --git a/src/Compiler/ErrorMap.php b/src/Compiler/ErrorMap.php new file mode 100644 index 0000000..53645f1 --- /dev/null +++ b/src/Compiler/ErrorMap.php @@ -0,0 +1,99 @@ +getResources() as $version => $resources) { + foreach ($resources as $group => $data) { + // Groups can have children via the `\` delimiter, but for the error map we only care about the + // top-level group. + if (strpos($group, '\\') != false) { + $parts = explode('\\', $group); + $group = array_shift($parts); + } + + foreach ($data['resources'] as $resource_name => $resource) { + /** @var Action\Documentation $action */ + foreach ($resource['actions'] as $identifier => $action) { + /** @var ReturnAnnotation|ErrorAnnotation $response */ + foreach ($action->getResponses() as $response) { + if (!$response instanceof ErrorAnnotation) { + continue; + } + + $error_code = $response->getErrorCode(); + if (empty($error_code)) { + continue; + } + + $path = $action->getPath(); + $this->error_map[$version][$group][$error_code][] = [ + 'path' => $path->getCleanPath(), + 'method' => $action->getMethod(), + 'http_code' => $response->getHttpCode(), + 'error_code' => $error_code, + 'description' => $response->getDescription() + ]; + } + } + } + } + } + + foreach ($this->error_map as $version => $groups) { + foreach ($groups as $group => $resources) { + foreach ($resources as $identifier => $errors) { + usort($this->error_map[$version][$group][$identifier], function (array $a, array $b): int { + // If the error codes match, then fallback to sorting by the path. + if ($a['error_code'] == $b['error_code']) { + // If the paths match, then fallback to sorting by their methods. + if ($a['path'] == $b['path']) { + return ($a['method'] < $b['method']) ? -1 : 1; + } + + return ($a['path'] < $b['path']) ? -1 : 1; + } + + return ($a['error_code'] < $b['error_code']) ? -1 : 1; + }); + } + + ksort($this->error_map[$version][$group]); + } + } + + return $this->error_map; + } + + /** + * Take compiled API documentation and convert it into a Markdown-based changelog over the life of the API. + * + * @return array + * @throws \Exception + */ + public function toMarkdown(): array + { + $markdown = new Markdown($this->config); + $markdown->setErrorMap($this->compile()); + return $markdown->compile(); + } +} diff --git a/src/Generator/ErrorMap/Formats/Markdown.php b/src/Compiler/ErrorMap/Formats/Markdown.php similarity index 67% rename from src/Generator/ErrorMap/Formats/Markdown.php rename to src/Compiler/ErrorMap/Formats/Markdown.php index 1e90759..1a6323a 100644 --- a/src/Generator/ErrorMap/Formats/Markdown.php +++ b/src/Compiler/ErrorMap/Formats/Markdown.php @@ -1,31 +1,21 @@ error_map as $version => $namespaces) { + foreach ($this->error_map as $version => $groups) { $content = ''; $api_name = $this->config->getName(); @@ -54,11 +44,11 @@ public function generate(): array $content .= $this->line(2); } - foreach ($namespaces as $namespace => $actions) { - $content .= sprintf('## %s', $namespace); + foreach ($groups as $group => $actions) { + $content .= sprintf('## %s', $group); $content .= $this->line(1); - $content .= '| Error Code | URI | Method | HTTP Code | Description |'; + $content .= '| Error Code | Path | Method | HTTP Code | Description |'; $content .= $this->line(1); $content .= '| :--- | :--- | :--- | :--- | :--- |'; $content .= $this->line(1); @@ -68,7 +58,7 @@ public function generate(): array $content .= sprintf( '| %s | %s | %s | %s | %s |', $error['error_code'], - $error['uri'], + $error['path'], $error['method'], $error['http_code'], $error['description'] diff --git a/src/Compiler/Specification.php b/src/Compiler/Specification.php new file mode 100644 index 0000000..1c29845 --- /dev/null +++ b/src/Compiler/Specification.php @@ -0,0 +1,43 @@ +representations[$representation])) ? $this->representations[$representation] : false; + } + + /** + * Convert an MSON sample data into a piece of data for that appropriate field type. + * + * @param bool|string $data + * @param string $type + * @return bool|string + */ + protected function convertSampleDataToCompatibleDataType($data, string $type) + { + if ($type === 'boolean') { + if ($data === '0') { + return 'false'; + } elseif ($data === '1') { + return 'true'; + } + } + + return $data; + } +} diff --git a/src/Generator/Blueprint.php b/src/Compiler/Specification/ApiBlueprint.php similarity index 72% rename from src/Generator/Blueprint.php rename to src/Compiler/Specification/ApiBlueprint.php index 4cdafa3..c64e974 100644 --- a/src/Generator/Blueprint.php +++ b/src/Compiler/Specification/ApiBlueprint.php @@ -1,57 +1,49 @@ config->getBlueprintNamespaceExcludes(); + $group_excludes = $this->config->getCompilerGroupExclusions(); $resources = $this->getResources(); - $blueprints = []; + $specifications = []; - /** @var array $data */ - foreach ($resources as $version => $namespaces) { + foreach ($resources as $version => $groups) { $this->version = $version; $this->representations = $this->getRepresentations($this->version); // Process resource groups. - foreach ($namespaces as $namespace => $data) { - // If this namespace has been designated in the config file to be excluded, then exclude it. - if (in_array($namespace, $namespace_excludes)) { + /** @var array $data */ + foreach ($groups as $group => $data) { + // If this group has been designated in the config file to be excluded, then exclude it. + if (in_array($group, $group_excludes)) { continue; } - $contents = sprintf('# Group %s', $namespace); + $contents = sprintf('# Group %s', $group); $contents .= $this->line(); // Sort the resources so they're alphabetical. @@ -63,7 +55,7 @@ public function generate(): array foreach ($data['resources'] as $identifier => $resource) { /** @var Action\Documentation $action */ foreach ($resource['actions'] as $action) { - $resource_key = sprintf('%s [%s]', $identifier, $action->getUri()->getCleanPath()); + $resource_key = sprintf('%s [%s]', $identifier, $action->getPath()->getCleanPath()); if (!isset($resource_contents[$resource_key])) { $resource_contents[$resource_key] = [ 'description' => $resource['description'], @@ -84,12 +76,11 @@ public function generate(): array $action_contents .= $this->processRequest($action); $coded_responses = []; - /** @var ReturnAnnotation|ThrowsAnnotation $response */ + /** @var ReturnAnnotation|ErrorAnnotation $response */ foreach ($action->getResponses() as $response) { $coded_responses[$response->getHttpCode()][] = $response; } - // Keep things tidy. ksort($coded_responses); foreach ($coded_responses as $http_code => $responses) { @@ -102,7 +93,7 @@ public function generate(): array } // Since there are instances where the same resource might be used with multiple endpoints, and on the - // same group, we need to abstract out the resource and action concatenation so we can generate unique + // same group, we need to abstract out the resource and action concatenation so we can compile unique // resource and action headers for each resource action. // // It would be nice to clean this code up at some po as it's a... bit... messy. @@ -127,7 +118,7 @@ public function generate(): array } $contents = trim($contents); - $blueprints[$this->version]['groups'][$namespace] = $contents; + $specifications[$this->version]['groups'][$group] = $contents; } // Process representation data structures. @@ -143,25 +134,25 @@ public function generate(): array $contents = sprintf('## %s', $identifier); $contents .= $this->line(); - $contents .= $this->processRepresentationFields($fields, 0); + $contents .= $this->processMSON($fields, 0); $contents = trim($contents); - $blueprints[$this->version]['structures'][$identifier] = $contents; + $specifications[$this->version]['structures'][$identifier] = $contents; } } // Process the combined file. - $blueprints[$this->version]['combined'] = $this->processCombinedFile( - $blueprints[$this->version]['groups'], - $blueprints[$this->version]['structures'] + $specifications[$this->version]['combined'] = $this->processCombinedFile( + $specifications[$this->version]['groups'], + $specifications[$this->version]['structures'] ); } - return $blueprints; + return $specifications; } /** - * Process an action and generate a scopes description. + * Process an action and compile a scopes description. * * @param Action\Documentation $action * @return false|string @@ -191,40 +182,33 @@ protected function processScopes(Action\Documentation $action) } /** - * Process an action and generate a parameters Blueprint. + * Process an action and compile a parameters Blueprint. * * @param Action\Documentation $action * @return false|string */ protected function processParameters(Action\Documentation $action) { - $segments = $action->getUriSegments(); - if (empty($segments)) { + $params = $action->getPathParameters(); + if (empty($params)) { return false; } $blueprint = '+ Parameters'; $blueprint .= $this->line(); - $translations = $this->config->getUriSegmentTranslations(); - - /** @var UriSegmentAnnotation $segment */ - foreach ($segments as $segment) { - $field = $segment->getField(); - foreach ($translations as $from => $to) { - $field = str_replace($from, $to, $field); - } - + /** @var PathParamAnnotation $param */ + foreach ($params as $param) { /** @var array $values */ - $values = $segment->getValues(); - $type = $this->convertTypeToCompatibleType($segment->getType()); + $values = $param->getValues(); + $type = $this->convertTypeToCompatibleType($param->getType()); $blueprint .= $this->tab(); $blueprint .= sprintf( '- `%s` (%s, required) - %s', - $field, + $param->getField(), $type, - $segment->getDescription() + $param->getDescription() ); $blueprint .= $this->line(); @@ -251,14 +235,14 @@ protected function processParameters(Action\Documentation $action) } /** - * Process an action and generate a Request Blueprint. + * Process an action and compile a Request Blueprint. * * @param Action\Documentation $action * @return string */ protected function processRequest(Action\Documentation $action): string { - $params = $action->getParameters(); + $params = $action->getExplodedAllQueryParameterDotNotation(); if (empty($params)) { return ''; } @@ -266,60 +250,22 @@ protected function processRequest(Action\Documentation $action): string $blueprint = '+ Request'; $blueprint .= $this->line(); - // Build up request attributes. $blueprint .= $this->tab(); $blueprint .= '+ Attributes'; $blueprint .= $this->line(); - - /** @var ParamAnnotation $param */ - foreach ($params as $param) { - $values = $param->getValues(); - $type = $this->convertTypeToCompatibleType($param->getType()); - $sample_data = $this->convertSampleDataToCompatibleDataType($param->getSampleData(), $type); - - $blueprint .= $this->tab(2); - $blueprint .= sprintf( - '- `%s`%s (%s%s%s) - %s', - $param->getField(), - ($sample_data !== false) ? sprintf(': `%s`', $sample_data) : '', - (!empty($values) && $param->getType() !== 'enum') ? 'enum[' . $type . ']' : $type, - ($param->isRequired()) ? ', required' : null, - ($param->isNullable()) ? ', nullable' : null, - $param->getDescription() - ); - - $blueprint .= $this->line(); - - if (!empty($values)) { - $blueprint .= $this->tab(3); - $blueprint .= '+ Members'; - $blueprint .= $this->line(); - - foreach ($values as $value => $value_description) { - $blueprint .= $this->tab(4); - $blueprint .= sprintf( - '+ `%s`%s', - $value, - (!empty($value_description)) ? sprintf(' - %s', $value_description) : '' - ); - - $blueprint .= $this->line(); - } - } - } + $blueprint .= $this->processMSON($params, 2); return $blueprint; } /** - * Process an action and response array and generate a Response Blueprint. + * Process an action and response array and compile a Response Blueprint. * * @param Action\Documentation $action * @param string $http_code * @param array $responses * @return string * @throws \Exception If a non-200 response is missing a description. - * @throws NoAnnotationsException If a used representation does not have any documentation. */ protected function processResponses(Action\Documentation $action, string $http_code, array $responses = []): string { @@ -341,13 +287,13 @@ protected function processResponses(Action\Documentation $action, string $http_c $blueprint .= sprintf('There are %s ways that this status code can be encountered:', $response_count); $blueprint .= $this->line(); - /** @var ReturnAnnotation|ThrowsAnnotation $response */ + /** @var ReturnAnnotation|ErrorAnnotation $response */ foreach ($responses as $response) { $description = $response->getDescription(); $description = (!empty($description)) ? $description : 'Standard request.'; $blueprint .= $this->tab(2); $blueprint .= sprintf(' * %s', $description); - if ($response instanceof ThrowsAnnotation) { + if ($response instanceof ErrorAnnotation) { $error_code = $response->getErrorCode(); if ($error_code) { $blueprint .= sprintf(' Unique error code: %s', $error_code); @@ -359,7 +305,7 @@ protected function processResponses(Action\Documentation $action, string $http_c } } - /** @var ReturnAnnotation|ThrowsAnnotation $response */ + /** @var ReturnAnnotation|ErrorAnnotation $response */ $response = array_shift($responses); $representation = $response->getRepresentation(); $representations = $this->getRepresentations($this->version); @@ -367,7 +313,7 @@ protected function processResponses(Action\Documentation $action, string $http_c return $blueprint; } - /** @var \Mill\Parser\Representation\Documentation $docs */ + /** @var Documentation $docs */ $docs = $representations[$representation]; $fields = $docs->getExplodedContentDotNotation(); if (!empty($fields)) { @@ -394,7 +340,7 @@ protected function processResponses(Action\Documentation $action, string $http_c * @param int $indent * @return string */ - private function processRepresentationFields(array $fields = [], int $indent = 2): string + private function processMSON(array $fields = [], int $indent = 2): string { $blueprint = ''; @@ -403,9 +349,9 @@ private function processRepresentationFields(array $fields = [], int $indent = 2 $blueprint .= $this->tab($indent); $data = []; - if (isset($field[Documentation::DOT_NOTATION_ANNOTATION_DATA_KEY])) { + if (isset($field[Application::DOT_NOTATION_ANNOTATION_DATA_KEY])) { /** @var array $data */ - $data = $field[Documentation::DOT_NOTATION_ANNOTATION_DATA_KEY]; + $data = $field[Application::DOT_NOTATION_ANNOTATION_DATA_KEY]; $type = $this->convertTypeToCompatibleType( $data['type'], (isset($data['subtype'])) ? $data['subtype'] : false @@ -432,10 +378,11 @@ private function processRepresentationFields(array $fields = [], int $indent = 2 } $blueprint .= sprintf( - '- `%s`%s (%s%s) - %s', + '- `%s`%s (%s%s%s) - %s', $field_name, ($sample_data !== false) ? sprintf(': `%s`', $sample_data) : '', $type, + (isset($data['required']) && $data['required']) ? ', required' : null, ($data['nullable']) ? ', nullable' : null, $description ); @@ -467,7 +414,7 @@ private function processRepresentationFields(array $fields = [], int $indent = 2 } // Process any exploded dot notation children of this field. - unset($field[Documentation::DOT_NOTATION_ANNOTATION_DATA_KEY]); + unset($field[Application::DOT_NOTATION_ANNOTATION_DATA_KEY]); if (!empty($field)) { // If this is an array, and has a subtype of object, we should indent a bit so we can properly render // out the array objects. @@ -476,9 +423,9 @@ private function processRepresentationFields(array $fields = [], int $indent = 2 $blueprint .= ' - (object)'; $blueprint .= $this->line(); - $blueprint .= $this->processRepresentationFields($field, $indent + 2); + $blueprint .= $this->processMSON($field, $indent + 2); } else { - $blueprint .= $this->processRepresentationFields($field, $indent + 1); + $blueprint .= $this->processMSON($field, $indent + 1); } } } @@ -516,7 +463,6 @@ protected function processCombinedFile(array $groups = [], array $structures = [ $blueprint .= $this->line(2); } - // Keep things tidy in the combined file. ksort($structures); $blueprint .= '# Data Structures'; @@ -530,26 +476,6 @@ protected function processCombinedFile(array $groups = [], array $structures = [ return $blueprint; } - /** - * Convert an MSON sample data into an API Blueprint-compatible piece of data for that appropriate field type. - * - * @param bool|string $data - * @param string $type - * @return bool|string - */ - private function convertSampleDataToCompatibleDataType($data, string $type) - { - if ($type == 'boolean') { - if ($data === '0') { - return 'false'; - } elseif ($data === '1') { - return 'true'; - } - } - - return $data; - } - /** * Convert a Mill-supported documentation into an API Blueprint-compatible type. * @@ -570,10 +496,10 @@ private function convertTypeToCompatibleType(string $type, $subtype = false): st return 'number'; break; - // API Blueprint doesn't have support for dates, timestamps, or URI's, but we still want to - // keep that metadata in our documentation (because they might be useful if you're using Mill as an API - // to display your documentation in some other format), so just convert these on the fly to strings so - // they're able pass blueprint validation. + // API Blueprint doesn't have support for dates, timestamps, or paths, but we still want to keep that + // metadata in our documentation (because they might be useful if you're using Mill as an API to display + // your documentation in some other format), so just convert these on the fly to strings so they're able + // pass blueprint validation. case 'date': case 'datetime': case 'timestamp': @@ -587,7 +513,7 @@ private function convertTypeToCompatibleType(string $type, $subtype = false): st if ($representation) { return 'array[' . $representation->getLabel() . ']'; } elseif ($subtype !== 'object') { - return 'array[' . $subtype . ']'; + return 'array[' . $this->convertTypeToCompatibleType($subtype) . ']'; } } @@ -604,15 +530,4 @@ private function convertTypeToCompatibleType(string $type, $subtype = false): st return $type; } - - /** - * Pull a representation from the current versioned set of representations. - * - * @param string $representation - * @return false|\Mill\Parser\Representation\Documentation - */ - private function getRepresentation(string $representation) - { - return (isset($this->representations[$representation])) ? $this->representations[$representation] : false; - } } diff --git a/src/Compiler/Specification/OpenApi.php b/src/Compiler/Specification/OpenApi.php new file mode 100644 index 0000000..65bc24e --- /dev/null +++ b/src/Compiler/Specification/OpenApi.php @@ -0,0 +1,680 @@ +config->getCompilerGroupExclusions(); + $resources = $this->getResources(); + + $specifications = []; + + foreach ($resources as $version => $groups) { + $this->version = $version; + $this->representations = $this->getRepresentations($this->version); + + $specifications[$this->version] = [ + 'openapi' => '3.0.0', + 'info' => [ + 'title' => $this->config->getName(), + 'version' => $this->version, + 'contact' => (function (): array { + $contact = $this->config->getContactInformation(); + $spec = []; + + foreach (['name', 'email'] as $data) { + if (isset($contact[$data])) { + $spec[$data] = $contact[$data]; + } + } + + $spec['url'] = $contact['url']; + return $spec; + })() + ], + 'tags' => (function () use ($groups, $group_excludes): array { + $tags = array_filter( + array_map( + function (string $group) use ($group_excludes): ?array { + if (in_array($group, $group_excludes)) { + return []; + } + + return [ + 'name' => $group + ]; + }, + array_keys($groups) + ) + ); + + // Excluding some groups and filtering off empty arrays will leave gaps in the keys of the tags + // array, resulting in some funky looking compiled YAML. + sort($tags); + + return $tags; + })(), + 'servers' => (function (): array { + $spec = []; + foreach ($this->config->getServers() as $server) { + $spec[] = [ + 'url' => $server['url'], + 'description' => $server['description'] + ]; + } + + return $spec; + })(), + 'paths' => [], + 'components' => [ + 'securitySchemes' => $this->processSecuritySchemes() + ], + 'security' => [ + [ + 'oauth2' => array_keys($this->config->getScopes()) + ] + ] + ]; + + // Process resource groups. + /** @var array $data */ + foreach ($groups as $group => $data) { + // If this group has been designated in the config file to be excluded, then exclude it. + if (in_array($group, $group_excludes)) { + continue; + } + + // Sort the resources so they're alphabetical. + ksort($data['resources']); + + /** @var array $resource */ + foreach ($data['resources'] as $identifier => $resource) { + /** @var Action\Documentation $action */ + foreach ($resource['actions'] as $action) { + $method = strtolower($action->getMethod()); + $identifier = $action->getPath()->getCleanPath(); + if (!isset($specifications[$this->version]['paths'][$identifier])) { + $specifications[$this->version]['paths'][$identifier] = []; + } + + $spec = [ + 'summary' => $action->getLabel(), + 'description' => $action->getDescription(), + 'operationId' => $this->transformActionIntoOperationId($action), + 'tags' => [ + $group + ], + 'parameters' => $this->processParameters($action), + 'requestBody' => $this->processRequest($action), + 'responses' => $this->processResponses($action), + 'security' => $this->processSecurity($action), + 'x-mill-vendortags' => $this->processVendorTags($action), + 'x-mill-visibility-private' => $action->getPath()->isVisible(), + 'x-mill-deprecated' => $action->getPath()->isDeprecated() + ]; + + foreach ([ + 'description', + 'parameters', + 'requestBody', + 'security', + 'x-mill-vendortags' + ] as $key) { + if (empty($spec[$key])) { + unset($spec[$key]); + } + } + + // Only include the `x-mill-visibility-private` tag if the action is private. + if (!$spec['x-mill-visibility-private']) { + unset($spec['x-mill-visibility-private']); + } + + // Only include the `x-mill-deprecated` tag if the action is deprecated. + if (!$spec['x-mill-deprecated']) { + unset($spec['x-mill-deprecated']); + } + + $specifications[$this->version]['paths'][$identifier][$method] = $spec; + } + } + } + + // Process representation data structures. + if (!empty($this->representations)) { + foreach ($this->representations as $representation) { + $fields = $representation->getExplodedContentDotNotation(); + if (empty($fields)) { + continue; + } + + $identifier = $this->getReferenceName($representation->getLabel()); + $specifications[$this->version]['components']['schemas'][$identifier] = [ + 'properties' => $this->processMSON(DataAnnotation::PAYLOAD_FORMAT, $fields) + ]; + } + } + } + + return $specifications; + } + + /** + * @return array + */ + protected function processSecuritySchemes(): array + { + $spec = []; + $flows = $this->config->getAuthenticationFlows(); + + if (isset($flows['bearer'])) { + $spec['bearer'] = [ + 'type' => 'http', + 'scheme' => 'bearer', + 'bearerFormat' => $flows['bearer']['format'] + ]; + } + + if (isset($flows['oauth2']) && !empty($flows['oauth2'])) { + $spec['oauth2'] = [ + 'type' => 'oauth2', + 'flows' => (function () use ($flows): array { + $spec = []; + $scopes = []; + foreach ($this->config->getScopes() as $scope => $data) { + $scopes[$scope] = $data['description']; + } + + if (isset($flows['oauth2']['authorization_code'])) { + $spec['authorizationCode'] = [ + 'authorizationUrl' => $flows['oauth2']['authorization_code']['authorization_url'], + 'tokenUrl' => $flows['oauth2']['authorization_code']['token_url'], + 'scopes' => $scopes + ]; + } + + if (isset($flows['oauth2']['client_credentials'])) { + $spec['clientCredentials'] = [ + 'tokenUrl' => $flows['oauth2']['client_credentials']['token_url'], + 'scopes' => $scopes + ]; + } + + return $spec; + })() + ]; + } + + return $spec; + } + + /** + * @param Action\Documentation $action + * @return array + */ + protected function processParameters(Action\Documentation $action): array + { + return array_merge( + $this->processMSON(PathParamAnnotation::PAYLOAD_FORMAT, $action->getExplodedPathParameterDotNotation()), + $this->processMSON(QueryParamAnnotation::PAYLOAD_FORMAT, $action->getExplodedQueryParameterDotNotation()) + ); + } + + /** + * @param Action\Documentation $action + * @return array + * @throws \Exception + */ + protected function processRequest(Action\Documentation $action): array + { + $params = $action->getExplodedParameterDotNotation(); + if (empty($params)) { + return []; + } + + return [ + 'required' => !empty( + array_reduce( + $action->getParameters(), + /** @param mixed $carry */ + function ($carry, ParamAnnotation $param): ?array { + if ($param->isRequired()) { + $carry[] = $param->getField(); + } + + return $carry; + } + ) + ), + 'content' => [ + $action->getContentType($this->version) => [ + 'schema' => (function () use ($params): array { + $spec = [ + 'type' => 'object', + 'properties' => $this->processMSON(ParamAnnotation::PAYLOAD_FORMAT, $params) + ]; + + $spec = $this->extractRequiredFields($spec); + + return $spec; + })() + ] + ] + ]; + } + + /** + * @param Action\Documentation $action + * @return array + * @throws \Exception + */ + protected function processResponses(Action\Documentation $action): array + { + $schema = []; + $coded_responses = []; + + /** @var ReturnAnnotation|ErrorAnnotation $response */ + foreach ($action->getResponses() as $response) { + $http_code = substr($response->getHttpCode(), 0, 3); + $coded_responses[$http_code][] = $response; + } + + foreach ($coded_responses as $http_code => $responses) { + $total_responses = count($responses); + + // OpenAPI doesn't have support for multiple responses of the same HTTP code, so let's mash them down + // together, but document to the developer what's going on. + if ($total_responses > 1) { + $description = sprintf( + 'There are %s ways that this status code can be encountered:', + (new \NumberFormatter('en', \NumberFormatter::SPELLOUT))->format(count($responses)) + ); + + $description .= $this->line(); + } else { + /** @var string $description */ + $description = current($responses)->getDescription(); + } + + /** @var ReturnAnnotation|ErrorAnnotation $response */ + foreach ($responses as $response) { + $response_description = $response->getDescription(); + if ($total_responses > 1) { + $description .= sprintf(' * %s', $response_description); + } + + if ($response instanceof ErrorAnnotation) { + $error_code = $response->getErrorCode(); + if ($error_code) { + $description .= sprintf(' Returns a unique error code of `%s`.', $error_code); + } + } + + $description .= $this->line(); + } + + $spec = [ + 'description' => trim($description) ?: 'Standard request.' + ]; + + /** @var ReturnAnnotation|ErrorAnnotation $response */ + $response = array_shift($responses); + $representation = $response->getRepresentation(); + $representations = $this->getRepresentations($this->version); + if (isset($representations[$representation])) { + /** @var Documentation $docs */ + $docs = $representations[$representation]; + $fields = $docs->getExplodedContentDotNotation(); + if (!empty($fields)) { + $ref_name = $this->getReferenceName($docs->getLabel()); + $response_schema = [ + '$ref' => '#/components/schemas/' . $ref_name + ]; + + if ($response instanceof ReturnAnnotation && $response->getType() === 'collection') { + $response_schema = [ + 'type' => 'array', + 'items' => [ + '$ref' => '#/components/schemas/' . $ref_name + ] + ]; + } + + $spec['content'] = [ + $action->getContentType($this->version) => [ + 'schema' => $response_schema + ] + ]; + } + } + + $schema[$http_code] = $spec; + } + + return $schema; + } + + /** + * @param Action\Documentation $action + * @return array + */ + protected function processSecurity(Action\Documentation $action): array + { + $scopes = $action->getScopes(); + if (empty($scopes)) { + return []; + } + + return [ + [ + 'oauth2' => array_map(function (ScopeAnnotation $scope): string { + return $scope->getScope(); + }, $scopes) + ] + ]; + } + + /** + * @param Action\Documentation $action + * @return array + */ + protected function processVendorTags(Action\Documentation $action): array + { + $vendor_tags = $action->getVendorTags(); + if (empty($vendor_tags)) { + return []; + } + + return array_map(function (VendorTagAnnotation $vendor_tag): string { + return $vendor_tag->getVendorTag(); + }, $vendor_tags); + } + + /** + * @param Action\Documentation $action + * @return string + * @throws \Exception + */ + private function transformActionIntoOperationId(Action\Documentation $action): string + { + $path = $action->getPath()->getCleanPath(); + $path = str_replace(['{', '}'], '', $path); + $path = str_replace('/', ' ', $path); + $path = ucwords($path); + $path = str_replace(' ', '', $path); + + return strtolower($action->getMethod()) . $path; + } + + /** + * @param string $payload_format + * @param array $fields + * @return array + */ + private function processMSON(string $payload_format, array $fields = []): array + { + $schema = []; + + /** @var array $field */ + foreach ($fields as $field_name => $field) { + $data = []; + if (isset($field[Application::DOT_NOTATION_ANNOTATION_DATA_KEY])) { + /** @var array $data */ + $data = $field[Application::DOT_NOTATION_ANNOTATION_DATA_KEY]; + + $spec = [ + 'name' => $field_name, + 'in' => $payload_format, + 'description' => $data['description'], + 'required' => (array_key_exists('required', $data) && $data['required']), + 'schema' => [ + 'type' => $this->convertTypeToCompatibleType($data['type']) + ] + ]; + + if (!empty($data['scopes'])) { + // If this description doesn't end with punctuation, add a period before we display a list of + // required authentication scopes. + $spec['description'] .= (!in_array(substr($spec['description'], -1), ['.', '!', '?'])) ? '.' : ''; + $spec['description'] .= sprintf( + ' This data requires a bearer token with the %s scope%s.', + '`' . implode('`, `', array_map(function (array $scope): string { + return $scope['scope']; + }, $data['scopes'])) . '`', + (count($data['scopes']) > 1) ? 's' : null + ); + } + + if ($data['sample_data'] !== false) { + $spec['schema']['example'] = $this->convertSampleDataToCompatibleDataType( + $data['sample_data'], + $spec['schema']['type'] + ); + } + + if (array_key_exists('nullable', $data) && $data['nullable']) { + $spec['schema']['nullable'] = true; + } + + if ($spec['schema']['type'] === 'object') { + $representation = $this->getRepresentation($data['type']); + if ($representation) { + $ref = '#/components/schemas/' . $this->getReferenceName($representation->getLabel()); + + if ($payload_format === DataAnnotation::PAYLOAD_FORMAT) { + unset($spec['schema']['type']); + + $spec['allOf'] = [ + [ + '$ref' => $ref + ] + ]; + } else { + $spec['schema']['$ref'] = $ref; + } + } + } + + // Only enum's support options/members. + if (($data['type'] === 'enum' || (isset($data['subtype']) && $data['subtype'] === 'enum')) && + !empty($data['values']) + ) { + $addendum = ''; + $spec['schema']['enum'] = []; + + foreach ($data['values'] as $value => $value_description) { + $spec['schema']['enum'][] = $value; + + if (!empty($value_description)) { + $addendum .= sprintf(' * `%s` - %s', $value, $value_description); + $addendum .= $this->line(); + } + } + + if (!empty($addendum)) { + $spec['description'] .= $this->line(2); + $spec['description'] .= 'Option descriptions:'; + $spec['description'] .= $this->line(); + $spec['description'] .= $addendum; + } + } + } else { + $spec = [ + 'name' => $field_name, + 'in' => $payload_format, + 'schema' => [ + 'type' => 'object' + ] + ]; + } + + // If we're processing MSON for a component, clean it up so it can be used as a component. + if (in_array($payload_format, [DataAnnotation::PAYLOAD_FORMAT, ParamAnnotation::PAYLOAD_FORMAT])) { + if (isset($spec['schema'])) { + $spec += $spec['schema']; + unset($spec['schema']); + } + + unset($spec['name']); + unset($spec['in']); + + if ($payload_format === DataAnnotation::PAYLOAD_FORMAT) { + unset($spec['required']); + } + } + + // Process any exploded dot notation children of this field. + unset($field[Application::DOT_NOTATION_ANNOTATION_DATA_KEY]); + if (!empty($field)) { + if (empty($data)) { + $spec['properties'] = $this->processMSON($payload_format, $field); + } elseif ($data['type'] === 'array' && $data['subtype'] === 'object') { + $spec['items'] = [ + 'type' => 'object', + 'properties' => $this->processMSON($payload_format, $field) + ]; + } elseif ($data['type'] === 'object') { + $spec['properties'] = $this->processMSON($payload_format, $field); + } else { + $spec['items'] = $this->processMSON($payload_format, $field); + } + } elseif ($data['type'] === 'array') { + $spec['items'] = [ + 'type' => 'string' + ]; + } + + // Request body requirement definitions need to be separate from the item schema. + if ($payload_format === ParamAnnotation::PAYLOAD_FORMAT) { + $spec = $this->extractRequiredFields($spec); + } + + // Path and query parameters should not be keyed off the field name. + if (in_array($payload_format, [ + PathParamAnnotation::PAYLOAD_FORMAT, + QueryParamAnnotation::PAYLOAD_FORMAT + ])) { + $schema[] = $spec; + } else { + $schema[$field_name] = $spec; + } + } + + return $schema; + } + + /** + * @param array $spec + * @return array + */ + private function extractRequiredFields(array $spec): array + { + /** @var array $properties */ + $properties = []; + if (isset($spec['properties'])) { + $properties = $spec['properties']; + } elseif (isset($spec['items']['properties'])) { + $properties = $spec['items']['properties']; + } + + if (!empty($properties)) { + $required = []; + foreach ($properties as $name => $property) { + if (!array_key_exists('required', $property)) { + continue; + } elseif ($property['required']) { + $required[] = $name; + } + + unset($properties[$name]['required']); + } + + if (isset($spec['properties'])) { + $spec['properties'] = $properties; + if (!empty($required)) { + $spec['required'] = $required; + } + } elseif (isset($spec['items']['properties'])) { + $spec['items']['properties'] = $properties; + if (!empty($required)) { + $spec['items']['required'] = $required; + } + } + } + + return $spec; + } + + /** + * Convert a Mill-supported documentation into an OpenAPI-compatible type. + * + * @link https://swagger.io/docs/specification/data-models/data-types/ + * @param string $type + * @return string + */ + private function convertTypeToCompatibleType(string $type): string + { + switch ($type) { + case 'array': + case 'boolean': + case 'number': + case 'string': + return $type; + break; + + case 'float': + case 'integer': + return 'number'; + break; + + case 'date': + case 'datetime': + case 'enum': + case 'timestamp': + case 'uri': + return 'string'; + break; + + default: + return 'object'; + } + + return $type; + } + + /** + * @param string $name + * @return string + */ + private function getReferenceName(string $name): string + { + return (new Slugify())->slugify($name); + } +} diff --git a/src/Generator/Traits/ChangelogTemplate.php b/src/Compiler/Traits/ChangelogTemplate.php similarity index 94% rename from src/Generator/Traits/ChangelogTemplate.php rename to src/Compiler/Traits/ChangelogTemplate.php index 7a747ea..c643601 100644 --- a/src/Generator/Traits/ChangelogTemplate.php +++ b/src/Compiler/Traits/ChangelogTemplate.php @@ -1,21 +1,15 @@ joinWords( @@ -121,7 +115,7 @@ protected function transformTemplateIntoMarkdown(string $template, array $conten case 'method': case 'parameter': case 'representation': - case 'uri': + case 'path': $searches[] = '{' . $key . '}'; if (is_array($value)) { $replacements[] = $this->joinWords( diff --git a/src/Generator/Traits/Markdown.php b/src/Compiler/Traits/Markdown.php similarity index 93% rename from src/Generator/Traits/Markdown.php rename to src/Compiler/Traits/Markdown.php index 331afd4..a264435 100644 --- a/src/Generator/Traits/Markdown.php +++ b/src/Compiler/Traits/Markdown.php @@ -1,5 +1,5 @@ base_dir . $xml['bootstrap']; } - if (isset($xml->capabilities)) { - $config->capabilities = []; - $config->loadCapabilities($xml->capabilities->capability); - } - - if (isset($xml->scopes)) { - $config->scopes = []; - $config->loadScopes($xml->scopes->scope); - } - - if (isset($xml->uriSegments) && - isset($xml->uriSegments->translations)) { - $config->uri_segment_translations = []; - $config->loadUriSegmentTranslations($xml->uriSegments->translations->translation); - } - - if (isset($xml->parameterTokens)) { - $config->loadParameterTokens($xml->parameterTokens->token); - } - - if (isset($xml->generators)) { - $config->loadGeneratorSettings($xml->generators); - } - - $config->api_versions = []; - $config->loadVersions($xml->versions->version); - - $config->controllers = []; - $config->loadControllers($xml->controllers); - - $config->representations = []; - $config->error_representations = []; - $config->excluded_representations = []; - $config->loadRepresentations($xml->representations); - $config->loadErrorRepresentations($xml->representations); + $config->loadAuthentication($xml); + $config->loadCompilerSettings($xml); + $config->loadControllers($xml); + $config->loadErrorRepresentations($xml); + $config->loadInfo($xml); + $config->loadParameterTokens($xml); + $config->loadPathParamTranslations($xml); + $config->loadRepresentations($xml); + $config->loadServers($xml); + $config->loadVendorTags($xml); + $config->loadVersions($xml); return $config; } /** - * Load an array of application capabilities into the configuration system. - * - * @param SimpleXMLElement $capabilities + * @param SimpleXMLElement $xml */ - protected function loadCapabilities(SimpleXMLElement $capabilities): void + protected function loadVendorTags(SimpleXMLElement $xml): void { - /** @var SimpleXMLElement $capability */ - foreach ($capabilities as $capability) { - $this->addCapability((string) $capability['name']); + if (!isset($xml->vendorTags)) { + return; } - // Keep things tidy. - $this->capabilities = array_unique($this->capabilities); - } + /** @var SimpleXMLElement $vendor_tag */ + foreach ($xml->vendorTags->vendorTag as $vendor_tag) { + $this->vendor_tags[] = (string) $vendor_tag['name']; + } - /** - * Add a new application capability into the instance config. - * - * @param string $capability - */ - public function addCapability(string $capability): void - { - $this->capabilities[] = $capability; + $this->vendor_tags = array_unique($this->vendor_tags); } /** - * Load an array of authentication scopes into the configuration system. - * - * @param SimpleXMLElement $scopes + * @param SimpleXMLElement $xml */ - protected function loadScopes(SimpleXMLElement $scopes): void + protected function loadAuthentication(SimpleXMLElement $xml): void { - /** @var SimpleXMLElement $scope */ - foreach ($scopes as $scope) { - $this->scopes[] = (string) $scope['name']; + /** @var SimpleXMLElement $authentication */ + $authentication = $xml->authentication; + + if (isset($authentication->flows->bearer)) { + $this->authentication_flows['bearer'] = [ + 'format' => (isset($authentication->flows->bearer['format'])) + ? (string) $authentication->flows->bearer['format'] + : 'bearer' + ]; + } + + if (isset($authentication->flows->oauth2)) { + $this->authentication_flows['oauth2'] = []; + + if (isset($authentication->flows->oauth2->authorizationCode)) { + $this->authentication_flows['oauth2']['authorization_code'] = [ + 'authorization_url' => (string) $authentication->flows->oauth2->authorizationCode['url'], + 'token_url' => (string) $authentication->flows->oauth2->authorizationCode['tokenUrl'] + ]; + } + + if (isset($authentication->flows->oauth2->clientCredentials)) { + $this->authentication_flows['oauth2']['client_credentials'] = [ + 'token_url' => (string) $authentication->flows->oauth2->clientCredentials['url'] + ]; + } } - // Keep things tidy. - $this->scopes = array_unique($this->scopes); + if (isset($authentication->scopes)) { + /** @var SimpleXMLElement $scope */ + foreach ($authentication->scopes->scope as $scope) { + $name = (string) $scope['name']; + $this->scopes[$name] = [ + 'name' => $name, + 'description' => (string) $scope['description'] + ]; + } + + ksort($this->scopes); + } } /** - * Load an array of URI segment translations into the configuration system. - * - * @param SimpleXMLElement $translations + * @param SimpleXMLElement $xml */ - protected function loadUriSegmentTranslations($translations): void + protected function loadPathParamTranslations(SimpleXMLElement $xml): void { + if (!isset($xml->pathParams)) { + return; + } elseif (!isset($xml->pathParams->translations)) { + return; + } + /** @var SimpleXMLElement $translation */ - foreach ($translations as $translation) { + foreach ($xml->pathParams->translations->translation as $translation) { $translate_from = trim((string) $translation['from']); $translate_to = trim((string) $translation['to']); - $this->addUriSegmentTranslation($translate_from, $translate_to); + $this->addPathParamTranslation($translate_from, $translate_to); } } /** - * Add a new URI segment translation into the instance config. - * - * @param string $from - * @param string $to - * @throws DomainException If an invalid uriSegment translation text was found. + * @param SimpleXMLElement $xml */ - public function addUriSegmentTranslation($from, $to): void + protected function loadParameterTokens(SimpleXMLElement $xml): void { - if (empty($from) || empty($to)) { - throw new DomainException( - 'An invalid translation text was supplied in the Mill `uriSegmentTranslations` section.' - ); + if (!isset($xml->parameterTokens)) { + return; } - $this->uri_segment_translations[$from] = $to; - } - - /** - * Load an array of `@api-param` replacement tokens into the configuration system. - * - * @param SimpleXMLElement $tokens - */ - protected function loadParameterTokens(SimpleXMLElement $tokens): void - { /** @var SimpleXMLElement $token */ - foreach ($tokens as $token) { + foreach ($xml->parameterTokens->token as $token) { $parameter = trim((string) $token['name']); $annotation = trim((string) $token); $this->addParameterToken($parameter, $annotation); } - // Keep things tidy. $this->parameter_tokens = array_unique($this->parameter_tokens); } /** - * Add a new `@api-param` replacement token into the instance config. - * - * @param string $parameter - * @param string $annotation - * @throws DomainException If an invalid parameterTokens token name was found. + * @param SimpleXMLElement $xml */ - public function addParameterToken(string $parameter, string $annotation): void + protected function loadCompilerSettings(SimpleXMLElement $xml): void { - if (empty($parameter) || empty($annotation)) { - throw new DomainException( - 'An invalid parameter token name was supplied in the Mill `parameterTokens` section.' - ); + if (!isset($xml->compilers)) { + return; } - $this->parameter_tokens['{' . $parameter . '}'] = $annotation; - } - - /** - * Load in generator settings. - * - * @param SimpleXMLElement $generators - */ - protected function loadGeneratorSettings(SimpleXMLElement $generators): void - { - if (isset($generators->blueprint)) { - if (isset($generators->blueprint->excludes)) { - /** @var SimpleXMLElement $exclude */ - foreach ($generators->blueprint->excludes->exclude as $exclude) { - $namespace = trim((string) $exclude['namespace']); + if (isset($xml->compilers->excludes)) { + /** @var SimpleXMLElement $exclude */ + foreach ($xml->compilers->excludes->exclude as $exclude) { + $group = trim((string) $exclude['group']); - $this->addBlueprintNamespaceExclude($namespace); - } + $this->addCompilerGroupExclusion($group); } } } /** - * Add a new API Blueprint resource namespace generator exclusion. + * Load in an API information configuration definition. * - * @param string $namespace - * @throws DomainException If an invalid Blueprint generator namespace exclude was detected. + * @param SimpleXMLElement $xml */ - public function addBlueprintNamespaceExclude(string $namespace): void + protected function loadInfo(SimpleXMLElement $xml): void { - if (empty($namespace)) { - throw new DomainException( - 'An invalid Blueprint generator namespace exclude was supplied in the Mill `generators` section.' - ); + if (isset($xml->info->terms)) { + $this->terms = (string) $xml->info->terms['url']; + } + + foreach (['name', 'email', 'url'] as $contact_data) { + if (isset($xml->info->contact[$contact_data])) { + $this->contact[$contact_data] = (string) $xml->info->contact[$contact_data]; + } } - $this->blueprint_namespace_excludes[] = $namespace; + if (isset($xml->info->externalDocs)) { + foreach ($xml->info->externalDocs->externalDoc as $external_doc) { + $this->external_documentation[] = [ + 'name' => (string) $external_doc['name'], + 'url' => (string) $external_doc['url'] + ]; + } + } } /** - * Remove a currently configured API Blueprint resource namespace generator exclusion. - * - * @param string $namespace + * @param SimpleXMLElement $xml */ - public function removeBlueprintNamespaceExclude(string $namespace): void + protected function loadServers(SimpleXMLElement $xml): void { - $excludes = array_flip($this->blueprint_namespace_excludes); - if (isset($excludes[$namespace])) { - unset($excludes[$namespace]); + foreach ($xml->servers->server as $server) { + $this->servers[] = [ + 'url' => (string) $server['url'], + 'description' => (string) $server['description'] + ]; } - - $this->blueprint_namespace_excludes = array_flip($excludes); } /** - * Load in a versions configuration definition. - * - * @param SimpleXMLElement $versions + * @param SimpleXMLElement $xml * @throws InvalidArgumentException If multiple configured default API versions were detected. * @throws DomainException If no default API version was set. */ - protected function loadVersions(SimpleXMLElement $versions): void + protected function loadVersions(SimpleXMLElement $xml): void { $api_versions = []; - foreach ($versions as $version) { + foreach ($xml->versions->version as $version) { $version_number = (string) $version['name']; $description = trim((string) $version); @@ -424,7 +355,6 @@ protected function loadVersions(SimpleXMLElement $versions): void throw new DomainException('You must set a default API version.'); } - // Keep things tidy. $sorted_numerical = Semver::sort(array_keys($api_versions)); foreach ($sorted_numerical as $version) { $this->api_versions[] = $api_versions[$version]; @@ -435,16 +365,14 @@ protected function loadVersions(SimpleXMLElement $versions): void } /** - * Load in a controllers configuration definition. - * - * @param SimpleXMLElement $controllers + * @param SimpleXMLElement $xml * @throws InvalidArgumentException If a directory configured does not exist. * @throws InvalidArgumentException If no controllers were detected. */ - protected function loadControllers(SimpleXMLElement $controllers): void + protected function loadControllers(SimpleXMLElement $xml): void { /** @var SimpleXMLElement $controllers */ - $controllers = $controllers->filter; + $controllers = $xml->controllers->filter; $excludes = []; if (isset($controllers->excludes)) { @@ -456,7 +384,6 @@ protected function loadControllers(SimpleXMLElement $controllers): void $excludes[] = (string) $exclude['name']; } - // Keep things tidy. $excludes = array_unique($excludes); } @@ -480,7 +407,6 @@ protected function loadControllers(SimpleXMLElement $controllers): void ); } - // Keep things tidy. $this->controllers = array_unique($this->controllers); sort($this->controllers); @@ -490,38 +416,16 @@ protected function loadControllers(SimpleXMLElement $controllers): void } /** - * Add a new resource controller into the instance config. - * - * @param string $class - * @throws InvalidArgumentException If a class could not be found. - */ - public function addController(string $class): void - { - if (!class_exists($class)) { - throw new InvalidArgumentException( - sprintf( - 'The `%s` controller class could not be called. Is your bootstrap set up properly?', - $class - ) - ); - } - - $this->controllers[] = $class; - } - - /** - * Load in a representations configuration definition. - * - * @param SimpleXMLElement $representations + * @param SimpleXMLElement $xml * @throws UncallableRepresentationException If a configured representation does not exist. * @throws DomainException If a representation is configured without a `method` attribute. * @throws InvalidArgumentException If a directory configured does not exist. * @throws InvalidArgumentException If no representations were detected. */ - protected function loadRepresentations(SimpleXMLElement $representations): void + protected function loadRepresentations(SimpleXMLElement $xml): void { /** @var SimpleXMLElement $filters */ - $filters = $representations->filter; + $filters = $xml->representations->filter; // Process excludes. if (isset($filters->excludes)) { @@ -533,7 +437,6 @@ protected function loadRepresentations(SimpleXMLElement $representations): void $this->addExcludedRepresentation((string) $exclude['name']); } - // Keep things tidy. $this->excluded_representations = array_unique($this->excluded_representations); } @@ -575,7 +478,6 @@ protected function loadRepresentations(SimpleXMLElement $representations): void } } - // Keep things tidy ksort($this->representations); if (empty($this->representations)) { @@ -584,63 +486,18 @@ protected function loadRepresentations(SimpleXMLElement $representations): void } /** - * Add a representation into the excluded list of representations. - * - * @param string $class - */ - public function addExcludedRepresentation(string $class): void - { - $this->excluded_representations[] = $class; - } - - /** - * Remove a representation that has been set up to be excluded from compilation, from being excluded. - * - * @param string $class - */ - public function removeExcludedRepresentation(string $class): void - { - $excludes = array_flip($this->excluded_representations); - if (isset($excludes[$class])) { - unset($excludes[$class]); - } - - $this->excluded_representations = array_flip($excludes); - } - - /** - * Add a new representation into the instance config. - * - * @param string $class - * @param null|string $method - * @throws UncallableRepresentationException If the representation is uncallable. - */ - public function addRepresentation(string $class, string $method = null): void - { - if (!class_exists($class)) { - throw UncallableRepresentationException::create($class); - } - - $this->representations[$class] = [ - 'class' => $class, - 'method' => $method - ]; - } - - /** - * Load in an error representations configuration definition. - * - * @param SimpleXMLElement $representations + * @param SimpleXMLElement $xml * @throws UncallableErrorRepresentationException If a configured error representation class does not exist. * @throws DomainException If an error representation is missing a `method` attribute. */ - protected function loadErrorRepresentations(SimpleXMLElement $representations): void + protected function loadErrorRepresentations(SimpleXMLElement $xml): void { - /** @var SimpleXMLElement $errors */ - $errors = $representations->errors; + if (!isset($xml->representations->errors)) { + return; + } /** @var SimpleXMLElement $class */ - foreach ($errors->class as $class) { + foreach ($xml->representations->errors->class as $class) { $class_name = (string) $class['name']; $method = (string) $class['method'] ?: null; $needs_error_code = (string) $class['needsErrorCode']; @@ -665,24 +522,53 @@ protected function loadErrorRepresentations(SimpleXMLElement $representations): } /** - * Check if a given representation has been configured to be excluded. + * Get the name of your API. * - * @param string $class - * @return bool + * @return null|string */ - public function isRepresentationExcluded(string $class): bool + public function getName(): ?string { - return in_array($class, $this->getExcludedRepresentations()); + return $this->name; } /** - * Get the name of your API. + * Get the terms of service URL for your API. * * @return null|string */ - public function getName(): ?string + public function getTerms(): ?string { - return $this->name; + return $this->terms; + } + + /** + * Get the contact information for your API. + * + * @return array + */ + public function getContactInformation(): array + { + return $this->contact; + } + + /** + * Get external documentation for your API. + * + * @return array + */ + public function getExternalDocumentation(): array + { + return $this->external_documentation; + } + + /** + * Get your API servers. + * + * @return array + */ + public function getServers(): array + { + return $this->servers; } /** @@ -744,13 +630,23 @@ public function getApiVersion(string $version): array } /** - * Get the array of configured application capabilities. + * Get the array of configured application vendor tags. + * + * @return array + */ + public function getVendorTags(): array + { + return $this->vendor_tags; + } + + /** + * Get your API authentication flows. * * @return array */ - public function getCapabilities(): array + public function getAuthenticationFlows(): array { - return $this->capabilities; + return $this->authentication_flows; } /** @@ -764,13 +660,42 @@ public function getScopes(): array } /** - * Get the array of configured URI segment translations. + * Determine if a given scope has been configured. + * + * @param string $scope + * @return bool + */ + public function hasScope(string $scope): bool + { + return in_array($scope, array_keys($this->scopes)); + } + + /** + * Get the array of configured path param translations. * * @return array */ - public function getUriSegmentTranslations(): array + public function getPathParamTranslations(): array { - return $this->uri_segment_translations; + return $this->path_param_translations; + } + + /** + * Add a new path param translation into the instance config. + * + * @param string $from + * @param string $to + * @throws DomainException If an invalid pathParam translation text was found. + */ + public function addPathParamTranslation($from, $to): void + { + if (empty($from) || empty($to)) { + throw new DomainException( + 'An invalid translation text was supplied in the Mill `pathParams` `translations` section.' + ); + } + + $this->path_param_translations[$from] = $to; } /** @@ -783,6 +708,24 @@ public function getParameterTokens(): array return $this->parameter_tokens; } + /** + * Add a new `@api-param` replacement token into the instance config. + * + * @param string $parameter + * @param string $annotation + * @throws DomainException If an invalid parameterTokens token name was found. + */ + public function addParameterToken(string $parameter, string $annotation): void + { + if (empty($parameter) || empty($annotation)) { + throw new DomainException( + 'An invalid parameter token name was supplied in the Mill `parameterTokens` section.' + ); + } + + $this->parameter_tokens['{' . $parameter . '}'] = $annotation; + } + /** * Get the array of configured application controllers. * @@ -793,6 +736,26 @@ public function getControllers(): array return $this->controllers; } + /** + * Add a new resource controller into the instance config. + * + * @param string $class + * @throws InvalidArgumentException If a class could not be found. + */ + public function addController(string $class): void + { + if (!class_exists($class)) { + throw new InvalidArgumentException( + sprintf( + 'The `%s` controller class could not be called. Is your bootstrap set up properly?', + $class + ) + ); + } + + $this->controllers[] = $class; + } + /** * Get the array of configured application representations. * @@ -834,13 +797,100 @@ public function getExcludedRepresentations(): array } /** - * Get the array of configured API Blueprint resource namespace excludes. + * Check if a given representation has been configured to be excluded. + * + * @param string $class + * @return bool + */ + public function isRepresentationExcluded(string $class): bool + { + return in_array($class, $this->getExcludedRepresentations()); + } + + /** + * Add a new representation into the instance config. + * + * @param string $class + * @param null|string $method + * @throws UncallableRepresentationException If the representation is uncallable. + */ + public function addRepresentation(string $class, string $method = null): void + { + if (!class_exists($class)) { + throw UncallableRepresentationException::create($class); + } + + $this->representations[$class] = [ + 'class' => $class, + 'method' => $method + ]; + } + + /** + * Add a representation into the excluded list of representations. + * + * @param string $class + */ + public function addExcludedRepresentation(string $class): void + { + $this->excluded_representations[] = $class; + } + + /** + * Remove a representation that has been set up to be excluded from compilation, from being excluded. + * + * @param string $class + */ + public function removeExcludedRepresentation(string $class): void + { + $excludes = array_flip($this->excluded_representations); + if (isset($excludes[$class])) { + unset($excludes[$class]); + } + + $this->excluded_representations = array_flip($excludes); + } + + /** + * Get the array of configured compiler resource group exclusions. * * @return array */ - public function getBlueprintNamespaceExcludes(): array + public function getCompilerGroupExclusions(): array + { + return $this->compiler_group_exclusions; + } + + /** + * Add a new compiler resource group exclusion. + * + * @param string $group + * @throws DomainException If an invalid compiler group exclusion was detected. + */ + public function addCompilerGroupExclusion(string $group): void + { + if (empty($group)) { + throw new DomainException( + 'An invalid compiler group exclusion was supplied in the Mill `compilers` section.' + ); + } + + $this->compiler_group_exclusions[] = $group; + } + + /** + * Remove a currently configured compiler resource group exclusion. + * + * @param string $group + */ + public function removeCompilerGroupExclusion(string $group): void { - return $this->blueprint_namespace_excludes; + $excludes = array_flip($this->compiler_group_exclusions); + if (isset($excludes[$group])) { + unset($excludes[$group]); + } + + $this->compiler_group_exclusions = array_flip($excludes); } /** diff --git a/src/Container.php b/src/Container.php index 284f93a..a7996a1 100644 --- a/src/Container.php +++ b/src/Container.php @@ -7,9 +7,7 @@ class Container extends \Pimple\Container { - /** - * @var Container|null - */ + /** @var Container|null */ protected static $instance = null; /** @@ -43,7 +41,7 @@ public function __construct(array $values = []) * Return the current instance of the container. * * @codeCoverageIgnore - * @return self + * @return Container */ public static function getInstance(): self { diff --git a/src/Contracts/Arrayable.php b/src/Contracts/Arrayable.php new file mode 100644 index 0000000..5462280 --- /dev/null +++ b/src/Contracts/Arrayable.php @@ -0,0 +1,10 @@ +capability = $capability; - $exception->class = $class; - $exception->method = $method; - - return $exception; - } - - /** - * Get the capability that this exception occurred with. - * - * @return string - */ - public function getCapability(): string - { - return $this->capability; - } -} diff --git a/src/Exceptions/Annotations/InvalidMSONSyntaxException.php b/src/Exceptions/Annotations/InvalidMSONSyntaxException.php index 216d9ca..dff48f2 100644 --- a/src/Exceptions/Annotations/InvalidMSONSyntaxException.php +++ b/src/Exceptions/Annotations/InvalidMSONSyntaxException.php @@ -7,6 +7,14 @@ class InvalidMSONSyntaxException extends BaseException { use AnnotationExceptionTrait; + /** + * @param string $required_field + * @param string $annotation + * @param string $docblock + * @param string $class + * @param string $method + * @return InvalidMSONSyntaxException + */ public static function create( string $required_field, string $annotation, diff --git a/src/Exceptions/Annotations/InvalidScopeSuppliedException.php b/src/Exceptions/Annotations/InvalidScopeSuppliedException.php index fb3283e..668b2d8 100644 --- a/src/Exceptions/Annotations/InvalidScopeSuppliedException.php +++ b/src/Exceptions/Annotations/InvalidScopeSuppliedException.php @@ -7,11 +7,15 @@ class InvalidScopeSuppliedException extends BaseException { use AnnotationExceptionTrait; - /** - * @var string - */ + /** @var string */ public $scope; + /** + * @param string $scope + * @param string $class + * @param string $method + * @return InvalidScopeSuppliedException + */ public static function create(string $scope, string $class, string $method): InvalidScopeSuppliedException { $message = sprintf( diff --git a/src/Exceptions/Annotations/InvalidVendorTagSuppliedException.php b/src/Exceptions/Annotations/InvalidVendorTagSuppliedException.php new file mode 100644 index 0000000..1eb3cca --- /dev/null +++ b/src/Exceptions/Annotations/InvalidVendorTagSuppliedException.php @@ -0,0 +1,48 @@ +vendor_tag = $vendor_tag; + $exception->class = $class; + $exception->method = $method; + + return $exception; + } + + /** + * Get the vendor tag that this exception occurred with. + * + * @return string + */ + public function getVendorTag(): string + { + return $this->vendor_tag; + } +} diff --git a/src/Exceptions/Annotations/MissingRepresentationErrorCodeException.php b/src/Exceptions/Annotations/MissingRepresentationErrorCodeException.php index de11215..e841979 100644 --- a/src/Exceptions/Annotations/MissingRepresentationErrorCodeException.php +++ b/src/Exceptions/Annotations/MissingRepresentationErrorCodeException.php @@ -7,13 +7,19 @@ class MissingRepresentationErrorCodeException extends BaseException { use AnnotationExceptionTrait; + /** + * @param string $representation + * @param string $class + * @param string $method + * @return MissingRepresentationErrorCodeException + */ public static function create( string $representation, string $class, string $method ): MissingRepresentationErrorCodeException { $message = sprintf( - 'The `%s` error representation on `@api-throws %s` in %s::%s is missing an error code, but is required ' . + 'The `%s` error representation on `@api-error %s` in %s::%s is missing an error code, but is required ' . 'to have one in your config file.', $representation, $representation, diff --git a/src/Exceptions/Annotations/MissingRequiredFieldException.php b/src/Exceptions/Annotations/MissingRequiredFieldException.php index 4374a8d..d6947dd 100644 --- a/src/Exceptions/Annotations/MissingRequiredFieldException.php +++ b/src/Exceptions/Annotations/MissingRequiredFieldException.php @@ -7,6 +7,14 @@ class MissingRequiredFieldException extends BaseException { use AnnotationExceptionTrait; + /** + * @param string $required_field + * @param string $annotation + * @param string $docblock + * @param string $class + * @param string $method + * @return MissingRequiredFieldException + */ public static function create( string $required_field, string $annotation, diff --git a/src/Exceptions/Annotations/MultipleAnnotationsException.php b/src/Exceptions/Annotations/MultipleAnnotationsException.php index 4ef24cf..3590c3b 100644 --- a/src/Exceptions/Annotations/MultipleAnnotationsException.php +++ b/src/Exceptions/Annotations/MultipleAnnotationsException.php @@ -7,6 +7,12 @@ class MultipleAnnotationsException extends BaseException { use AnnotationExceptionTrait; + /** + * @param string $annotation + * @param string $class + * @param string|null $method + * @return MultipleAnnotationsException + */ public static function create( string $annotation, string $class, diff --git a/src/Exceptions/Annotations/RequiredAnnotationException.php b/src/Exceptions/Annotations/RequiredAnnotationException.php index 2b12958..c8de096 100644 --- a/src/Exceptions/Annotations/RequiredAnnotationException.php +++ b/src/Exceptions/Annotations/RequiredAnnotationException.php @@ -7,6 +7,12 @@ class RequiredAnnotationException extends BaseException { use AnnotationExceptionTrait; + /** + * @param string $annotation + * @param string $class + * @param string|null $method + * @return RequiredAnnotationException + */ public static function create(string $annotation, string $class, string $method = null): RequiredAnnotationException { $message = sprintf( diff --git a/src/Exceptions/Annotations/UncallableErrorCodeException.php b/src/Exceptions/Annotations/UncallableErrorCodeException.php deleted file mode 100644 index ff6c4d5..0000000 --- a/src/Exceptions/Annotations/UncallableErrorCodeException.php +++ /dev/null @@ -1,26 +0,0 @@ -docblock = $docblock; - $exception->class = $class; - $exception->method = $method; - - return $exception; - } -} diff --git a/src/Exceptions/Annotations/UnknownErrorRepresentationException.php b/src/Exceptions/Annotations/UnknownErrorRepresentationException.php index d644362..0342bde 100644 --- a/src/Exceptions/Annotations/UnknownErrorRepresentationException.php +++ b/src/Exceptions/Annotations/UnknownErrorRepresentationException.php @@ -7,13 +7,19 @@ class UnknownErrorRepresentationException extends BaseException { use AnnotationExceptionTrait; + /** + * @param string $representation + * @param string $class + * @param string $method + * @return UnknownErrorRepresentationException + */ public static function create( string $representation, string $class, string $method ): UnknownErrorRepresentationException { $message = sprintf( - 'The `@api-throws %s` in %s::%s has an unknown representation. Is it present in your config file?', + 'The `@api-error %s` in %s::%s has an unknown representation. Is it present in your config file?', $representation, $class, $method diff --git a/src/Exceptions/Annotations/UnknownRepresentationException.php b/src/Exceptions/Annotations/UnknownRepresentationException.php index 3bbdeba..b31257f 100644 --- a/src/Exceptions/Annotations/UnknownRepresentationException.php +++ b/src/Exceptions/Annotations/UnknownRepresentationException.php @@ -7,6 +7,12 @@ class UnknownRepresentationException extends BaseException { use AnnotationExceptionTrait; + /** + * @param string $representation + * @param string $class + * @param string $method + * @return UnknownRepresentationException + */ public static function create( string $representation, string $class, diff --git a/src/Exceptions/Annotations/UnknownReturnCodeException.php b/src/Exceptions/Annotations/UnknownReturnCodeException.php index 2875900..d1fbbfa 100644 --- a/src/Exceptions/Annotations/UnknownReturnCodeException.php +++ b/src/Exceptions/Annotations/UnknownReturnCodeException.php @@ -7,6 +7,13 @@ class UnknownReturnCodeException extends BaseException { use AnnotationExceptionTrait; + /** + * @param string $annotation + * @param string $docblock + * @param string $class + * @param string $method + * @return UnknownReturnCodeException + */ public static function create( string $annotation, string $docblock, diff --git a/src/Exceptions/Annotations/UnsupportedTypeException.php b/src/Exceptions/Annotations/UnsupportedTypeException.php index 28c9b93..aab85ab 100644 --- a/src/Exceptions/Annotations/UnsupportedTypeException.php +++ b/src/Exceptions/Annotations/UnsupportedTypeException.php @@ -7,6 +7,12 @@ class UnsupportedTypeException extends BaseException { use AnnotationExceptionTrait; + /** + * @param string $annotation + * @param string $class + * @param null|string $method + * @return UnsupportedTypeException + */ public static function create(string $annotation, string $class, ?string $method): UnsupportedTypeException { $message = sprintf( diff --git a/src/Exceptions/Config/UncallableErrorRepresentationException.php b/src/Exceptions/Config/UncallableErrorRepresentationException.php index fc33799..f2413ab 100644 --- a/src/Exceptions/Config/UncallableErrorRepresentationException.php +++ b/src/Exceptions/Config/UncallableErrorRepresentationException.php @@ -8,6 +8,10 @@ class UncallableErrorRepresentationException extends BaseException /** @var string */ public $representation; + /** + * @param string $representation + * @return UncallableErrorRepresentationException + */ public static function create(string $representation): UncallableErrorRepresentationException { $message = sprintf( diff --git a/src/Exceptions/Config/UncallableRepresentationException.php b/src/Exceptions/Config/UncallableRepresentationException.php index 007e9d5..f3f4831 100644 --- a/src/Exceptions/Config/UncallableRepresentationException.php +++ b/src/Exceptions/Config/UncallableRepresentationException.php @@ -8,6 +8,10 @@ class UncallableRepresentationException extends BaseException /** @var string */ public $representation; + /** + * @param string $representation + * @return UncallableRepresentationException + */ public static function create(string $representation): UncallableRepresentationException { $message = sprintf( diff --git a/src/Exceptions/Config/UnconfiguredErrorRepresentationException.php b/src/Exceptions/Config/UnconfiguredErrorRepresentationException.php index 28e1071..0a88949 100644 --- a/src/Exceptions/Config/UnconfiguredErrorRepresentationException.php +++ b/src/Exceptions/Config/UnconfiguredErrorRepresentationException.php @@ -8,6 +8,10 @@ class UnconfiguredErrorRepresentationException extends BaseException /** @var string */ public $representation; + /** + * @param string $representation + * @return UnconfiguredErrorRepresentationException + */ public static function create(string $representation): UnconfiguredErrorRepresentationException { $message = sprintf( diff --git a/src/Exceptions/Config/UnconfiguredRepresentationException.php b/src/Exceptions/Config/UnconfiguredRepresentationException.php index 9b5c199..5e85f13 100644 --- a/src/Exceptions/Config/UnconfiguredRepresentationException.php +++ b/src/Exceptions/Config/UnconfiguredRepresentationException.php @@ -8,6 +8,10 @@ class UnconfiguredRepresentationException extends BaseException /** @var string */ public $representation; + /** + * @param string $representation + * @return UnconfiguredRepresentationException + */ public static function create(string $representation): UnconfiguredRepresentationException { $message = sprintf( diff --git a/src/Exceptions/Config/ValidationException.php b/src/Exceptions/Config/ValidationException.php index 90c78b5..61dbf8a 100644 --- a/src/Exceptions/Config/ValidationException.php +++ b/src/Exceptions/Config/ValidationException.php @@ -5,6 +5,11 @@ class ValidationException extends BaseException { + /** + * @param int $line + * @param string $message + * @return ValidationException + */ public static function create(int $line, string $message): ValidationException { $message = sprintf( diff --git a/src/Exceptions/MSON/ImproperlyWrittenEnumException.php b/src/Exceptions/MSON/ImproperlyWrittenEnumException.php new file mode 100644 index 0000000..6068186 --- /dev/null +++ b/src/Exceptions/MSON/ImproperlyWrittenEnumException.php @@ -0,0 +1,33 @@ +annotation = $annotation; + $exception->class = $class; + $exception->method = $method; + + return $exception; + } +} diff --git a/src/Exceptions/MSON/MissingOptionsException.php b/src/Exceptions/MSON/MissingOptionsException.php index a050a7b..3e9fc0b 100644 --- a/src/Exceptions/MSON/MissingOptionsException.php +++ b/src/Exceptions/MSON/MissingOptionsException.php @@ -5,11 +5,15 @@ class MissingOptionsException extends BaseException { - /** - * @var null|string - */ + /** @var null|string */ public $type = null; + /** + * @param string $type + * @param string $class + * @param string $method + * @return MissingOptionsException + */ public static function create(string $type, string $class, string $method): MissingOptionsException { $message = sprintf( diff --git a/src/Exceptions/MethodNotImplementedException.php b/src/Exceptions/MethodNotImplementedException.php index d2c8344..9c8efeb 100644 --- a/src/Exceptions/MethodNotImplementedException.php +++ b/src/Exceptions/MethodNotImplementedException.php @@ -3,6 +3,11 @@ class MethodNotImplementedException extends BaseException { + /** + * @param string $class + * @param string $method + * @return MethodNotImplementedException + */ public static function create(string $class, string $method): MethodNotImplementedException { $message = sprintf( diff --git a/src/Exceptions/MethodNotSuppliedException.php b/src/Exceptions/MethodNotSuppliedException.php index 6560e82..0007147 100644 --- a/src/Exceptions/MethodNotSuppliedException.php +++ b/src/Exceptions/MethodNotSuppliedException.php @@ -3,6 +3,10 @@ class MethodNotSuppliedException extends BaseException { + /** + * @param string $class + * @return MethodNotSuppliedException + */ public static function create(string $class): MethodNotSuppliedException { $message = sprintf( diff --git a/src/Exceptions/Representation/DuplicateFieldException.php b/src/Exceptions/Representation/DuplicateFieldException.php index 5a15130..e9c967a 100644 --- a/src/Exceptions/Representation/DuplicateFieldException.php +++ b/src/Exceptions/Representation/DuplicateFieldException.php @@ -7,6 +7,12 @@ class DuplicateFieldException extends BaseException { use RepresentationExceptionTrait; + /** + * @param string $field + * @param string $class + * @param string $method + * @return DuplicateFieldException + */ public static function create(string $field, string $class, string $method): DuplicateFieldException { $message = sprintf( diff --git a/src/Exceptions/Representation/RestrictedFieldNameException.php b/src/Exceptions/Representation/RestrictedFieldNameException.php index 7638854..a3ff697 100644 --- a/src/Exceptions/Representation/RestrictedFieldNameException.php +++ b/src/Exceptions/Representation/RestrictedFieldNameException.php @@ -1,18 +1,23 @@ getResources() as $version => $resources) { - foreach ($resources as $namespace => $data) { - // Namespaces can have children via the `\` delimiter, but for the error map generator we only care - // about the top-level namespace. - if (strpos($namespace, '\\') != false) { - $parts = explode('\\', $namespace); - $namespace = array_shift($parts); - } - - foreach ($data['resources'] as $resource_name => $resource) { - /** @var Action\Documentation $action */ - foreach ($resource['actions'] as $identifier => $action) { - /** @var ReturnAnnotation|ThrowsAnnotation $response */ - foreach ($action->getResponses() as $response) { - if (!$response instanceof ThrowsAnnotation) { - continue; - } - - $error_code = $response->getErrorCode(); - if (empty($error_code)) { - continue; - } - - $uri = $action->getUri(); - $this->error_map[$version][$namespace][$error_code][] = [ - 'uri' => $uri->getCleanPath(), - 'method' => $action->getMethod(), - 'http_code' => $response->getHttpCode(), - 'error_code' => $error_code, - 'description' => $response->getDescription() - ]; - } - } - } - } - } - - // Keep things tidy - foreach ($this->error_map as $version => $namespaces) { - foreach ($namespaces as $namespace => $resources) { - foreach ($resources as $identifier => $errors) { - usort($this->error_map[$version][$namespace][$identifier], function (array $a, array $b): int { - // If the error codes match, then fallback to sorting by the URI. - if ($a['error_code'] == $b['error_code']) { - // If the URIs match, then fallback to sorting by their methods. - if ($a['uri'] == $b['uri']) { - return ($a['method'] < $b['method']) ? -1 : 1; - } - - return ($a['uri'] < $b['uri']) ? -1 : 1; - } - - return ($a['error_code'] < $b['error_code']) ? -1 : 1; - }); - } - - ksort($this->error_map[$version][$namespace]); - } - } - - return $this->error_map; - } - - /** - * Take compiled API documentation and generate a Markdown-based changelog over the life of the API. - * - * @return array - */ - public function generateMarkdown(): array - { - $markdown = new Markdown($this->config); - $markdown->setErrorMap($this->generate()); - return $markdown->generate(); - } -} diff --git a/src/Parser.php b/src/Parser.php index 6d7a11a..32bc713 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -9,26 +9,14 @@ use Mill\Parser\Version; use ReflectionClass; -/** - * Class for tokenizing a docblock on a given class or method. - * - */ class Parser { const REGEX_DECORATOR = '/^(?P(:\w+)+)?/u'; - /** - * The current class that we're going to be parsing. - * - * @var string - */ + /** @var string The current class that we're going to be parsing. */ protected $class; - /** - * The current class method that we're parsing. Used to give better error messaging. - * - * @var null|string - */ + /** @var null|string The current class method that we're parsing. Used to give better error messaging. */ protected $method; /** @@ -43,6 +31,7 @@ public function __construct(string $class) * Get an array of HTTP (GET, POST, PUT, PATCH, DELETE) methods that are implemented on the current class. * * @return array + * @throws \ReflectionException */ public function getHttpMethods() { @@ -63,8 +52,9 @@ public function getHttpMethods() /** * Locate, and parse, the annotations for a class or method. * - * @param null|string $method_name - * @return array An array containing all the found annotations. + * @param string|null $method_name + * @return array + * @throws UnsupportedDecoratorException */ public function getAnnotations(string $method_name = null): array { @@ -87,31 +77,32 @@ public function getAnnotations(string $method_name = null): array * * @link https://github.com/facebook/libphutil/blob/master/src/parser/docblock/PhutilDocblockParser.php * @param string $docblock - * @param boolean $parse_description If we want to parse out an unstructured `description` annotation. - * @return array Array of parsed annotations. + * @param bool $parse_description If we want to parse out an unstructured `description` annotation. + * @return array + * @throws UnsupportedDecoratorException */ protected function parseDocblock(string $docblock, bool $parse_description = true): array { $original_docblock = $docblock; $annotations = []; + $annotation_tags = []; $matches = null; $parser = self::getAnnotationsFromDocblock($docblock); $tags = $parser->getTags(); - if (!empty($tags)) { - $annotation_tags = []; - - /** @var UnknownTag $tag */ - foreach ($tags as $tag) { - // If this isn't a Mill annotation, then ignore it. - $annotation = $tag->getTagName(); - if (substr($annotation, 0, 4) !== 'api-') { - continue; - } - $annotation_tags[] = $tag; + /** @var UnknownTag $tag */ + foreach ($tags as $tag) { + // If this isn't a Mill annotation, then ignore it. + $annotation = $tag->getTagName(); + if (substr($annotation, 0, 4) !== 'api-') { + continue; } + $annotation_tags[] = $tag; + } + + if (!empty($annotation_tags)) { $annotations = $this->parseAnnotations($annotation_tags, $original_docblock); } @@ -140,6 +131,8 @@ protected function parseDocblock(string $docblock, bool $parse_description = tru * @param array $tags * @param string $original_content * @return array + * @throws Exceptions\Version\UnrecognizedSchemaException + * @throws UnsupportedDecoratorException */ protected function parseAnnotations(array $tags, string $original_content): array { @@ -181,36 +174,6 @@ protected function parseAnnotations(array $tags, string $original_content): arra return $annotations; } - /** - * Hydrate an annotation with some data. - * - * @param string $name - * @param string $class - * @param string $method - * @param array $data - * @return Annotation - */ - public function hydrateAnnotation(string $name, string $class, string $method, array $data = []): Annotation - { - $annotation_class = $this->getAnnotationClass(str_replace('_', '', $name)); - - $version = null; - if (!empty($data['version'])) { - $version = new Version($data['version'], $class, $method); - } - - return $annotation_class::hydrate( - array_merge( - $data, - [ - 'class' => $class, - 'method' => $method - ] - ), - $version - ); - } - /** * Build up an array of annotation data. * @@ -290,12 +253,24 @@ private function getAnnotationClass(string $annotation): string $annotation = 'ContentType'; break; + case 'maxversion': + $annotation = 'MaxVersion'; + break; + case 'minversion': $annotation = 'MinVersion'; break; - case 'urisegment': - $annotation = 'UriSegment'; + case 'pathparam': + $annotation = 'PathParam'; + break; + + case 'queryparam': + $annotation = 'QueryParam'; + break; + + case 'vendortag': + $annotation = 'VendorTag'; break; default: @@ -317,8 +292,8 @@ public static function getAnnotationsFromDocblock(string $docblock): Docblock } /** - * @param null|string $method - * @return self + * @param string|null $method + * @return Parser */ public function setMethod(string $method = null): self { @@ -335,6 +310,6 @@ public function setMethod(string $method = null): self protected function getAnnotationNameFromTag(UnknownTag $tag): string { $annotation = $tag->getTagName(); - return substr($annotation, 4); + return strtolower(substr($annotation, 4)); } } diff --git a/src/Parser/Annotation.php b/src/Parser/Annotation.php index a9a17b2..82f8399 100644 --- a/src/Parser/Annotation.php +++ b/src/Parser/Annotation.php @@ -1,147 +1,66 @@ - */ + /** @var array Array of all available aliases for this annotation. */ protected $aliases = []; - /** - * Flag designating that this annotation is aliased or not. - * - * @var bool - */ + /** @var bool Flag designating that this annotation is aliased or not. */ protected $aliased = false; - /** - * Array of parsed data from this annotation. - * - * @var array - */ + /** @var array Array of parsed data from this annotation. */ protected $parsed_data = []; - /** - * An array of items that should be included in an array representation of this annotation. - * - * @var array - */ - protected $arrayable = []; - /** * @param string $doc * @param string $class @@ -162,7 +81,7 @@ public function __construct(string $doc, string $class, string $method = null, V /** * Process and parse the annotation docblock that was created. * - * @return self + * @return Annotation */ public function process(): self { @@ -224,6 +143,10 @@ protected function optional(string $field, $allow_zero = false) if ($allow_zero && $this->parsed_data[$field] === '0') { return $this->parsed_data[$field]; } elseif (empty($this->parsed_data[$field])) { + if (is_array($this->parsed_data[$field])) { + return []; + } + return false; } @@ -259,81 +182,6 @@ abstract protected function parser(): array; */ abstract protected function interpreter(): void; - /** - * With an array of data that was output from an Annotation, via `toArray()`, hydrate a new Annotation object. - * - * @param array $data - * @param null|Version $version - * @return self - */ - public static function hydrate(array $data = [], Version $version = null) - { - $class = get_called_class(); - - /** @var Annotation $annotation */ - $annotation = new $class('', $data['class'], $data['method'], $version); - - if (array_key_exists('capability', $data) && !empty($data['capability'])) { - // Since capability annotations have a `capability` value, let's avoid created a CapabilityAnnotation within - // another CapabilityAnnotation. - if ($annotation instanceof CapabilityAnnotation) { - $capability = $data['capability']; - } else { - $capability = (new CapabilityAnnotation( - $data['capability'], - $data['class'], - $data['method'], - $version - ))->process(); - } - - $annotation->setCapability($capability); - } - - if ($annotation->requiresVisibilityDecorator()) { - $annotation->setVisibility($data['visible']); - } - - if ($annotation->supportsAliasing()) { - $annotation->setAliased($data['aliased']); - - $aliases = []; - foreach ($data['aliases'] as $alias) { - $aliases[] = UriAnnotation::hydrate(array_merge([ - 'class' => $data['class'], - 'method' => $data['method'] - ], $alias)); - } - - $annotation->setAliases($aliases); - } - - if ($annotation->supportsDeprecation()) { - $annotation->setDeprecated($data['deprecated']); - } - - if ($annotation->supportsScopes()) { - $scopes = []; - foreach ($data['scopes'] as $scope) { - $scopes[] = ScopeAnnotation::hydrate(array_merge( - $scope, - [ - 'class' => __CLASS__, - 'method' => __METHOD__ - ] - )); - } - - $annotation->setScopes($scopes); - } - - if ($annotation->supportsVersioning() && $version) { - $annotation->setVersion($version); - } - - return $annotation; - } - /** * Does this annotation require a visibility decorator? * @@ -345,33 +193,33 @@ public function requiresVisibilityDecorator(): bool } /** - * Does this annotation support aliasing? + * Does this annotation support versioning? * * @return bool */ - public function supportsAliasing(): bool + public function supportsDeprecation(): bool { - return static::SUPPORTS_ALIASING; + return static::SUPPORTS_DEPRECATION; } /** - * Does this annotation support versioning? + * Does this annotation support authentication scopes? * * @return bool */ - public function supportsDeprecation(): bool + public function supportsScopes(): bool { - return static::SUPPORTS_DEPRECATION; + return static::SUPPORTS_SCOPES; } /** - * Does this annotation support authentication scopes? + * Does this annotation support vendor tags? * * @return bool */ - public function supportsScopes(): bool + public function supportsVendorTags(): bool { - return static::SUPPORTS_SCOPES; + return static::SUPPORTS_VENDOR_TAGS; } /** @@ -385,14 +233,12 @@ public function supportsVersioning(): bool } /** - * Convert the parsed annotation into an array. - * - * @return array + * {{@inheritdoc}} */ public function toArray(): array { $arr = []; - foreach ($this->arrayable as $var) { + foreach (static::ARRAYABLE as $var) { if ($this->{$var} instanceof Annotation) { $arr += $this->{$var}->toArray(); } else { @@ -405,23 +251,10 @@ public function toArray(): array $arr['visible'] = $this->isVisible(); } - // If this annotation supports aliasing, then we should include any aliasing data about it. - if ($this->supportsAliasing()) { - $arr['aliased'] = $this->isAliased(); - $arr['aliases'] = []; - - /** @var Annotation $alias */ - foreach ($this->getAliases() as $alias) { - $arr['aliases'][] = $alias->toArray(); - } - } - - // If this annotation supports deprecation, then we should include its designation. if ($this->supportsDeprecation()) { $arr['deprecated'] = $this->isDeprecated(); } - // If this annotation supports authentication scopes, then we should include those scopes. if ($this->supportsScopes()) { $arr['scopes'] = []; @@ -431,7 +264,15 @@ public function toArray(): array } } - // If this annotation supports versioning, then we should include its version + if ($this->supportsVendorTags()) { + $arr['vendor_tags'] = []; + + /** @var Annotation $scope */ + foreach ($this->getVendorTags() as $vendor_tag) { + $arr['vendor_tags'][] = $vendor_tag->getVendorTag(); + } + } + if ($this->supportsVersioning()) { $arr['version'] = false; @@ -485,7 +326,7 @@ public function hasVisibility(): bool * Set the visibility on the current annotation. * * @param bool $visibility - * @return self + * @return Annotation */ public function setVisibility(bool $visibility): self { @@ -507,7 +348,7 @@ public function isDeprecated(): bool * Set if this annotation is deprecated or not. * * @param bool $deprecated - * @return self + * @return Annotation */ public function setDeprecated(bool $deprecated): self { @@ -529,7 +370,7 @@ public function isAliased(): bool * Set if this annotation is an alias or not. * * @param bool $aliased - * @return self + * @return Annotation */ public function setAliased(bool $aliased): self { @@ -540,8 +381,8 @@ public function setAliased(bool $aliased): self /** * Set any aliases to this annotation. * - * @param array $aliases - * @return self + * @param array $aliases + * @return Annotation */ public function setAliases(array $aliases): self { @@ -563,7 +404,7 @@ public function getAliases(): array * Set any required authentication scopes to this annotation. * * @param array $scopes - * @return self + * @return Annotation */ public function setScopes(array $scopes): self { @@ -582,25 +423,23 @@ public function getScopes(): array } /** - * Return the capability, if any, that has been set. - * - * @return false|string|CapabilityAnnotation + * Return an array of vendor tags that this annotation possesses. + * @return array */ - public function getCapability() + public function getVendorTags(): array { - return $this->capability; + return $this->vendor_tags; } /** - * Set a capability that this annotation requires. This is specifically used in tandem with representation depth - * parsing. + * Set vendor tags that this annotation possesses. * - * @param false|string $capability - * @return self + * @param array $vendor_tags + * @return Annotation */ - public function setCapability($capability): self + public function setVendorTags(array $vendor_tags = []): self { - $this->capability = $capability; + $this->vendor_tags = $vendor_tags; return $this; } @@ -619,7 +458,7 @@ public function getVersion() * depth parsing. * * @param Version $version - * @return self + * @return Annotation */ public function setVersion(Version $version): self { diff --git a/src/Parser/Annotations/CapabilityAnnotation.php b/src/Parser/Annotations/CapabilityAnnotation.php deleted file mode 100644 index 714ddc2..0000000 --- a/src/Parser/Annotations/CapabilityAnnotation.php +++ /dev/null @@ -1,64 +0,0 @@ -docblock; - - if (!empty($capability)) { - // Validate the supplied capability with what has been configured as allowable. - $capabilities = Container::getConfig()->getCapabilities(); - if (!in_array($capability, $capabilities)) { - /** @var string $method */ - $method = $this->method; - throw InvalidCapabilitySuppliedException::create($capability, $this->class, $method); - } - } - - return [ - 'capability' => $capability - ]; - } - - /** - * {@inheritdoc} - */ - protected function interpreter(): void - { - $this->capability = $this->required('capability'); - } - - /** - * {@inheritdoc} - */ - public static function hydrate(array $data = [], Version $version = null): self - { - /** @var CapabilityAnnotation $annotation */ - $annotation = parent::hydrate($data, $version); - return $annotation; - } -} diff --git a/src/Parser/Annotations/ContentTypeAnnotation.php b/src/Parser/Annotations/ContentTypeAnnotation.php index 0d832ae..a877f8f 100644 --- a/src/Parser/Annotations/ContentTypeAnnotation.php +++ b/src/Parser/Annotations/ContentTypeAnnotation.php @@ -2,28 +2,18 @@ namespace Mill\Parser\Annotations; use Mill\Parser\Annotation; -use Mill\Parser\Version; -/** - * Handler for the `@api-contentType` annotation. - * - */ class ContentTypeAnnotation extends Annotation { const SUPPORTS_VERSIONING = true; - /** @var string */ - protected $content_type; - - /** - * An array of items that should be included in an array representation of this annotation. - * - * @var array - */ - protected $arrayable = [ + const ARRAYABLE = [ 'content_type' ]; + /** @var string */ + protected $content_type; + /** * {@inheritdoc} */ @@ -42,18 +32,6 @@ protected function interpreter(): void $this->content_type = $this->required('content_type'); } - /** - * {@inheritdoc} - */ - public static function hydrate(array $data = [], Version $version = null): self - { - /** @var ContentTypeAnnotation $annotation */ - $annotation = parent::hydrate($data, $version); - $annotation->setContentType($data['content_type']); - - return $annotation; - } - /** * @return string */ @@ -64,7 +42,7 @@ public function getContentType(): string /** * @param string $content_type - * @return self + * @return ContentTypeAnnotation */ public function setContentType(string $content_type): self { diff --git a/src/Parser/Annotations/DataAnnotation.php b/src/Parser/Annotations/DataAnnotation.php index b2ffa53..2d5a8ea 100644 --- a/src/Parser/Annotations/DataAnnotation.php +++ b/src/Parser/Annotations/DataAnnotation.php @@ -1,90 +1,58 @@ $mson->getType(), 'subtype' => $mson->getSubtype(), 'nullable' => $mson->isNullable(), - 'capability' => $mson->getCapability(), + 'vendor_tags' => $mson->getVendorTags(), 'description' => $mson->getDescription(), 'values' => $mson->getValues() ]; - // Create a capability annotation if one was supplied. - if (!empty($parsed['capability'])) { - $parsed['capability'] = (new CapabilityAnnotation( - $parsed['capability'], - $this->class, - $this->method - ))->process(); + if (!empty($parsed['vendor_tags'])) { + $parsed['vendor_tags'] = array_map( + /** @return Annotation */ + function (string $tag) use ($method) { + return (new VendorTagAnnotation( + $tag, + $this->class, + $method + ))->process(); + }, + $parsed['vendor_tags'] + ); } if (!empty($parsed['identifier'])) { - if (strtoupper($parsed['identifier']) === Documentation::DOT_NOTATION_ANNOTATION_DATA_KEY) { + if (strtoupper($parsed['identifier']) === Application::DOT_NOTATION_ANNOTATION_DATA_KEY) { throw RestrictedFieldNameException::create($this->class, $this->method); } } @@ -140,28 +113,10 @@ protected function interpreter(): void $this->description = $this->required('description'); $this->values = $this->optional('values'); - $this->capability = $this->optional('capability'); + $this->vendor_tags = $this->optional('vendor_tags'); $this->nullable = $this->optional('nullable'); } - /** - * {@inheritdoc} - */ - public static function hydrate(array $data = [], Version $version = null): self - { - /** @var DataAnnotation $annotation */ - $annotation = parent::hydrate($data, $version); - $annotation->setDescription($data['description']); - $annotation->setIdentifier($data['identifier']); - $annotation->setNullable($data['nullable']); - $annotation->setSampleData($data['sample_data']); - $annotation->setSubtype($data['subtype']); - $annotation->setType($data['type']); - $annotation->setValues($data['values']); - - return $annotation; - } - /** * @return string */ @@ -172,7 +127,7 @@ public function getDescription(): string /** * @param string $description - * @return self + * @return DataAnnotation */ public function setDescription(string $description): self { @@ -190,7 +145,7 @@ public function getIdentifier(): string /** * @param string $identifier - * @return self + * @return DataAnnotation */ public function setIdentifier(string $identifier): self { @@ -202,9 +157,9 @@ public function setIdentifier(string $identifier): self * Set a dot notation prefix on the identifier. * * @param string $prefix - * @return self + * @return DataAnnotation */ - public function setIdentifierPrefix($prefix): self + public function setIdentifierPrefix(string $prefix): self { $this->identifier = $prefix . '.' . $this->identifier; return $this; @@ -220,7 +175,7 @@ public function isNullable(): bool /** * @param bool $nullable - * @return self + * @return DataAnnotation */ public function setNullable(bool $nullable): self { @@ -238,7 +193,7 @@ public function getSampleData() /** * @param false|string $sample_data - * @return self + * @return DataAnnotation */ public function setSampleData($sample_data): self { @@ -247,38 +202,38 @@ public function setSampleData($sample_data): self } /** - * @return false|string + * @return string */ - public function getSubtype() + public function getType(): string { - return $this->subtype; + return $this->type; } /** - * @param false|string $subtype - * @return self + * @param string $type + * @return DataAnnotation */ - public function setSubtype($subtype): self + public function setType(string $type): self { - $this->subtype = $subtype; + $this->type = $type; return $this; } /** - * @return string + * @return false|string */ - public function getType(): string + public function getSubtype() { - return $this->type; + return $this->subtype; } /** - * @param string $type - * @return self + * @param false|string $subtype + * @return DataAnnotation */ - public function setType(string $type): self + public function setSubtype($subtype): self { - $this->type = $type; + $this->subtype = $subtype; return $this; } @@ -292,7 +247,7 @@ public function getValues() /** * @param array|false|null $values - * @return self + * @return DataAnnotation */ public function setValues($values): self { diff --git a/src/Parser/Annotations/DescriptionAnnotation.php b/src/Parser/Annotations/DescriptionAnnotation.php index 72ea8d7..14bec07 100644 --- a/src/Parser/Annotations/DescriptionAnnotation.php +++ b/src/Parser/Annotations/DescriptionAnnotation.php @@ -2,26 +2,16 @@ namespace Mill\Parser\Annotations; use Mill\Parser\Annotation; -use Mill\Parser\Version; -/** - * Handler for descriptions. - * - */ class DescriptionAnnotation extends Annotation { - /** @var string */ - protected $description; - - /** - * An array of items that should be included in an array representation of this annotation. - * - * @var array - */ - protected $arrayable = [ + const ARRAYABLE = [ 'description' ]; + /** @var string */ + protected $description; + /** * {@inheritdoc} */ @@ -40,18 +30,6 @@ protected function interpreter(): void $this->description = $this->required('description'); } - /** - * {@inheritdoc} - */ - public static function hydrate(array $data = [], Version $version = null): self - { - /** @var DescriptionAnnotation $annotation */ - $annotation = parent::hydrate($data, $version); - $annotation->setDescription($data['description']); - - return $annotation; - } - /** * @return string */ @@ -62,7 +40,7 @@ public function getDescription(): string /** * @param string $description - * @return self + * @return DescriptionAnnotation */ public function setDescription(string $description): self { diff --git a/src/Parser/Annotations/ErrorAnnotation.php b/src/Parser/Annotations/ErrorAnnotation.php new file mode 100644 index 0000000..0014799 --- /dev/null +++ b/src/Parser/Annotations/ErrorAnnotation.php @@ -0,0 +1,165 @@ +docblock); + + /** @var string $method */ + $method = $this->method; + $mson = (new MSON($this->class, $method))->allowAllSubtypes()->parse($content); + $parsed = [ + 'http_code' => $mson->getField(), + 'representation' => $mson->getType(), + 'error_code' => $mson->getSubtype(), + 'vendor_tags' => $mson->getVendorTags(), + 'description' => $mson->getDescription() + ]; + + if (!empty($parsed['http_code'])) { + if (!$this->isValidHttpCode($parsed['http_code'])) { + throw UnknownReturnCodeException::create('error', $this->docblock, $this->class, $method); + } + + $parsed['http_code'] .= ' ' . $this->getHttpCodeMessage($parsed['http_code']); + } + + if (!empty($parsed['vendor_tags'])) { + $parsed['vendor_tags'] = array_map( + /** @return Annotation */ + function (string $tag) use ($method) { + return (new VendorTagAnnotation( + $tag, + $this->class, + $method + ))->process(); + }, + $parsed['vendor_tags'] + ); + } + + if (!empty($parsed['description'])) { + if (preg_match(self::REGEX_ERROR_SUB_TYPE, $parsed['description'], $matches)) { + $parsed['description'] = sprintf('If %s was not found in the %s.', $matches[1], $matches[2]); + } elseif (preg_match(self::REGEX_ERROR_TYPE, $parsed['description'], $matches)) { + $parsed['description'] = sprintf('If %s was not found.', $matches[1]); + } + } + + // Now that we've parsed out both the representation and error code, make sure that a representation that + // requires an error code, actually has one. + if (!empty($parsed['representation'])) { + // If this representation requires an error code (as defined in the config file), but we don't have one, + // throw an error. + if ($config->doesErrorRepresentationNeedAnErrorCode($parsed['representation']) && + empty($parsed['error_code']) + ) { + throw MissingRepresentationErrorCodeException::create( + $parsed['representation'], + $this->class, + $method + ); + } + } + + return $parsed; + } + + /** + * {@inheritdoc} + */ + protected function interpreter(): void + { + $this->http_code = $this->required('http_code'); + $this->representation = $this->required('representation'); + + $this->error_code = $this->optional('error_code'); + if ($this->error_code) { + $this->error_code = (string)$this->error_code; + } + + $this->vendor_tags = $this->optional('vendor_tags'); + $this->description = $this->required('description'); + } + + /** + * @return string + */ + public function getDescription(): string + { + return $this->description; + } + + /** + * @param string $description + * @return ErrorAnnotation + */ + public function setDescription(string $description): self + { + $this->description = $description; + return $this; + } + + /** + * @return false|null|string + */ + public function getErrorCode() + { + return $this->error_code; + } + + /** + * @param false|null|string $error_code + * @return ErrorAnnotation + */ + public function setErrorCode($error_code): self + { + $this->error_code = $error_code; + return $this; + } +} diff --git a/src/Parser/Annotations/GroupAnnotation.php b/src/Parser/Annotations/GroupAnnotation.php new file mode 100644 index 0000000..a199e97 --- /dev/null +++ b/src/Parser/Annotations/GroupAnnotation.php @@ -0,0 +1,50 @@ + $this->docblock + ]; + } + + /** + * {@inheritdoc} + */ + protected function interpreter(): void + { + $this->group = $this->required('group'); + } + + /** + * @return string + */ + public function getGroup(): string + { + return $this->group; + } + + /** + * @param string $group + * @return GroupAnnotation + */ + public function setGroup(string $group): self + { + $this->group = $group; + return $this; + } +} diff --git a/src/Parser/Annotations/LabelAnnotation.php b/src/Parser/Annotations/LabelAnnotation.php index 0fd0354..8e96346 100644 --- a/src/Parser/Annotations/LabelAnnotation.php +++ b/src/Parser/Annotations/LabelAnnotation.php @@ -2,26 +2,16 @@ namespace Mill\Parser\Annotations; use Mill\Parser\Annotation; -use Mill\Parser\Version; -/** - * Handler for the `@api-label` annotation. - * - */ class LabelAnnotation extends Annotation { - /** @var string */ - protected $label; - - /** - * An array of items that should be included in an array representation of this annotation. - * - * @var array - */ - protected $arrayable = [ + const ARRAYABLE = [ 'label' ]; + /** @var string */ + protected $label; + /** * {@inheritdoc} */ @@ -40,18 +30,6 @@ protected function interpreter(): void $this->label = $this->required('label'); } - /** - * {@inheritdoc} - */ - public static function hydrate(array $data = [], Version $version = null): self - { - /** @var LabelAnnotation $annotation */ - $annotation = parent::hydrate($data, $version); - $annotation->setLabel($data['label']); - - return $annotation; - } - /** * @return string */ @@ -62,7 +40,7 @@ public function getLabel(): string /** * @param string $label - * @return self + * @return LabelAnnotation */ public function setLabel(string $label): self { diff --git a/src/Parser/Annotations/MaxVersionAnnotation.php b/src/Parser/Annotations/MaxVersionAnnotation.php new file mode 100644 index 0000000..818aee5 --- /dev/null +++ b/src/Parser/Annotations/MaxVersionAnnotation.php @@ -0,0 +1,64 @@ +method; + + $parsed = new Version($this->docblock, $this->class, $method); + if ($parsed->isRange()) { + throw AbsoluteVersionException::create('max', $this->docblock, $this->class, $method); + } + + return [ + 'maximum_version' => $parsed->getConstraint() + ]; + } + + /** + * {@inheritdoc} + */ + protected function interpreter(): void + { + // The Version class already does all of our validation, so if we're at this point, we have a good version and + // don't need to run it through `$this->required()` again. + $this->maximum_version = $this->parsed_data['maximum_version']; + } + + /** + * @return string + */ + public function getMaximumVersion(): string + { + return $this->maximum_version; + } + + /** + * @param string $maximum_version + * @return MaxVersionAnnotation + */ + public function setMaximumVersion(string $maximum_version): self + { + $this->maximum_version = $maximum_version; + return $this; + } +} diff --git a/src/Parser/Annotations/MinVersionAnnotation.php b/src/Parser/Annotations/MinVersionAnnotation.php index 65c86d1..46ddef8 100644 --- a/src/Parser/Annotations/MinVersionAnnotation.php +++ b/src/Parser/Annotations/MinVersionAnnotation.php @@ -1,37 +1,23 @@ docblock, $this->class, $method); if ($parsed->isRange()) { - throw AbsoluteMinimumVersionException::create($this->docblock, $this->class, $method); + throw AbsoluteVersionException::create('min', $this->docblock, $this->class, $method); } return [ @@ -58,18 +44,6 @@ protected function interpreter(): void $this->minimum_version = $this->parsed_data['minimum_version']; } - /** - * {@inheritdoc} - */ - public static function hydrate(array $data = [], Version $version = null): self - { - /** @var MinVersionAnnotation $annotation */ - $annotation = parent::hydrate($data, $version); - $annotation->setMinimumVersion($data['minimum_version']); - - return $annotation; - } - /** * @return string */ @@ -80,7 +54,7 @@ public function getMinimumVersion(): string /** * @param string $minimum_version - * @return self + * @return MinVersionAnnotation */ public function setMinimumVersion(string $minimum_version): self { diff --git a/src/Parser/Annotations/ParamAnnotation.php b/src/Parser/Annotations/ParamAnnotation.php index 9079cad..00f2115 100644 --- a/src/Parser/Annotations/ParamAnnotation.php +++ b/src/Parser/Annotations/ParamAnnotation.php @@ -1,92 +1,66 @@ $mson->getField(), 'sample_data' => $mson->getSampleData(), 'type' => $mson->getType(), + 'subtype' => $mson->getSubtype(), 'required' => $mson->isRequired(), 'nullable' => $mson->isNullable(), - 'capability' => $mson->getCapability(), + 'vendor_tags' => $mson->getVendorTags(), 'description' => $mson->getDescription(), 'values' => $mson->getValues() ]; - // Create a capability annotation if one was supplied. - if (!empty($parsed['capability'])) { - $parsed['capability'] = (new CapabilityAnnotation( - $parsed['capability'], - $this->class, - $this->method - ))->process(); + if (!empty($parsed['field'])) { + if (strtoupper($parsed['field']) === Application::DOT_NOTATION_ANNOTATION_DATA_KEY) { + throw RestrictedFieldNameException::create($this->class, $this->method); + } + } + + if (!empty($parsed['vendor_tags'])) { + $parsed['vendor_tags'] = array_map( + /** @return Annotation */ + function (string $tag) use ($method) { + return (new VendorTagAnnotation( + $tag, + $this->class, + $method + ))->process(); + }, + $parsed['vendor_tags'] + ); } return $parsed; @@ -132,30 +118,21 @@ protected function interpreter(): void $this->field = $this->required('field'); $this->sample_data = $this->optional('sample_data'); $this->type = $this->required('type'); + $this->subtype = $this->optional('subtype'); $this->description = $this->required('description'); $this->required = $this->boolean('required'); $this->values = $this->optional('values'); - $this->capability = $this->optional('capability'); + $this->vendor_tags = $this->optional('vendor_tags'); $this->nullable = $this->optional('nullable'); } /** - * {@inheritdoc} + * @return string */ - public static function hydrate(array $data = [], Version $version = null): self + public function getPayloadFormat(): string { - /** @var ParamAnnotation $annotation */ - $annotation = parent::hydrate($data, $version); - $annotation->setDescription($data['description']); - $annotation->setField($data['field']); - $annotation->setNullable($data['nullable']); - $annotation->setRequired($data['required']); - $annotation->setSampleData($data['sample_data']); - $annotation->setType($data['type']); - $annotation->setValues($data['values']); - - return $annotation; + return static::PAYLOAD_FORMAT; } /** @@ -168,7 +145,7 @@ public function getField(): string /** * @param string $field - * @return self + * @return ParamAnnotation */ public function setField(string $field): self { @@ -186,7 +163,7 @@ public function getSampleData() /** * @param false|string $sample_data - * @return self + * @return ParamAnnotation */ public function setSampleData($sample_data): self { @@ -204,7 +181,7 @@ public function getType(): string /** * @param string $type - * @return self + * @return ParamAnnotation */ public function setType(string $type): self { @@ -212,6 +189,24 @@ public function setType(string $type): self return $this; } + /** + * @return false|string + */ + public function getSubtype() + { + return $this->subtype; + } + + /** + * @param false|string $subtype + * @return ParamAnnotation + */ + public function setSubtype($subtype): self + { + $this->subtype = $subtype; + return $this; + } + /** * @return string */ @@ -222,7 +217,7 @@ public function getDescription(): string /** * @param string $description - * @return self + * @return ParamAnnotation */ public function setDescription(string $description): self { @@ -240,7 +235,7 @@ public function isRequired(): bool /** * @param bool $required - * @return self + * @return ParamAnnotation */ public function setRequired(bool $required): self { @@ -258,7 +253,7 @@ public function isNullable(): bool /** * @param bool $nullable - * @return self + * @return ParamAnnotation */ public function setNullable(bool $nullable): self { @@ -276,7 +271,7 @@ public function getValues() /** * @param array|null $values - * @return self + * @return ParamAnnotation */ public function setValues($values): self { diff --git a/src/Parser/Annotations/PathAnnotation.php b/src/Parser/Annotations/PathAnnotation.php new file mode 100644 index 0000000..b25715a --- /dev/null +++ b/src/Parser/Annotations/PathAnnotation.php @@ -0,0 +1,102 @@ +docblock); + if (!empty($path)) { + } + + return [ + 'path' => $path + ]; + } + + /** + * {@inheritdoc} + */ + protected function interpreter(): void + { + $this->path = $this->required('path'); + + // If we have any path param translations configured, let's process them. + $translations = Container::getConfig()->getPathParamTranslations(); + foreach ($translations as $from => $to) { + if (preg_match('/([@#+*!~])' . $from . '(\/|$)/', $this->path, $matches)) { + $this->path = preg_replace('/([@#+*!~])' . $from . '(\/|$)/', '$1' . $to . '$2', $this->path); + } + } + } + + /** + * @return string + */ + public function getPath(): string + { + return $this->path; + } + + /** + * @param string $path + * @return PathAnnotation + */ + public function setPath(string $path): self + { + $this->path = $path; + return $this; + } + + /** + * @return string + */ + public function getCleanPath(): string + { + return preg_replace('/[@#+*!~]((\w|_)+)(\/|$)/', '{$1}$3', $this->getPath()); + } + + /** + * @param PathParamAnnotation $param + * @return bool + */ + public function doesPathHaveParam(PathParamAnnotation $param): bool + { + return strpos($this->getCleanPath(), '{' . $param->getField() . '}') !== false; + } + + /** + * {{@inheritdoc}} + */ + public function toArray(): array + { + $arr = parent::toArray(); + $arr['aliased'] = $this->isAliased(); + $arr['aliases'] = []; + + /** @var Annotation $alias */ + foreach ($this->getAliases() as $alias) { + $arr['aliases'][] = $alias->toArray(); + } + + ksort($arr); + + return $arr; + } +} diff --git a/src/Parser/Annotations/PathParamAnnotation.php b/src/Parser/Annotations/PathParamAnnotation.php new file mode 100644 index 0000000..b8c412c --- /dev/null +++ b/src/Parser/Annotations/PathParamAnnotation.php @@ -0,0 +1,69 @@ +docblock); + + /** @var string $method */ + $method = $this->method; + $mson = (new MSON($this->class, $method))->parse($content); + $parsed = [ + 'field' => $mson->getField(), + 'sample_data' => $mson->getSampleData(), + 'type' => $mson->getType(), + 'description' => $mson->getDescription(), + 'values' => $mson->getValues() + ]; + + if (!empty($parsed['field'])) { + // If we have any path param translations configured, let's process them. + $translations = Container::getConfig()->getPathParamTranslations(); + if (isset($translations[$parsed['field']])) { + $parsed['field'] = $translations[$parsed['field']]; + } + } + + return $parsed; + } + + /** + * {@inheritdoc} + */ + protected function interpreter(): void + { + $this->required = true; + + $this->field = $this->required('field'); + $this->sample_data = $this->optional('sample_data'); + $this->type = $this->required('type'); + $this->description = $this->required('description'); + + $this->values = $this->optional('values'); + } +} diff --git a/src/Parser/Annotations/QueryParamAnnotation.php b/src/Parser/Annotations/QueryParamAnnotation.php new file mode 100644 index 0000000..1b6d6f0 --- /dev/null +++ b/src/Parser/Annotations/QueryParamAnnotation.php @@ -0,0 +1,7 @@ +setDescription($data['description']); - $annotation->setHttpCode($data['http_code']); - $annotation->setRepresentation($data['representation']); - $annotation->setType($data['type']); - - return $annotation; - } - /** * Grab the HTTP code for a given response type. * @@ -180,7 +148,7 @@ public function getDescription() /** * @param false|null|string $description - * @return self + * @return ReturnAnnotation */ public function setDescription($description): self { @@ -198,7 +166,7 @@ public function getType(): string /** * @param string $type - * @return self + * @return ReturnAnnotation */ public function setType(string $type): self { diff --git a/src/Parser/Annotations/ScopeAnnotation.php b/src/Parser/Annotations/ScopeAnnotation.php index 5604040..42f05b1 100644 --- a/src/Parser/Annotations/ScopeAnnotation.php +++ b/src/Parser/Annotations/ScopeAnnotation.php @@ -4,34 +4,20 @@ use Mill\Container; use Mill\Exceptions\Annotations\InvalidScopeSuppliedException; use Mill\Parser\Annotation; -use Mill\Parser\Version; -/** - * Handler for the `@api-scope` annotation. - * - */ class ScopeAnnotation extends Annotation { + const ARRAYABLE = [ + 'description', + 'scope' + ]; + /** @var string */ protected $scope; - /** - * Description for why this scope is required. - * - * @var false|null|string - */ + /** @var false|null|string Description for why this scope is required. */ protected $description = null; - /** - * An array of items that should be included in an array representation of this annotation. - * - * @var array - */ - protected $arrayable = [ - 'description', - 'scope' - ]; - /** * {@inheritdoc} * @throws InvalidScopeSuppliedException If a supplied scope isn't present in the config file. @@ -45,8 +31,7 @@ protected function parser(): array if (!empty($scope)) { // Validate the supplied scope with what has been configured as allowable. - $scopes = Container::getConfig()->getScopes(); - if (!in_array($scope, $scopes)) { + if (!Container::getConfig()->hasScope($scope)) { /** @var string $method */ $method = $this->method; throw InvalidScopeSuppliedException::create($scope, $this->class, $method); @@ -68,19 +53,6 @@ protected function interpreter(): void $this->description = $this->optional('description'); } - /** - * {@inheritdoc} - */ - public static function hydrate(array $data = [], Version $version = null): self - { - /** @var ScopeAnnotation $annotation */ - $annotation = parent::hydrate($data, $version); - $annotation->setScope($data['scope']); - $annotation->setDescription($data['description']); - - return $annotation; - } - /** * @return string */ @@ -91,7 +63,7 @@ public function getScope(): string /** * @param string $scope - * @return self + * @return ScopeAnnotation */ public function setScope(string $scope): self { @@ -109,7 +81,7 @@ public function getDescription() /** * @param false|null|string $description - * @return self + * @return ScopeAnnotation */ public function setDescription($description): self { diff --git a/src/Parser/Annotations/ThrowsAnnotation.php b/src/Parser/Annotations/ThrowsAnnotation.php deleted file mode 100644 index 27737fb..0000000 --- a/src/Parser/Annotations/ThrowsAnnotation.php +++ /dev/null @@ -1,230 +0,0 @@ -method; - - $parsed = []; - $content = trim($this->docblock); - - // HTTP code is surrounded by +plusses+. - if (preg_match(self::REGEX_THROW_HTTP_CODE, $content, $matches)) { - $parsed['http_code'] = $matches[1]; - - if (!$this->isValidHttpCode($parsed['http_code'])) { - throw UnknownReturnCodeException::create('throws', $this->docblock, $this->class, $method); - } - - $parsed['http_code'] .= ' ' . $this->getHttpCodeMessage($parsed['http_code']); - $content = trim(preg_replace(self::REGEX_THROW_HTTP_CODE, '', $content)); - } - - $parts = explode(' ', $content); - $parsed['representation'] = array_shift($parts); - - // Representation is by itself, so put the pieces back together so we can do some more regex. - $content = implode(' ', $parts); - - if (!empty($parsed['representation'])) { - $representation = $parsed['representation']; - - // Verify that the supplied representation class exists. If it's being excluded, we can just go ahead and - // set it here anyways, as we'll be looking further up the stack to determine if we should actually parse it - // for documentation. - // - // If the class doesn't exist, this method call will throw an exception back out. - try { - $config->doesErrorRepresentationExist($representation); - } catch (UnconfiguredErrorRepresentationException $e) { - throw UnknownErrorRepresentationException::create($representation, $this->class, $method); - } - } - - // Error codes are marked with `(\SomeError\Class::CASE)` or `(1337)` parens. - if (preg_match(self::REGEX_ERROR_CODE, $content, $matches)) { - $error_code = substr($matches[1], 1, -1); - if (is_numeric($error_code)) { - $parsed['error_code'] = $error_code; - } else { - if (!defined($error_code)) { - throw UncallableErrorCodeException::create($this->docblock, $this->class, $method); - } - - $parsed['error_code'] = constant($error_code); - } - - $content = trim(preg_replace(self::REGEX_ERROR_CODE, '', $content)); - } - - // Capability is surrounded by +plusses+. - if (preg_match(self::REGEX_CAPABILITY, $content, $matches)) { - $capability = substr($matches[1], 1, -1); - $parsed['capability'] = (new CapabilityAnnotation($capability, $this->class, $method))->process(); - - $content = trim(preg_replace(self::REGEX_CAPABILITY, '', $content)); - } - - $description = trim($content); - if (!empty($description)) { - if (preg_match(self::REGEX_THROW_SUB_TYPE, $description, $matches)) { - $description = sprintf('If %s was not found in the %s.', $matches[1], $matches[2]); - } elseif (preg_match(self::REGEX_THROW_TYPE, $description, $matches)) { - $description = sprintf('If %s was not found.', $matches[1]); - } - - $parsed['description'] = $description; - } - - // Now that we've parsed out both the representation and error code, make sure that a representation that - // requires an error code, actually has one. - if (!empty($parsed['representation'])) { - $representation = $parsed['representation']; - - // If this representation requires an error code (as defined in the config file), but we don't have one, - // throw an error. - if ($config->doesErrorRepresentationNeedAnErrorCode($representation) && !isset($parsed['error_code'])) { - throw MissingRepresentationErrorCodeException::create( - $representation, - $this->class, - $method - ); - } - } - - return $parsed; - } - - /** - * {@inheritdoc} - */ - protected function interpreter(): void - { - $this->http_code = $this->required('http_code'); - $this->representation = $this->required('representation'); - - $this->error_code = $this->optional('error_code'); - if ($this->error_code) { - $this->error_code = (string)$this->error_code; - } - - $this->capability = $this->optional('capability'); - $this->description = $this->required('description'); - } - - /** - * {@inheritdoc} - */ - public static function hydrate(array $data = [], Version $version = null) - { - /** @var ThrowsAnnotation $annotation */ - $annotation = parent::hydrate($data, $version); - $annotation->setDescription($data['description']); - $annotation->setErrorCode($data['error_code']); - $annotation->setHttpCode($data['http_code']); - $annotation->setRepresentation($data['representation']); - - return $annotation; - } - - /** - * @return string - */ - public function getDescription(): string - { - return $this->description; - } - - /** - * @param string $description - * @return self - */ - public function setDescription(string $description): self - { - $this->description = $description; - return $this; - } - - /** - * @return false|null|string - */ - public function getErrorCode() - { - return $this->error_code; - } - - /** - * @param false|null|string $error_code - * @return self - */ - public function setErrorCode($error_code): self - { - $this->error_code = $error_code; - return $this; - } -} diff --git a/src/Parser/Annotations/Traits/HasHttpCodeResponseTrait.php b/src/Parser/Annotations/Traits/HasHttpCodeResponseTrait.php index 3f63821..b32ad27 100644 --- a/src/Parser/Annotations/Traits/HasHttpCodeResponseTrait.php +++ b/src/Parser/Annotations/Traits/HasHttpCodeResponseTrait.php @@ -1,17 +1,9 @@ docblock; - - // Namespace is surrounded by `{curly braces}`. - if (preg_match(self::NAMESPACE_REGEX, $content, $matches)) { - $parsed['namespace'] = $matches[1]; - $content = trim(preg_replace(self::NAMESPACE_REGEX, '', $content)); - } - - $parsed['path'] = trim($content); - - return $parsed; - } - - /** - * {@inheritdoc} - */ - protected function interpreter(): void - { - $this->namespace = $this->required('namespace'); - $this->path = $this->required('path'); - } - - /** - * {@inheritdoc} - */ - public static function hydrate(array $data = [], Version $version = null) - { - /** @var UriAnnotation $annotation */ - $annotation = parent::hydrate($data, $version); - $annotation->setNamespace($data['namespace']); - $annotation->setPath($data['path']); - - return $annotation; - } - - /** - * @return string - */ - public function getNamespace(): string - { - return $this->namespace; - } - - /** - * @param string $namespace - * @return self - */ - public function setNamespace(string $namespace): self - { - $this->namespace = $namespace; - return $this; - } - - /** - * @return string - */ - public function getPath(): string - { - return $this->path; - } - - /** - * @param string $path - * @return self - */ - public function setPath(string $path): self - { - $this->path = $path; - return $this; - } - - /** - * @return string - */ - public function getCleanPath(): string - { - $path = preg_replace('/[@#+*!~]((\w|_)+)(\/|$)/', '{$1}$3', $this->getPath()); - - // If we have any URI segment translations configured, let's process them. - $translations = Container::getConfig()->getUriSegmentTranslations(); - foreach ($translations as $from => $to) { - $path = str_replace('{' . $from . '}', '{' . $to . '}', $path); - } - - return $path; - } -} diff --git a/src/Parser/Annotations/UriSegmentAnnotation.php b/src/Parser/Annotations/UriSegmentAnnotation.php deleted file mode 100644 index 82d2c81..0000000 --- a/src/Parser/Annotations/UriSegmentAnnotation.php +++ /dev/null @@ -1,212 +0,0 @@ -docblock); - - // URI is surrounded by `{curly braces}`. - if (preg_match(self::REGEX_URI, $content, $matches)) { - $parsed['uri'] = substr($matches[1], 1, -1); - $content = trim(preg_replace(self::REGEX_URI, '', $content)); - } - - /** @var string $method */ - $method = $this->method; - $mson = (new MSON($this->class, $method))->parse($content); - $parsed = array_merge($parsed, [ - 'field' => $mson->getField(), - 'type' => $mson->getType(), - 'description' => $mson->getDescription(), - 'values' => $mson->getValues() - ]); - - return $parsed; - } - - /** - * {@inheritdoc} - */ - protected function interpreter(): void - { - $this->uri = $this->required('uri', false); - - $this->field = $this->required('field'); - $this->type = $this->required('type'); - $this->description = $this->required('description'); - - $this->values = $this->optional('values'); - } - - /** - * {@inheritdoc} - */ - public static function hydrate(array $data = [], Version $version = null) - { - /** @var UriSegmentAnnotation $annotation */ - $annotation = parent::hydrate($data, $version); - $annotation->setDescription($data['description']); - $annotation->setField($data['field']); - $annotation->setType($data['type']); - $annotation->setUri($data['uri']); - $annotation->setValues($data['values']); - - return $annotation; - } - - /** - * @return string - */ - public function getUri(): string - { - return $this->uri; - } - - /** - * @param string $uri - * @return self - */ - public function setUri(string $uri): self - { - $this->uri = $uri; - return $this; - } - - /** - * @return string - */ - public function getField(): string - { - return $this->field; - } - - /** - * @param string $field - * @return self - */ - public function setField(string $field): self - { - $this->field = $field; - return $this; - } - - /** - * @return string - */ - public function getType(): string - { - return $this->type; - } - - /** - * @param string $type - * @return self - */ - public function setType(string $type): self - { - $this->type = $type; - return $this; - } - - /** - * @return string - */ - public function getDescription(): string - { - return $this->description; - } - - /** - * @param string $description - * @return self - */ - public function setDescription(string $description): self - { - $this->description = $description; - return $this; - } - - /** - * @return array|false|null - */ - public function getValues() - { - return $this->values; - } - - /** - * @param array|false|null $values - * @return self - */ - public function setValues($values): self - { - $this->values = $values; - return $this; - } -} diff --git a/src/Parser/Annotations/VendorTagAnnotation.php b/src/Parser/Annotations/VendorTagAnnotation.php new file mode 100644 index 0000000..15cf6ed --- /dev/null +++ b/src/Parser/Annotations/VendorTagAnnotation.php @@ -0,0 +1,65 @@ +docblock); + + if (!empty($vendor_tag)) { + // Validate the supplied vendor tag with what has been configured as allowable. + $vendor_tags = Container::getConfig()->getVendorTags(); + if (!in_array($vendor_tag, $vendor_tags)) { + /** @var string $method */ + $method = $this->method; + throw InvalidVendorTagSuppliedException::create($vendor_tag, $this->class, $method); + } + } + + return [ + 'vendor_tag' => $vendor_tag + ]; + } + + /** + * {@inheritdoc} + */ + protected function interpreter(): void + { + $this->vendor_tag = $this->required('vendor_tag'); + } + + /** + * @return string + */ + public function getVendorTag(): string + { + return $this->vendor_tag; + } + + /** + * @param string $vendor_tag + * @return VendorTagAnnotation + */ + public function setVendorTag(string $vendor_tag): self + { + $this->vendor_tag = $vendor_tag; + return $this; + } +} diff --git a/src/Parser/MSON.php b/src/Parser/MSON.php index 8a4789a..83f257d 100644 --- a/src/Parser/MSON.php +++ b/src/Parser/MSON.php @@ -2,15 +2,21 @@ namespace Mill\Parser; use Mill\Container; +use Mill\Contracts\Arrayable; +use Mill\Exceptions\Annotations\UnknownErrorRepresentationException; use Mill\Exceptions\Annotations\UnsupportedTypeException; +use Mill\Exceptions\Config\UnconfiguredErrorRepresentationException; use Mill\Exceptions\Config\UnconfiguredRepresentationException; +use Mill\Exceptions\MSON\ImproperlyWrittenEnumException; use Mill\Exceptions\MSON\MissingOptionsException; -class MSON +class MSON implements Arrayable { /** * This is the regex to match Mill-flavored MSON enum members. * + * The regex here supports multi-line member value descriptions, but not multi-paragraph. + * * Examples: * * - content_rating `G` (string, optional, MOVIE_RATINGS) - This denotes the @@ -27,9 +33,9 @@ class MSON * - `PG-13` * * @var string - * @todo Add a test for a member description that exists on multiple lines. */ - const REGEX_MSON_ENUM = '/(?:\+ Members\n(?:\s*?))?(?:- `(?P.*?)`( - (?P.*?))?)(?:$|\n)/ui'; + const REGEX_MSON_ENUM = '/(?:\+ Members\n(?:\s*?))?(?:- `(?P.*?)`( - ' . + '(?P(.*)((((\s+)([a-zA-Z\s.,`"_!?@~!%]+)(\n)*))+)?))?)/uim'; /** * Take a multi-line string or paragraph, remove any multi-lines, and contract sentences. @@ -39,106 +45,62 @@ class MSON * - "If there is a problem with the * request." becomes "If there is a problem with the request." * + * @var string * @todo This does not currently support multi-paragraph strings. */ const REGEX_CLEAN_MULTILINE = '/(\s)?[ \t]*(\r\n|\n)[ \t]*(\s)/'; - /** - * Controller that this MSON is being parsed from. - * - * @var string - */ + /** @var array Supported MSON field types. */ + const SUPPORTED_TYPES = [ + 'array', + 'boolean', + 'date', + 'datetime', + 'float', + 'enum', + 'integer', + 'number', + 'object', + 'string', + 'timestamp', + 'uri' + ]; + + /** @var string Controller that this MSON is being parsed from. */ protected $class; - /** - * Controller method that MSON is being parsed from. - * - * @var string - */ + /** @var string Controller method that MSON is being parsed from. */ protected $method; - /** - * Name of the field that was parsed out of the MSON content. - * - * @var null|string - */ + /** @var null|string Name of the field that was parsed out of the MSON content. */ protected $field = null; - /** - * Sample data that was parsed out of the MSON content. - * - * @var false|string - */ + /** @var false|string Sample data that was parsed out of the MSON content. */ protected $sample_data = false; - /** - * Type of field that this MSON content represents. - * - * @var null|string - */ + /** @var null|string Type of field that this MSON content represents. */ protected $type = null; - /** - * Subtype of the type of field that this MSON content represents. - * - * @var false|string - */ + /** @var false|string Subtype of the type of field that this MSON content represents. */ protected $subtype = false; - /** - * Is this MSON content designated as being required? - * - * @var bool - */ + /** @var bool Is this MSON content designated as being required? */ protected $is_required = false; - /** - * Is this MSON content designated as nullable? - * - * @var bool - */ + /** @var bool Is this MSON content designated as nullable? */ protected $is_nullable = false; - /** - * Application-specific capability that was parsed out of the MSON content. - * - * @var false|string - */ - protected $capability = false; + /** @var array Application-specific vendor tags that were parsed out of the MSON content. */ + protected $vendor_tags = []; - /** - * Parsed description from the MSON content. - * - * @var null|string - */ + /** @var null|string Parsed description from the MSON content. */ protected $description = null; - /** - * Array of enumerated values from the MSON content. - * - * @var array - */ + /** @var array Array of enumerated values from the MSON content. */ protected $values = []; - /** - * Supported MSON field types. - * - * @var array - */ - protected $supported_types = [ - 'array', - 'boolean', - 'date', - 'datetime', - 'float', - 'enum', - 'integer', - 'number', - 'object', - 'string', - 'timestamp', - 'uri' - ]; + /** @var bool Allow all kind of subtypes. Used for `@api-error` annotations to allow error codes. */ + protected $allow_all_subtypes = false; /** * @param string $class @@ -154,9 +116,11 @@ public function __construct(string $class, string $method) * Given a piece of Mill-flavored MSON content, parse it out. * * @param string $content - * @return self - * @throws UnsupportedTypeException If an unsupported MSON field type has been supplied. - * @throws MissingOptionsException If a supplied MSON type of `enum` missing corresponding acceptable values. + * @return MSON + * @throws ImproperlyWrittenEnumException + * @throws MissingOptionsException + * @throws UnknownErrorRepresentationException + * @throws UnsupportedTypeException */ public function parse(string $content): self { @@ -168,9 +132,10 @@ public function parse(string $content): self * - content_rating (string) - MPAA rating * - content_rating `G` (string, required) - MPAA rating * - content_rating `G` (string, required, nullable) - MPAA rating - * - content_rating `G` (string, optional, MOVIE_RATINGS) - MPAA rating - * - content_rating `G` (string, optional, nullable, MOVIE_RATINGS) - MPAA rating - * - content_rating `G` (string, MOVIE_RATINGS) - MPAA rating + * - content_rating `G` (string, optional, tag:MOVIE_RATINGS) - MPAA rating + * - content_rating `G` (string, optional, nullable, tag:MOVIE_RATINGS) - MPAA rating + * - content_rating `G` (string, tag:MOVIE_RATINGS) - MPAA rating + * - content_rating `G` (string, tag:MOVIE_RATINGS, needs:validUser) - MPAA rating * - websites.description (string) - The websites' description * - websites (array) - The users' list of websites. * - cast (array<\Mill\Examples\Showtimes\Representations\Person>) - Cast @@ -180,11 +145,11 @@ public function parse(string $content): self */ $regex_mson = '/((?P[\w.\*]+) (`(?P.+)` )?' . '\((?P[\w\\\]+)(<(?P[\w\\\]+)>)?(, (?Prequired|optional))?(, ' . - '(?Pnullable))?(, (?P\w+))?\)(\n|\s)+-(\n|\s)+(?P.+))/uis'; + '(?Pnullable))?(?P(, ([\w]+:[\w]+))*?)\)(\n|\s)+-(\n|\s)+(?P.+))/uis'; preg_match($regex_mson, $content, $matches); - foreach (['field', 'type', 'description', 'sample_data', 'subtype', 'capability'] as $name) { + foreach (['field', 'type', 'description', 'sample_data', 'subtype'] as $name) { if (isset($matches[$name])) { // Sample data can be input as "0", so we need some special casing to account for that. if (!empty($matches[$name]) || $name === 'sample_data') { @@ -193,6 +158,20 @@ public function parse(string $content): self } } + if (isset($matches['vendor_tag'])) { + $vendor_tags = explode(',', $matches['vendor_tag']); + $vendor_tags = array_filter($vendor_tags); + $vendor_tags = array_values($vendor_tags); + $vendor_tags = array_map( + function (string $tag): string { + return trim($tag); + }, + $vendor_tags + ); + + $this->vendor_tags = $vendor_tags; + } + if (isset($matches['required'])) { if (!empty($matches['required']) && strtolower($matches['required']) == 'required') { $this->is_required = true; @@ -209,19 +188,27 @@ public function parse(string $content): self if (!empty($this->type)) { $config = Container::getConfig(); - if (!in_array(strtolower($this->type), $this->supported_types)) { + if (!in_array(strtolower($this->type), self::SUPPORTED_TYPES)) { try { - // If this isn't a valid representation, then it's an invalid type. - $config->doesRepresentationExist($this->type); + // If we're allowing all subtypes, then we're dealing with error states and the `@api-error` + // annotation, so we should look at error representations instead here. + if ($this->allow_all_subtypes) { + $config->doesErrorRepresentationExist($this->type); + } else { + // If this isn't a valid representation, then it's an invalid type. + $config->doesRepresentationExist($this->type); + } } catch (UnconfiguredRepresentationException $e) { throw UnsupportedTypeException::create($content, $this->class, $this->method); + } catch (UnconfiguredErrorRepresentationException $e) { + throw UnknownErrorRepresentationException::create($content, $this->class, $this->method); } } if (!empty($this->subtype)) { switch ($this->type) { case 'array': - if (!in_array(strtolower($this->subtype), $this->supported_types)) { + if (!in_array(strtolower($this->subtype), self::SUPPORTED_TYPES)) { try { // If this isn't a valid representation, then it's an invalid type. $config->doesRepresentationExist($this->subtype); @@ -232,6 +219,10 @@ public function parse(string $content): self break; default: + if ($this->allow_all_subtypes) { + break; + } + throw UnsupportedTypeException::create($content, $this->class, $this->method); } } @@ -253,8 +244,16 @@ public function parse(string $content): self $this->description = preg_replace(self::REGEX_CLEAN_MULTILINE, ' ', $this->description); } - if ($this->type === 'enum' && empty($this->values)) { - throw MissingOptionsException::create($this->type, $this->class, $this->method); + if (empty($this->values)) { + if ($this->type === 'enum') { + throw MissingOptionsException::create($this->type, $this->class, $this->method); + } elseif ($this->subtype === 'enum') { + throw MissingOptionsException::create($this->subtype, $this->class, $this->method); + } + } + + if (($this->type !== 'enum' && $this->subtype !== 'enum') && !empty($this->values)) { + throw ImproperlyWrittenEnumException::create($content, $this->class, $this->method); } return $this; @@ -272,12 +271,11 @@ protected function parseValues(array $values, array $descriptions): array $enum = []; foreach ($values as $k => $value) { $value = trim($value); - $description = trim($descriptions[$k]); + $description = preg_replace(self::REGEX_CLEAN_MULTILINE, ' ', trim($descriptions[$k])); $enum[$value] = $description; } - // Keep the array of values alphabetical so it's cleaner when generated into documentation. ksort($enum); return $enum; @@ -344,13 +342,13 @@ public function isNullable(): bool } /** - * Application-specific capability that was parsed out of the MSON content. + * Application-specific vendor tags that were parsed from the MSON content. * - * @return false|string + * @return array */ - public function getCapability() + public function getVendorTags(): array { - return $this->capability; + return $this->vendor_tags; } /** @@ -374,14 +372,22 @@ public function getValues(): array } /** - * Get parsed MSON content in an array. + * Allow all kind of subtypes. Used for `@api-error` annotations to allow error codes. * - * @return array + * @return MSON + */ + public function allowAllSubtypes(): self + { + $this->allow_all_subtypes = true; + return $this; + } + + /** + * {{@inheritdoc}} */ public function toArray(): array { return [ - 'capability' => $this->getCapability(), 'description' => $this->getDescription(), 'field' => $this->getField(), 'nullable' => $this->isNullable(), @@ -389,7 +395,8 @@ public function toArray(): array 'sample_data' => $this->getSampleData(), 'subtype' => $this->getSubtype(), 'type' => $this->getType(), - 'values' => $this->getValues() + 'values' => $this->getValues(), + 'vendor_tags' => $this->getVendorTags(), ]; } } diff --git a/src/Parser/Representation/Documentation.php b/src/Parser/Representation/Documentation.php index 1b1f021..43c27d4 100644 --- a/src/Parser/Representation/Documentation.php +++ b/src/Parser/Representation/Documentation.php @@ -2,44 +2,22 @@ namespace Mill\Parser\Representation; use Dflydev\DotAccessData\Data; +use Mill\Application; +use Mill\Contracts\Arrayable; use Mill\Exceptions\Annotations\MultipleAnnotationsException; use Mill\Exceptions\Annotations\RequiredAnnotationException; use Mill\Exceptions\Resource\NoAnnotationsException; use Mill\Parser; -/** - * Class for parsing a docblock on a given representation class and method for documentation. - * - */ -class Documentation +class Documentation implements Arrayable { - /** - * When building out dot-notation annotation keys for generating API Blueprint files (or any other generator), - * we use this key to designate the content of an annotations' data. - * - * @var string - */ - const DOT_NOTATION_ANNOTATION_DATA_KEY = '__FIELD_DATA__'; - - /** - * Name of the representation class that we're going to be parsing for documentation. - * - * @var string - */ + /** @var string Name of the representation class that we're going to be parsing for documentation. */ protected $class; - /** - * Name of the representation class method that we're going to be parsing for documentation. - * - * @var string - */ + /** @var string Name of the representation class method that we're going to be parsing for documentation. */ protected $method; - /** - * Short description/label/title of the representation. - * - * @var string - */ + /** @var string Short description/label/title of the representation. */ protected $label; /** @@ -49,11 +27,7 @@ class Documentation */ protected $description = null; - /** - * Array of parsed field annotations that exist on this representation. - * - * @var array - */ + /** @var array Array of parsed field annotations that exist on this representation. */ protected $representation = []; /** @@ -69,11 +43,13 @@ public function __construct(string $class, string $method) /** * Parse the instance controller and method into actionable annotations and documentation. * - * @return self - * @throws NoAnnotationsException If no annotations were found on the class. - * @throws NoAnnotationsException If no annotations were found on the method. - * @throws RequiredAnnotationException If a required `@api-label` annotation is missing. - * @throws MultipleAnnotationsException If multiple `@api-label` annotations were found. + * @return Documentation + * @throws MultipleAnnotationsException + * @throws NoAnnotationsException + * @throws RequiredAnnotationException + * @throws \Mill\Exceptions\MethodNotSuppliedException + * @throws \Mill\Exceptions\Representation\DuplicateFieldException + * @throws \Mill\Exceptions\Resource\UnsupportedDecoratorException */ public function parse(): self { @@ -132,46 +108,47 @@ public function filterRepresentationForVersion(string $version): array } /** - * Filter down, and return, all annotations on this representation that match a specific visibility. + * Filter down, and return, all annotations on this representation that match a specific vendor tag. * * @psalm-suppress RedundantCondition - * @param array|null $only_capabilities + * @param array|null $only_vendor_tags * @return array */ - public function filterAnnotationsForVisibility(?array $only_capabilities): array + public function filterAnnotationsForVisibility(?array $only_vendor_tags): array { - if (is_null($only_capabilities)) { + if (is_null($only_vendor_tags)) { return $this->representation; } /** @var Parser\Annotation $annotation */ foreach ($this->representation as $name => $annotation) { - // If this annotation has a capability, but that capability isn't in the set of capabilities we're - // generating documentation for, filter it out. - $capability = $annotation->getCapability(); - if (!empty($capability)) { - if ($capability instanceof Parser\Annotations\CapabilityAnnotation) { - /** @var Parser\Annotations\CapabilityAnnotation $capability */ - $capability = $capability->getCapability(); - } - - // If we don't even have capabilities to look for, then filter this annotation out completely. - if (!is_null($only_capabilities) && empty($only_capabilities)) { + // If this annotation has vendor tags, but those vendor tags aren't in the set of vendor tags we're + // compiling documentation for, filter it out. + $vendor_tags = $annotation->getVendorTags(); + if (!empty($vendor_tags)) { + // If we don't even have vendor tags to look for, then filter this annotation out completely. + if (!is_null($only_vendor_tags) && empty($only_vendor_tags)) { unset($this->representation[$name]); continue; } - if (!empty($capability) && - ( - !is_null($only_capabilities) && - !in_array($capability, $only_capabilities) - ) - ) { + $all_found = true; + + /** @var Parser\Annotations\VendorTagAnnotation $vendor_tag */ + foreach ($vendor_tags as $vendor_tag) { + $vendor_tag = $vendor_tag->getVendorTag(); + + if (!is_null($only_vendor_tags) && !in_array($vendor_tag, $only_vendor_tags)) { + $all_found = false; + } + } + + if (!$all_found) { unset($this->representation[$name]); continue; } - // Capabilities override individual annotation visibility. + // Vendor tags requirements override individual annotation visibility. continue; } } @@ -241,7 +218,7 @@ public function getExplodedContentDotNotation(): array $arr = $this->toArray(); foreach ($arr['content'] as $field => $data) { $content->set($field, [ - self::DOT_NOTATION_ANNOTATION_DATA_KEY => $data + Application::DOT_NOTATION_ANNOTATION_DATA_KEY => $data ]); } @@ -249,9 +226,7 @@ public function getExplodedContentDotNotation(): array } /** - * Convert the parsed representation method documentation into an array. - * - * @return array + * {{@inheritdoc}} */ public function toArray(): array { @@ -266,7 +241,6 @@ public function toArray(): array $data['content'][$key] = $annotation->toArray(); } - // Keep things tidy. ksort($data['content']); return $data; diff --git a/src/Parser/Representation/RepresentationParser.php b/src/Parser/Representation/RepresentationParser.php index 41fe9b2..bd4ae9f 100644 --- a/src/Parser/Representation/RepresentationParser.php +++ b/src/Parser/Representation/RepresentationParser.php @@ -11,25 +11,24 @@ use Mill\Parser\Annotations\ScopeAnnotation; use Mill\Parser\Version; -/** - * Class for parsing the docblock on a representation. - * - */ class RepresentationParser extends Parser { - // http://stackoverflow.com/a/13114141/1886079 + /** + * @link http://stackoverflow.com/a/13114141/1886079 + * @var string + */ const DOC_PATTERN = '~/\*\*(.*?)\*/~s'; - // Everything between the second asterisk and the first annotation + /** @var string Everything between the second asterisk and the first annotation */ const DOC_BODY_MATCH = '~\s*\*\s+(.*)~'; /** * Locate, and parse, the annotations for a representation method. * - * @param null|string $method_name - * @return array An array containing all the found annotations. - * @throws MethodNotSuppliedException If a method is not supplied to parse. - * @throws MethodNotImplementedException If the supplied method does not exist on the supplied controller. + * @param string|null $method_name + * @return array + * @throws DuplicateFieldException + * @throws MethodNotSuppliedException */ public function getAnnotations(string $method_name = null): array { @@ -46,13 +45,12 @@ public function getAnnotations(string $method_name = null): array $annotations = $this->parse($code); if (count($annotations) > 1) { - // Keep things tidy. ksort($annotations); // Run through all created annotations and cascade any versioning down into any present child annotations. /** @var DataAnnotation $annotation */ foreach ($annotations as $identifier => $annotation) { - if (!$annotation->getVersion() && !$annotation->getCapability() && !$annotation->getScopes()) { + if (!$annotation->getVersion() && !$annotation->getVendorTags() && !$annotation->getScopes()) { continue; } @@ -69,6 +67,9 @@ public function getAnnotations(string $method_name = null): array * @param array $tags * @param string $original_content * @return array + * @throws DuplicateFieldException + * @throws MethodNotSuppliedException + * @throws \Mill\Exceptions\Version\UnrecognizedSchemaException */ public function parseAnnotations(array $tags, string $original_content): array { @@ -130,7 +131,7 @@ public function parseAnnotations(array $tags, string $original_content): array $prefix = array_shift($pointer); // Pass in the current array (by reference) of found annotations that we have so we can do depth - // traversal for version and capability requirements of any implied children, by way of dot-notation. + // traversal for version and requirements of any implied children, by way of dot-notation. $parser = new self($see_class); $see_annotations = $parser->getAnnotations($see_method); @@ -160,6 +161,12 @@ public function parseAnnotations(array $tags, string $original_content): array } } + // If this `@api-see` is being used with `@api-scope` annotations, the scope should filter down + // the pipe. + if (!empty($scopes)) { + $annotation->setScopes($scopes); + } + // If this `@api-see` has a prefix to attach to found annotation identifiers, do so. if (!empty($prefix)) { $see_annotations[$prefix . '.' . $name] = $annotation->setIdentifierPrefix($prefix); @@ -179,7 +186,8 @@ public function parseAnnotations(array $tags, string $original_content): array * * @param string $code * @return array - * @throws DuplicateFieldException If a found field exists more than once. + * @throws DuplicateFieldException + * @throws \Mill\Exceptions\Resource\UnsupportedDecoratorException */ public function parse(string $code): array { @@ -208,7 +216,7 @@ public function parse(string $code): array } /** - * Given a DataAnnotation object, carry any versioning or capabilities on it down to any dot-notation children in + * Given a DataAnnotation object, carry any versioning or vendor tags on it down to any dot-notation children in * an array of other annotations. * * @param DataAnnotation $parent @@ -218,13 +226,11 @@ private function carryAnnotationSettingsToChildren(DataAnnotation $parent, array { $parent_identifier = $parent->getIdentifier(); $parent_version = $parent->getVersion(); + $parent_vendor_tags = $parent->getVendorTags(); /** @var array $parent_scopes */ $parent_scopes = $parent->getScopes(); - /** @var string $parent_capability */ - $parent_capability = $parent->getCapability(); - /** @var DataAnnotation $annotation */ foreach ($annotations as $identifier => $annotation) { if ($identifier === $parent_identifier) { @@ -240,8 +246,8 @@ private function carryAnnotationSettingsToChildren(DataAnnotation $parent, array $annotation->setVersion($parent_version); } - if (!empty($parent_capability) && !$annotation->getCapability()) { - $annotation->setCapability($parent_capability); + if (!empty($parent_vendor_tags) && !$annotation->getVendorTags()) { + $annotation->setVendorTags($parent_vendor_tags); } if (!empty($parent_scopes) && !$annotation->getScopes()) { diff --git a/src/Parser/Resource/Action/Documentation.php b/src/Parser/Resource/Action/Documentation.php index 6f4de06..ddab4b5 100644 --- a/src/Parser/Resource/Action/Documentation.php +++ b/src/Parser/Resource/Action/Documentation.php @@ -1,89 +1,65 @@ description = $annotation->getDescription(); } - // Parse out the `@api-contentType` annotation. - if (!isset($annotations['contentType'])) { - throw RequiredAnnotationException::create('contentType', $this->class, $this->method); + // Parse out the `@api-group` annotation. + if (!isset($annotations['group'])) { + throw RequiredAnnotationException::create('group', $this->class, $this->method); + } elseif (count($annotations['group']) > 1) { + throw MultipleAnnotationsException::create('group', $this->class, $this->method); } else { - $this->content_types = $annotations['contentType']; + /** @var \Mill\Parser\Annotations\GroupAnnotation $annotation */ + $annotation = reset($annotations['group']); + $this->group = $annotation->getGroup(); + } + + // Parse out the `@api-contenttype` annotation. + if (!isset($annotations['contenttype'])) { + throw RequiredAnnotationException::create('contenttype', $this->class, $this->method); + } else { + $this->content_types = $annotations['contenttype']; } // Parse out any remaining annotations. foreach ($annotations as $key => $data) { - if (!in_array($key, self::$ACCEPTED_ANNOTATIONS)) { + if (!in_array($key, self::ACCEPTED_ANNOTATIONS)) { continue; } @@ -168,8 +161,8 @@ public function parseAnnotations(array $annotations = []): self // If we're dealing with parameter annotations, let's set us up the ability to later sort them in // alphabetical order by keying their annotation array off the parameter field name. - if ($key === 'param') { - /** @var Parser\Annotations\ParamAnnotation $annotation */ + if (in_array($key, ['param', 'pathparam', 'queryparam'])) { + /** @var Parser\Annotations\ParamAnnotation|Parser\Annotations\QueryParamAnnotation $annotation */ $this->annotations[$key][$annotation->getField()] = $annotation; } else { $this->annotations[$key][] = $annotation; @@ -182,35 +175,43 @@ public function parseAnnotations(array $annotations = []): self ksort($this->annotations['param']); } + if (isset($this->annotations['pathparam'])) { + ksort($this->annotations['pathparam']); + } + + if (isset($this->annotations['queryparam'])) { + ksort($this->annotations['queryparam']); + } + // Run through the parsed annotations and verify that we aren't missing any required annotations. - foreach (self::$REQUIRED_ANNOTATIONS as $required) { + foreach (self::REQUIRED_ANNOTATIONS as $required) { if (!isset($this->annotations[$required])) { throw RequiredAnnotationException::create($required, $this->class, $this->method); } } - // Process any URI aliases, and also verify that we don't have any public annotations on a private action. + // Process any path aliases, and also verify that we don't have any public annotations on a private action. $visibilities = []; $aliases = []; - /** @var \Mill\Parser\Annotations\UriAnnotation $uri */ - foreach ($this->annotations['uri'] as $uri) { - $visibilities[] = ($uri->isVisible()) ? 'public' : 'private'; + /** @var \Mill\Parser\Annotations\PathAnnotation $path */ + foreach ($this->annotations['path'] as $path) { + $visibilities[] = ($path->isVisible()) ? 'public' : 'private'; - if ($uri->isAliased()) { - $aliases[] = $uri; + if ($path->isAliased()) { + $aliases[] = $path; } } if (!empty($aliases)) { - if (count($aliases) >= count($this->annotations['uri'])) { - throw TooManyAliasedUrisException::create($this->class, $this->method); + if (count($aliases) >= count($this->annotations['path'])) { + throw TooManyAliasedPathsException::create($this->class, $this->method); } - /** @var \Mill\Parser\Annotations\UriAnnotation $uri */ - foreach ($this->annotations['uri'] as $uri) { - if (!$uri->isAliased()) { - $uri->setAliases($aliases); + /** @var \Mill\Parser\Annotations\PathAnnotation $path */ + foreach ($this->annotations['path'] as $path) { + if (!$path->isAliased()) { + $path->setAliases($aliases); } } } @@ -221,7 +222,7 @@ public function parseAnnotations(array $annotations = []): self return $this; } elseif (in_array('private', $visibilities)) { foreach ($this->annotations as $key => $data) { - if ($key === 'uri') { + if ($key === 'path') { continue; } @@ -251,6 +252,26 @@ public function getClass(): string return $this->class; } + /** + * Get the HTTP method that we're parsing. + * + * @return string + */ + public function getMethod(): string + { + return $this->method; + } + + /** + * Get all annotations that this action has been configured with. + * + * @return array + */ + public function getAnnotations(): array + { + return $this->annotations; + } + /** * Get the class method documented label. * @@ -262,23 +283,23 @@ public function getLabel(): string } /** - * Get the HTTP method that we're parsing. + * Get the description of this action. * - * @return string + * @return null|string */ - public function getMethod(): string + public function getDescription() { - return $this->method; + return $this->description; } /** - * Get all annotations that this action has been configured with. + * Get the group that this action belongs to. * - * @return array + * @return string */ - public function getAnnotations(): array + public function getGroup(): string { - return $this->annotations; + return $this->group; } /** @@ -293,7 +314,7 @@ public function getContentTypes(): array /** * @param array $content_types - * @return self + * @return Documentation */ public function setContentTypes(array $content_types): self { @@ -330,78 +351,86 @@ public function getContentType($version = null): string } /** - * Get the description of this action. - * - * @return null|string - */ - public function getDescription() - { - return $this->description; - } - - /** - * Get the raw URI annotations that are part of this action. + * Get the raw path annotations that are part of this action. * * @return array */ - public function getUris(): array + public function getPaths(): array { - return $this->annotations['uri']; + return $this->annotations['path']; } /** - * Get the current URI for this action. + * Get the current path for this action. * - * @return UriAnnotation + * @return PathAnnotation */ - public function getUri(): UriAnnotation + public function getPath(): PathAnnotation { - $uris = $this->getUris(); - return array_shift($uris); + $paths = []; + + /** @var PathAnnotation $path */ + foreach ($this->getPaths() as $k => $path) { + if (!$path->isAliased()) { + $paths[] = $path; + } + } + + if (empty($paths)) { + throw new \Exception( + sprintf( + 'There were zero non-aliased paths detected in this %s::%s.', + $this->getClass(), + $this->getMethod() + ) + ); + } + + return array_shift($paths); } /** - * Set the lone URI that this action runs under for a specific namespace. + * Set the lone path that this action runs under for a specific group. * - * This is used in the Compiler system when grouping actions under namespaces. If an action runs on the `Me\Videos` - * and `Users\Videos` namespaces, we don't want the action in the `Me\Videos` namespace to have actions with - * `Users\Videos` URIs. + * This is used in the Compiler system when grouping actions under groups. If an action runs on the `Me\Videos` + * and `Users\Videos` groups, we don't want the action in the `Me\Videos` group to have actions with `Users\Videos` + * URIs. * - * @param UriAnnotation $uri + * @param PathAnnotation $path */ - public function setUri(UriAnnotation $uri): void + public function setPath(PathAnnotation $path): void { - $this->annotations['uri'] = [$uri]; + $this->annotations['path'] = [$path]; } /** - * Get a combined array of the resource action URI and any URI aliases that it might have. + * Get a combined array of the resource action path and any path aliases that it might have. * * @return array */ - public function getUriAndAliases() + public function getPathAndAliases() { - $uri = $this->getUri(); - $uris = array_merge([ - $uri->getCleanPath() => $uri - ], $this->getUriAliases()); + $path = $this->getPath(); + $paths = array_merge([ + $path->getCleanPath() => $path + ], $this->getPathAliases()); - ksort($uris); + ksort($paths); - return $uris; + return $paths; } /** - * Get a path-keyed array of any URI aliases that this action might have. + * Get a path-keyed array of any path aliases that this action might have. * * @return array */ - public function getUriAliases() + public function getPathAliases() { $aliases = []; - /** @var UriAnnotation $alias */ - foreach ($this->getUri()->getAliases() as $alias) { + /** @var PathAnnotation $alias */ + foreach ($this->getPath()->getAliases() as $alias) { $aliases[$alias->getCleanPath()] = $alias; } @@ -409,37 +438,37 @@ public function getUriAliases() } /** - * Get the raw URI segment annotations that are part of this action. + * Get the raw path param annotations that are part of this action. * * @return array */ - public function getUriSegments(): array + public function getPathParameters(): array { - return (isset($this->annotations['uriSegment'])) ? $this->annotations['uriSegment'] : []; + return (isset($this->annotations['pathparam'])) ? $this->annotations['pathparam'] : []; } /** - * Set the URI segments that this action has. + * Set the path params that this action has. * - * This is used in the Compiler system when grouping actions under namespaces. If an action broadcasts on - * `/me/videos` and `/users/:id/videos`, we don't want the URI segments for `/users/:id/videos` to be a part of the - * compiled `/me/videos` action. + * This is used in the Compiler system when grouping actions under groups. If an action broadcasts on `/me/videos` + * and `/users/:id/videos`, we don't want the params for `/users/:id/videos` to be a part of the compiled + * `/me/videos` action. * - * @param array $segments + * @param array $params */ - public function setUriSegments(array $segments = []): void + public function setPathParams(array $params = []): void { - $this->annotations['uriSegment'] = $segments; + $this->annotations['pathparam'] = $params; } /** - * Get back any application capabilities that this action has set as being required. + * Get back any application vendor tags that this action has set. * * @return array */ - public function getCapabilities(): array + public function getVendorTags(): array { - return (isset($this->annotations['capability'])) ? $this->annotations['capability'] : []; + return (isset($this->annotations['vendortag'])) ? $this->annotations['vendortag'] : []; } /** @@ -462,29 +491,58 @@ public function getParameters(): array return (isset($this->annotations['param'])) ? $this->annotations['param'] : []; } + /** + * Get back any query parameters that this action has available. + * + * @return array + */ + public function getQueryParameters(): array + { + return (isset($this->annotations['queryparam'])) ? $this->annotations['queryparam'] : []; + } + + /** + * Get back all parameters that this action has available. + * + * @return array + */ + public function getAllParameters(): array + { + return array_merge($this->getPathParameters(), $this->getParameters(), $this->getQueryParameters()); + } + /** * Get back any responses that this action can throw. This will include both returns (`@api-return`) and exceptions - * (`@api-throws`). + * (`@api-error`). * * @return array */ public function getResponses(): array { $return = (isset($this->annotations['return'])) ? $this->annotations['return'] : []; - $throws = (isset($this->annotations['throws'])) ? $this->annotations['throws'] : []; + $error = (isset($this->annotations['error'])) ? $this->annotations['error'] : []; - return array_merge($return, $throws); + return array_merge($return, $error); } /** * Get the (absolute) minimum version that this action is supported on. * - /** * @return null|Parser\Annotations\MinVersionAnnotation */ public function getMinimumVersion(): ?Parser\Annotations\MinVersionAnnotation { - return (isset($this->annotations['minVersion'])) ? $this->annotations['minVersion'][0] : null; + return (isset($this->annotations['minversion'])) ? $this->annotations['minversion'][0] : null; + } + + /** + * Get the (absolute) maximum version that this action is supported on. + * + * @return null|Parser\Annotations\MaxVersionAnnotation + */ + public function getMaximumVersion(): ?Parser\Annotations\MaxVersionAnnotation + { + return (isset($this->annotations['maxversion'])) ? $this->annotations['maxversion'][0] : null; } /** @@ -514,30 +572,30 @@ public function filterAnnotationsForVersion(string $version): array /** * Filter down, and return, all annotations on this action that match a specific visibility. This can either be - * public, public+private, or capability-locked. + * public, public+private, or vendor tagged. * * @psalm-suppress RedundantCondition * @param bool $allow_private - * @param array|null $only_capabilities + * @param array|null $only_vendor_tags * @return array */ - public function filterAnnotationsForVisibility(bool $allow_private, ?array $only_capabilities): array + public function filterAnnotationsForVisibility(bool $allow_private, ?array $only_vendor_tags): array { - if ($allow_private && empty($only_capabilities)) { + if ($allow_private && empty($only_vendor_tags)) { return $this->annotations; } - $method_capabilities = $this->getCapabilities(); + $method_vendor_tags = $this->getVendorTags(); foreach ($this->annotations as $name => $data) { /** @var Parser\Annotation $annotation */ foreach ($data as $k => $annotation) { - // While URI annotations are already filtered within the generator, so we don't need to further filter + // While URI annotations are already filtered within the compiler, so we don't need to further filter // them out, we do need to filter URI aliases as those can have their independent visibilities. - if ($annotation instanceof UriAnnotation) { + if ($annotation instanceof PathAnnotation) { $aliases = $annotation->getAliases(); if (!empty($aliases)) { - /** @var UriAnnotation $alias */ + /** @var PathAnnotation $alias */ foreach ($aliases as $k => $alias) { // If this annotation isn't visible, and we don't want private documentation, filter it out. if (!$allow_private && $alias->hasVisibility() && !$alias->isVisible()) { @@ -551,38 +609,32 @@ public function filterAnnotationsForVisibility(bool $allow_private, ?array $only continue; } - // If this annotation has a capability, but that capability isn't in the set of capabilities we're - // generating documentation for, filter it out. - $capability = $annotation->getCapability(); - if (!empty($capability) || !empty($method_capabilities)) { - // If we don't even have capabilities to look for, then filter this annotation out completely. - if (!is_null($only_capabilities) && empty($only_capabilities)) { + // If this annotation has vendor tags, but those vendor tags aren't in the set of vendor tags we're + // compiling documentation for, filter it out. + $vendor_tags = $annotation->getVendorTags(); + if (!empty($vendor_tags) || !empty($method_vendor_tags)) { + // If we don't even have vendor tags to look for, then filter this annotation out completely. + if (!is_null($only_vendor_tags) && empty($only_vendor_tags)) { unset($this->annotations[$name][$k]); continue; } $all_found = true; - /** @var Parser\Annotations\CapabilityAnnotation $method_capability */ - foreach ($method_capabilities as $method_capability) { - /** @var string $capability */ - $capability = $method_capability->getCapability(); - if (!is_null($only_capabilities) && !in_array($capability, $only_capabilities)) { + /** @var Parser\Annotations\VendorTagAnnotation $method_vendor_tag */ + foreach ($method_vendor_tags as $method_vendor_tag) { + $vendor_tag = $method_vendor_tag->getVendorTag(); + if (!is_null($only_vendor_tags) && !in_array($vendor_tag, $only_vendor_tags)) { $all_found = false; } } - if (!$all_found || - ( - !empty($capability) && - (!is_null($only_capabilities) && !in_array($capability, $only_capabilities)) - ) - ) { + if (!$all_found) { unset($this->annotations[$name][$k]); continue; } - // Capabilities override individual annotation visibility. + // Vendor tag requirements override individual annotation visibility. continue; } @@ -597,55 +649,73 @@ public function filterAnnotationsForVisibility(bool $allow_private, ?array $only } /** - * Hydrate a new resource action documentation object with an array of data generated from `toArray`. + * Convert the parsed parameter documentation content dot notation field names into an exploded array. * - * @param array $data - * @return self + * @return array */ - public static function hydrate(array $data): self + public function getExplodedParameterDotNotation(): array { - $annotations = []; - $parser = new Parser($data['class']); - $action = new self($data['class'], $data['method']); + return $this->buildExplodedDotNotation($this->getParameters()); + } - $annotations['label'][] = $parser->hydrateAnnotation('label', $data['class'], $data['method'], $data); + /** + * Convert the parsed query parameter documentation content dot notation field names into an exploded array. + * + * @return array + */ + public function getExplodedQueryParameterDotNotation(): array + { + return $this->buildExplodedDotNotation($this->getQueryParameters()); + } - if (!empty($data['description'])) { - $annotations['description'][] = $parser->hydrateAnnotation( - 'description', - $data['class'], - $data['method'], - $data - ); - } + /** + * Convert the parsed path parameter documentation content dot notation field names into an exploded array. + * + * @return array + */ + public function getExplodedPathParameterDotNotation(): array + { + return $this->buildExplodedDotNotation($this->getPathParameters()); + } - foreach ($data['content_types'] as $content_type) { - $annotations['contentType'][] = $parser->hydrateAnnotation( - 'content_type', - $data['class'], - $data['method'], - $content_type - ); + /** + * Convert all parsed parameter (query and normal parameter) documentation content dot notation field names into an + * exploded array. + * + * @return array + */ + public function getExplodedAllQueryParameterDotNotation(): array + { + return $this->buildExplodedDotNotation(array_merge( + $this->getParameters(), + $this->getQueryParameters() + )); + } + + /** + * @param array $parameters + * @return array + */ + private function buildExplodedDotNotation($parameters = []): array + { + if (empty($parameters)) { + return []; } - foreach ($data['annotations'] as $name => $datas) { - foreach ($datas as $annotation_data) { - $annotations[$name][] = $parser->hydrateAnnotation( - $name, - $data['class'], - $data['method'], - $annotation_data - ); - } + $content = new Data(); + + /** @var Parser\Annotations\ParamAnnotation|Parser\Annotations\QueryParamAnnotation $parameter */ + foreach ($parameters as $name => $parameter) { + $content->set($name, [ + Application::DOT_NOTATION_ANNOTATION_DATA_KEY => $parameter->toArray() + ]); } - return $action->parseAnnotations($annotations); + return $content->export(); } /** - * Convert the parsed resource action documentation into an array. - * - * @return array + * {{@inheritdoc}} */ public function toArray(): array { @@ -653,6 +723,7 @@ public function toArray(): array 'class' => $this->class, 'label' => $this->label, 'description' => $this->description, + 'group' => $this->group, 'content_types' => [], 'method' => $this->method, 'annotations' => [] diff --git a/src/Parser/Resource/Documentation.php b/src/Parser/Resource/Documentation.php index cc3578a..90d9906 100644 --- a/src/Parser/Resource/Documentation.php +++ b/src/Parser/Resource/Documentation.php @@ -1,50 +1,27 @@ parseMethods()->toArray();` * - * @return self + * @return Documentation + * @throws MultipleAnnotationsException + * @throws RequiredAnnotationException + * @throws \Mill\Exceptions\Resource\MissingVisibilityDecoratorException + * @throws \Mill\Exceptions\Resource\NoAnnotationsException + * @throws \Mill\Exceptions\Resource\PublicDecoratorOnPrivateActionException + * @throws \Mill\Exceptions\Resource\TooManyAliasedPathsException + * @throws \Mill\Exceptions\Resource\UnsupportedDecoratorException + * @throws \ReflectionException */ public function parseMethods(): self { @@ -103,6 +89,14 @@ public function parseMethods(): self * Return the parsed method documentation for HTTP Methods that are implemented on the current class. * * @return array + * @throws MultipleAnnotationsException + * @throws RequiredAnnotationException + * @throws \Mill\Exceptions\Resource\MissingVisibilityDecoratorException + * @throws \Mill\Exceptions\Resource\NoAnnotationsException + * @throws \Mill\Exceptions\Resource\PublicDecoratorOnPrivateActionException + * @throws \Mill\Exceptions\Resource\TooManyAliasedPathsException + * @throws \Mill\Exceptions\Resource\UnsupportedDecoratorException + * @throws \ReflectionException */ public function getMethods(): array { @@ -133,7 +127,15 @@ public function getClass(): string * * @param string $method * @return Action\Documentation - * @throws MethodNotImplementedException If the instance class does not implement the supplied method. + * @throws MethodNotImplementedException + * @throws MultipleAnnotationsException + * @throws RequiredAnnotationException + * @throws \Mill\Exceptions\Resource\MissingVisibilityDecoratorException + * @throws \Mill\Exceptions\Resource\NoAnnotationsException + * @throws \Mill\Exceptions\Resource\PublicDecoratorOnPrivateActionException + * @throws \Mill\Exceptions\Resource\TooManyAliasedPathsException + * @throws \Mill\Exceptions\Resource\UnsupportedDecoratorException + * @throws \ReflectionException */ public function getMethod(string $method): Action\Documentation { @@ -149,9 +151,7 @@ public function getMethod(string $method): Action\Documentation } /** - * Convert the parsed resource documentation into an array. - * - * @return array + * {{@inheritdoc}} */ public function toArray(): array { diff --git a/src/Parser/Version.php b/src/Parser/Version.php index 57b923a..d0b8825 100644 --- a/src/Parser/Version.php +++ b/src/Parser/Version.php @@ -7,31 +7,15 @@ use Composer\Semver\VersionParser; use Mill\Exceptions\Version\UnrecognizedSchemaException; -/** - * Version parsing class for realizing a `@api-version` annotation. - * - */ class Version { - /** - * The parsed semver constraint object. - * - * @var ConstraintInterface - */ + /** @var ConstraintInterface The parsed semver constraint object. */ protected $constraint; - /** - * Class that this version is within. - * - * @var string - */ + /** @var string Class that this version is within. */ protected $class; - /** - * Class method that this version is within. - * - * @var string - */ + /** @var string Class method that this version is within. */ protected $method; /** diff --git a/src/Reader.php b/src/Reader.php index d8f010f..da6ce07 100644 --- a/src/Reader.php +++ b/src/Reader.php @@ -4,19 +4,16 @@ use Mill\Exceptions\MethodNotImplementedException; use ReflectionClass; -/** - * Class for loading a class (or class method) and reading its docblock. - * - */ class Reader { /** * Load, and pull, the annotations for a class or class method. * * @param string $class - * @param null|string $method - * @return false|string + * @param string|null $method + * @return bool|string * @throws MethodNotImplementedException If the supplied method does not exist on the supplied class. + * @throws \ReflectionException */ public function getAnnotations(string $class, string $method = null) { @@ -44,6 +41,7 @@ public function getAnnotations(string $class, string $method = null) * @param string $method * @return string * @throws MethodNotImplementedException If the supplied method does not exist on the supplied class. + * @throws \ReflectionException */ public function getRepresentationAnnotations(string $class, string $method): string { diff --git a/tests/Command/ChangelogTest.php b/tests/Command/ChangelogTest.php index 98b70a4..f1c32da 100644 --- a/tests/Command/ChangelogTest.php +++ b/tests/Command/ChangelogTest.php @@ -30,13 +30,13 @@ public function setUp(): void /** * @dataProvider providerTestCommand * @param bool $private_objects - * @param array $capabilities + * @param array $vendor_tags * @param string $expected_file */ - public function testCommand(bool $private_objects, array $capabilities, string $expected_file): void + public function testCommand(bool $private_objects, array $vendor_tags, string $expected_file): void { /** @var string $output_dir */ - $output_dir = tempnam(sys_get_temp_dir(), 'mill-generate-test-'); + $output_dir = tempnam(sys_get_temp_dir(), 'mill-changelog-test-'); if (file_exists($output_dir)) { unlink($output_dir); } @@ -53,17 +53,17 @@ public function testCommand(bool $private_objects, array $capabilities, string $ $params['--private'] = $private_objects; } - if (!empty($capabilities)) { - $params['--capability'] = $capabilities; + if (!empty($vendor_tags)) { + $params['--vendor_tag'] = $vendor_tags; } $this->tester->execute($params); - $blueprints_dir = __DIR__ . '/../../resources/examples/Showtimes/blueprints'; + $control_dir = __DIR__ . '/../../resources/examples/Showtimes/compiled'; $this->assertFileEquals( - $blueprints_dir . '/' . $expected_file, + $control_dir . '/' . $expected_file, $output_dir . '/changelog.md', - 'Generated changelog does not match.' + 'Compiled changelog does not match.' ); } @@ -85,34 +85,34 @@ public function providerTestCommand(): array // Complete changelog. All documentation parsed. 'complete-changelog' => [ 'private_objects' => true, - 'capabilities' => [], + 'vendor_tags' => [], 'expected_file' => 'changelog.md' ], - // Changelog with public-only parsed docs and all capabilities. - 'changelog-public-docs-with-all-capabilities' => [ + // Changelog with public-only parsed docs and all vendor tags. + 'changelog-public-docs-with-all-vendor-tags' => [ 'private_objects' => false, - 'capabilities' => [], - 'expected' => 'changelog-public-only-all-capabilities.md' + 'vendor_tags' => [], + 'expected' => 'changelog-public-only-all-vendor-tags.md' ], - // Changelog with public-only parsed docs and matched `BUY_TICKETS` and `FEATURE_FLAG` capabilities - 'changelog-public-only-matched-with-tickets-and-feature-capabilities' => [ + // Changelog with public-only parsed docs and matched `tag:BUY_TICKETS` and `tag:FEATURE_FLAG` vendor tags. + 'changelog-public-only-matched-with-tickets-and-feature-vendor-tags' => [ 'private_objects' => false, - 'capabilities' => [ - 'BUY_TICKETS', - 'FEATURE_FLAG' + 'vendor_tags' => [ + 'tag:BUY_TICKETS', + 'tag:FEATURE_FLAG' ], - 'expected' => 'changelog-public-only-matched-with-tickets-and-feature-capabilities.md' + 'expected' => 'changelog-public-only-matched-with-tickets-and-feature-vendor-tags.md' ], - // Changelog with public-only parsed docs and matched `DELETE_CONTENT` capabilities - 'changelog-public-only-matched-with-delete-capabilities' => [ + // Changelog with public-only parsed docs and matched the `tag:DELETE_CONTENT` vendor tag. + 'changelog-public-only-matched-with-delete-vendor-tags' => [ 'private_objects' => false, - 'capabilities' => [ - 'DELETE_CONTENT' + 'vendor_tags' => [ + 'tag:DELETE_CONTENT' ], - 'expected' => 'changelog-public-only-matched-with-delete-capabilities.md' + 'expected' => 'changelog-public-only-matched-with-delete-vendor-tags.md' ] ]; } diff --git a/tests/Command/GenerateTest.php b/tests/Command/CompileTest.php similarity index 52% rename from tests/Command/GenerateTest.php rename to tests/Command/CompileTest.php index 6468c0f..201d30e 100644 --- a/tests/Command/GenerateTest.php +++ b/tests/Command/CompileTest.php @@ -1,12 +1,20 @@ add(new Generate); + $application->add(new Compile); - $this->command = $application->find('generate'); + $this->command = $application->find('compile'); $this->tester = new CommandTester($this->command); $this->config_file = __DIR__ . '/../../resources/examples/mill.xml'; } - public function testCommand(): void + public function testCommandForOpenApi(): void { - /** @var string $output_dir */ - $output_dir = tempnam(sys_get_temp_dir(), 'mill-generate-test-'); - if (file_exists($output_dir)) { - unlink($output_dir); + $output_dir = $this->getTempOutputDirectory('openapi'); + + $this->tester->execute([ + 'command' => $this->command->getName(), + '--config' => $this->config_file, + '--format' => Compile::FORMAT_OPENAPI, + 'output' => $output_dir + ]); + + $output = $this->tester->getDisplay(); + + foreach (self::VERSIONS as $version) { + $this->assertContains('API version: ' . $version, $output); } - mkdir($output_dir); + $this->assertSame(array_merge(['.', '..'], self::VERSIONS), scandir($output_dir)); + + $control_dir = __DIR__ . '/../../resources/examples/Showtimes/compiled/'; + foreach (self::VERSIONS as $version) { + $this->assertFileEquals( + $control_dir . '/' . $version . '/api.yaml', + $output_dir . '/' . $version . '/api.yaml', + 'Compiled OpenAPI spec for version `' . $version . ' does not match.' + ); + } + } + + public function testCommandForApiBlueprint(): void + { + $output_dir = $this->getTempOutputDirectory('apiblueprint'); $this->tester->execute([ 'command' => $this->command->getName(), '--config' => $this->config_file, + '--format' => Compile::FORMAT_API_BLUEPRINT, 'output' => $output_dir ]); - $versions = [ - '1.0', - '1.1', - '1.1.1', - '1.1.2', - '1.1.3' - ]; - $output = $this->tester->getDisplay(); - $this->assertNotContains('Running a dry run', $output); - foreach ($versions as $version) { + foreach (self::VERSIONS as $version) { $this->assertContains('API version: ' . $version, $output); } - $this->assertSame(array_merge(['.', '..'], $versions), scandir($output_dir)); + $this->assertSame(array_merge(['.', '..'], self::VERSIONS), scandir($output_dir)); - $blueprints_dir = __DIR__ . '/../../resources/examples/Showtimes/blueprints'; + $control_dir = __DIR__ . '/../../resources/examples/Showtimes/compiled/'; $representations = [ 'Coded error', 'Error', @@ -74,68 +97,59 @@ public function testCommand(): void 'Theaters' ]; - foreach ($versions as $version) { + foreach (self::VERSIONS as $version) { foreach ($representations as $name) { $this->assertFileEquals( - $blueprints_dir . '/' . $version . '/representations/' . $name . '.apib', + $control_dir . '/' . $version . '/representations/' . $name . '.apib', $output_dir . '/' . $version . '/representations/' . $name . '.apib', - 'Generated representation `' . $name . '.apib` for version `' . $version . '`` does not match.' + 'Compiled representation `' . $name . '.apib` for version `' . $version . '`` does not match.' ); } foreach ($resources as $name) { $this->assertFileEquals( - $blueprints_dir . '/' . $version . '/resources/' . $name . '.apib', + $control_dir . '/' . $version . '/resources/' . $name . '.apib', $output_dir . '/' . $version . '/resources/' . $name . '.apib', - 'Generated resource `' . $name . '.apib` for version `' . $version . '`` does not match.' + 'Compiled resource `' . $name . '.apib` for version `' . $version . '`` does not match.' ); } } } - public function testCommandWithDryRun(): void - { - $this->tester->execute([ - 'command' => $this->command->getName(), - '--config' => $this->config_file, - '--dry-run' => true, - 'output' => sys_get_temp_dir() - ]); - - $output = $this->tester->getDisplay(); - $this->assertContains('Running a dry run', $output); - $this->assertContains('API version: 1.0', $output); - $this->assertContains('API version: 1.1', $output); - } - - public function testCommandWithDefaultVersion(): void + /** + * @dataProvider providerFormats + */ + public function testCommandWithDefaultVersion(string $format, string $format_name): void { $this->tester->execute([ 'command' => $this->command->getName(), '--config' => $this->config_file, - '--dry-run' => true, '--default' => true, + '--format' => $format, 'output' => sys_get_temp_dir() ]); $output = $this->tester->getDisplay(); - $this->assertContains('Running a dry run', $output); + $this->assertContains($format_name, $output); $this->assertNotContains('API version: 1.0', $output); $this->assertContains('API version: 1.1', $output); } - public function testCommandWithSpecificConstraint(): void + /** + * @dataProvider providerFormats + */ + public function testCommandWithSpecificConstraint(string $format, string $format_name): void { $this->tester->execute([ 'command' => $this->command->getName(), '--config' => $this->config_file, - '--dry-run' => true, '--constraint' => '1.0', + '--format' => $format, 'output' => sys_get_temp_dir() ]); $output = $this->tester->getDisplay(); - $this->assertContains('Running a dry run', $output); + $this->assertContains($format_name, $output); $this->assertContains('API version: 1.0', $output); $this->assertNotContains('API version: 1.1', $output); } @@ -157,7 +171,6 @@ public function testCommandFailsOnInvalidVersionConstraint(): void $this->tester->execute([ 'command' => $this->command->getName(), '--config' => $this->config_file, - '--dry-run' => true, '--constraint' => '1.^', 'output' => sys_get_temp_dir() ]); @@ -166,4 +179,42 @@ public function testCommandFailsOnInvalidVersionConstraint(): void $this->assertContains('1.^', $output); $this->assertContains('unrecognized schema', $output); } + + public function testCommandFailsOnInvalidFormat(): void + { + $this->tester->execute([ + 'command' => $this->command->getName(), + '--config' => $this->config_file, + '--format' => 'raml', + 'output' => sys_get_temp_dir() + ]); + + $output = $this->tester->getDisplay(); + $this->assertContains('raml', $output); + $this->assertContains('unknown compilation format', $output); + } + + /** + * @return array + */ + public function providerFormats(): array + { + return [ + [Compile::FORMAT_API_BLUEPRINT, 'API Blueprint'], + [Compile::FORMAT_OPENAPI, 'OpenAPI'], + ]; + } + + private function getTempOutputDirectory(string $format): string + { + /** @var string $output_dir */ + $output_dir = tempnam(sys_get_temp_dir(), 'mill-compile-' . $format . '-test-'); + if (file_exists($output_dir)) { + unlink($output_dir); + } + + mkdir($output_dir); + + return $output_dir; + } } diff --git a/tests/Command/ErrorMapTest.php b/tests/Command/ErrorMapTest.php index a711156..ff8a7a3 100644 --- a/tests/Command/ErrorMapTest.php +++ b/tests/Command/ErrorMapTest.php @@ -30,13 +30,13 @@ public function setUp(): void /** * @dataProvider providerTestCommand * @param bool $private_objects - * @param array $capabilities + * @param array $vendor_tags * @param string $expected_file */ - public function testCommand(bool $private_objects, array $capabilities, string $expected_file): void + public function testCommand(bool $private_objects, array $vendor_tags, string $expected_file): void { /** @var string $output_dir */ - $output_dir = tempnam(sys_get_temp_dir(), 'mill-generate-test-'); + $output_dir = tempnam(sys_get_temp_dir(), 'mill-errormap-test-'); if (file_exists($output_dir)) { unlink($output_dir); } @@ -53,8 +53,8 @@ public function testCommand(bool $private_objects, array $capabilities, string $ $params['--private'] = $private_objects; } - if (!empty($capabilities)) { - $params['--capability'] = $capabilities; + if (!empty($vendor_tags)) { + $params['--vendor_tag'] = $vendor_tags; } $this->tester->execute($params); @@ -67,49 +67,30 @@ public function testCommand(bool $private_objects, array $capabilities, string $ ]; $output = $this->tester->getDisplay(); - $this->assertNotContains('Running a dry run', $output); $this->assertNotContains('API version: 1.1.2', $output); foreach ($versions as $version) { $this->assertContains('API version: ' . $version, $output); - $blueprints_dir = __DIR__ . '/../../resources/examples/Showtimes/blueprints/' . $version; + $control_dir = __DIR__ . '/../../resources/examples/Showtimes/compiled/' . $version; $this->assertFileEquals( - $blueprints_dir . '/' . $expected_file, + $control_dir . '/' . $expected_file, $output_dir . '/' . $version . '/errors.md', - 'Generated error map does not match.' + 'Compiled error map does not match.' ); } } - public function testCommandWithDryRun(): void - { - $this->tester->execute([ - 'command' => $this->command->getName(), - '--config' => $this->config_file, - '--dry-run' => true, - 'output' => sys_get_temp_dir() - ]); - - $output = $this->tester->getDisplay(); - $this->assertContains('Running a dry run', $output); - $this->assertContains('API version: 1.0', $output); - $this->assertContains('API version: 1.1.1', $output); - $this->assertNotContains('API version: 1.1.2', $output); - } - public function testCommandWithDefaultVersion(): void { $this->tester->execute([ 'command' => $this->command->getName(), '--config' => $this->config_file, - '--dry-run' => true, '--default' => true, 'output' => sys_get_temp_dir() ]); $output = $this->tester->getDisplay(); - $this->assertContains('Running a dry run', $output); // In our test cases, there's no error codes under the default API version, so this shouldn't be creating error // maps. @@ -121,13 +102,11 @@ public function testCommandWithSpecificConstraint(): void $this->tester->execute([ 'command' => $this->command->getName(), '--config' => $this->config_file, - '--dry-run' => true, '--constraint' => '1.0', 'output' => sys_get_temp_dir() ]); $output = $this->tester->getDisplay(); - $this->assertContains('Running a dry run', $output); $this->assertContains('API version: 1.0', $output); $this->assertNotContains('API version: 1.1', $output); } @@ -149,7 +128,6 @@ public function testCommandFailsOnInvalidVersionConstraint(): void $this->tester->execute([ 'command' => $this->command->getName(), '--config' => $this->config_file, - '--dry-run' => true, '--constraint' => '1.^', 'output' => sys_get_temp_dir() ]); @@ -165,34 +143,34 @@ public function providerTestCommand(): array // Complete error map. All documentation parsed. 'complete-error-map' => [ 'private_objects' => true, - 'capabilities' => [], + 'vendor_tags' => [], 'expected_file' => 'errors.md' ], - // Error map with public-only parsed docs and all capabilities. - 'error-map-public-docs-with-all-capabilities' => [ + // Error map with public-only parsed docs and all vendor tags. + 'error-map-public-docs-with-all-vendor-tags' => [ 'private_objects' => false, - 'capabilities' => [], - 'expected' => 'errors-public-only-all-capabilities.md' + 'vendor_tags' => [], + 'expected' => 'errors-public-only-all-vendor-tags.md' ], - // Error map with public-only parsed docs and unmatched capabilities - 'error-map-public-docs-with-unmatched-capabilities' => [ + // Error map with public-only parsed docs and unmatched vendor tags. + 'error-map-public-docs-with-unmatched-vendor-tags' => [ 'private_objects' => false, - 'capabilities' => [ - 'BUY_TICKETS', - 'FEATURE_FLAG' + 'vendor_tags' => [ + 'tag:BUY_TICKETS', + 'tag:FEATURE_FLAG' ], - 'expected' => 'errors-public-only-unmatched-capabilities.md' + 'expected' => 'errors-public-only-unmatched-vendor-tags.md' ], - // Error map with public-only parsed docs and matched capabilities - 'error-map-public-docs-with-matched-capabilities' => [ + // Error map with public-only parsed docs and matched vendor tags. + 'error-map-public-docs-with-matched-vendor-tags' => [ 'private_objects' => false, - 'capabilities' => [ + 'vendor_tags' => [ 'DELETE_CONTENT' ], - 'expected' => 'errors-public-only-matched-capabilities.md' + 'expected' => 'errors-public-only-matched-vendor-tags.md' ] ]; } diff --git a/tests/Generator/Changelog/Formats/JsonTest.php b/tests/Compiler/Changelog/Formats/JsonTest.php similarity index 57% rename from tests/Generator/Changelog/Formats/JsonTest.php rename to tests/Compiler/Changelog/Formats/JsonTest.php index 3b8fd0b..92637a7 100644 --- a/tests/Generator/Changelog/Formats/JsonTest.php +++ b/tests/Compiler/Changelog/Formats/JsonTest.php @@ -1,26 +1,26 @@ getConfig()); - $generator->setChangelog((new Changelog($this->getConfig()))->generate()); - $generated = $generator->generate(); - $generated = array_shift($generated); - $generated = json_decode($generated, true); + $compiler = new Json($this->getConfig()); + $compiler->setChangelog((new Changelog($this->getConfig()))->compile()); + $compiled = $compiler->compile(); + $compiled = array_shift($compiled); + $compiled = json_decode($compiled, true); $this->assertSame([ '1.1.3', '1.1.2', '1.1.1', '1.1' - ], array_keys($generated)); + ], array_keys($compiled)); // v1.1.3 $this->assertSame([ @@ -31,112 +31,112 @@ public function testGenerate(): void 'added' => [ 'resources' => [ [ - 'The following Movies resources have added:', + 'The following Movies resources have added:', [ [ - '/movie/{id} now ' . - 'throws the following errors on GET requests:', + '/movie/{id} now ' . + 'returns the following errors on GET requests:', [ - '404 ' . 'Not Found with a Error representation: For no reason.', - '404 ' . 'Not Found with a Error representation: For some ' . 'other reason.' ] ], [ - '/movies/{id} now ' . - 'throws the following errors on GET requests:', + '/movies/{id} now ' . + 'returns the following errors on GET requests:', [ - '404 ' . 'Not Found with a Error representation: For no reason.', - '404 ' . 'Not Found with a Error representation: For some ' . 'other reason.' ] ], [ - '/movies/{id} now ' . - 'throws the following errors on PATCH requests:', + '/movies/{id} now ' . + 'returns the following errors on PATCH requests:', [ - '404 ' . 'Not Found with a Error representation: If the ' . 'trailer URL could not be validated.', - '' . '403 Forbidden with a Coded error representation: ' . 'If something cool happened.', - '' . '403 Forbidden with a Coded error representation: ' . 'If the user is not allowed to edit that movie.' ] ], - 'On /movies/{id}' . - ', , PATCH ' . 'requests now return a 202 Accepted with a Movie ' . 'representation.', - 'POST on POST on /movies now returns a 201 Created.' ] ] @@ -150,7 +150,7 @@ public function testGenerate(): void 'data-mill-representation="Movie">Movie representation.' ] ] - ], $generated['1.1.3'], '1.1.3 changelog does not match'); + ], $compiled['1.1.3'], '1.1.3 changelog does not match'); // v1.1.2 $this->assertSame([ @@ -160,112 +160,114 @@ public function testGenerate(): void 'changed' => [ 'resources' => [ [ - 'The following Movies resources have changed:', + 'The following Movies resources have changed:', [ - 'On /movie/{id}, GET requests now ' . - 'return a ' . - 'application/mill.example.movie Content-Type header.', - 'On /movies/{id}, GET requests now ' . - 'return a ' . - 'application/mill.example.movie Content-Type header.', - 'On /movies/{id}, PATCH requests now ' . - 'return a ' . - 'application/mill.example.movie Content-Type header.', - 'On /movies, GET requests now ' . - 'return a ' . - 'application/mill.example.movie Content-Type header.', - 'On /movies, POST requests now ' . - 'return a ' . - 'application/mill.example.movie Content-Type header.' + 'On /movie/{id}, ' . + 'GET requests ' . + 'now return a ' . + 'application/mill.example.movie+json Content-Type header.', + 'On /movies/{id}, ' . + 'GET requests ' . + 'now return a ' . + 'application/mill.example.movie+json Content-Type header.', + 'On /movies/{id}, ' . + 'PATCH requests ' . + 'now return a ' . + 'application/mill.example.movie+json Content-Type header.', + 'On /movies, GET requests ' . + 'now return a ' . + 'application/mill.example.movie+json Content-Type header.', + 'On /movies, POST requests ' . + 'now return a ' . + 'application/mill.example.movie+json Content-Type header.' ] ], [ - 'The following Theaters resources have changed:', + 'The following Theaters resources have changed:', [ - 'On /theaters/{id}, ' . - 'GET requests now ' . - 'return a ' . - 'application/mill.example.theater Content-Type header.', - 'On /theaters/{id}, ' . - 'PATCH requests now ' . - 'return a ' . - 'application/mill.example.theater Content-Type header.', - 'On /theaters, GET requests now ' . - 'return a ' . - 'application/mill.example.theater Content-Type header.', - 'On /theaters, POST requests now ' . - 'return a ' . - 'application/mill.example.theater Content-Type header.' + 'On /theaters/{id}' . + ', GET requests ' . + 'now return a ' . + 'application/mill.example.theater+json Content-Type header.', + 'On /theaters/{id}' . + ', PATCH ' . + 'requests now return a ' . + 'application/mill.example.theater+json Content-Type header.', + 'On /theaters, ' . + 'GET requests ' . + 'now return a ' . + 'application/mill.example.theater+json Content-Type header.', + 'On /theaters, ' . + 'POST requests ' . + 'now return a ' . + 'application/mill.example.theater+json Content-Type header.' ] ] ] @@ -273,29 +275,29 @@ public function testGenerate(): void 'removed' => [ 'resources' => [ [ - 'The following Theaters resources have removed:', + 'The following Theaters resources have removed:', [ - 'PATCH' . - ' requests to requests to /theaters/{id} no longer returns a ' . - '403 ' . 'Forbidden with a Coded error representation: If ' . 'something cool happened.' ] ] ] ] - ], $generated['1.1.2'], '1.1.2 changelog does not match'); + ], $compiled['1.1.2'], '1.1.2 changelog does not match'); // v1.1.1 $this->assertSame([ @@ -305,22 +307,22 @@ public function testGenerate(): void 'added' => [ 'resources' => [ [ - 'The following Movies resources have added:', + 'The following Movies resources have added:', [ - 'A ' . + 'A ' . 'imdb request parameter was added to PATCH on ' . + 'data-mill-resource-group="Movies" data-mill-method="PATCH" ' . + 'data-mill-path="/movies/{id}" data-mill-parameter="imdb">PATCH on ' . '/movies/{id}.' ] ] ] ] - ], $generated['1.1.1'], '1.1.1 changelog does not match'); + ], $compiled['1.1.1'], '1.1.1 changelog does not match'); // v1.1 $this->assertSame([ @@ -347,38 +349,41 @@ public function testGenerate(): void ], 'resources' => [ [ - 'The following Movies resources have added:', + 'The following Movies resources have added:', [ [ - '/movies/{id} has been added with support ' . + '/movies/{id} has been added with support ' . 'for the following HTTP methods:', [ - 'PATCH', - 'DELETE' + 'PATCH', + 'DELETE' ] ], - 'A page' . + 'A page' . ' request parameter was added to GET on ' . + 'data-mill-resource-group="Movies" data-mill-method="GET" ' . + 'data-mill-path="/movies" data-mill-parameter="page">GET on ' . '/movies.', [ 'The following parameters have been added to POST on /movies:', + 'data-mill-resource-group="Movies" data-mill-method="POST" ' . + 'data-mill-path="/movies">POST on /movies:', [ - 'imdb', - 'trailer' . - '' + 'imdb', + 'trailer' ] ] ] @@ -393,26 +398,26 @@ public function testGenerate(): void 'data-mill-representation="Theater">Theater representation.' ] ] - ], $generated['1.1'], '1.1 changelog does not match'); + ], $compiled['1.1'], '1.1 changelog does not match'); } /** - * @dataProvider providerTestGenerateCases + * @dataProvider providerTestCompilerCases * @param array $changelog * @param array $expected */ - public function testGenerateCases(array $changelog, array $expected): void + public function testCompilerCases(array $changelog, array $expected): void { - $generator = new Json($this->getConfig()); - $generator->setChangelog($changelog); - $generated = $generator->generate(); - $generated = array_shift($generated); - $generated = json_decode($generated, true); + $compiler = new Json($this->getConfig()); + $compiler->setChangelog($changelog); + $compiled = $compiler->compile(); + $compiled = array_shift($compiled); + $compiled = json_decode($compiled, true); - $this->assertSame($expected, $generated); + $this->assertSame($expected, $compiled); } - public function providerTestGenerateCases(): array + public function providerTestCompilerCases(): array { return [ 'added-multiple-action-returns' => [ @@ -425,25 +430,25 @@ public function providerTestGenerateCases(): array Changelog::CHANGESET_TYPE_ACTION_RETURN => [ 'hashed-set.1' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'POST', - 'uri' => '/movies', + 'path' => '/movies', 'http_code' => '201 Created', 'representation' => false ], [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'POST', - 'uri' => '/movies', + 'path' => '/movies', 'http_code' => '200 OK', 'representation' => 'Movie' ] ], 'hashed-set.2' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'GET', - 'uri' => '/movies', + 'path' => '/movies', 'http_code' => '200 OK', 'representation' => 'Movie' ] @@ -460,45 +465,45 @@ public function providerTestGenerateCases(): array 'added' => [ 'resources' => [ [ - 'The following Movies resources have added:', + 'The following Movies resources have added:', [ [ 'The POST on /movies now ' . + 'data-mill-resource-group="Movies" data-mill-method="POST" ' . + 'data-mill-path="/movies">POST on /movies now ' . 'returns the following responses:', [ '201 Created', '200 OK with a Movie representation' ] ], - 'On /movies' . ', GET requests now return a ' . '200 OK with a Movie representation.' ] ] @@ -517,25 +522,25 @@ public function providerTestGenerateCases(): array Changelog::CHANGESET_TYPE_ACTION_RETURN => [ 'hashed-set.1' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'POST', - 'uri' => '/movies', + 'path' => '/movies', 'http_code' => '201 Created', 'representation' => false ], [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'POST', - 'uri' => '/movies', + 'path' => '/movies', 'http_code' => '200 OK', 'representation' => 'Movie' ] ], 'hashed-set.2' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'GET', - 'uri' => '/movies', + 'path' => '/movies', 'http_code' => '200 OK', 'representation' => 'Movie' ] @@ -552,45 +557,45 @@ public function providerTestGenerateCases(): array 'removed' => [ 'resources' => [ [ - 'The following Movies resources have removed:', + 'The following Movies resources have removed:', [ [ 'The POST on /movies no ' . + 'data-mill-resource-group="Movies" data-mill-method="POST" ' . + 'data-mill-path="/movies">POST on /movies no ' . 'longer returns the following responses:', [ '201 Created', '200 OK with a Movie representation' ] ], - 'On /movies' . ', GET requests no longer return ' . 'a 200 OK with a Movie representation.' ] ] diff --git a/tests/Generator/ChangelogTest.php b/tests/Compiler/ChangelogTest.php similarity index 80% rename from tests/Generator/ChangelogTest.php rename to tests/Compiler/ChangelogTest.php index 6c7fd46..a1590a8 100644 --- a/tests/Generator/ChangelogTest.php +++ b/tests/Compiler/ChangelogTest.php @@ -1,23 +1,23 @@ getConfig()); - $generator->setLoadPrivateDocs($private_objects); - $generator->setLoadCapabilityDocs($capabilities); - $changelog = $generator->generate(); + $compiler = new Changelog($this->getConfig()); + $compiler->setLoadPrivateDocs($private_objects); + $compiler->setLoadVendorTagDocs($vendor_tags); + $changelog = $compiler->compile(); $this->assertSame(array_keys($expected), array_keys($changelog)); @@ -38,15 +38,12 @@ public function testGeneration(bool $private_objects, ?array $capabilities, arra } } - public function testJsonGeneration(): void + public function testCompilationToJSON(): void { - $generator = new Changelog($this->getConfig()); - $changelog = $generator->generateJson(); + $compiler = new Changelog($this->getConfig()); + $changelog = $compiler->toJson(); $changelog = json_decode($changelog, true); - // We don't need to test the full functionality of the JSON extension to the Changelog generator, since that's - // being done in `Generator\Changelog\JsonTest`, we just want to make sure that we at least have the expected - // array keys. $this->assertSame([ '1.1.3', '1.1.2', @@ -55,26 +52,26 @@ public function testJsonGeneration(): void ], array_keys($changelog)); } - public function providerTestGeneration(): array + public function providerTestCompilation(): array { // Save us the effort of copy and pasting the same base actions over and over. $actions = [ '1.1.3' => [ '/movie/{id}' => [ - 'throws' => [ - '2e302f7f79' => [ + 'error' => [ + 'cc15434d35' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'GET', - 'uri' => '/movie/{id}', + 'path' => '/movie/{id}', 'http_code' => '404 Not Found', 'representation' => 'Error', 'description' => 'For no reason.' ], [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'GET', - 'uri' => '/movie/{id}', + 'path' => '/movie/{id}', 'http_code' => '404 Not Found', 'representation' => 'Error', 'description' => 'For some other reason.' @@ -84,11 +81,11 @@ public function providerTestGeneration(): array ], '/movies' => [ 'return' => [ - '3781891d58' => [ + '8bedc22ca1' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'POST', - 'uri' => '/movies', + 'path' => '/movies', 'http_code' => '201 Created', 'representation' => false ] @@ -96,54 +93,54 @@ public function providerTestGeneration(): array ] ], '/movies/{id}' => [ - 'return' => [ - '162944fa14' => [ - [ - 'resource_namespace' => 'Movies', - 'method' => 'PATCH', - 'uri' => '/movies/{id}', - 'http_code' => '202 Accepted', - 'representation' => 'Movie' - ] - ] - ], - 'throws' => [ - 'e7dc298139' => [ + 'error' => [ + 'edcc6fcf49' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'GET', - 'uri' => '/movies/{id}', + 'path' => '/movies/{id}', 'http_code' => '404 Not Found', 'representation' => 'Error', 'description' => 'For no reason.' ], [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'GET', - 'uri' => '/movies/{id}', + 'path' => '/movies/{id}', 'http_code' => '404 Not Found', 'representation' => 'Error', 'description' => 'For some other reason.' ] ], - '162944fa14' => [ + '0a4c5505a8' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'PATCH', - 'uri' => '/movies/{id}', + 'path' => '/movies/{id}', 'http_code' => '404 Not Found', 'representation' => 'Error', 'description' => 'If the trailer URL could not be validated.' ], [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'PATCH', - 'uri' => '/movies/{id}', + 'path' => '/movies/{id}', 'http_code' => '403 Forbidden', 'representation' => 'Coded error', 'description' => 'If the user is not allowed to edit that movie.' ] ] + ], + 'return' => [ + '0a4c5505a8' => [ + [ + 'resource_group' => 'Movies', + 'method' => 'PATCH', + 'path' => '/movies/{id}', + 'http_code' => '202 Accepted', + 'representation' => 'Movie' + ] + ] ] ] ], @@ -152,10 +149,10 @@ public function providerTestGeneration(): array 'content_type' => [ '979fc6e97f' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'GET', - 'uri' => '/movie/{id}', - 'content_type' => 'application/mill.example.movie' + 'path' => '/movie/{id}', + 'content_type' => 'application/mill.example.movie+json' ] ] ] @@ -164,18 +161,18 @@ public function providerTestGeneration(): array 'content_type' => [ '979fc6e97f' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'GET', - 'uri' => '/movies', - 'content_type' => 'application/mill.example.movie' + 'path' => '/movies', + 'content_type' => 'application/mill.example.movie+json' ] ], '066564ef49' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'POST', - 'uri' => '/movies', - 'content_type' => 'application/mill.example.movie' + 'path' => '/movies', + 'content_type' => 'application/mill.example.movie+json' ] ] ] @@ -184,18 +181,18 @@ public function providerTestGeneration(): array 'content_type' => [ '979fc6e97f' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'GET', - 'uri' => '/movies/{id}', - 'content_type' => 'application/mill.example.movie' + 'path' => '/movies/{id}', + 'content_type' => 'application/mill.example.movie+json' ] ], 'f4628f751a' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'PATCH', - 'uri' => '/movies/{id}', - 'content_type' => 'application/mill.example.movie' + 'path' => '/movies/{id}', + 'content_type' => 'application/mill.example.movie+json' ] ] ] @@ -204,29 +201,29 @@ public function providerTestGeneration(): array 'content_type' => [ '979fc6e97f' => [ [ - 'resource_namespace' => 'Theaters', + 'resource_group' => 'Theaters', 'method' => 'GET', - 'uri' => '/theaters', - 'content_type' => 'application/mill.example.theater' + 'path' => '/theaters', + 'content_type' => 'application/mill.example.theater+json' ] ], '066564ef49' => [ [ - 'resource_namespace' => 'Theaters', + 'resource_group' => 'Theaters', 'method' => 'POST', - 'uri' => '/theaters', - 'content_type' => 'application/mill.example.theater' + 'path' => '/theaters', + 'content_type' => 'application/mill.example.theater+json' ] ] ] ], '/theaters/{id}' => [ - 'action_throws' => [ - 'b3a16c4d74' => [ + 'error' => [ + '6f3f64d865' => [ [ - 'resource_namespace' => 'Theaters', + 'resource_group' => 'Theaters', 'method' => 'PATCH', - 'uri' => '/theaters/{id}', + 'path' => '/theaters/{id}', 'http_code' => '403 Forbidden', 'representation' => 'Coded error', 'description' => 'If something cool happened.' @@ -236,18 +233,18 @@ public function providerTestGeneration(): array 'content_type' => [ '979fc6e97f' => [ [ - 'resource_namespace' => 'Theaters', + 'resource_group' => 'Theaters', 'method' => 'GET', - 'uri' => '/theaters/{id}', - 'content_type' => 'application/mill.example.theater' + 'path' => '/theaters/{id}', + 'content_type' => 'application/mill.example.theater+json' ] ], 'f4628f751a' => [ [ - 'resource_namespace' => 'Theaters', + 'resource_group' => 'Theaters', 'method' => 'PATCH', - 'uri' => '/theaters/{id}', - 'content_type' => 'application/mill.example.theater' + 'path' => '/theaters/{id}', + 'content_type' => 'application/mill.example.theater+json' ] ] ] @@ -256,11 +253,11 @@ public function providerTestGeneration(): array '1.1.1' => [ '/movies/{id}' => [ 'param' => [ - '162944fa14' => [ + '0a4c5505a8' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'PATCH', - 'uri' => '/movies/{id}', + 'path' => '/movies/{id}', 'parameter' => 'imdb', 'description' => 'IMDB URL' ] @@ -271,27 +268,27 @@ public function providerTestGeneration(): array '1.1' => [ '/movies' => [ 'param' => [ - '776d02bb83' => [ + '6d082f8d4f' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'GET', - 'uri' => '/movies', + 'path' => '/movies', 'parameter' => 'page', 'description' => 'Page of results to pull.' ] ], - '3781891d58' => [ + '8bedc22ca1' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'POST', - 'uri' => '/movies', + 'path' => '/movies', 'parameter' => 'imdb', 'description' => 'IMDB URL' ], [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'POST', - 'uri' => '/movies', + 'path' => '/movies', 'parameter' => 'trailer', 'description' => 'Trailer URL' ] @@ -300,16 +297,16 @@ public function providerTestGeneration(): array ], '/movies/{id}' => [ 'action' => [ - 'd81e7058dd' => [ + '0b3fdf5321' => [ [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'PATCH', - 'uri' => '/movies/{id}' + 'path' => '/movies/{id}' ], [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'DELETE', - 'uri' => '/movies/{id}' + 'path' => '/movies/{id}' ] ] ] @@ -364,7 +361,7 @@ public function providerTestGeneration(): array // Complete changelog. All documentation parsed. 'complete-changelog' => [ 'private_objects' => true, - 'capabilities' => null, + 'vendor_tags' => null, 'expected' => [ '1.1.3' => [ '_details' => [ @@ -375,26 +372,25 @@ public function providerTestGeneration(): array 'resources' => [ 'Movies' => [ '/movie/{id}' => [ - Changelog::CHANGESET_TYPE_ACTION_THROWS => [ - '2e302f7f79' => $actions['1.1.3']['/movie/{id}']['throws']['2e302f7f79'] + Changelog::CHANGESET_TYPE_ACTION_ERROR => [ + 'cc15434d35' => $actions['1.1.3']['/movie/{id}']['error']['cc15434d35'] ] ], '/movies/{id}' => [ - Changelog::CHANGESET_TYPE_ACTION_THROWS => [ - 'e7dc298139' => $actions['1.1.3']['/movies/{id}']['throws']['e7dc298139'], - '162944fa14' => call_user_func( + Changelog::CHANGESET_TYPE_ACTION_ERROR => [ + 'edcc6fcf49' => $actions['1.1.3']['/movies/{id}']['error']['edcc6fcf49'], + '0a4c5505a8' => call_user_func( function () use ($actions): array { - $actions = - $actions['1.1.3']['/movies/{id}']['throws']['162944fa14']; + $actions = $actions['1.1.3']['/movies/{id}']['error']['0a4c5505a8']; // Add in the "If something cool happened." exception. It's the - // only private/capability-free exception we're testing, and it + // only private/vendor tag-free exception we're testing, and it // should be available on the complete changelog. $actions[2] = $actions[1]; $actions[1] = [ - 'resource_namespace' => 'Movies', + 'resource_group' => 'Movies', 'method' => 'PATCH', - 'uri' => '/movies/{id}', + 'path' => '/movies/{id}', 'http_code' => '403 Forbidden', 'representation' => 'Coded error', 'description' => 'If something cool happened.' @@ -405,12 +401,12 @@ function () use ($actions): array { ) ], Changelog::CHANGESET_TYPE_ACTION_RETURN => [ - '162944fa14' => $actions['1.1.3']['/movies/{id}']['return']['162944fa14'] + '0a4c5505a8' => $actions['1.1.3']['/movies/{id}']['return']['0a4c5505a8'] ] ], '/movies' => [ Changelog::CHANGESET_TYPE_ACTION_RETURN => [ - '3781891d58' => $actions['1.1.3']['/movies']['return']['3781891d58'] + '8bedc22ca1' => $actions['1.1.3']['/movies']['return']['8bedc22ca1'] ] ] ] @@ -477,9 +473,8 @@ function () use ($actions): array { 'resources' => [ 'Theaters' => [ '/theaters/{id}' => [ - Changelog::CHANGESET_TYPE_ACTION_THROWS => [ - 'b3a16c4d74' => - $actions['1.1.2']['/theaters/{id}']['action_throws']['b3a16c4d74'] + Changelog::CHANGESET_TYPE_ACTION_ERROR => [ + '6f3f64d865' => $actions['1.1.2']['/theaters/{id}']['error']['6f3f64d865'] ] ] ] @@ -495,7 +490,7 @@ function () use ($actions): array { 'Movies' => [ '/movies/{id}' => [ Changelog::CHANGESET_TYPE_ACTION_PARAM => [ - '162944fa14' => $actions['1.1.1']['/movies/{id}']['param']['162944fa14'] + '0a4c5505a8' => $actions['1.1.1']['/movies/{id}']['param']['0a4c5505a8'] ] ] ] @@ -518,13 +513,13 @@ function () use ($actions): array { 'Movies' => [ '/movies/{id}' => [ Changelog::CHANGESET_TYPE_ACTION => [ - 'd81e7058dd' => $actions['1.1']['/movies/{id}']['action']['d81e7058dd'] + '0b3fdf5321' => $actions['1.1']['/movies/{id}']['action']['0b3fdf5321'] ] ], '/movies' => [ Changelog::CHANGESET_TYPE_ACTION_PARAM => [ - '776d02bb83' => $actions['1.1']['/movies']['param']['776d02bb83'], - '3781891d58' => $actions['1.1']['/movies']['param']['3781891d58'] + '6d082f8d4f' => $actions['1.1']['/movies']['param']['6d082f8d4f'], + '8bedc22ca1' => $actions['1.1']['/movies']['param']['8bedc22ca1'] ] ] ] @@ -543,14 +538,14 @@ function () use ($actions): array { ] ], - // Changelog with public-only parsed docs and all capabilities. - 'changelog-public-docs-with-all-capabilities' => [ + // Changelog with public-only parsed docs and all vendor tags. + 'changelog-public-docs-with-all-vendor-tags' => [ 'private_objects' => false, - 'capabilities' => [ - 'BUY_TICKETS', - 'DELETE_CONTENT', - 'FEATURE_FLAG', - 'MOVIE_RATINGS' + 'vendor_tags' => [ + 'tag:BUY_TICKETS', + 'tag:DELETE_CONTENT', + 'tag:FEATURE_FLAG', + 'tag:MOVIE_RATINGS' ], 'expected' => [ '1.1.3' => [ @@ -562,17 +557,17 @@ function () use ($actions): array { 'resources' => [ 'Movies' => [ '/movies/{id}' => [ - Changelog::CHANGESET_TYPE_ACTION_THROWS => [ - 'e7dc298139' => $actions['1.1.3']['/movies/{id}']['throws']['e7dc298139'], - '162944fa14' => $actions['1.1.3']['/movies/{id}']['throws']['162944fa14'] + Changelog::CHANGESET_TYPE_ACTION_ERROR => [ + 'edcc6fcf49' => $actions['1.1.3']['/movies/{id}']['error']['edcc6fcf49'], + '0a4c5505a8' => $actions['1.1.3']['/movies/{id}']['error']['0a4c5505a8'] ], Changelog::CHANGESET_TYPE_ACTION_RETURN => [ - '162944fa14' => $actions['1.1.3']['/movies/{id}']['return']['162944fa14'] + '0a4c5505a8' => $actions['1.1.3']['/movies/{id}']['return']['0a4c5505a8'] ] ], '/movies' => [ Changelog::CHANGESET_TYPE_ACTION_RETURN => [ - '3781891d58' => $actions['1.1.3']['/movies']['return']['3781891d58'] + '8bedc22ca1' => $actions['1.1.3']['/movies']['return']['8bedc22ca1'] ] ] ] @@ -634,9 +629,8 @@ function () use ($actions): array { 'resources' => [ 'Theaters' => [ '/theaters/{id}' => [ - Changelog::CHANGESET_TYPE_ACTION_THROWS => [ - 'b3a16c4d74' => - $actions['1.1.2']['/theaters/{id}']['action_throws']['b3a16c4d74'] + Changelog::CHANGESET_TYPE_ACTION_ERROR => [ + '6f3f64d865' => $actions['1.1.2']['/theaters/{id}']['error']['6f3f64d865'] ] ] ] @@ -652,7 +646,7 @@ function () use ($actions): array { 'Movies' => [ '/movies/{id}' => [ Changelog::CHANGESET_TYPE_ACTION_PARAM => [ - '162944fa14' => $actions['1.1.1']['/movies/{id}']['param']['162944fa14'] + '0a4c5505a8' => $actions['1.1.1']['/movies/{id}']['param']['0a4c5505a8'] ] ] ] @@ -675,13 +669,13 @@ function () use ($actions): array { 'Movies' => [ '/movies/{id}' => [ Changelog::CHANGESET_TYPE_ACTION => [ - 'd81e7058dd' => $actions['1.1']['/movies/{id}']['action']['d81e7058dd'] + '0b3fdf5321' => $actions['1.1']['/movies/{id}']['action']['0b3fdf5321'] ] ], '/movies' => [ Changelog::CHANGESET_TYPE_ACTION_PARAM => [ - '776d02bb83' => $actions['1.1']['/movies']['param']['776d02bb83'], - '3781891d58' => $actions['1.1']['/movies']['param']['3781891d58'] + '6d082f8d4f' => $actions['1.1']['/movies']['param']['6d082f8d4f'], + '8bedc22ca1' => $actions['1.1']['/movies']['param']['8bedc22ca1'] ] ] ] @@ -700,12 +694,12 @@ function () use ($actions): array { ] ], - // Changelog with public-only parsed docs with matched - 'changelog-public-docs-with-matched-capabilities' => [ + // Changelog with public-only parsed docs with matched. + 'changelog-public-docs-with-matched-vendor-tags' => [ 'private_objects' => false, - 'capabilities' => [ - 'BUY_TICKETS', - 'FEATURE_FLAG' + 'vendor_tags' => [ + 'tag:BUY_TICKETS', + 'tag:FEATURE_FLAG' ], 'expected' => [ '1.1.3' => [ @@ -717,17 +711,17 @@ function () use ($actions): array { 'resources' => [ 'Movies' => [ '/movies/{id}' => [ - Changelog::CHANGESET_TYPE_ACTION_THROWS => [ - 'e7dc298139' => $actions['1.1.3']['/movies/{id}']['throws']['e7dc298139'], - '162944fa14' => $actions['1.1.3']['/movies/{id}']['throws']['162944fa14'] + Changelog::CHANGESET_TYPE_ACTION_ERROR => [ + 'edcc6fcf49' => $actions['1.1.3']['/movies/{id}']['error']['edcc6fcf49'], + '0a4c5505a8' => $actions['1.1.3']['/movies/{id}']['error']['0a4c5505a8'] ], Changelog::CHANGESET_TYPE_ACTION_RETURN => [ - '162944fa14' => $actions['1.1.3']['/movies/{id}']['return']['162944fa14'] + '0a4c5505a8' => $actions['1.1.3']['/movies/{id}']['return']['0a4c5505a8'] ] ], '/movies' => [ Changelog::CHANGESET_TYPE_ACTION_RETURN => [ - '3781891d58' => $actions['1.1.3']['/movies']['return']['3781891d58'] + '8bedc22ca1' => $actions['1.1.3']['/movies']['return']['8bedc22ca1'] ] ] ] @@ -791,9 +785,8 @@ function () use ($actions): array { 'resources' => [ 'Theaters' => [ '/theaters/{id}' => [ - Changelog::CHANGESET_TYPE_ACTION_THROWS => [ - 'b3a16c4d74' => - $actions['1.1.2']['/theaters/{id}']['action_throws']['b3a16c4d74'] + Changelog::CHANGESET_TYPE_ACTION_ERROR => [ + '6f3f64d865' => $actions['1.1.2']['/theaters/{id}']['error']['6f3f64d865'] ] ] ] @@ -809,7 +802,7 @@ function () use ($actions): array { 'Movies' => [ '/movies/{id}' => [ Changelog::CHANGESET_TYPE_ACTION_PARAM => [ - '162944fa14' => $actions['1.1.1']['/movies/{id}']['param']['162944fa14'] + '0a4c5505a8' => $actions['1.1.1']['/movies/{id}']['param']['0a4c5505a8'] ] ] ] @@ -832,9 +825,9 @@ function () use ($actions): array { 'Movies' => [ '/movies/{id}' => [ Changelog::CHANGESET_TYPE_ACTION => [ - 'd81e7058dd' => call_user_func( + '0b3fdf5321' => call_user_func( function () use ($actions): array { - $actions = $actions['1.1']['/movies/{id}']['action']['d81e7058dd']; + $actions = $actions['1.1']['/movies/{id}']['action']['0b3fdf5321']; // Remove the `DELETE` method from `/movies/{id}`, since that // shouldn't be available under these conditions. @@ -846,8 +839,8 @@ function () use ($actions): array { ], '/movies' => [ Changelog::CHANGESET_TYPE_ACTION_PARAM => [ - '776d02bb83' => $actions['1.1']['/movies']['param']['776d02bb83'], - '3781891d58' => $actions['1.1']['/movies']['param']['3781891d58'] + '6d082f8d4f' => $actions['1.1']['/movies']['param']['6d082f8d4f'], + '8bedc22ca1' => $actions['1.1']['/movies']['param']['8bedc22ca1'] ] ] ] @@ -866,10 +859,10 @@ function () use ($actions): array { ], ], - // Changelog with public-only parsed docs + // Changelog with public-only parsed docs. 'changelog-public-docs' => [ 'private_objects' => false, - 'capabilities' => [], + 'vendor_tags' => [], 'expected' => [ '1.1.3' => [ '_details' => [ @@ -880,17 +873,17 @@ function () use ($actions): array { 'resources' => [ 'Movies' => [ '/movies/{id}' => [ - Changelog::CHANGESET_TYPE_ACTION_THROWS => [ - 'e7dc298139' => $actions['1.1.3']['/movies/{id}']['throws']['e7dc298139'], - '162944fa14' => $actions['1.1.3']['/movies/{id}']['throws']['162944fa14'] + Changelog::CHANGESET_TYPE_ACTION_ERROR => [ + 'edcc6fcf49' => $actions['1.1.3']['/movies/{id}']['error']['edcc6fcf49'], + '0a4c5505a8' => $actions['1.1.3']['/movies/{id}']['error']['0a4c5505a8'] ], Changelog::CHANGESET_TYPE_ACTION_RETURN => [ - '162944fa14' => $actions['1.1.3']['/movies/{id}']['return']['162944fa14'] + '0a4c5505a8' => $actions['1.1.3']['/movies/{id}']['return']['0a4c5505a8'] ] ], '/movies' => [ Changelog::CHANGESET_TYPE_ACTION_RETURN => [ - '3781891d58' => $actions['1.1.3']['/movies']['return']['3781891d58'] + '8bedc22ca1' => $actions['1.1.3']['/movies']['return']['8bedc22ca1'] ] ] ] @@ -943,9 +936,8 @@ function () use ($actions): array { 'resources' => [ 'Theaters' => [ '/theaters/{id}' => [ - Changelog::CHANGESET_TYPE_ACTION_THROWS => [ - 'b3a16c4d74' => - $actions['1.1.2']['/theaters/{id}']['action_throws']['b3a16c4d74'] + Changelog::CHANGESET_TYPE_ACTION_ERROR => [ + '6f3f64d865' => $actions['1.1.2']['/theaters/{id}']['error']['6f3f64d865'] ] ] ] @@ -961,7 +953,7 @@ function () use ($actions): array { 'Movies' => [ '/movies/{id}' => [ Changelog::CHANGESET_TYPE_ACTION_PARAM => [ - '162944fa14' => $actions['1.1.1']['/movies/{id}']['param']['162944fa14'] + '0a4c5505a8' => $actions['1.1.1']['/movies/{id}']['param']['0a4c5505a8'] ] ] ] @@ -994,10 +986,9 @@ function () use ($representations): array { 'Movies' => [ '/movies/{id}' => [ Changelog::CHANGESET_TYPE_ACTION => [ - 'd81e7058dd' => call_user_func( + '0b3fdf5321' => call_user_func( function () use ($actions): array { - $hash = 'd81e7058ddfce86beb09ddb2a2461ea16d949637'; - $actions = $actions['1.1']['/movies/{id}']['action']['d81e7058dd']; + $actions = $actions['1.1']['/movies/{id}']['action']['0b3fdf5321']; // Remove the `DELETE` method from `/movies/{id}`, since that // shouldn't be available under these conditions. @@ -1009,8 +1000,8 @@ function () use ($actions): array { ], '/movies' => [ Changelog::CHANGESET_TYPE_ACTION_PARAM => [ - '776d02bb83' => $actions['1.1']['/movies']['param']['776d02bb83'], - '3781891d58' => $actions['1.1']['/movies']['param']['3781891d58'] + '6d082f8d4f' => $actions['1.1']['/movies']['param']['6d082f8d4f'], + '8bedc22ca1' => $actions['1.1']['/movies']['param']['8bedc22ca1'] ] ] ] diff --git a/tests/Generator/ErrorMapTest.php b/tests/Compiler/ErrorMapTest.php similarity index 84% rename from tests/Generator/ErrorMapTest.php rename to tests/Compiler/ErrorMapTest.php index c0c852e..407c6bc 100644 --- a/tests/Generator/ErrorMapTest.php +++ b/tests/Compiler/ErrorMapTest.php @@ -1,23 +1,23 @@ getConfig()); - $generator->setLoadPrivateDocs($private_objects); - $generator->setLoadCapabilityDocs($capabilities); - $error_map = $generator->generate(); + $compiler = new ErrorMap($this->getConfig()); + $compiler->setLoadPrivateDocs($private_objects); + $compiler->setLoadVendorTagDocs($vendor_tags); + $error_map = $compiler->compile(); $this->assertSame(array_keys($expected), array_keys($error_map)); @@ -38,26 +38,26 @@ public function testGeneration(bool $private_objects, ?array $capabilities, arra } } - public function providerTestGeneration(): array + public function providerTestCompilation(): array { // Save us the effort of copy and pasting the same action data over and over. $errors = [ '/movies/+id::PATCH:666' => [ - 'uri' => '/movies/{id}', + 'path' => '/movies/{id}', 'method' => 'PATCH', 'http_code' => '403 Forbidden', 'error_code' => '666', 'description' => 'If the user is not allowed to edit that movie.' ], '/movies/+id::PATCH:1337' => [ - 'uri' => '/movies/{id}', + 'path' => '/movies/{id}', 'method' => 'PATCH', 'http_code' => '403 Forbidden', 'error_code' => '1337', 'description' => 'If something cool happened.' ], '/theaters/+id::PATCH::1337' => [ - 'uri' => '/theaters/{id}', + 'path' => '/theaters/{id}', 'method' => 'PATCH', 'http_code' => '403 Forbidden', 'error_code' => '1337', @@ -69,7 +69,7 @@ public function providerTestGeneration(): array // Complete error map. All documentation parsed. 'complete-error-map' => [ 'private_objects' => true, - 'capabilities' => null, + 'vendor_tags' => null, 'expected' => [ '1.0' => [ 'Theaters' => [ @@ -105,14 +105,14 @@ public function providerTestGeneration(): array ] ], - // Error map with public-only parsed docs and all capabilities. - 'error-map-public-docs-with-all-capabilities' => [ + // Error map with public-only parsed docs and all vendor tags. + 'error-map-public-docs-with-all-vendor-tags' => [ 'private_objects' => false, - 'capabilities' => [ - 'BUY_TICKETS', - 'DELETE_CONTENT', - 'FEATURE_FLAG', - 'MOVIE_RATINGS' + 'vendor_tags' => [ + 'tag:BUY_TICKETS', + 'tag:DELETE_CONTENT', + 'tag:FEATURE_FLAG', + 'tag:MOVIE_RATINGS' ], 'expected' => [ '1.0' => [ @@ -146,12 +146,12 @@ public function providerTestGeneration(): array ] ], - // Error map with public-only parsed docs and unmatched capabilities - 'error-map-public-docs-with-unmatched-capabilities' => [ + // Error map with public-only parsed docs and unmatched vendor tags. + 'error-map-public-docs-with-unmatched-vendor-tags' => [ 'private_objects' => false, - 'capabilities' => [ - 'BUY_TICKETS', - 'FEATURE_FLAG' + 'vendor_tags' => [ + 'tag:BUY_TICKETS', + 'tag:FEATURE_FLAG' ], 'expected' => [ '1.0' => [ @@ -185,11 +185,11 @@ public function providerTestGeneration(): array ], ], - // Error map with public-only parsed docs and matched capabilities - 'error-map-public-docs-with-matched-capabilities' => [ + // Error map with public-only parsed docs and matched vendor tags. + 'error-map-public-docs-with-matched-vendor-tags' => [ 'private_objects' => false, - 'capabilities' => [ - 'DELETE_CONTENT' + 'vendor_tags' => [ + 'tag:DELETE_CONTENT' ], 'expected' => [ '1.0' => [ @@ -223,10 +223,10 @@ public function providerTestGeneration(): array ] ], - // Error map with public-only parsed docs + // Error map with public-only parsed docs. 'error-map-public-docs' => [ 'private_objects' => false, - 'capabilities' => [], + 'vendor_tags' => [], 'expected' => [ '1.0' => [ 'Theaters' => [ diff --git a/tests/Compiler/Specification/ApiBlueprintTest.php b/tests/Compiler/Specification/ApiBlueprintTest.php new file mode 100644 index 0000000..ab5cc60 --- /dev/null +++ b/tests/Compiler/Specification/ApiBlueprintTest.php @@ -0,0 +1,72 @@ +getConfig()); + $compiled = $compiler->compile(); + + foreach ($compiled as $version => $section) { + $version_dir = $control_dir . $version . DIRECTORY_SEPARATOR; + foreach ($section['groups'] as $group => $content) { + $file = $version_dir . 'resources' . DIRECTORY_SEPARATOR . str_replace('\\', '-', ucwords($group)); + $expected = file_get_contents($file . '.apib'); + + $this->assertSame( + $expected, + $content, + sprintf( + 'The compiled resource `%s`, on version %s does not match the expected content.', + $group, + $version + ) + ); + } + + foreach ($section['structures'] as $representation => $content) { + $file = $version_dir . 'representations' . DIRECTORY_SEPARATOR . $representation; + $expected = file_get_contents($file . '.apib'); + + $this->assertSame( + $expected, + $content, + sprintf( + 'The compiled representation `%s`, on version %s does not match the expected content.', + $representation, + $version + ) + ); + } + } + } + + public function testCompilationWithAnExcludedGroup(): void + { + $this->getConfig()->addCompilerGroupExclusion('Movies'); + + $compiler = new ApiBlueprint($this->getConfig()); + $compiled = $compiler->compile(); + + $this->assertSame([ + '1.0', + '1.1', + '1.1.1', + '1.1.2', + '1.1.3' + ], array_keys($compiled)); + + foreach ($compiled as $version => $section) { + $this->assertArrayNotHasKey('Movies', $section['groups']); + $this->assertArrayHasKey('Theaters', $section['groups']); + } + + $this->getConfig()->removeCompilerGroupExclusion('Movies'); + } +} diff --git a/tests/Compiler/Specification/OpenApiTest.php b/tests/Compiler/Specification/OpenApiTest.php new file mode 100644 index 0000000..a20560e --- /dev/null +++ b/tests/Compiler/Specification/OpenApiTest.php @@ -0,0 +1,55 @@ +getConfig()); + $compiled = $compiler->compile(); + + foreach ($compiled as $version => $spec) { + $expected = file_get_contents($control_dir . $version . DIRECTORY_SEPARATOR . 'api.yaml'); + $content = Yaml::dump($spec, PHP_INT_MAX, 2, true); + + $this->assertSame( + $expected, + $content, + sprintf( + 'The compiled version %s does not match the expected content.', + $version + ) + ); + } + } + + public function testCompilationWithAnExcludedGroup(): void + { + $this->getConfig()->addCompilerGroupExclusion('Movies'); + + $compiler = new OpenApi($this->getConfig()); + $compiled = $compiler->compile(); + + $this->assertSame([ + '1.0', + '1.1', + '1.1.1', + '1.1.2', + '1.1.3' + ], array_keys($compiled)); + + foreach ($compiled as $version => $spec) { + $this->assertSame([ + ['name' => 'Theaters'] + ], $spec['tags']); + } + + $this->getConfig()->removeCompilerGroupExclusion('Movies'); + } +} diff --git a/tests/GeneratorTest.php b/tests/CompilerTest.php similarity index 81% rename from tests/GeneratorTest.php rename to tests/CompilerTest.php index 7302638..3908e19 100644 --- a/tests/GeneratorTest.php +++ b/tests/CompilerTest.php @@ -1,20 +1,16 @@ getConfig()); - $generator->generate(); + $compiler = new Compiler($this->getConfig()); + $compiler->compile(); $this->assertSame([ '1.0', @@ -22,56 +18,56 @@ public function testGeneratorParsesAllVersions(): void '1.1.1', '1.1.2', '1.1.3' - ], array_keys($generator->getResources())); + ], array_keys($compiler->getResources())); } - public function testGeneratorButExcludeARepresentation(): void + public function testExcludeARepresentation(): void { $config = $this->getConfig(); $config->addExcludedRepresentation('\Mill\Examples\Showtimes\Representations\Movie'); $version_obj = new Version('1.0', __CLASS__, __METHOD__); - $generator = new Generator($config, $version_obj); - $generator->generate(); + $compiler = new Compiler($config, $version_obj); + $compiler->compile(); $this->assertSame([ '\Mill\Examples\Showtimes\Representations\CodedError', '\Mill\Examples\Showtimes\Representations\Error', '\Mill\Examples\Showtimes\Representations\Person', '\Mill\Examples\Showtimes\Representations\Theater' - ], array_keys($generator->getRepresentations($version_obj->getConstraint()))); + ], array_keys($compiler->getRepresentations($version_obj->getConstraint()))); // Clean up after ourselves. $config->removeExcludedRepresentation('\Mill\Examples\Showtimes\Representations\Movie'); } /** - * @dataProvider providerGeneratorWithVersion + * @dataProvider providerWithVersion * @param string $version * @param bool $private_objects - * @param array|null $capabilities + * @param array|null $vendor_tags * @param array $expected_representations * @param array $expected_resources */ - public function testGeneratorWithVersion( + public function testWithVersion( string $version, bool $private_objects, - ?array $capabilities, + ?array $vendor_tags, array $expected_representations, array $expected_resources ): void { $version_obj = new Version($version, __CLASS__, __METHOD__); - $generator = new Generator($this->getConfig(), $version_obj); - $generator->setLoadPrivateDocs($private_objects); - $generator->setLoadCapabilityDocs($capabilities); - $generator->generate(); + $compiler = new Compiler($this->getConfig(), $version_obj); + $compiler->setLoadPrivateDocs($private_objects); + $compiler->setLoadVendorTagDocs($vendor_tags); + $compiler->compile(); // Verify resources - $resources = $generator->getResources(); + $resources = $compiler->getResources(); $this->assertArrayHasKey($version, $resources); $resources = $resources[$version]; - $this->assertSame($resources, $generator->getResources($version)); + $this->assertSame($resources, $compiler->getResources($version)); foreach ($expected_resources as $group => $data) { $this->assertArrayHasKey($group, $resources); @@ -103,31 +99,28 @@ public function testGeneratorWithVersion( foreach ($actual_resource['actions'] as $identifier => $action) { $this->assertInstanceOf(Documentation::class, $action); - // We don't need to test every facet of the generated MethodDocumentation, because we're doing + // We don't need to test every facet of the compiled MethodDocumentation, because we're doing // that in other tests, we just want to make sure that these actions were grouped and compiled // properly. $annotations = $action->toArray()['annotations']; $expected_action = $expected['actions.data'][$identifier]; $this->assertSame($expected_action['method'], $action->getMethod()); - $this->assertSame($annotations['uri'][0]['path'], $expected_action['uri']); + $this->assertSame($annotations['path'][0]['path'], $expected_action['path']); $this->assertSame( - $annotations['uri'][0]['visible'], - $expected_action['uri.visible'], - $expected_action['uri'] . '::' . $expected_action['method'] + $annotations['path'][0]['visible'], + $expected_action['path.visible'], + $expected_action['path'] . '::' . $expected_action['method'] ); - if ($expected_action['uriSegment'] === false) { + if ($expected_action['pathparam'] === false) { $this->assertArrayNotHasKey( - 'uriSegment', + 'pathparam', $annotations, - $expected_action['uri'] . ' has a segment' + $expected_action['path'] . ' has a path param' ); } else { - $this->assertSame( - $annotations['uriSegment'], - $expected_action['uriSegment'] - ); + $this->assertSame($annotations['pathparam'], $expected_action['pathparam']); } $this->assertSame( @@ -136,7 +129,13 @@ public function testGeneratorWithVersion( $identifier . ' does not have the right parameters.' ); - // Verify that we've generated the right public/private/protected annotations for this action. + $this->assertSame( + $expected_action['queryparams.keys'], + array_keys($action->getQueryParameters()), + $identifier . ' does not have the right query parameters.' + ); + + // Verify that we've compiled the right public/private/protected annotations for this action. ksort($annotations); $this->assertSame( array_keys($expected_action['annotations.sum']), @@ -156,12 +155,12 @@ public function testGeneratorWithVersion( } // Verify representations - $representations = $generator->getRepresentations(); + $representations = $compiler->getRepresentations(); $this->assertArrayHasKey($version, $representations); $representations = $representations[$version]; $this->assertCount(count($expected_representations), $representations); - $this->assertSame($representations, $generator->getRepresentations($version_obj)); + $this->assertSame($representations, $compiler->getRepresentations($version_obj)); foreach ($expected_representations as $name => $data) { $this->assertArrayHasKey($name, $representations); @@ -177,53 +176,58 @@ public function testGeneratorWithVersion( } } - public function providerGeneratorWithVersion(): array + public function providerWithVersion(): array { // Save us the effort of copy and pasting the same base actions over and over. $actions = [ '/movie/+id::GET' => [ - 'uri' => '/movie/+id', + 'path' => '/movie/+id', 'method' => 'GET', - 'uriSegment' => [ + 'pathparam' => [ [ 'description' => 'Movie ID', 'field' => 'id', + 'required' => true, + 'sample_data' => '1234', 'type' => 'integer', - 'uri' => '/movie/+id', - 'values' => false + 'values' => [] ] ], - 'uri.visible' => false, + 'path.visible' => false, 'params.keys' => [], + 'queryparams.keys' => [], 'annotations.sum' => [ - 'return' => 2, - 'throws' => 1, - 'uri' => 1, - 'uriSegment' => 1 + 'error' => 1, + 'path' => 1, + 'pathparam' => 1, + 'return' => 2 ] ], '/movies::GET' => [ - 'uri' => '/movies', + 'path' => '/movies', 'method' => 'GET', - 'uriSegment' => false, - 'uri.visible' => true, - 'params.keys' => [ + 'pathparam' => false, + 'path.visible' => true, + 'params.keys' => [], + 'queryparams.keys' => [ 'location' ], 'annotations.sum' => [ - 'param' => 1, - 'return' => 1, - 'throws' => 1, - 'uri' => 1 + 'error' => 1, + 'path' => 1, + 'queryparam' => 1, + 'return' => 1 ] ], '/movies::POST' => [ - 'uri' => '/movies', + 'path' => '/movies', 'method' => 'POST', - 'uriSegment' => false, - 'uri.visible' => true, + 'pathparam' => false, + 'path.visible' => true, 'params.keys' => [ 'cast', + 'cast.name', + 'cast.role', 'content_rating', 'description', 'director', @@ -233,50 +237,56 @@ public function providerGeneratorWithVersion(): array 'rotten_tomatoes_score', 'runtime' ], + 'queryparams.keys' => [], 'annotations.sum' => [ - 'param' => 9, + 'error' => 2, + 'param' => 11, + 'path' => 1, 'return' => 1, - 'scope' => 1, - 'throws' => 2, - 'uri' => 1 + 'scope' => 1 ] ], '/movies/+id::GET' => [ - 'uri' => '/movies/+id', + 'path' => '/movies/+id', 'method' => 'GET', - 'uriSegment' => [ + 'pathparam' => [ [ 'description' => 'Movie ID', 'field' => 'id', + 'required' => true, + 'sample_data' => '1234', 'type' => 'integer', - 'uri' => '/movies/+id', - 'values' => false + 'values' => [] ] ], - 'uri.visible' => true, + 'path.visible' => true, 'params.keys' => [], + 'queryparams.keys' => [], 'annotations.sum' => [ - 'return' => 2, - 'throws' => 1, - 'uri' => 1, - 'uriSegment' => 1 + 'error' => 1, + 'path' => 1, + 'pathparam' => 1, + 'return' => 2 ] ], '/movies/+id::PATCH' => [ - 'uri' => '/movies/+id', + 'path' => '/movies/+id', 'method' => 'PATCH', - 'uriSegment' => [ + 'pathparam' => [ [ 'description' => 'Movie ID', 'field' => 'id', + 'required' => true, + 'sample_data' => '1234', 'type' => 'integer', - 'uri' => '/movies/+id', - 'values' => false + 'values' => [] ] ], - 'uri.visible' => true, + 'path.visible' => true, 'params.keys' => [ 'cast', + 'cast.name', + 'cast.role', 'content_rating', 'description', 'director', @@ -287,141 +297,153 @@ public function providerGeneratorWithVersion(): array 'runtime', 'trailer' ], + 'queryparams.keys' => [], 'annotations.sum' => [ - 'minVersion' => 1, - 'param' => 10, + 'error' => 3, + 'minversion' => 1, + 'param' => 12, + 'path' => 1, + 'pathparam' => 1, 'return' => 1, - 'scope' => 1, - 'throws' => 3, - 'uri' => 1, - 'uriSegment' => 1 + 'scope' => 1 ] ], '/movies/+id::DELETE' => [ - 'uri' => '/movies/+id', + 'path' => '/movies/+id', 'method' => 'DELETE', - 'uriSegment' => [ + 'pathparam' => [ [ 'description' => 'Movie ID', 'field' => 'id', + 'required' => true, + 'sample_data' => '1234', 'type' => 'integer', - 'uri' => '/movies/+id', - 'values' => false + 'values' => [] ] ], - 'uri.visible' => false, + 'path.visible' => false, 'params.keys' => [], + 'queryparams.keys' => [], 'annotations.sum' => [ - 'capability' => 1, - 'minVersion' => 1, + 'error' => 1, + 'maxversion' => 1, + 'minversion' => 1, + 'path' => 1, + 'pathparam' => 1, 'return' => 1, 'scope' => 1, - 'throws' => 1, - 'uri' => 1, - 'uriSegment' => 1 + 'vendortag' => 1 ] ], '/theaters::GET' => [ - 'uri' => '/theaters', + 'path' => '/theaters', 'method' => 'GET', - 'uriSegment' => false, - 'uri.visible' => true, - 'params.keys' => [ + 'pathparam' => false, + 'path.visible' => true, + 'params.keys' => [], + 'queryparams.keys' => [ 'location' ], 'annotations.sum' => [ - 'param' => 1, - 'return' => 1, - 'throws' => 1, - 'uri' => 1 + 'error' => 1, + 'path' => 1, + 'queryparam' => 1, + 'return' => 1 ] ], '/theaters::POST' => [ - 'uri' => '/theaters', + 'path' => '/theaters', 'method' => 'POST', - 'uriSegment' => false, - 'uri.visible' => true, + 'pathparam' => false, + 'path.visible' => true, 'params.keys' => [ 'address', 'name', 'phone_number' ], + 'queryparams.keys' => [], 'annotations.sum' => [ + 'error' => 1, 'param' => 3, + 'path' => 1, 'return' => 1, - 'scope' => 1, - 'throws' => 1, - 'uri' => 1 + 'scope' => 1 ] ], '/theaters/+id::GET' => [ - 'uri' => '/theaters/+id', + 'path' => '/theaters/+id', 'method' => 'GET', - 'uriSegment' => [ + 'pathparam' => [ [ 'description' => 'Theater ID', 'field' => 'id', + 'required' => true, + 'sample_data' => '1234', 'type' => 'integer', - 'uri' => '/theaters/+id', - 'values' => false + 'values' => [] ] ], - 'uri.visible' => true, + 'path.visible' => true, 'params.keys' => [], + 'queryparams.keys' => [], 'annotations.sum' => [ - 'return' => 2, - 'throws' => 1, - 'uri' => 1, - 'uriSegment' => 1 + 'error' => 1, + 'path' => 1, + 'pathparam' => 1, + 'return' => 2 ] ], '/theaters/+id::PATCH' => [ - 'uri' => '/theaters/+id', + 'path' => '/theaters/+id', 'method' => 'PATCH', - 'uriSegment' => [ + 'pathparam' => [ [ 'description' => 'Theater ID', 'field' => 'id', + 'required' => true, + 'sample_data' => '1234', 'type' => 'integer', - 'uri' => '/theaters/+id', - 'values' => false + 'values' => [] ] ], - 'uri.visible' => true, + 'path.visible' => true, 'params.keys' => [ 'address', 'name', 'phone_number' ], + 'queryparams.keys' => [], 'annotations.sum' => [ + 'error' => 3, 'param' => 3, + 'path' => 1, + 'pathparam' => 1, 'return' => 1, - 'scope' => 1, - 'throws' => 3, - 'uri' => 1, - 'uriSegment' => 1 + 'scope' => 1 ] ], '/theaters/+id::DELETE' => [ - 'uri' => '/theaters/+id', + 'path' => '/theaters/+id', 'method' => 'DELETE', - 'uriSegment' => [ + 'pathparam' => [ [ 'description' => 'Theater ID', 'field' => 'id', + 'required' => true, + 'sample_data' => '1234', 'type' => 'integer', - 'uri' => '/theaters/+id', - 'values' => false + 'values' => [] ] ], - 'uri.visible' => false, + 'path.visible' => false, 'params.keys' => [], + 'queryparams.keys' => [], 'annotations.sum' => [ + 'error' => 1, + 'path' => 1, + 'pathparam' => 1, 'return' => 1, - 'scope' => 1, - 'throws' => 1, - 'uri' => 1, - 'uriSegment' => 1 + 'scope' => 1 ] ] ]; @@ -495,7 +517,7 @@ public function providerGeneratorWithVersion(): array 'version-1.0' => [ 'version' => '1.0', 'private_objects' => true, - 'capabilities' => null, + 'vendor_tags' => null, 'expected.representations' => array_merge($error_representations, [ '\Mill\Examples\Showtimes\Representations\Movie' => $representations['Movie'], '\Mill\Examples\Showtimes\Representations\Person' => $representations['Person'], @@ -544,15 +566,15 @@ function () use ($representations): array { ] ], - // API v1.0 with public-only docs and all capabilities - 'version-1.0-public-docs-with-all-capabilities' => [ + // API v1.0 with public-only docs and all vendor tags. + 'version-1.0-public-docs-with-all-vendor-tags' => [ 'version' => '1.0', 'private_objects' => false, - 'capabilities' => [ - 'BUY_TICKETS', - 'DELETE_CONTENT', - 'FEATURE_FLAG', - 'MOVIE_RATINGS' + 'vendor_tags' => [ + 'tag:BUY_TICKETS', + 'tag:DELETE_CONTENT', + 'tag:FEATURE_FLAG', + 'tag:MOVIE_RATINGS' ], 'expected.representations' => array_merge($error_representations, [ '\Mill\Examples\Showtimes\Representations\Movie' => $representations['Movie'], @@ -604,7 +626,7 @@ function () use ($representations): array { 'version-1.1' => [ 'version' => '1.1', 'private_objects' => true, - 'capabilities' => null, + 'vendor_tags' => null, 'expected.representations' => array_merge($error_representations, [ '\Mill\Examples\Showtimes\Representations\Movie' => call_user_func( function () use ($representations): array { @@ -636,8 +658,8 @@ function () use ($representations): array { '/movies/+id::DELETE' => $actions['/movies/+id::DELETE'], '/movies::GET' => call_user_func(function () use ($actions): array { $action = $actions['/movies::GET']; - $action['params.keys'][] = 'page'; - $action['annotations.sum']['param']++; + $action['queryparams.keys'][] = 'page'; + $action['annotations.sum']['queryparam']++; return $action; }), @@ -675,13 +697,13 @@ function () use ($representations): array { ] ], - // API v1.1 with public-only docs and unmatched capabilities - 'version-1.1-public-docs-with-unmatched-capabilities' => [ + // API v1.1 with public-only docs and unmatched vendor tags. + 'version-1.1-public-docs-with-unmatched-vendor-tags' => [ 'version' => '1.1', 'private_objects' => false, - 'capabilities' => [ - 'BUY_TICKETS', - 'FEATURE_FLAG' + 'vendor_tags' => [ + 'tag:BUY_TICKETS', + 'tag:FEATURE_FLAG' ], 'expected.representations' => array_merge($error_representations, [ '\Mill\Examples\Showtimes\Representations\Movie' => call_user_func( @@ -712,8 +734,8 @@ function () use ($representations): array { '/movies/+id::PATCH' => $actions['/movies/+id::PATCH'], '/movies::GET' => call_user_func(function () use ($actions): array { $action = $actions['/movies::GET']; - $action['params.keys'][] = 'page'; - $action['annotations.sum']['param']++; + $action['queryparams.keys'][] = 'page'; + $action['annotations.sum']['queryparam']++; return $action; }), @@ -750,12 +772,12 @@ function () use ($representations): array { ] ], - // API v1.1 with public-only docs and matched capabilities - 'version-1.1-public-docs-with-matched-capabilities' => [ + // API v1.1 with public-only docs and matched vendor tags. + 'version-1.1-public-docs-with-matched-vendor-tags' => [ 'version' => '1.1', 'private_objects' => false, - 'capabilities' => [ - 'DELETE_CONTENT' + 'vendor_tags' => [ + 'tag:DELETE_CONTENT' ], 'expected.representations' => array_merge($error_representations, [ '\Mill\Examples\Showtimes\Representations\Movie' => call_user_func( @@ -786,8 +808,8 @@ function () use ($representations): array { '/movies/+id::DELETE' => $actions['/movies/+id::DELETE'], '/movies::GET' => call_user_func(function () use ($actions): array { $action = $actions['/movies::GET']; - $action['params.keys'][] = 'page'; - $action['annotations.sum']['param']++; + $action['queryparams.keys'][] = 'page'; + $action['annotations.sum']['queryparam']++; return $action; }), @@ -828,7 +850,7 @@ function () use ($representations): array { 'version-1.1.1' => [ 'version' => '1.1.1', 'private_objects' => true, - 'capabilities' => null, + 'vendor_tags' => null, 'expected.representations' => array_merge($error_representations, [ '\Mill\Examples\Showtimes\Representations\Movie' => call_user_func( function () use ($representations): array { @@ -869,8 +891,8 @@ function () use ($representations): array { '/movies/+id::DELETE' => $actions['/movies/+id::DELETE'], '/movies::GET' => call_user_func(function () use ($actions): array { $action = $actions['/movies::GET']; - $action['params.keys'][] = 'page'; - $action['annotations.sum']['param']++; + $action['queryparams.keys'][] = 'page'; + $action['annotations.sum']['queryparam']++; return $action; }), @@ -912,7 +934,7 @@ function () use ($representations): array { 'version-1.1.1-public-only-documentation' => [ 'version' => '1.1.1', 'private_objects' => true, - 'capabilities' => [], + 'vendor_tags' => [], 'expected.representations' => array_merge($error_representations, [ '\Mill\Examples\Showtimes\Representations\Movie' => call_user_func( function () use ($representations): array { @@ -951,8 +973,8 @@ function () use ($representations): array { }), '/movies::GET' => call_user_func(function () use ($actions): array { $action = $actions['/movies::GET']; - $action['params.keys'][] = 'page'; - $action['annotations.sum']['param']++; + $action['queryparams.keys'][] = 'page'; + $action['annotations.sum']['queryparam']++; return $action; }), diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index 8d57af0..3e03376 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -12,6 +12,31 @@ public function testLoadFromXML(): void $config = $this->getConfig(); $this->assertSame('Mill unit test API, Showtimes', $config->getName()); + $this->assertSame('https://example.com/terms', $config->getTerms()); + + $this->assertSame([ + 'name' => 'Get help!', + 'email' => 'support@example.com', + 'url' => 'https://developer.example.com/help' + ], $config->getContactInformation()); + + $this->assertSame([ + [ + 'name' => 'Developer Docs', + 'url' => 'https://developer.example.com' + ] + ], $config->getExternalDocumentation()); + + $this->assertSame([ + [ + 'url' => 'https://api.example.com', + 'description' => 'Production' + ], + [ + 'url' => 'https://api.example.local', + 'description' => 'Development' + ] + ], $config->getServers()); $this->assertSame('1.0', $config->getFirstApiVersion()); $this->assertSame('1.1.2', $config->getDefaultApiVersion()); @@ -46,21 +71,48 @@ public function testLoadFromXML(): void ], $config->getApiVersions()); $this->assertSame([ - 'FakeExcludeNamespace' - ], $config->getBlueprintNamespaceExcludes()); + 'FakeExcludeGroup' + ], $config->getCompilerGroupExclusions()); $this->assertSame([ - 'BUY_TICKETS', - 'DELETE_CONTENT', - 'FEATURE_FLAG', - 'MOVIE_RATINGS' - ], $config->getCapabilities()); + 'tag:BUY_TICKETS', + 'tag:DELETE_CONTENT', + 'tag:FEATURE_FLAG', + 'tag:MOVIE_RATINGS' + ], $config->getVendorTags()); $this->assertSame([ - 'create', - 'delete', - 'edit', - 'public' + 'bearer' => [ + 'format' => 'bearer' + ], + 'oauth2' => [ + 'authorization_code' => [ + 'authorization_url' => '/oauth/authorize', + 'token_url' => '/oauth/access_token' + ], + 'client_credentials' => [ + 'token_url' => '/oauth/authorize/client' + ] + ] + ], $config->getAuthenticationFlows()); + + $this->assertSame([ + 'create' => [ + 'name' => 'create', + 'description' => 'Create' + ], + 'delete' => [ + 'name' => 'delete', + 'description' => 'Delete' + ], + 'edit' => [ + 'name' => 'edit', + 'description' => 'Edit' + ], + 'public' => [ + 'name' => 'public', + 'description' => 'Public' + ] ], $config->getScopes()); $this->assertSame([ @@ -115,7 +167,7 @@ public function testLoadFromXML(): void $this->assertSame([ 'movie_id' => 'id', 'theater_id' => 'id' - ], $config->getUriSegmentTranslations()); + ], $config->getPathParamTranslations()); } /** @@ -164,26 +216,51 @@ public function testXSDValidation(): void /** * @dataProvider providerLoadFromXMLFailuresOnVariousBadXMLFiles - * @param array $includes * @param array $exception_details * @param string $xml * @throws \Exception */ - public function testLoadFromXMLFailuresOnVariousBadXMLFiles( - array $includes, - array $exception_details, - string $xml - ): void { + public function testLoadFromXMLFailuresOnVariousBadXMLFiles(array $exception_details, string $xml): void + { + /** @var string $provider */ + $provider = $this->getName(); + if (isset($exception_details['exception'])) { $this->expectException($exception_details['exception']); } else { - $this->expectException('\DomainException'); + $this->expectException(\DomainException::class); $this->expectExceptionMessageRegExp($exception_details['regex']); } // Customize the provider XML so we don't need to have a bunch of DRY'd XML everywhere. - $versions = $controllers = $representations = false; - if (in_array('versions', $includes)) { + $info = $servers = $versions = $controllers = $representations = $authentication = false; + if (strpos($provider, 'info.') === false) { + $info = << + + + + + + + + +XML; + } + + if (strpos($provider, 'servers.') === false) { + $servers = << + + + +XML; + } + + if (strpos($provider, 'versions.') === false) { $versions = << @@ -192,7 +269,7 @@ public function testLoadFromXMLFailuresOnVariousBadXMLFiles( XML; } - if (in_array('controllers', $includes)) { + if (strpos($provider, 'controllers.') === false) { $controllers = << @@ -202,7 +279,7 @@ public function testLoadFromXMLFailuresOnVariousBadXMLFiles( XML; } - if (in_array('representations', $includes)) { + if (strpos($provider, 'representations.') === false) { $representations = << @@ -212,12 +289,37 @@ public function testLoadFromXMLFailuresOnVariousBadXMLFiles( XML; } + if (strpos($provider, 'authentication.') === false) { + $authentication = << + + + + + + + + + + + + + + + + +XML; + } + $xml = << - + + $info + $servers $versions $controllers $representations + $authentication $xml XML; @@ -252,7 +354,6 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array * */ 'versions.no-default' => [ - 'includes' => ['controllers', 'representations'], 'exception' => [ 'regex' => '/You must set/' ], @@ -264,7 +365,6 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array ], 'versions.multiple-defaults' => [ - 'includes' => ['controllers', 'representations'], 'exception' => [ 'exception' => \InvalidArgumentException::class, 'regex' => '/Multiple default API versions/' @@ -278,22 +378,19 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array ], /** - * + * * */ - 'generators.blueprint.exclude.invalid' => [ - 'includes' => ['versions', 'controllers', 'representations'], + 'compilers.exclude.invalid' => [ 'exception' => [ - 'regex' => '/invalid Blueprint generator namespace/' + 'regex' => '/invalid compiler group exclusion/' ], 'xml' => << - - - - - - + + + + + XML ], @@ -302,7 +399,6 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array * */ 'controllers.directory.invalid' => [ - 'includes' => ['versions', 'representations'], 'exception' => [ 'exception' => \InvalidArgumentException::class, 'regex' => '/does not exist/' @@ -317,7 +413,6 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array ], 'controllers.none-found' => [ - 'includes' => ['versions', 'representations'], 'exception' => [ 'exception' => \InvalidArgumentException::class, 'regex' => '/requires a set of controllers/' @@ -332,7 +427,6 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array ], 'controllers.class.uncallable' => [ - 'includes' => ['versions', 'representations'], 'exception' => [ 'exception' => \InvalidArgumentException::class, 'regex' => '/could not be called/' @@ -351,7 +445,6 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array * */ 'representations.none-found' => [ - 'includes' => ['versions', 'controllers'], 'exception' => [ 'exception' => \InvalidArgumentException::class, 'regex' => '/requires a set of representations/' @@ -366,7 +459,6 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array ], 'representations.class.missing-method' => [ - 'includes' => ['versions', 'controllers'], 'exception' => [ 'regex' => '/missing a `method`/' ], @@ -380,7 +472,6 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array ], 'representations.class.uncallable' => [ - 'includes' => ['versions', 'controllers'], 'exception' => [ 'exception' => UncallableRepresentationException::class ], @@ -394,7 +485,6 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array ], 'representations.directory.invalid' => [ - 'includes' => ['versions', 'controllers'], 'exception' => [ 'exception' => \InvalidArgumentException::class, 'regex' => '/does not exist/' @@ -409,7 +499,6 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array ], 'representations.error.uncallable' => [ - 'includes' => ['versions', 'controllers'], 'exception' => [ 'exception' => UncallableErrorRepresentationException::class ], @@ -427,7 +516,6 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array ], 'representations.error.missing-method' => [ - 'includes' => ['versions', 'controllers'], 'exception' => [ 'regex' => '/missing a `method`/' ], @@ -449,7 +537,6 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array * */ 'parametertokens.invalid' => [ - 'includes' => ['versions', 'controllers', 'representations'], 'exception' => [ 'regex' => '/invalid parameter token/' ], @@ -461,20 +548,19 @@ public function providerLoadFromXMLFailuresOnVariousBadXMLFiles(): array ], /** - * + * * */ - 'urisegments.invalid' => [ - 'includes' => ['versions', 'controllers', 'representations'], + 'pathparams.invalid' => [ 'exception' => [ 'regex' => '/invalid translation text/' ], 'xml' => << + - + XML ] ]; diff --git a/tests/Generator/BlueprintTest.php b/tests/Generator/BlueprintTest.php deleted file mode 100644 index f33fe01..0000000 --- a/tests/Generator/BlueprintTest.php +++ /dev/null @@ -1,73 +0,0 @@ -getConfig()); - $generated = $blueprint->generate(); - - foreach ($generated as $version => $section) { - foreach ($section['groups'] as $group => $content) { - $file = $dir . $version . self::DS . 'resources' . self::DS . str_replace('\\', '-', ucwords($group)); - $expected = file_get_contents($file . '.apib'); - - $this->assertSame( - $expected, - $content, - sprintf( - 'The generated resource `%s`, on version %s does not match the expected content.', - $group, - $version - ) - ); - } - - foreach ($section['structures'] as $representation => $content) { - $file = $dir . $version . self::DS . 'representations' . self::DS . $representation; - $expected = file_get_contents($file . '.apib'); - - $this->assertSame( - $expected, - $content, - sprintf( - 'The generated representation `%s`, on version %s does not match the expected content.', - $representation, - $version - ) - ); - } - } - } - - public function testGenerationWithAnExcludedGroup(): void - { - $this->getConfig()->addBlueprintNamespaceExclude('Movies'); - - $blueprint = new Blueprint($this->getConfig()); - $generated = $blueprint->generate(); - - $this->assertSame([ - '1.0', - '1.1', - '1.1.1', - '1.1.2', - '1.1.3' - ], array_keys($generated)); - - foreach ($generated as $version => $section) { - $this->assertArrayNotHasKey('Movies', $section['groups']); - $this->assertArrayHasKey('Theaters', $section['groups']); - } - - $this->getConfig()->removeBlueprintNamespaceExclude('Movies'); - } -} diff --git a/tests/Parser/Annotations/CapabilityAnnotationTest.php b/tests/Parser/Annotations/CapabilityAnnotationTest.php deleted file mode 100644 index 6155f6f..0000000 --- a/tests/Parser/Annotations/CapabilityAnnotationTest.php +++ /dev/null @@ -1,90 +0,0 @@ -process(); - - $this->assertAnnotation($annotation, $expected); - } - - /** - * @dataProvider providerAnnotation - * @param string $content - * @param array $expected - */ - public function testHydrate(string $content, array $expected): void - { - $annotation = CapabilityAnnotation::hydrate(array_merge( - $expected, - [ - 'class' => __CLASS__, - 'method' => __METHOD__ - ] - )); - - $this->assertAnnotation($annotation, $expected); - } - - private function assertAnnotation(CapabilityAnnotation $annotation, array $expected): void - { - $this->assertFalse($annotation->requiresVisibilityDecorator()); - $this->assertFalse($annotation->supportsVersioning()); - $this->assertFalse($annotation->supportsDeprecation()); - $this->assertFalse($annotation->supportsAliasing()); - - $this->assertSame($expected, $annotation->toArray()); - $this->assertSame($expected['capability'], $annotation->getCapability()); - $this->assertFalse($annotation->getVersion()); - $this->assertEmpty($annotation->getAliases()); - } - - public function providerAnnotation(): array - { - return [ - '_complete' => [ - 'content' => 'BUY_TICKETS', - 'expected' => [ - 'capability' => 'BUY_TICKETS' - ] - ] - ]; - } - - public function providerAnnotationFailsOnInvalidContent(): array - { - return [ - 'missing-capability' => [ - 'annotation' => CapabilityAnnotation::class, - 'content' => '', - 'expected.exception' => MissingRequiredFieldException::class, - 'expected.exception.asserts' => [ - 'getRequiredField' => 'capability', - 'getAnnotation' => 'capability', - 'getDocblock' => '', - 'getValues' => [] - ] - ], - 'capability-was-not-configured' => [ - 'annotation' => CapabilityAnnotation::class, - 'content' => 'UnconfiguredCapability', - 'expected.exception' => InvalidCapabilitySuppliedException::class, - 'expected.exception.asserts' => [ - 'getCapability' => 'UnconfiguredCapability' - ] - ] - ]; - } -} diff --git a/tests/Parser/Annotations/ContentTypeAnnotationTest.php b/tests/Parser/Annotations/ContentTypeAnnotationTest.php index 91e99d9..80f68ba 100644 --- a/tests/Parser/Annotations/ContentTypeAnnotationTest.php +++ b/tests/Parser/Annotations/ContentTypeAnnotationTest.php @@ -21,35 +21,16 @@ public function testAnnotation(string $content, ?Version $version, array $expect $this->assertAnnotation($annotation, $expected); } - /** - * @dataProvider providerAnnotation - * @param string $content - * @param Version|null $version - * @param array $expected - */ - public function testHydrate(string $content, ?Version $version, array $expected): void - { - $annotation = ContentTypeAnnotation::hydrate(array_merge( - $expected, - [ - 'class' => __CLASS__, - 'method' => __METHOD__ - ] - ), $version); - - $this->assertAnnotation($annotation, $expected); - } - private function assertAnnotation(ContentTypeAnnotation $annotation, array $expected): void { - $this->assertFalse($annotation->requiresVisibilityDecorator()); - $this->assertTrue($annotation->supportsVersioning()); $this->assertFalse($annotation->supportsDeprecation()); - $this->assertFalse($annotation->supportsAliasing()); + $this->assertTrue($annotation->supportsVersioning()); + $this->assertFalse($annotation->supportsVendorTags()); + $this->assertFalse($annotation->requiresVisibilityDecorator()); $this->assertSame($expected, $annotation->toArray()); $this->assertSame($expected['content_type'], $annotation->getContentType()); - $this->assertFalse($annotation->getCapability()); + $this->assertEmpty($annotation->getVendorTags()); if ($expected['version']) { $this->assertInstanceOf(Version::class, $annotation->getVersion()); diff --git a/tests/Parser/Annotations/DataAnnotationTest.php b/tests/Parser/Annotations/DataAnnotationTest.php index 89cd4a2..e6af898 100644 --- a/tests/Parser/Annotations/DataAnnotationTest.php +++ b/tests/Parser/Annotations/DataAnnotationTest.php @@ -3,8 +3,8 @@ use Mill\Exceptions\Annotations\UnsupportedTypeException; use Mill\Exceptions\Representation\RestrictedFieldNameException; -use Mill\Parser\Annotations\CapabilityAnnotation; use Mill\Parser\Annotations\DataAnnotation; +use Mill\Parser\Annotations\VendorTagAnnotation; use Mill\Parser\Version; class DataAnnotationTest extends AnnotationTest @@ -21,39 +21,25 @@ public function testAnnotation(string $content, ?Version $version, array $expect $this->assertAnnotation($annotation, $expected); } - /** - * @dataProvider providerAnnotation - * @param string $content - * @param Version|null $version - * @param array $expected - */ - public function testHydrate(string $content, ?Version $version, array $expected): void - { - $annotation = DataAnnotation::hydrate(array_merge( - $expected, - [ - 'class' => __CLASS__, - 'method' => __METHOD__ - ] - ), $version); - - $this->assertAnnotation($annotation, $expected); - } - private function assertAnnotation(DataAnnotation $annotation, array $expected): void { - $this->assertFalse($annotation->requiresVisibilityDecorator()); - $this->assertTrue($annotation->supportsVersioning()); $this->assertFalse($annotation->supportsDeprecation()); + $this->assertTrue($annotation->supportsVersioning()); + $this->assertTrue($annotation->supportsVendorTags()); + $this->assertFalse($annotation->requiresVisibilityDecorator()); $this->assertSame($expected['identifier'], $annotation->getIdentifier()); $this->assertSame($expected, $annotation->toArray()); - if (is_string($expected['capability'])) { - $this->assertInstanceOf(CapabilityAnnotation::class, $annotation->getCapability()); - } else { - $this->assertFalse($annotation->getCapability()); - } + $this->assertSame( + $expected['vendor_tags'], + array_map( + function (VendorTagAnnotation $tag): string { + return $tag->getVendorTag(); + }, + $annotation->getVendorTags() + ) + ); if ($expected['version']) { $this->assertInstanceOf(Version::class, $annotation->getVersion()); @@ -65,13 +51,57 @@ private function assertAnnotation(DataAnnotation $annotation, array $expected): public function providerAnnotation(): array { return [ + '_complete' => [ + 'content' => '/** + * @api-data content_rating `G` (enum, nullable, tag:MOVIE_RATINGS) - MPAA rating + * + Members + * - `G` + * - `PG` + * - `PG-13` + * - `R` + * - `NC-17` + * - `X` + * - `NR` + * - `UR` + * @api-version 1.0 + * @api-scope public + *', + 'version' => new Version('1.0', __CLASS__, __METHOD__), + 'expected' => [ + 'description' => 'MPAA rating', + 'identifier' => 'content_rating', + 'nullable' => true, + 'sample_data' => 'G', + 'scopes' => [ + [ + 'description' => false, + 'scope' => 'public' + ] + ], + 'subtype' => false, + 'type' => 'enum', + 'values' => [ + 'G' => '', + 'NC-17' => '', + 'NR' => '', + 'PG' => '', + 'PG-13' => '', + 'R' => '', + 'UR' => '', + 'X' => '' + ], + 'vendor_tags' => [ + 'tag:MOVIE_RATINGS' + ], + 'version' => '1.0' + ] + ], 'bare' => [ 'content' => '/** * @api-data content_rating (string) - MPAA rating */', 'version' => null, 'expected' => [ - 'capability' => false, 'description' => 'MPAA rating', 'identifier' => 'content_rating', 'nullable' => false, @@ -79,7 +109,8 @@ public function providerAnnotation(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], @@ -90,7 +121,6 @@ public function providerAnnotation(): array */', 'version' => new Version('1.0', __CLASS__, __METHOD__), 'expected' => [ - 'capability' => false, 'description' => 'MPAA rating', 'identifier' => 'content_rating', 'nullable' => false, @@ -98,7 +128,8 @@ public function providerAnnotation(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => '1.0' ] ], @@ -108,7 +139,6 @@ public function providerAnnotation(): array */', 'version' => null, 'expected' => [ - 'capability' => false, 'description' => 'URL to purchase tickets', 'identifier' => 'tickets.url', 'nullable' => true, @@ -116,25 +146,8 @@ public function providerAnnotation(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, - 'version' => false - ] - ], - 'capability' => [ - 'content' => '/** - * @api-data tickets.url (string, BUY_TICKETS) - URL to purchase tickets - */', - 'version' => null, - 'expected' => [ - 'capability' => 'BUY_TICKETS', - 'description' => 'URL to purchase tickets', - 'identifier' => 'tickets.url', - 'nullable' => false, - 'sample_data' => false, - 'scopes' => [], - 'subtype' => false, - 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], @@ -153,7 +166,6 @@ public function providerAnnotation(): array */', 'version' => null, 'expected' => [ - 'capability' => false, 'description' => 'MPAA rating', 'identifier' => 'content_rating', 'nullable' => false, @@ -171,6 +183,7 @@ public function providerAnnotation(): array 'UR' => '', 'X' => '' ], + 'vendor_tags' => [], 'version' => false ] ], @@ -181,7 +194,6 @@ public function providerAnnotation(): array */', 'version' => null, 'expected' => [ - 'capability' => false, 'description' => 'URL to purchase tickets', 'identifier' => 'tickets.url', 'nullable' => false, @@ -194,51 +206,29 @@ public function providerAnnotation(): array ], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], - '_complete' => [ + 'vendor-tag' => [ 'content' => '/** - * @api-data content_rating `G` (enum, nullable, MOVIE_RATINGS) - MPAA rating - * + Members - * - `G` - * - `PG` - * - `PG-13` - * - `R` - * - `NC-17` - * - `X` - * - `NR` - * - `UR` - * @api-version 1.0 - * @api-scope public - *', - 'version' => new Version('1.0', __CLASS__, __METHOD__), + * @api-data tickets.url (string, tag:BUY_TICKETS) - URL to purchase tickets + */', + 'version' => null, 'expected' => [ - 'capability' => 'MOVIE_RATINGS', - 'description' => 'MPAA rating', - 'identifier' => 'content_rating', - 'nullable' => true, - 'sample_data' => 'G', - 'scopes' => [ - [ - 'description' => false, - 'scope' => 'public' - ] - ], + 'description' => 'URL to purchase tickets', + 'identifier' => 'tickets.url', + 'nullable' => false, + 'sample_data' => false, + 'scopes' => [], 'subtype' => false, - 'type' => 'enum', - 'values' => [ - 'G' => '', - 'NC-17' => '', - 'NR' => '', - 'PG' => '', - 'PG-13' => '', - 'R' => '', - 'UR' => '', - 'X' => '' + 'type' => 'string', + 'values' => [], + 'vendor_tags' => [ + 'tag:BUY_TICKETS' ], - 'version' => '1.0' + 'version' => false ] ], 'zeroed-out-sample_data' => [ @@ -247,7 +237,6 @@ public function providerAnnotation(): array */', 'version' => null, 'expected' => [ - 'capability' => false, 'description' => 'Is this user a staff member?', 'identifier' => 'is_staff', 'nullable' => false, @@ -255,7 +244,8 @@ public function providerAnnotation(): array 'scopes' => [], 'subtype' => false, 'type' => 'boolean', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], @@ -278,7 +268,7 @@ public function providerAnnotationFailsOnInvalidContent(): array 'restricted-field-name-is-detected' => [ 'annotation' => DataAnnotation::class, 'content' => '/** - * @api-data __FIELD_DATA__ (string) - This is an restricted field name + * @api-data __NESTED_DATA__ (string) - This is an restricted field name */', 'expected.exception' => RestrictedFieldNameException::class, 'expected.exception.asserts' => [] diff --git a/tests/Parser/Annotations/DescriptionAnnotationTest.php b/tests/Parser/Annotations/DescriptionAnnotationTest.php index 078a35e..78a4ff8 100644 --- a/tests/Parser/Annotations/DescriptionAnnotationTest.php +++ b/tests/Parser/Annotations/DescriptionAnnotationTest.php @@ -19,33 +19,15 @@ public function testAnnotation(string $content, array $expected): void $this->assertAnnotation($annotation, $expected); } - /** - * @dataProvider providerAnnotation - * @param string $content - * @param array $expected - */ - public function testHydrate(string $content, array $expected): void - { - $annotation = DescriptionAnnotation::hydrate(array_merge( - $expected, - [ - 'class' => __CLASS__, - 'method' => __METHOD__ - ] - )); - - $this->assertAnnotation($annotation, $expected); - } - private function assertAnnotation(DescriptionAnnotation $annotation, array $expected): void { - $this->assertFalse($annotation->requiresVisibilityDecorator()); - $this->assertFalse($annotation->supportsVersioning()); $this->assertFalse($annotation->supportsDeprecation()); - $this->assertFalse($annotation->supportsAliasing()); + $this->assertFalse($annotation->supportsVersioning()); + $this->assertFalse($annotation->supportsVendorTags()); + $this->assertFalse($annotation->requiresVisibilityDecorator()); $this->assertSame($expected, $annotation->toArray()); - $this->assertFalse($annotation->getCapability()); + $this->assertEmpty($annotation->getVendorTags()); $this->assertFalse($annotation->getVersion()); $this->assertEmpty($annotation->getAliases()); } diff --git a/tests/Parser/Annotations/ThrowsAnnotationTest.php b/tests/Parser/Annotations/ErrorAnnotationTest.php similarity index 59% rename from tests/Parser/Annotations/ThrowsAnnotationTest.php rename to tests/Parser/Annotations/ErrorAnnotationTest.php index 03c67c6..6a17b36 100644 --- a/tests/Parser/Annotations/ThrowsAnnotationTest.php +++ b/tests/Parser/Annotations/ErrorAnnotationTest.php @@ -1,16 +1,16 @@ process(); $annotation->setVisibility($visible); $this->assertAnnotation($annotation, $expected); } - /** - * @dataProvider providerAnnotation - * @param string $content - * @param Version|null $version - * @param bool $visible - * @param array $expected - */ - public function testHydrate(string $content, $version, bool $visible, array $expected): void + private function assertAnnotation(ErrorAnnotation $annotation, array $expected): void { - /** @var ThrowsAnnotation $annotation */ - $annotation = ThrowsAnnotation::hydrate(array_merge( - $expected, - [ - 'class' => __CLASS__, - 'method' => __METHOD__ - ] - ), $version); - - $this->assertAnnotation($annotation, $expected); - } - - private function assertAnnotation(ThrowsAnnotation $annotation, array $expected): void - { - $this->assertTrue($annotation->requiresVisibilityDecorator()); - $this->assertTrue($annotation->supportsVersioning()); $this->assertFalse($annotation->supportsDeprecation()); - $this->assertFalse($annotation->supportsAliasing()); + $this->assertTrue($annotation->supportsVersioning()); + $this->assertTrue($annotation->supportsVendorTags()); + $this->assertTrue($annotation->requiresVisibilityDecorator()); $this->assertSame($expected, $annotation->toArray()); $this->assertSame($expected['description'], $annotation->getDescription()); @@ -62,11 +41,15 @@ private function assertAnnotation(ThrowsAnnotation $annotation, array $expected) $this->assertSame($expected['representation'], $annotation->getRepresentation()); $this->assertSame($expected['error_code'], $annotation->getErrorCode()); - if (is_string($expected['capability'])) { - $this->assertInstanceOf(CapabilityAnnotation::class, $annotation->getCapability()); - } else { - $this->assertFalse($annotation->getCapability()); - } + $this->assertSame( + $expected['vendor_tags'], + array_map( + function (VendorTagAnnotation $tag): string { + return $tag->getVendorTag(); + }, + $annotation->getVendorTags() + ) + ); if ($expected['version']) { $this->assertInstanceOf(Version::class, $annotation->getVersion()); @@ -81,192 +64,197 @@ public function providerAnnotation(): array { return [ 'bare' => [ - 'content' => '{404} \Mill\Examples\Showtimes\Representations\Error If the movie could not be found.', - 'version' => null, - 'visible' => true, - 'expected' => [ - 'capability' => false, - 'description' => 'If the movie could not be found.', - 'error_code' => false, - 'http_code' => '404 Not Found', - 'representation' => '\Mill\Examples\Showtimes\Representations\Error', - 'version' => false, - 'visible' => true - ] - ], - 'capability' => [ - 'content' => '{404} \Mill\Examples\Showtimes\Representations\Error +BUY_TICKETS+ If the movie could ' . - 'not be found.', + 'content' => '404 (\Mill\Examples\Showtimes\Representations\Error) - If the movie could not be found.', 'version' => null, 'visible' => true, 'expected' => [ - 'capability' => 'BUY_TICKETS', 'description' => 'If the movie could not be found.', 'error_code' => false, 'http_code' => '404 Not Found', 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], 'version' => false, 'visible' => true ] ], 'description' => [ - 'content' => '{400} \Mill\Examples\Showtimes\Representations\Error If an unknown error occurred.', + 'content' => '400 (\Mill\Examples\Showtimes\Representations\Error) - If an unknown error occurred.', 'version' => null, 'visible' => true, 'expected' => [ - 'capability' => false, 'description' => 'If an unknown error occurred.', 'error_code' => false, 'http_code' => '400 Bad Request', 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], 'version' => false, 'visible' => true ] ], 'description.has_parenthesis' => [ - 'content' => '{403} \Mill\Examples\Showtimes\Representations\Error This is a description with a ' . + 'content' => '403 (\Mill\Examples\Showtimes\Representations\Error) - This is a description with a ' . '(parenthesis of something).', 'version' => null, 'visible' => true, 'expected' => [ - 'capability' => false, 'description' => 'This is a description with a (parenthesis of something).', 'error_code' => false, 'http_code' => '403 Forbidden', 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], 'version' => false, 'visible' => true ] ], - 'description.throw_type' => [ - 'content' => '{404} \Mill\Examples\Showtimes\Representations\Error {movie}', + 'description.error_type' => [ + 'content' => '404 (\Mill\Examples\Showtimes\Representations\Error) - {movie}', 'version' => null, 'visible' => true, 'expected' => [ - 'capability' => false, 'description' => 'If movie was not found.', 'error_code' => false, 'http_code' => '404 Not Found', 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], 'version' => false, 'visible' => true ] ], - 'description.throw_type.subthrow_type' => [ - 'content' => '{404} \Mill\Examples\Showtimes\Representations\Error {movie,theater}', + 'description.error_type.suberror_type' => [ + 'content' => '404 (\Mill\Examples\Showtimes\Representations\Error) - {movie,theater}', 'version' => null, 'visible' => true, 'expected' => [ - 'capability' => false, 'description' => 'If movie was not found in the theater.', 'error_code' => false, 'http_code' => '404 Not Found', 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], 'version' => false, 'visible' => true ] ], 'error_code' => [ - 'content' => '{403} \Mill\Examples\Showtimes\Representations\CodedError ' . - '(Mill\Examples\Showtimes\Representations\CodedError::DISALLOWED) If the user is not allowed to ' . - 'edit that movie.', + 'content' => '403 (\Mill\Examples\Showtimes\Representations\CodedError<666>) - If the user is not ' . + 'allowed to edit that movie.', 'version' => null, 'visible' => true, 'expected' => [ - 'capability' => false, 'description' => 'If the user is not allowed to edit that movie.', 'error_code' => '666', 'http_code' => '403 Forbidden', 'representation' => '\Mill\Examples\Showtimes\Representations\CodedError', + 'vendor_tags' => [], 'version' => false, 'visible' => true ] ], 'error_code.numerical' => [ - 'content' => '{403} \Mill\Examples\Showtimes\Representations\CodedError (1337) If something cool ' . + 'content' => '403 (\Mill\Examples\Showtimes\Representations\CodedError<1337>) - If something cool ' . 'happened.', 'version' => null, 'visible' => true, 'expected' => [ - 'capability' => false, 'description' => 'If something cool happened.', 'error_code' => '1337', 'http_code' => '403 Forbidden', 'representation' => '\Mill\Examples\Showtimes\Representations\CodedError', + 'vendor_tags' => [], 'version' => false, 'visible' => true ] ], 'private' => [ - 'content' => '{404} \Mill\Examples\Showtimes\Representations\Error {movie}', + 'content' => '404 (\Mill\Examples\Showtimes\Representations\Error) - {movie}', 'version' => null, 'visible' => false, 'expected' => [ - 'capability' => false, 'description' => 'If movie was not found.', 'error_code' => false, 'http_code' => '404 Not Found', 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], 'version' => false, 'visible' => false ] ], + 'vendor-tag' => [ + 'content' => '404 (\Mill\Examples\Showtimes\Representations\Error, tag:BUY_TICKETS) - If the movie ' . + 'could not be found.', + 'version' => null, + 'visible' => true, + 'expected' => [ + 'description' => 'If the movie could not be found.', + 'error_code' => false, + 'http_code' => '404 Not Found', + 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [ + 'tag:BUY_TICKETS' + ], + 'version' => false, + 'visible' => true + ] + ], 'versioned' => [ - 'content' => '{404} \Mill\Examples\Showtimes\Representations\Error {movie}', + 'content' => '404 (\Mill\Examples\Showtimes\Representations\Error) - {movie}', 'version' => new Version('1.1 - 1.2', __CLASS__, __METHOD__), 'visible' => false, 'expected' => [ - 'capability' => false, 'description' => 'If movie was not found.', 'error_code' => false, 'http_code' => '404 Not Found', 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], 'version' => '1.1 - 1.2', 'visible' => false ] ], '_complete.description' => [ - 'content' => '{404} \Mill\Examples\Showtimes\Representations\Error +BUY_TICKETS+ ' . - 'If the tickets URL does not exist.', + 'content' => '404 (\Mill\Examples\Showtimes\Representations\Error, tag:BUY_TICKETS) - If the tickets ' . + 'URL does not exist.', 'version' => null, 'visible' => true, 'expected' => [ - 'capability' => 'BUY_TICKETS', 'description' => 'If the tickets URL does not exist.', 'error_code' => false, 'http_code' => '404 Not Found', 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [ + 'tag:BUY_TICKETS' + ], 'version' => false, 'visible' => true ] ], '_complete.error_code' => [ - 'content' => '{404} \Mill\Examples\Showtimes\Representations\CodedError ' . - '(\Mill\Examples\Showtimes\Representations\CodedError::DISALLOWED) +BUY_TICKETS+ ' . + 'content' => '404 (\Mill\Examples\Showtimes\Representations\CodedError<666>, tag:BUY_TICKETS) - ' . '{movie,theater}', 'version' => null, 'visible' => true, 'expected' => [ - 'capability' => 'BUY_TICKETS', 'description' => 'If movie was not found in the theater.', 'error_code' => '666', 'http_code' => '404 Not Found', 'representation' => '\Mill\Examples\Showtimes\Representations\CodedError', + 'vendor_tags' => [ + 'tag:BUY_TICKETS' + ], 'version' => false, 'visible' => true ] ], '_complete.type_subtype' => [ - 'content' => '{404} \Mill\Examples\Showtimes\Representations\Error +BUY_TICKETS+ ' . - '{movie,theater}', + 'content' => '404 (\Mill\Examples\Showtimes\Representations\Error, tag:BUY_TICKETS) - {movie,theater}', 'version' => null, 'visible' => true, 'expected' => [ - 'capability' => 'BUY_TICKETS', 'description' => 'If movie was not found in the theater.', 'error_code' => false, 'http_code' => '404 Not Found', 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [ + 'tag:BUY_TICKETS' + ], 'version' => false, 'visible' => true ] @@ -277,65 +265,20 @@ public function providerAnnotation(): array public function providerAnnotationFailsOnInvalidContent(): array { return [ - 'missing-http-code' => [ - 'annotation' => ThrowsAnnotation::class, - 'content' => '', - 'expected.exception' => MissingRequiredFieldException::class, - 'expected.exception.asserts' => [ - 'getRequiredField' => 'http_code', - 'getAnnotation' => 'throws', - 'getDocblock' => '', - 'getValues' => [] - ] - ], - 'missing-representation' => [ - 'annotation' => ThrowsAnnotation::class, - 'content' => '{404} \Mill\Examples\Showtimes\Representations\Error', - 'expected.exception' => MissingRequiredFieldException::class, - 'expected.exception.asserts' => [ - 'getRequiredField' => 'description', - 'getAnnotation' => 'throws', - 'getDocblock' => '{404} \Mill\Examples\Showtimes\Representations\Error', - 'getValues' => [] - ] - ], - 'missing-description' => [ - 'annotation' => ThrowsAnnotation::class, - 'content' => '{404}', - 'expected.exception' => MissingRequiredFieldException::class, - 'expected.exception.asserts' => [ - 'getRequiredField' => 'representation', - 'getAnnotation' => 'throws', - 'getDocblock' => '{404}', - 'getValues' => [] - ] - ], 'representation-is-unknown' => [ - 'annotation' => ThrowsAnnotation::class, - 'content' => '{404} \UnknownRepresentation', + 'annotation' => ErrorAnnotation::class, + 'content' => '404 (\UnknownRepresentation) - For some reason.', 'expected.exception' => UnknownErrorRepresentationException::class, 'expected.exception.asserts' => [ 'getRequiredField' => null, 'getAnnotation' => null, - 'getDocblock' => '\UnknownRepresentation', - 'getValues' => [] - ] - ], - 'error-code-is-uncallable' => [ - 'annotation' => ThrowsAnnotation::class, - 'content' => '{404} \Mill\Examples\Showtimes\Representations\CodedError (\Uncallable::CONSTANT)', - 'expected.exception' => UncallableErrorCodeException::class, - 'expected.exception.asserts' => [ - 'getRequiredField' => null, - 'getAnnotation' => null, - 'getDocblock' => '{404} \Mill\Examples\Showtimes\Representations\CodedError ' . - '(\Uncallable::CONSTANT)', + 'getDocblock' => '404 (\UnknownRepresentation) - For some reason.', 'getValues' => [] ] ], 'error-code-is-required-but-missing' => [ - 'annotation' => ThrowsAnnotation::class, - 'content' => '{403} \Mill\Examples\Showtimes\Representations\CodedError', + 'annotation' => ErrorAnnotation::class, + 'content' => '403 (\Mill\Examples\Showtimes\Representations\CodedError) - For some reason.', 'expected.exception' => MissingRepresentationErrorCodeException::class, 'expected.exception.asserts' => [ 'getRequiredField' => null, @@ -345,13 +288,13 @@ public function providerAnnotationFailsOnInvalidContent(): array ] ], 'http-code-is-invalid' => [ - 'annotation' => ThrowsAnnotation::class, - 'content' => '{440} \Mill\Examples\Showtimes\Representations\Error', + 'annotation' => ErrorAnnotation::class, + 'content' => '440 (\Mill\Examples\Showtimes\Representations\Error) - For some reason.', 'expected.exception' => UnknownReturnCodeException::class, 'expected.exception.asserts' => [ 'getRequiredField' => null, 'getAnnotation' => null, - 'getDocblock' => '{440} \Mill\Examples\Showtimes\Representations\Error', + 'getDocblock' => '440 (\Mill\Examples\Showtimes\Representations\Error) - For some reason.', 'getValues' => [] ] ] diff --git a/tests/Parser/Annotations/GroupAnnotationTest.php b/tests/Parser/Annotations/GroupAnnotationTest.php new file mode 100644 index 0000000..e7942e3 --- /dev/null +++ b/tests/Parser/Annotations/GroupAnnotationTest.php @@ -0,0 +1,63 @@ +process(); + + $this->assertAnnotation($annotation, $expected); + } + + private function assertAnnotation(GroupAnnotation $annotation, array $expected): void + { + $this->assertFalse($annotation->supportsDeprecation()); + $this->assertFalse($annotation->supportsVersioning()); + $this->assertFalse($annotation->supportsVendorTags()); + $this->assertFalse($annotation->requiresVisibilityDecorator()); + + $this->assertSame($expected, $annotation->toArray()); + $this->assertEmpty($annotation->getVendorTags()); + $this->assertFalse($annotation->getVersion()); + $this->assertEmpty($annotation->getAliases()); + } + + public function providerAnnotation(): array + { + return [ + '_complete' => [ + 'content' => 'Movies\Coming Soon', + 'expected' => [ + 'group' => 'Movies\Coming Soon' + ] + ] + ]; + } + + public function providerAnnotationFailsOnInvalidContent(): array + { + return [ + 'missing-group' => [ + 'annotation' => GroupAnnotation::class, + 'content' => '', + 'expected.exception' => MissingRequiredFieldException::class, + 'expected.exception.asserts' => [ + 'getRequiredField' => 'group', + 'getAnnotation' => 'group', + 'getDocblock' => '', + 'getValues' => [] + ] + ] + ]; + } +} diff --git a/tests/Parser/Annotations/LabelAnnotationTest.php b/tests/Parser/Annotations/LabelAnnotationTest.php index bbcfa34..f874adc 100644 --- a/tests/Parser/Annotations/LabelAnnotationTest.php +++ b/tests/Parser/Annotations/LabelAnnotationTest.php @@ -19,33 +19,15 @@ public function testAnnotation(string $content, array $expected): void $this->assertAnnotation($annotation, $expected); } - /** - * @dataProvider providerAnnotation - * @param string $content - * @param array $expected - */ - public function testHydrate(string $content, array $expected): void - { - $annotation = LabelAnnotation::hydrate(array_merge( - $expected, - [ - 'class' => __CLASS__, - 'method' => __METHOD__ - ] - )); - - $this->assertAnnotation($annotation, $expected); - } - private function assertAnnotation(LabelAnnotation $annotation, array $expected): void { - $this->assertFalse($annotation->requiresVisibilityDecorator()); - $this->assertFalse($annotation->supportsVersioning()); $this->assertFalse($annotation->supportsDeprecation()); - $this->assertFalse($annotation->supportsAliasing()); + $this->assertFalse($annotation->supportsVersioning()); + $this->assertFalse($annotation->supportsVendorTags()); + $this->assertFalse($annotation->requiresVisibilityDecorator()); $this->assertSame($expected, $annotation->toArray()); - $this->assertFalse($annotation->getCapability()); + $this->assertEmpty($annotation->getVendorTags()); $this->assertFalse($annotation->getVersion()); $this->assertEmpty($annotation->getAliases()); } diff --git a/tests/Parser/Annotations/MaxVersionAnnotationTest.php b/tests/Parser/Annotations/MaxVersionAnnotationTest.php new file mode 100644 index 0000000..86f9647 --- /dev/null +++ b/tests/Parser/Annotations/MaxVersionAnnotationTest.php @@ -0,0 +1,74 @@ +process(); + + $this->assertAnnotation($annotation, $expected); + } + + private function assertAnnotation(MaxVersionAnnotation $annotation, array $expected): void + { + $this->assertFalse($annotation->supportsDeprecation()); + $this->assertFalse($annotation->supportsVersioning()); + $this->assertFalse($annotation->supportsVendorTags()); + $this->assertFalse($annotation->requiresVisibilityDecorator()); + + $this->assertSame($expected, $annotation->toArray()); + $this->assertEmpty($annotation->getVendorTags()); + $this->assertFalse($annotation->getVersion()); + $this->assertEmpty($annotation->getAliases()); + } + + public function providerAnnotation(): array + { + return [ + '_complete' => [ + 'content' => '1.2', + 'expected' => [ + 'maximum_version' => '1.2' + ] + ] + ]; + } + + public function providerAnnotationFailsOnInvalidContent(): array + { + return [ + 'does-not-have-an-absolute-version' => [ + 'annotation' => MaxVersionAnnotation::class, + 'content' => '~1.2', + 'expected.exception' => AbsoluteVersionException::class, + 'expected.exception.asserts' => [ + 'getRequiredField' => null, + 'getAnnotation' => '~1.2', + 'getDocblock' => null, + 'getValues' => [] + ] + ], + 'missing-maximum-version' => [ + 'annotation' => MaxVersionAnnotation::class, + 'content' => '', + 'expected.exception' => UnrecognizedSchemaException::class, + 'expected.exception.asserts' => [ + 'getVersion' => '', + 'getValidationMessage' => 'The supplied version, ``, has an unrecognized schema. Please consult ' . + 'the versioning documentation.' + ] + ] + ]; + } +} diff --git a/tests/Parser/Annotations/MinVersionAnnotationTest.php b/tests/Parser/Annotations/MinVersionAnnotationTest.php index 16cd35c..48bcc62 100644 --- a/tests/Parser/Annotations/MinVersionAnnotationTest.php +++ b/tests/Parser/Annotations/MinVersionAnnotationTest.php @@ -1,7 +1,7 @@ assertAnnotation($annotation, $expected); } - /** - * @dataProvider providerAnnotation - * @param string $content - * @param array $expected - */ - public function testHydrate(string $content, array $expected): void - { - $annotation = MinVersionAnnotation::hydrate(array_merge( - $expected, - [ - 'class' => __CLASS__, - 'method' => __METHOD__ - ] - )); - - $this->assertAnnotation($annotation, $expected); - } - private function assertAnnotation(MinVersionAnnotation $annotation, array $expected): void { - $this->assertFalse($annotation->requiresVisibilityDecorator()); - $this->assertFalse($annotation->supportsVersioning()); $this->assertFalse($annotation->supportsDeprecation()); - $this->assertFalse($annotation->supportsAliasing()); + $this->assertFalse($annotation->supportsVersioning()); + $this->assertFalse($annotation->supportsVendorTags()); + $this->assertFalse($annotation->requiresVisibilityDecorator()); $this->assertSame($expected, $annotation->toArray()); - $this->assertFalse($annotation->getCapability()); + $this->assertEmpty($annotation->getVendorTags()); $this->assertFalse($annotation->getVersion()); $this->assertEmpty($annotation->getAliases()); } @@ -69,7 +51,7 @@ public function providerAnnotationFailsOnInvalidContent(): array 'does-not-have-an-absolute-version' => [ 'annotation' => MinVersionAnnotation::class, 'content' => '~1.2', - 'expected.exception' => AbsoluteMinimumVersionException::class, + 'expected.exception' => AbsoluteVersionException::class, 'expected.exception.asserts' => [ 'getRequiredField' => null, 'getAnnotation' => '~1.2', diff --git a/tests/Parser/Annotations/ParamAnnotationTest.php b/tests/Parser/Annotations/ParamAnnotationTest.php index b8cfdbe..1bf0c3c 100644 --- a/tests/Parser/Annotations/ParamAnnotationTest.php +++ b/tests/Parser/Annotations/ParamAnnotationTest.php @@ -1,14 +1,16 @@ assertAnnotation($annotation, $expected); } - /** - * @dataProvider providerAnnotation - * @param string $content - * @param Version|null $version - * @param bool $visible - * @param bool $deprecated - * @param array $expected - */ - public function testHydrate( - string $content, - ?Version $version, - bool $visible, - bool $deprecated, - array $expected - ): void { - $annotation = ParamAnnotation::hydrate(array_merge( - $expected, - [ - 'class' => __CLASS__, - 'method' => __METHOD__ - ] - ), $version); - - $this->assertAnnotation($annotation, $expected); - } - - private function assertAnnotation(ParamAnnotation $annotation, array $expected): void + protected function assertAnnotation(ParamAnnotation $annotation, array $expected): void { - $this->assertTrue($annotation->requiresVisibilityDecorator()); - $this->assertTrue($annotation->supportsVersioning()); $this->assertTrue($annotation->supportsDeprecation()); - $this->assertFalse($annotation->supportsAliasing()); + $this->assertTrue($annotation->supportsVersioning()); + $this->assertTrue($annotation->supportsVendorTags()); + $this->assertTrue($annotation->requiresVisibilityDecorator()); + + $this->assertSame(static::PAYLOAD_FORMAT, $annotation->getPayloadFormat()); $this->assertSame($expected, $annotation->toArray()); $this->assertSame($expected['field'], $annotation->getField()); @@ -72,11 +50,15 @@ private function assertAnnotation(ParamAnnotation $annotation, array $expected): $this->assertSame($expected['required'], $annotation->isRequired()); $this->assertSame($expected['values'], $annotation->getValues()); - if (is_string($expected['capability'])) { - $this->assertInstanceOf(CapabilityAnnotation::class, $annotation->getCapability()); - } else { - $this->assertFalse($annotation->getCapability()); - } + $this->assertSame( + $expected['vendor_tags'], + array_map( + function (VendorTagAnnotation $tag): string { + return $tag->getVendorTag(); + }, + $annotation->getVendorTags() + ) + ); if ($expected['version']) { $this->assertInstanceOf(Version::class, $annotation->getVersion()); @@ -91,7 +73,7 @@ public function providerAnnotation(): array { return [ '_complete' => [ - 'content' => 'content_rating `G` (string, optional, nullable, MOVIE_RATINGS) - MPAA rating + 'content' => 'content_rating `G` (enum, optional, nullable, tag:MOVIE_RATINGS) - MPAA rating + Members - `G` - G rated - `PG` - PG rated @@ -100,25 +82,28 @@ public function providerAnnotation(): array 'visible' => true, 'deprecated' => false, 'expected' => [ - 'capability' => 'MOVIE_RATINGS', 'deprecated' => false, 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => true, 'required' => false, 'sample_data' => 'G', - 'type' => 'string', + 'subtype' => false, + 'type' => 'enum', 'values' => [ 'G' => 'G rated', 'PG' => 'PG rated', 'PG-13' => 'PG-13 rated' ], + 'vendor_tags' => [ + 'tag:MOVIE_RATINGS' + ], 'version' => false, 'visible' => true ] ], '_complete-with-markdown-description' => [ - 'content' => 'content_rating `G` (string, optional, nullable, MOVIE_RATINGS) - This denotes the + 'content' => 'content_rating `G` (enum, optional, nullable, tag:MOVIE_RATINGS) - This denotes the [MPAA rating](http://www.mpaa.org/film-ratings/) for the movie. + Members - `G` - G rated @@ -128,38 +113,22 @@ public function providerAnnotation(): array 'visible' => true, 'deprecated' => false, 'expected' => [ - 'capability' => 'MOVIE_RATINGS', 'deprecated' => false, 'description' => 'This denotes the [MPAA rating](http://www.mpaa.org/film-ratings/) for the movie.', 'field' => 'content_rating', 'nullable' => true, 'required' => false, 'sample_data' => 'G', - 'type' => 'string', + 'subtype' => false, + 'type' => 'enum', 'values' => [ 'G' => 'G rated', 'PG' => 'PG rated', 'PG-13' => 'PG-13 rated' ], - 'version' => false, - 'visible' => true - ] - ], - 'capability' => [ - 'content' => 'content_rating `G` (string, REQUIRED, MOVIE_RATINGS) - MPAA rating', - 'version' => null, - 'visible' => true, - 'deprecated' => false, - 'expected' => [ - 'capability' => 'MOVIE_RATINGS', - 'deprecated' => false, - 'description' => 'MPAA rating', - 'field' => 'content_rating', - 'nullable' => false, - 'required' => true, - 'sample_data' => 'G', - 'type' => 'string', - 'values' => false, + 'vendor_tags' => [ + 'tag:MOVIE_RATINGS' + ], 'version' => false, 'visible' => true ] @@ -170,21 +139,22 @@ public function providerAnnotation(): array 'visible' => true, 'deprecated' => true, 'expected' => [ - 'capability' => false, 'deprecated' => true, 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => false, 'required' => true, 'sample_data' => 'G', + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ] ], 'enum-with-no-descriptions' => [ - 'content' => 'is_kid_friendly `yes` (string, optional) - Is this movie kid friendly? + 'content' => 'is_kid_friendly `yes` (enum, optional) - Is this movie kid friendly? + Members - `yes` - `no`', @@ -192,24 +162,25 @@ public function providerAnnotation(): array 'visible' => true, 'deprecated' => false, 'expected' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'Is this movie kid friendly?', 'field' => 'is_kid_friendly', 'nullable' => false, 'required' => false, 'sample_data' => 'yes', - 'type' => 'string', + 'subtype' => false, + 'type' => 'enum', 'values' => [ 'no' => '', 'yes' => '' ], + 'vendor_tags' => [], 'version' => false, 'visible' => true ] ], 'enum-with-no-set-default' => [ - 'content' => 'content_rating `G` (string, optional) - MPAA rating + 'content' => 'content_rating `G` (enum, optional) - MPAA rating + Members - `G` - G rated - `PG` - PG rated @@ -223,14 +194,14 @@ public function providerAnnotation(): array 'visible' => true, 'deprecated' => false, 'expected' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => false, 'required' => false, 'sample_data' => 'G', - 'type' => 'string', + 'subtype' => false, + 'type' => 'enum', 'values' => [ 'G' => 'G rated', 'NC-17' => 'NC-17 rated', @@ -241,6 +212,7 @@ public function providerAnnotation(): array 'UR' => 'Unrated', 'X' => 'X-rated' ], + 'vendor_tags' => [], 'version' => false, 'visible' => true ] @@ -251,15 +223,16 @@ public function providerAnnotation(): array 'visible' => true, 'deprecated' => false, 'expected' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => true, 'required' => true, 'sample_data' => 'G', + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ] @@ -270,15 +243,16 @@ public function providerAnnotation(): array 'visible' => false, 'deprecated' => false, 'expected' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => false, 'required' => true, 'sample_data' => 'G', + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => false ] @@ -289,15 +263,16 @@ public function providerAnnotation(): array 'visible' => true, 'deprecated' => false, 'expected' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'The page number to show.', 'field' => 'page', 'nullable' => false, 'required' => false, 'sample_data' => false, + 'subtype' => false, 'type' => 'integer', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ] @@ -311,18 +286,41 @@ public function providerAnnotation(): array 'visible' => true, 'deprecated' => false, 'expected' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'Filter to apply to the results.', 'field' => 'filter', 'nullable' => false, 'required' => false, 'sample_data' => false, - 'type' => 'string', + 'subtype' => false, + 'type' => 'enum', 'values' => [ 'embeddable' => 'Embeddable', 'playable' => 'Playable' ], + 'vendor_tags' => [], + 'version' => false, + 'visible' => true + ] + ], + 'vendor-tag' => [ + 'content' => 'content_rating `G` (string, REQUIRED, tag:MOVIE_RATINGS) - MPAA rating', + 'version' => null, + 'visible' => true, + 'deprecated' => false, + 'expected' => [ + 'deprecated' => false, + 'description' => 'MPAA rating', + 'field' => 'content_rating', + 'nullable' => false, + 'required' => true, + 'sample_data' => 'G', + 'subtype' => false, + 'type' => 'string', + 'values' => [], + 'vendor_tags' => [ + 'tag:MOVIE_RATINGS' + ], 'version' => false, 'visible' => true ] @@ -333,28 +331,28 @@ public function providerAnnotation(): array 'visible' => true, 'deprecated' => false, 'expected' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => false, 'required' => false, 'sample_data' => 'G', + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => '1.1 - 1.2', 'visible' => true ] ], 'with-a-long-description' => [ - 'content' => 'content_rating `G` (string, required) - Voluptate culpa ex, eiusmod rump sint id. Venison - non ribeye landjaeger laboris, enim jowl culpa meatloaf dolore mollit anim. Bacon shankle eiusmod + 'content' => 'content_rating `G` (string, required) - Voluptate culpa ex, eiusmod rump sint id. Venison + non ribeye landjaeger laboris, enim jowl culpa meatloaf dolore mollit anim. Bacon shankle eiusmod hamburger enim. Laboris lorem pastrami t-bone tempor ullamco swine commodo tri-tip in sirloin.', 'version' => null, 'visible' => false, 'deprecated' => false, 'expected' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'Voluptate culpa ex, eiusmod rump sint id. Venison non ribeye landjaeger ' . 'laboris, enim jowl culpa meatloaf dolore mollit anim. Bacon shankle eiusmod hamburger enim. ' . @@ -363,8 +361,10 @@ public function providerAnnotation(): array 'nullable' => false, 'required' => true, 'sample_data' => 'G', + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => false ] @@ -375,34 +375,38 @@ public function providerAnnotation(): array 'visible' => true, 'deprecated' => false, 'expected' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => false, 'required' => false, 'sample_data' => 'G', + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ] ], - 'without-defined-requirement-but-capability' => [ - 'content' => 'content_rating `G` (string, MOVIE_RATINGS) - MPAA rating', + 'without-defined-requirement-but-vendor-tag' => [ + 'content' => 'content_rating `G` (string, tag:MOVIE_RATINGS) - MPAA rating', 'version' => null, 'visible' => true, 'deprecated' => false, 'expected' => [ - 'capability' => 'MOVIE_RATINGS', 'deprecated' => false, 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => false, 'required' => false, 'sample_data' => 'G', + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [ + 'tag:MOVIE_RATINGS' + ], 'version' => false, 'visible' => true ] @@ -413,15 +417,16 @@ public function providerAnnotation(): array 'visible' => true, 'deprecated' => false, 'expected' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => false, 'required' => false, 'sample_data' => false, + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ] @@ -432,16 +437,6 @@ public function providerAnnotation(): array public function providerAnnotationFailsOnInvalidContent(): array { return [ - 'invalid-mson' => [ - 'annotation' => ParamAnnotation::class, - 'content' => '', - 'expected.exception' => InvalidMSONSyntaxException::class, - 'expected.exception.asserts' => [ - 'getAnnotation' => 'param', - 'getDocblock' => '', - 'getValues' => [] - ] - ], 'unsupported-type' => [ 'annotation' => ParamAnnotation::class, 'content' => 'content_rating `G` (str) - MPAA rating', @@ -450,6 +445,12 @@ public function providerAnnotationFailsOnInvalidContent(): array 'getAnnotation' => 'content_rating `G` (str) - MPAA rating', 'getDocblock' => null ] + ], + 'restricted-field-name-is-detected' => [ + 'annotation' => ParamAnnotation::class, + 'content' => '__NESTED_DATA__ (string) - MPAA rating', + 'expected.exception' => RestrictedFieldNameException::class, + 'expected.exception.asserts' => [] ] ]; } diff --git a/tests/Parser/Annotations/PathAnnotationTest.php b/tests/Parser/Annotations/PathAnnotationTest.php new file mode 100644 index 0000000..3d98a3a --- /dev/null +++ b/tests/Parser/Annotations/PathAnnotationTest.php @@ -0,0 +1,118 @@ +process(); + $annotation->setVisibility($visible); + $annotation->setDeprecated($deprecated); + + $this->assertAnnotation($annotation, $expected); + } + + private function assertAnnotation(PathAnnotation $annotation, array $expected): void + { + $this->assertTrue($annotation->supportsDeprecation()); + $this->assertFalse($annotation->supportsVersioning()); + $this->assertFalse($annotation->supportsVendorTags()); + $this->assertTrue($annotation->requiresVisibilityDecorator()); + + $this->assertSame($expected['array'], $annotation->toArray()); + $this->assertSame($expected['clean.path'], $annotation->getCleanPath()); + $this->assertEmpty($annotation->getVendorTags()); + $this->assertFalse($annotation->getVersion()); + $this->assertEmpty($annotation->getAliases()); + } + + public function testConfiguredPathParamTranslations(): void + { + $this->getConfig()->addPathParamTranslation('movie_id', 'id'); + + $annotation = new PathAnnotation('/movies/+movie_id/showtimes', __CLASS__, __METHOD__); + $annotation->process(); + + $this->assertSame('/movies/{id}/showtimes', $annotation->getCleanPath()); + $this->assertSame('/movies/+id/showtimes', $annotation->toArray()['path']); + } + + public function providerAnnotation(): array + { + return [ + 'private' => [ + 'content' => '/movies/+id/showtimes', + 'visible' => false, + 'deprecated' => false, + 'expected' => [ + 'clean.path' => '/movies/{id}/showtimes', + 'array' => [ + 'aliased' => false, + 'aliases' => [], + 'deprecated' => false, + 'path' => '/movies/+id/showtimes', + 'visible' => false + ] + ] + ], + 'public' => [ + 'content' => '/movies/+id/showtimes', + 'visible' => true, + 'deprecated' => false, + 'expected' => [ + 'clean.path' => '/movies/{id}/showtimes', + 'array' => [ + 'aliased' => false, + 'aliases' => [], + 'deprecated' => false, + 'path' => '/movies/+id/showtimes', + 'visible' => true + ] + ] + ], + 'public.deprecated' => [ + 'content' => '/movies/+id/showtimes', + 'visible' => true, + 'deprecated' => true, + 'expected' => [ + 'clean.path' => '/movies/{id}/showtimes', + 'array' => [ + 'aliased' => false, + 'aliases' => [], + 'deprecated' => true, + 'path' => '/movies/+id/showtimes', + 'visible' => true + ] + ] + ] + ]; + } + + public function providerAnnotationFailsOnInvalidContent(): array + { + return [ + 'missing-path' => [ + 'annotation' => PathAnnotation::class, + 'content' => '', + 'expected.exception' => MissingRequiredFieldException::class, + 'expected.exception.asserts' => [ + 'getRequiredField' => 'path', + 'getAnnotation' => 'path', + 'getDocblock' => '', + 'getValues' => [] + ] + ] + ]; + } +} diff --git a/tests/Parser/Annotations/UriSegmentAnnotationTest.php b/tests/Parser/Annotations/PathParamAnnotationTest.php similarity index 50% rename from tests/Parser/Annotations/UriSegmentAnnotationTest.php rename to tests/Parser/Annotations/PathParamAnnotationTest.php index 118c006..f6f3a69 100644 --- a/tests/Parser/Annotations/UriSegmentAnnotationTest.php +++ b/tests/Parser/Annotations/PathParamAnnotationTest.php @@ -2,59 +2,38 @@ namespace Mill\Tests\Parser\Annotations; use Mill\Exceptions\Annotations\InvalidMSONSyntaxException; -use Mill\Exceptions\Annotations\MissingRequiredFieldException; use Mill\Exceptions\Annotations\UnsupportedTypeException; -use Mill\Parser\Annotations\UriSegmentAnnotation; +use Mill\Parser\Annotations\PathParamAnnotation; -class UriSegmentAnnotationTest extends AnnotationTest +class PathParamAnnotationTest extends AnnotationTest { /** * @dataProvider providerAnnotation - * @param string $uri - * @param string $segment + * @param string $param * @param array $expected */ - public function testAnnotation(string $uri, string $segment, array $expected): void + public function testAnnotation(string $param, array $expected): void { - $annotation = new UriSegmentAnnotation($segment, __CLASS__, __METHOD__, null); + $annotation = new PathParamAnnotation($param, __CLASS__, __METHOD__, null); $annotation->process(); $this->assertAnnotation($annotation, $expected); } - /** - * @dataProvider providerAnnotation - * @param string $uri - * @param string $segment - * @param array $expected - */ - public function testHydrate(string $uri, string $segment, array $expected): void + private function assertAnnotation(PathParamAnnotation $annotation, array $expected): void { - /** @var UriSegmentAnnotation $annotation */ - $annotation = UriSegmentAnnotation::hydrate(array_merge( - $expected, - [ - 'class' => __CLASS__, - 'method' => __METHOD__ - ] - )); - - $this->assertAnnotation($annotation, $expected); - } - - private function assertAnnotation(UriSegmentAnnotation $annotation, array $expected): void - { - $this->assertFalse($annotation->requiresVisibilityDecorator()); - $this->assertFalse($annotation->supportsVersioning()); $this->assertFalse($annotation->supportsDeprecation()); - $this->assertFalse($annotation->supportsAliasing()); + $this->assertFalse($annotation->supportsVersioning()); + $this->assertFalse($annotation->supportsVendorTags()); + $this->assertFalse($annotation->requiresVisibilityDecorator()); + + $this->assertSame('path', $annotation->getPayloadFormat()); $this->assertSame($expected, $annotation->toArray()); - $this->assertSame($expected['uri'], $annotation->getUri()); $this->assertSame($expected['field'], $annotation->getField()); $this->assertSame($expected['type'], $annotation->getType()); $this->assertSame($expected['description'], $annotation->getDescription()); - $this->assertFalse($annotation->getCapability()); + $this->assertEmpty($annotation->getVendorTags()); $this->assertFalse($annotation->getVersion()); $this->assertEmpty($annotation->getAliases()); } @@ -63,27 +42,38 @@ public function providerAnnotation(): array { return [ 'bare' => [ - 'uri' => '/movies/+id', - 'segment' => '{/movies/+id} id (string) - Movie ID', + 'param' => 'id (string) - Movie ID', 'expected' => [ 'description' => 'Movie ID', 'field' => 'id', + 'required' => true, + 'sample_data' => false, 'type' => 'string', - 'uri' => '/movies/+id', - 'values' => false + 'values' => [] + ] + ], + 'sample_data' => [ + 'param' => 'id `1234` (string) - Movie ID', + 'expected' => [ + 'description' => 'Movie ID', + 'field' => 'id', + 'required' => true, + 'sample_data' => '1234', + 'type' => 'string', + 'values' => [] ] ], '_complete' => [ - 'uri' => '/movies/+id/showtimes/*date', - 'segment' => '{/movies/+id/showtimes/*date} date (string) - Date to look for movie showtimes. + 'param' => 'date `2018-06-09` (enum) - Date to look for movie showtimes. + Members - `today` - `tomorrow`', 'expected' => [ 'description' => 'Date to look for movie showtimes.', 'field' => 'date', - 'type' => 'string', - 'uri' => '/movies/+id/showtimes/*date', + 'required' => true, + 'sample_data' => '2018-06-09', + 'type' => 'enum', 'values' => [ 'today' => '', 'tomorrow' => '' @@ -97,29 +87,18 @@ public function providerAnnotationFailsOnInvalidContent(): array { return [ 'invalid-mson' => [ - 'annotation' => UriSegmentAnnotation::class, - 'content' => '{/movies/+id}', + 'annotation' => PathParamAnnotation::class, + 'content' => 'date', 'expected.exception' => InvalidMSONSyntaxException::class, 'expected.exception.asserts' => [ - 'getAnnotation' => 'urisegment', - 'getDocblock' => '{/movies/+id}', - 'getValues' => [] - ] - ], - 'missing-uri' => [ - 'annotation' => UriSegmentAnnotation::class, - 'content' => '', - 'expected.exception' => MissingRequiredFieldException::class, - 'expected.exception.asserts' => [ - 'getRequiredField' => 'uri', - 'getAnnotation' => 'urisegment', - 'getDocblock' => '', + 'getAnnotation' => 'pathparam', + 'getDocblock' => 'date', 'getValues' => [] ] ], 'unsupported-type' => [ - 'annotation' => UriSegmentAnnotation::class, - 'content' => '{/movies/+id} id (str) - Movie ID', + 'annotation' => PathParamAnnotation::class, + 'content' => 'id (str) - Movie ID', 'expected.exception' => UnsupportedTypeException::class, 'expected.exception.asserts' => [ 'getAnnotation' => 'id (str) - Movie ID', diff --git a/tests/Parser/Annotations/QueryParamAnnotationTest.php b/tests/Parser/Annotations/QueryParamAnnotationTest.php new file mode 100644 index 0000000..a3dfcb9 --- /dev/null +++ b/tests/Parser/Annotations/QueryParamAnnotationTest.php @@ -0,0 +1,56 @@ +process(); + $annotation->setVisibility($visible); + $annotation->setDeprecated($deprecated); + + $this->assertAnnotation($annotation, $expected); + } + + public function providerAnnotationFailsOnInvalidContent(): array + { + return [ + 'unsupported-type' => [ + 'annotation' => QueryParamAnnotation::class, + 'content' => 'content_rating `G` (str) - MPAA rating', + 'expected.exception' => UnsupportedTypeException::class, + 'expected.exception.asserts' => [ + 'getAnnotation' => 'content_rating `G` (str) - MPAA rating', + 'getDocblock' => null + ] + ], + 'restricted-field-name-is-detected' => [ + 'annotation' => QueryParamAnnotation::class, + 'content' => '__NESTED_DATA__ (string) - MPAA rating', + 'expected.exception' => RestrictedFieldNameException::class, + 'expected.exception.asserts' => [] + ] + ]; + } +} diff --git a/tests/Parser/Annotations/ReturnAnnotationTest.php b/tests/Parser/Annotations/ReturnAnnotationTest.php index 9012e88..b08c1e1 100644 --- a/tests/Parser/Annotations/ReturnAnnotationTest.php +++ b/tests/Parser/Annotations/ReturnAnnotationTest.php @@ -25,39 +25,19 @@ public function testAnnotation(string $content, bool $visible, $version, array $ $this->assertAnnotation($annotation, $expected); } - /** - * @dataProvider providerAnnotation - * @param string $content - * @param bool $visible - * @param Version|null $version - * @param array $expected - */ - public function testHydrate(string $content, bool $visible, $version, array $expected): void - { - $annotation = ReturnAnnotation::hydrate(array_merge( - $expected, - [ - 'class' => __CLASS__, - 'method' => __METHOD__ - ] - ), $version); - - $this->assertAnnotation($annotation, $expected); - } - private function assertAnnotation(ReturnAnnotation $annotation, array $expected): void { - $this->assertTrue($annotation->requiresVisibilityDecorator()); - $this->assertTrue($annotation->supportsVersioning()); $this->assertFalse($annotation->supportsDeprecation()); - $this->assertFalse($annotation->supportsAliasing()); + $this->assertTrue($annotation->supportsVersioning()); + $this->assertFalse($annotation->supportsVendorTags()); + $this->assertTrue($annotation->requiresVisibilityDecorator()); $this->assertSame($expected, $annotation->toArray()); $this->assertSame($expected['description'], $annotation->getDescription()); $this->assertSame($expected['http_code'], $annotation->getHttpCode()); $this->assertSame($expected['representation'], $annotation->getRepresentation()); $this->assertSame($expected['type'], $annotation->getType()); - $this->assertFalse($annotation->getCapability()); + $this->assertEmpty($annotation->getVendorTags()); if ($expected['version']) { $this->assertInstanceOf(Version::class, $annotation->getVersion()); diff --git a/tests/Parser/Annotations/ScopeAnnotationTest.php b/tests/Parser/Annotations/ScopeAnnotationTest.php index 4600d1b..7a96e81 100644 --- a/tests/Parser/Annotations/ScopeAnnotationTest.php +++ b/tests/Parser/Annotations/ScopeAnnotationTest.php @@ -20,35 +20,17 @@ public function testAnnotation(string $content, array $expected): void $this->assertAnnotation($annotation, $expected); } - /** - * @dataProvider providerAnnotation - * @param string $content - * @param array $expected - */ - public function testHydrate(string $content, array $expected): void - { - $annotation = ScopeAnnotation::hydrate(array_merge( - $expected, - [ - 'class' => __CLASS__, - 'method' => __METHOD__ - ] - )); - - $this->assertAnnotation($annotation, $expected); - } - private function assertAnnotation(ScopeAnnotation $annotation, array $expected): void { - $this->assertFalse($annotation->requiresVisibilityDecorator()); - $this->assertFalse($annotation->supportsVersioning()); $this->assertFalse($annotation->supportsDeprecation()); - $this->assertFalse($annotation->supportsAliasing()); + $this->assertFalse($annotation->supportsVersioning()); + $this->assertFalse($annotation->supportsVendorTags()); + $this->assertFalse($annotation->requiresVisibilityDecorator()); $this->assertSame($expected, $annotation->toArray()); $this->assertSame($expected['scope'], $annotation->getScope()); $this->assertSame($expected['description'], $annotation->getDescription()); - $this->assertFalse($annotation->getCapability()); + $this->assertEmpty($annotation->getVendorTags()); $this->assertFalse($annotation->getVersion()); $this->assertEmpty($annotation->getAliases()); } diff --git a/tests/Parser/Annotations/UriAnnotationTest.php b/tests/Parser/Annotations/UriAnnotationTest.php deleted file mode 100644 index e8a8de6..0000000 --- a/tests/Parser/Annotations/UriAnnotationTest.php +++ /dev/null @@ -1,185 +0,0 @@ -process(); - $annotation->setVisibility($visible); - $annotation->setDeprecated($deprecated); - - $this->assertAnnotation($annotation, $expected); - } - - /** - * @dataProvider providerAnnotation - * @param string $content - * @param bool $visible - * @param bool $deprecated - * @param array $expected - */ - public function testHydrate(string $content, bool $visible, bool $deprecated, array $expected): void - { - /** @var UriAnnotation $annotation */ - $annotation = UriAnnotation::hydrate(array_merge( - $expected['array'], - [ - 'class' => __CLASS__, - 'method' => __METHOD__ - ] - )); - - $this->assertAnnotation($annotation, $expected); - } - - private function assertAnnotation(UriAnnotation $annotation, array $expected): void - { - $this->assertTrue($annotation->requiresVisibilityDecorator()); - $this->assertFalse($annotation->supportsVersioning()); - $this->assertTrue($annotation->supportsDeprecation()); - $this->assertTrue($annotation->supportsAliasing()); - - $this->assertSame($expected['array'], $annotation->toArray()); - $this->assertSame($expected['clean.path'], $annotation->getCleanPath()); - $this->assertFalse($annotation->getCapability()); - $this->assertFalse($annotation->getVersion()); - $this->assertEmpty($annotation->getAliases()); - } - - public function testConfiguredUriSegmentTranslations(): void - { - $this->getConfig()->addUriSegmentTranslation('movie_id', 'id'); - - $annotation = new UriAnnotation('{Movies\Showtimes} /movies/+movie_id/showtimes', __CLASS__, __METHOD__); - $annotation->process(); - - $this->assertSame('/movies/{id}/showtimes', $annotation->getCleanPath()); - $this->assertSame('/movies/+movie_id/showtimes', $annotation->toArray()['path']); - } - - public function providerAnnotation(): array - { - return [ - 'private' => [ - 'content' => '{Movies\Showtimes} /movies/+id/showtimes', - 'visible' => false, - 'deprecated' => false, - 'expected' => [ - 'clean.path' => '/movies/{id}/showtimes', - 'array' => [ - 'aliased' => false, - 'aliases' => [], - 'deprecated' => false, - 'namespace' => 'Movies\Showtimes', - 'path' => '/movies/+id/showtimes', - 'visible' => false - ] - ] - ], - 'private.namespace_with_no_depth' => [ - 'content' => '{Movies} /movies', - 'visible' => false, - 'deprecated' => false, - 'expected' => [ - 'clean.path' => '/movies', - 'array' => [ - 'aliased' => false, - 'aliases' => [], - 'deprecated' => false, - 'namespace' => 'Movies', - 'path' => '/movies', - 'visible' => false - ] - ] - ], - 'public' => [ - 'content' => '{Movies\Showtimes} /movies/+id/showtimes', - 'visible' => true, - 'deprecated' => false, - 'expected' => [ - 'clean.path' => '/movies/{id}/showtimes', - 'array' => [ - 'aliased' => false, - 'aliases' => [], - 'deprecated' => false, - 'namespace' => 'Movies\Showtimes', - 'path' => '/movies/+id/showtimes', - 'visible' => true - ] - ] - ], - 'public.deprecated' => [ - 'content' => '{Movies\Showtimes} /movies/+id/showtimes', - 'visible' => true, - 'deprecated' => true, - 'expected' => [ - 'clean.path' => '/movies/{id}/showtimes', - 'array' => [ - 'aliased' => false, - 'aliases' => [], - 'deprecated' => true, - 'namespace' => 'Movies\Showtimes', - 'path' => '/movies/+id/showtimes', - 'visible' => true - ] - ] - ], - 'public.non-alphanumeric_namespace' => [ - 'content' => '{/} /', - 'visible' => true, - 'deprecated' => false, - 'expected' => [ - 'clean.path' => '/', - 'array' => [ - 'aliased' => false, - 'aliases' => [], - 'deprecated' => false, - 'namespace' => '/', - 'path' => '/', - 'visible' => true - ] - ] - ] - ]; - } - - public function providerAnnotationFailsOnInvalidContent(): array - { - return [ - 'missing-namespace' => [ - 'annotation' => UriAnnotation::class, - 'content' => '', - 'expected.exception' => MissingRequiredFieldException::class, - 'expected.exception.asserts' => [ - 'getRequiredField' => 'namespace', - 'getAnnotation' => 'uri', - 'getDocblock' => '', - 'getValues' => [] - ] - ], - 'missing-path' => [ - 'annotation' => UriAnnotation::class, - 'content' => '{Movies}', - 'expected.exception' => MissingRequiredFieldException::class, - 'expected.exception.asserts' => [ - 'getRequiredField' => 'path', - 'getAnnotation' => 'uri', - 'getDocblock' => '{Movies}', - 'getValues' => [] - ] - ] - ]; - } -} diff --git a/tests/Parser/Annotations/VendorTagAnnotationTest.php b/tests/Parser/Annotations/VendorTagAnnotationTest.php new file mode 100644 index 0000000..8354ddf --- /dev/null +++ b/tests/Parser/Annotations/VendorTagAnnotationTest.php @@ -0,0 +1,72 @@ +process(); + + $this->assertAnnotation($annotation, $expected); + } + + private function assertAnnotation(VendorTagAnnotation $annotation, array $expected): void + { + $this->assertFalse($annotation->supportsDeprecation()); + $this->assertFalse($annotation->supportsVersioning()); + $this->assertFalse($annotation->supportsVendorTags()); + $this->assertFalse($annotation->requiresVisibilityDecorator()); + + $this->assertSame($expected, $annotation->toArray()); + $this->assertSame($expected['vendor_tag'], $annotation->getVendorTag()); + $this->assertFalse($annotation->getVersion()); + $this->assertEmpty($annotation->getAliases()); + } + + public function providerAnnotation(): array + { + return [ + '_complete' => [ + 'content' => 'tag:BUY_TICKETS', + 'expected' => [ + 'vendor_tag' => 'tag:BUY_TICKETS' + ] + ] + ]; + } + + public function providerAnnotationFailsOnInvalidContent(): array + { + return [ + 'missing-tag' => [ + 'annotation' => VendorTagAnnotation::class, + 'content' => '', + 'expected.exception' => MissingRequiredFieldException::class, + 'expected.exception.asserts' => [ + 'getRequiredField' => 'vendor_tag', + 'getAnnotation' => 'vendortag', + 'getDocblock' => '', + 'getValues' => [] + ] + ], + 'tag-was-not-configured' => [ + 'annotation' => VendorTagAnnotation::class, + 'content' => 'tag:notConfigured', + 'expected.exception' => InvalidVendorTagSuppliedException::class, + 'expected.exception.asserts' => [ + 'getVendorTag' => 'tag:notConfigured' + ] + ] + ]; + } +} diff --git a/tests/Parser/MSONTest.php b/tests/Parser/MSONTest.php index 8da75a4..524448e 100644 --- a/tests/Parser/MSONTest.php +++ b/tests/Parser/MSONTest.php @@ -1,6 +1,9 @@ expectException('Mill\Exceptions\MSON\MissingOptionsException'); + $this->expectException(MissingOptionsException::class); $content = 'content_rating (enum) - MPAA rating'; (new MSON(__CLASS__, __METHOD__))->parse($content); } + public function testEnumFailsWhenWrittenAsAString(): void + { + $this->expectException(ImproperlyWrittenEnumException::class); + + $content = 'content_rating `G` (string, optional) - MPAA rating + + Members + - `G` - G rated + - `PG` - PG rated + - `PG-13` - PG-13 rated'; + + (new MSON(__CLASS__, __METHOD__))->parse($content); + } + /** * @dataProvider providerTestParseFailsOnInvalidTypes * @param string $content */ public function testParseFailsOnInvalidTypes(string $content): void { - $this->expectException('Mill\Exceptions\Annotations\UnsupportedTypeException'); + $this->expectException(UnsupportedTypeException::class); (new MSON(__CLASS__, __METHOD__))->parse($content); } @@ -39,31 +55,33 @@ public function providerTestParse(): array { return [ '_complete' => [ - 'content' => 'content_rating `G` (string, optional, MOVIE_RATINGS) - MPAA rating + 'content' => 'content_rating `G` (enum, optional, tag:MOVIE_RATINGS, needs:validUser) - MPAA rating + Members - `G` - G rated - `PG` - PG rated - `PG-13` - PG-13 rated', 'expected' => [ - 'capability' => 'MOVIE_RATINGS', 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => false, 'required' => false, 'sample_data' => 'G', 'subtype' => false, - 'type' => 'string', + 'type' => 'enum', 'values' => [ 'G' => 'G rated', 'PG' => 'PG rated', 'PG-13' => 'PG-13 rated' + ], + 'vendor_tags' => [ + 'tag:MOVIE_RATINGS', + 'needs:validUser' ] ] ], - 'capability' => [ - 'content' => 'content_rating `G` (string, REQUIRED, MOVIE_RATINGS) - MPAA rating', + 'vendor-tag' => [ + 'content' => 'content_rating `G` (string, REQUIRED, tag:MOVIE_RATINGS) - MPAA rating', 'expected' => [ - 'capability' => 'MOVIE_RATINGS', 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => false, @@ -71,7 +89,10 @@ public function providerTestParse(): array 'sample_data' => 'G', 'subtype' => false, 'type' => 'string', - 'values' => [] + 'values' => [], + 'vendor_tags' => [ + 'tag:MOVIE_RATINGS' + ] ] ], 'description-long' => [ @@ -79,7 +100,6 @@ public function providerTestParse(): array non ribeye landjaeger laboris, enim jowl culpa meatloaf dolore mollit anim. Bacon shankle eiusmod hamburger enim. Laboris lorem pastrami t-bone tempor ullamco swine commodo tri-tip in sirloin.', 'expected' => [ - 'capability' => false, 'description' => 'Voluptate culpa ex, eiusmod rump sint id. Venison non ribeye landjaeger ' . 'laboris, enim jowl culpa meatloaf dolore mollit anim. Bacon shankle eiusmod hamburger enim. ' . 'Laboris lorem pastrami t-bone tempor ullamco swine commodo tri-tip in sirloin.', @@ -89,77 +109,115 @@ public function providerTestParse(): array 'sample_data' => 'G', 'subtype' => false, 'type' => 'string', - 'values' => [] + 'values' => [], + 'vendor_tags' => [] ] ], 'description-markdown' => [ - 'content' => 'content_rating `G` (string, optional, nullable, MOVIE_RATINGS) - This denotes the + 'content' => 'content_rating `G` (enum, optional, nullable, tag:MOVIE_RATINGS) - This denotes the [MPAA rating](http://www.mpaa.org/film-ratings/) for the movie. + Members - `G` - G rated - `PG` - PG rated - `PG-13` - PG-13 rated', 'expected' => [ - 'capability' => 'MOVIE_RATINGS', 'description' => 'This denotes the [MPAA rating](http://www.mpaa.org/film-ratings/) for the movie.', 'field' => 'content_rating', 'nullable' => true, 'required' => false, 'sample_data' => 'G', 'subtype' => false, - 'type' => 'string', + 'type' => 'enum', 'values' => [ 'G' => 'G rated', 'PG' => 'PG rated', 'PG-13' => 'PG-13 rated' + ], + 'vendor_tags' => [ + 'tag:MOVIE_RATINGS' ] ] ], 'description-starts-on-new-line' => [ - 'content' => 'content_rating `G` (string) + 'content' => 'content_rating `G` (enum) - MPAA Rating + Members - `G` - G rated - `PG` - PG rated - `PG-13` - PG-13 rated', 'expected' => [ - 'capability' => false, 'description' => 'MPAA Rating', 'field' => 'content_rating', 'nullable' => false, 'required' => false, 'sample_data' => 'G', 'subtype' => false, - 'type' => 'string', + 'type' => 'enum', 'values' => [ 'G' => 'G rated', 'PG' => 'PG rated', 'PG-13' => 'PG-13 rated' - ] + ], + 'vendor_tags' => [] + ] + ], + 'enum-with-extra-long-descriptions' => [ + 'content' => 'access_type `default` (enum) - The promotion access type, which is a purchase option + that is not available on the container. VIP promotions always make the content free of charge. If + you use this type, you must further define the promotion with the `download` or `stream_period` + fields. + + Members + - `default` - Promotions grant discount on the existing purchase options for an On Demand + Container. + - `optional` + - `vip` - Promotions can be used to grant free access to vod content before it is released, or + to offer a purchase option that is not available on the container. "vip" promotions will + always make the content free, and must be further defined with the `download` or + `stream_period` fields.', + 'expected' => [ + 'description' => 'The promotion access type, which is a purchase option that is not available on ' . + 'the container. VIP promotions always make the content free of charge. If you use this ' . + 'type, you must further define the promotion with the `download` or `stream_period` fields.', + 'field' => 'access_type', + 'nullable' => false, + 'required' => false, + 'sample_data' => 'default', + 'subtype' => false, + 'type' => 'enum', + 'values' => [ + 'default' => 'Promotions grant discount on the existing purchase options for an On Demand ' . + 'Container.', + 'optional' => '', + 'vip' => 'Promotions can be used to grant free access to vod content before it is released, ' . + 'or to offer a purchase option that is not available on the container. "vip" promotions ' . + 'will always make the content free, and must be further defined with the `download` or ' . + '`stream_period` fields.', + ], + 'vendor_tags' => [] ] ], 'enum-without-descriptions' => [ - 'content' => 'is_kid_friendly `yes` (string, optional) - Is this movie kid friendly? + 'content' => 'is_kid_friendly `yes` (enum, optional) - Is this movie kid friendly? + Members - `yes` - `no`', 'expected' => [ - 'capability' => false, 'description' => 'Is this movie kid friendly?', 'field' => 'is_kid_friendly', 'nullable' => false, 'required' => false, 'sample_data' => 'yes', 'subtype' => false, - 'type' => 'string', + 'type' => 'enum', 'values' => [ 'no' => '', 'yes' => '' - ] + ], + 'vendor_tags' => [] ] ], 'enum-without-set-default' => [ - 'content' => 'content_rating `G` (string, optional) - MPAA rating + 'content' => 'content_rating `G` (enum, optional) - MPAA rating + Members - `G` - G rated - `PG` - PG rated @@ -170,14 +228,13 @@ public function providerTestParse(): array - `NR` - No rating - `UR` - Unrated', 'expected' => [ - 'capability' => false, 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => false, 'required' => false, 'sample_data' => 'G', 'subtype' => false, - 'type' => 'string', + 'type' => 'enum', 'values' => [ 'G' => 'G rated', 'NC-17' => 'NC-17 rated', @@ -187,13 +244,13 @@ public function providerTestParse(): array 'R' => 'R rated', 'UR' => 'Unrated', 'X' => 'X-rated' - ] + ], + 'vendor_tags' => [] ] ], 'field-dot-notation' => [ 'content' => 'content.rating `G` (string) - MPAA rating', 'expected' => [ - 'capability' => false, 'description' => 'MPAA rating', 'field' => 'content.rating', 'nullable' => false, @@ -201,13 +258,13 @@ public function providerTestParse(): array 'sample_data' => 'G', 'subtype' => false, 'type' => 'string', - 'values' => [] + 'values' => [], + 'vendor_tags' => [] ] ], 'type-array-with-subtype-object' => [ 'content' => 'websites (array) - The users\' list of websites.', 'expected' => [ - 'capability' => false, 'description' => 'The users\' list of websites.', 'field' => 'websites', 'nullable' => false, @@ -215,13 +272,13 @@ public function providerTestParse(): array 'sample_data' => '', 'subtype' => 'object', 'type' => 'array', - 'values' => [] + 'values' => [], + 'vendor_tags' => [] ] ], 'type-array-with-subytpe-representation' => [ 'content' => 'cast (array<\Mill\Examples\Showtimes\Representations\Person>) - Cast members', 'expected' => [ - 'capability' => false, 'description' => 'Cast members', 'field' => 'cast', 'nullable' => false, @@ -229,13 +286,13 @@ public function providerTestParse(): array 'sample_data' => '', 'subtype' => '\Mill\Examples\Showtimes\Representations\Person', 'type' => 'array', - 'values' => [] + 'values' => [], + 'vendor_tags' => [] ] ], 'type-representation' => [ 'content' => 'director (\Mill\Examples\Showtimes\Representations\Person) - Director', 'expected' => [ - 'capability' => false, 'description' => 'Director', 'field' => 'director', 'nullable' => false, @@ -243,13 +300,13 @@ public function providerTestParse(): array 'sample_data' => '', 'subtype' => false, 'type' => '\Mill\Examples\Showtimes\Representations\Person', - 'values' => [] + 'values' => [], + 'vendor_tags' => [] ] ], - 'without-defined-requirement-but-capability' => [ - 'content' => 'content_rating `G` (string, MOVIE_RATINGS) - MPAA rating', + 'without-defined-requirement-but-vendor-tags' => [ + 'content' => 'content_rating `G` (string, tag:MOVIE_RATINGS) - MPAA rating', 'expected' => [ - 'capability' => 'MOVIE_RATINGS', 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => false, @@ -257,13 +314,15 @@ public function providerTestParse(): array 'sample_data' => 'G', 'subtype' => false, 'type' => 'string', - 'values' => [] + 'values' => [], + 'vendor_tags' => [ + 'tag:MOVIE_RATINGS' + ] ] ], 'without-sample-data' => [ 'content' => 'content_rating (string) - MPAA rating', 'expected' => [ - 'capability' => false, 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => false, @@ -271,7 +330,8 @@ public function providerTestParse(): array 'sample_data' => '', 'subtype' => false, 'type' => 'string', - 'values' => [] + 'values' => [], + 'vendor_tags' => [] ] ] ]; diff --git a/tests/Parser/Representation/DocumentationTest.php b/tests/Parser/Representation/DocumentationTest.php index 0c5d75c..cba11b0 100644 --- a/tests/Parser/Representation/DocumentationTest.php +++ b/tests/Parser/Representation/DocumentationTest.php @@ -70,7 +70,6 @@ public function providerParseDocumentationReturnsRepresentation(): array 'description.length' => 41, 'content' => [ 'cast' => [ - 'capability' => false, 'description' => 'Cast', 'identifier' => 'cast', 'nullable' => false, @@ -83,11 +82,11 @@ public function providerParseDocumentationReturnsRepresentation(): array ], 'subtype' => '\Mill\Examples\Showtimes\Representations\Person', 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'content_rating' => [ - 'capability' => false, 'description' => 'MPAA rating', 'identifier' => 'content_rating', 'nullable' => false, @@ -105,10 +104,10 @@ public function providerParseDocumentationReturnsRepresentation(): array 'UR' => '', 'X' => '' ], + 'vendor_tags' => [], 'version' => false ], 'description' => [ - 'capability' => false, 'description' => 'Description', 'identifier' => 'description', 'nullable' => false, @@ -116,11 +115,11 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'director' => [ - 'capability' => false, 'description' => 'Director', 'identifier' => 'director', 'nullable' => false, @@ -133,71 +132,93 @@ public function providerParseDocumentationReturnsRepresentation(): array ], 'subtype' => false, 'type' => '\Mill\Examples\Showtimes\Representations\Person', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'external_urls' => [ - 'capability' => false, 'description' => 'External URLs', 'identifier' => 'external_urls', 'nullable' => false, 'sample_data' => false, - 'scopes' => [], - 'subtype' => false, - 'type' => 'object', - 'values' => false, + 'scopes' => [ + [ + 'description' => false, + 'scope' => 'public' + ] + ], + 'subtype' => 'object', + 'type' => 'array', + 'values' => [], + 'vendor_tags' => [], 'version' => '>=1.1' ], 'external_urls.imdb' => [ - 'capability' => false, 'description' => 'IMDB URL', 'identifier' => 'external_urls.imdb', 'nullable' => false, 'sample_data' => false, - 'scopes' => [], + 'scopes' => [ + [ + 'description' => false, + 'scope' => 'public' + ] + ], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => '>=1.1' ], 'external_urls.tickets' => [ - 'capability' => 'BUY_TICKETS', 'description' => 'Tickets URL', 'identifier' => 'external_urls.tickets', 'nullable' => false, 'sample_data' => false, - 'scopes' => [], + 'scopes' => [ + [ + 'description' => false, + 'scope' => 'public' + ] + ], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [ + 'tag:BUY_TICKETS' + ], 'version' => '>=1.1 <1.1.3' ], 'external_urls.trailer' => [ - 'capability' => false, 'description' => 'Trailer URL', 'identifier' => 'external_urls.trailer', 'nullable' => false, 'sample_data' => false, - 'scopes' => [], + 'scopes' => [ + [ + 'description' => false, + 'scope' => 'public' + ] + ], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => '>=1.1' ], 'genres' => [ - 'capability' => false, 'description' => 'Genres', 'identifier' => 'genres', 'nullable' => false, 'sample_data' => false, 'scopes' => [], - 'subtype' => false, + 'subtype' => 'uri', 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'id' => [ - 'capability' => false, 'description' => 'Unique ID', 'identifier' => 'id', 'nullable' => false, @@ -205,11 +226,11 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'number', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'kid_friendly' => [ - 'capability' => false, 'description' => 'Kid friendly?', 'identifier' => 'kid_friendly', 'nullable' => false, @@ -217,11 +238,11 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'boolean', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'name' => [ - 'capability' => false, 'description' => 'Name', 'identifier' => 'name', 'nullable' => false, @@ -229,11 +250,11 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'purchase.url' => [ - 'capability' => false, 'description' => 'URL to purchase the film.', 'identifier' => 'purchase.url', 'nullable' => false, @@ -241,11 +262,11 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'rotten_tomatoes_score' => [ - 'capability' => false, 'description' => 'Rotten Tomatoes score', 'identifier' => 'rotten_tomatoes_score', 'nullable' => false, @@ -253,11 +274,11 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'number', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'runtime' => [ - 'capability' => false, 'description' => 'Runtime', 'identifier' => 'runtime', 'nullable' => false, @@ -265,11 +286,11 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'showtimes' => [ - 'capability' => false, 'description' => 'Non-theater specific showtimes', 'identifier' => 'showtimes', 'nullable' => false, @@ -277,11 +298,11 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'theaters' => [ - 'capability' => false, 'description' => 'Theaters the movie is currently showing in', 'identifier' => 'theaters', 'nullable' => false, @@ -289,11 +310,11 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => '\Mill\Examples\Showtimes\Representations\Theater', 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'uri' => [ - 'capability' => false, 'description' => 'Movie URI', 'identifier' => 'uri', 'nullable' => false, @@ -301,14 +322,14 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'uri', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], 'content.exploded' => [ 'cast' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'Cast', 'identifier' => 'cast', 'nullable' => false, @@ -321,13 +342,13 @@ public function providerParseDocumentationReturnsRepresentation(): array ], 'subtype' => '\Mill\Examples\Showtimes\Representations\Person', 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], 'content_rating' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'MPAA rating', 'identifier' => 'content_rating', 'nullable' => false, @@ -345,12 +366,12 @@ public function providerParseDocumentationReturnsRepresentation(): array 'UR' => '', 'X' => '' ], + 'vendor_tags' => [], 'version' => false ] ], 'description' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'Description', 'identifier' => 'description', 'nullable' => false, @@ -358,13 +379,13 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], 'director' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'Director', 'identifier' => 'director', 'nullable' => false, @@ -377,83 +398,105 @@ public function providerParseDocumentationReturnsRepresentation(): array ], 'subtype' => false, 'type' => '\Mill\Examples\Showtimes\Representations\Person', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], 'external_urls' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'External URLs', 'identifier' => 'external_urls', 'nullable' => false, 'sample_data' => false, - 'scopes' => [], - 'subtype' => false, - 'type' => 'object', - 'values' => false, + 'scopes' => [ + [ + 'description' => false, + 'scope' => 'public' + ] + ], + 'subtype' => 'object', + 'type' => 'array', + 'values' => [], + 'vendor_tags' => [], 'version' => '>=1.1' ], 'imdb' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'IMDB URL', 'identifier' => 'external_urls.imdb', 'nullable' => false, 'sample_data' => false, - 'scopes' => [], + 'scopes' => [ + [ + 'description' => false, + 'scope' => 'public' + ] + ], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => '>=1.1' ] ], 'tickets' => [ - '__FIELD_DATA__' => [ - 'capability' => 'BUY_TICKETS', + '__NESTED_DATA__' => [ 'description' => 'Tickets URL', 'identifier' => 'external_urls.tickets', 'nullable' => false, 'sample_data' => false, - 'scopes' => [], + 'scopes' => [ + [ + 'description' => false, + 'scope' => 'public' + ] + ], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [ + 'tag:BUY_TICKETS' + ], 'version' => '>=1.1 <1.1.3' ] ], 'trailer' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'Trailer URL', 'identifier' => 'external_urls.trailer', 'nullable' => false, 'sample_data' => false, - 'scopes' => [], + 'scopes' => [ + [ + 'description' => false, + 'scope' => 'public' + ] + ], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => '>=1.1' ] ] ], 'genres' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'Genres', 'identifier' => 'genres', 'nullable' => false, 'sample_data' => false, 'scopes' => [], - 'subtype' => false, + 'subtype' => 'uri', 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], 'id' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'Unique ID', 'identifier' => 'id', 'nullable' => false, @@ -461,13 +504,13 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'number', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], 'kid_friendly' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'Kid friendly?', 'identifier' => 'kid_friendly', 'nullable' => false, @@ -475,13 +518,13 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'boolean', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], 'name' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'Name', 'identifier' => 'name', 'nullable' => false, @@ -489,14 +532,14 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], 'purchase' => [ 'url' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'URL to purchase the film.', 'identifier' => 'purchase.url', 'nullable' => false, @@ -504,14 +547,14 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ] ], 'rotten_tomatoes_score' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'Rotten Tomatoes score', 'identifier' => 'rotten_tomatoes_score', 'nullable' => false, @@ -519,13 +562,13 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'number', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], 'runtime' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'Runtime', 'identifier' => 'runtime', 'nullable' => false, @@ -533,13 +576,13 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], 'showtimes' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'Non-theater specific showtimes', 'identifier' => 'showtimes', 'nullable' => false, @@ -547,13 +590,13 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], 'theaters' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'Theaters the movie is currently showing in', 'identifier' => 'theaters', 'nullable' => false, @@ -561,13 +604,13 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => '\Mill\Examples\Showtimes\Representations\Theater', 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ], 'uri' => [ - '__FIELD_DATA__' => [ - 'capability' => false, + '__NESTED_DATA__' => [ 'description' => 'Movie URI', 'identifier' => 'uri', 'nullable' => false, @@ -575,7 +618,8 @@ public function providerParseDocumentationReturnsRepresentation(): array 'scopes' => [], 'subtype' => false, 'type' => 'uri', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ] diff --git a/tests/Parser/Representation/RepresentationParserTest.php b/tests/Parser/Representation/RepresentationParserTest.php index 1a3cddd..219de48 100644 --- a/tests/Parser/Representation/RepresentationParserTest.php +++ b/tests/Parser/Representation/RepresentationParserTest.php @@ -156,11 +156,11 @@ public function testRepresentationThatHasVersioningAcrossMultipleAnnotations(): return $annotation->toArray(); }, $annotations); - $this->assertEmpty($annotations['connections']['capability']); - $this->assertSame('FEATURE_FLAG', $annotations['connections.things']['capability']); - $this->assertSame('MOVIE_RATINGS', $annotations['connections.things.name']['capability']); - $this->assertSame('FEATURE_FLAG', $annotations['connections.things.uri']['capability']); - $this->assertEmpty($annotations['unrelated']['capability']); + $this->assertEmpty($annotations['connections']['vendor_tags']); + $this->assertSame(['tag:FEATURE_FLAG'], $annotations['connections.things']['vendor_tags']); + $this->assertSame(['tag:MOVIE_RATINGS'], $annotations['connections.things.name']['vendor_tags']); + $this->assertSame(['tag:FEATURE_FLAG'], $annotations['connections.things.uri']['vendor_tags']); + $this->assertEmpty($annotations['unrelated']['vendor_tags']); $this->assertSame('>=3.3', $annotations['connections']['version']); $this->assertSame('>=3.3', $annotations['connections.things']['version']); @@ -184,7 +184,6 @@ public function providerParseAnnotations(): array 'expected' => [ 'annotations' => [ 'cast' => [ - 'capability' => false, 'description' => 'Cast', 'identifier' => 'cast', 'nullable' => false, @@ -197,11 +196,11 @@ public function providerParseAnnotations(): array ], 'subtype' => '\Mill\Examples\Showtimes\Representations\Person', 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'content_rating' => [ - 'capability' => false, 'description' => 'MPAA rating', 'identifier' => 'content_rating', 'nullable' => false, @@ -219,10 +218,10 @@ public function providerParseAnnotations(): array 'UR' => '', 'X' => '' ], + 'vendor_tags' => [], 'version' => false ], 'description' => [ - 'capability' => false, 'description' => 'Description', 'identifier' => 'description', 'nullable' => false, @@ -230,11 +229,11 @@ public function providerParseAnnotations(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'director' => [ - 'capability' => false, 'description' => 'Director', 'identifier' => 'director', 'nullable' => false, @@ -247,71 +246,93 @@ public function providerParseAnnotations(): array ], 'subtype' => false, 'type' => '\Mill\Examples\Showtimes\Representations\Person', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'external_urls' => [ - 'capability' => false, 'description' => 'External URLs', 'identifier' => 'external_urls', 'nullable' => false, 'sample_data' => false, - 'scopes' => [], - 'subtype' => false, - 'type' => 'object', - 'values' => false, + 'scopes' => [ + [ + 'description' => false, + 'scope' => 'public' + ] + ], + 'subtype' => 'object', + 'type' => 'array', + 'values' => [], + 'vendor_tags' => [], 'version' => '>=1.1' ], 'external_urls.imdb' => [ - 'capability' => false, 'description' => 'IMDB URL', 'identifier' => 'external_urls.imdb', 'nullable' => false, 'sample_data' => false, - 'scopes' => [], + 'scopes' => [ + [ + 'description' => false, + 'scope' => 'public' + ] + ], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => '>=1.1' ], 'external_urls.tickets' => [ - 'capability' => 'BUY_TICKETS', 'description' => 'Tickets URL', 'identifier' => 'external_urls.tickets', 'nullable' => false, 'sample_data' => false, - 'scopes' => [], + 'scopes' => [ + [ + 'description' => false, + 'scope' => 'public' + ] + ], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [ + 'tag:BUY_TICKETS' + ], 'version' => '>=1.1 <1.1.3' ], 'external_urls.trailer' => [ - 'capability' => false, 'description' => 'Trailer URL', 'identifier' => 'external_urls.trailer', 'nullable' => false, 'sample_data' => false, - 'scopes' => [], + 'scopes' => [ + [ + 'description' => false, + 'scope' => 'public' + ] + ], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => '>=1.1' ], 'genres' => [ - 'capability' => false, 'description' => 'Genres', 'identifier' => 'genres', 'nullable' => false, 'sample_data' => false, 'scopes' => [], - 'subtype' => false, + 'subtype' => 'uri', 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'id' => [ - 'capability' => false, 'description' => 'Unique ID', 'identifier' => 'id', 'nullable' => false, @@ -319,11 +340,11 @@ public function providerParseAnnotations(): array 'scopes' => [], 'subtype' => false, 'type' => 'number', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'kid_friendly' => [ - 'capability' => false, 'description' => 'Kid friendly?', 'identifier' => 'kid_friendly', 'nullable' => false, @@ -331,11 +352,11 @@ public function providerParseAnnotations(): array 'scopes' => [], 'subtype' => false, 'type' => 'boolean', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'name' => [ - 'capability' => false, 'description' => 'Name', 'identifier' => 'name', 'nullable' => false, @@ -343,11 +364,11 @@ public function providerParseAnnotations(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'purchase.url' => [ - 'capability' => false, 'description' => 'URL to purchase the film.', 'identifier' => 'purchase.url', 'nullable' => false, @@ -355,11 +376,11 @@ public function providerParseAnnotations(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'rotten_tomatoes_score' => [ - 'capability' => false, 'description' => 'Rotten Tomatoes score', 'identifier' => 'rotten_tomatoes_score', 'nullable' => false, @@ -367,11 +388,11 @@ public function providerParseAnnotations(): array 'scopes' => [], 'subtype' => false, 'type' => 'number', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'runtime' => [ - 'capability' => false, 'description' => 'Runtime', 'identifier' => 'runtime', 'nullable' => false, @@ -379,11 +400,11 @@ public function providerParseAnnotations(): array 'scopes' => [], 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'showtimes' => [ - 'capability' => false, 'description' => 'Non-theater specific showtimes', 'identifier' => 'showtimes', 'nullable' => false, @@ -391,11 +412,11 @@ public function providerParseAnnotations(): array 'scopes' => [], 'subtype' => false, 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'theaters' => [ - 'capability' => false, 'description' => 'Theaters the movie is currently showing in', 'identifier' => 'theaters', 'nullable' => false, @@ -403,11 +424,11 @@ public function providerParseAnnotations(): array 'scopes' => [], 'subtype' => '\Mill\Examples\Showtimes\Representations\Theater', 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ], 'uri' => [ - 'capability' => false, 'description' => 'Movie URI', 'identifier' => 'uri', 'nullable' => false, @@ -415,7 +436,8 @@ public function providerParseAnnotations(): array 'scopes' => [], 'subtype' => false, 'type' => 'uri', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false ] ] diff --git a/tests/Parser/Resource/Action/DocumentationTest.php b/tests/Parser/Resource/Action/DocumentationTest.php index 8f0b77c..3b94874 100644 --- a/tests/Parser/Resource/Action/DocumentationTest.php +++ b/tests/Parser/Resource/Action/DocumentationTest.php @@ -2,6 +2,7 @@ namespace Mill\Tests\Parser\Resource\Action; use Mill\Exceptions\BaseException; +use Mill\Parser\Annotations\MaxVersionAnnotation; use Mill\Parser\Annotations\MinVersionAnnotation; use Mill\Parser\Resource\Action\Documentation; use Mill\Parser\Version; @@ -25,22 +26,6 @@ public function testParseMethodDocumentation(string $method, array $expected): v $this->assertMethodDocumentation($parser, $class_stub, $method, $expected); } - /** - * @dataProvider providerParseMethodDocumentation - * @param string $method - * @param array $expected - */ - public function testHydrate(string $method, array $expected): void - { - $class_stub = '\Mill\Examples\Showtimes\Controllers\Movie'; - $parser = (new Documentation($class_stub, $method))->parse(); - $docs = $parser->toArray(); - - $hydrate = Documentation::hydrate($docs); - - $this->assertMethodDocumentation($hydrate, $class_stub, $method, $expected); - } - private function assertMethodDocumentation( Documentation $parser, string $class, @@ -64,7 +49,7 @@ private function assertMethodDocumentation( ); } - $this->assertCount($expected['capabilities.total'], $parser->getCapabilities()); + $this->assertCount($expected['vendor_tags.total'], $parser->getVendorTags()); /** @var \Mill\Parser\Annotations\MinVersionAnnotation $min_version */ $min_version = $parser->getMinimumVersion(); @@ -75,6 +60,15 @@ private function assertMethodDocumentation( $this->assertNull($min_version); } + /** @var \Mill\Parser\Annotations\MaxVersionAnnotation $max_version */ + $max_version = $parser->getMaximumVersion(); + if ($expected['maximum_version']) { + $this->assertInstanceOf(MaxVersionAnnotation::class, $max_version); + $this->assertSame($expected['maximum_version'], $max_version->getMaximumVersion()); + } else { + $this->assertNull($max_version); + } + $this->assertCount(count($expected['annotations']), $parser->getAnnotations()); if (!isset($expected['annotations']['scope'])) { @@ -98,6 +92,7 @@ private function assertMethodDocumentation( $this->assertSame($expected['description'], $docs['description']); $this->assertSame($method, $docs['method']); $this->assertSame($expected['content_types'], $docs['content_types']); + $this->assertSame($expected['path'], $parser->getPath()->getPath()); if (empty($docs['annotations'])) { $this->fail('No parsed annotations for ' . $class); @@ -126,6 +121,15 @@ private function assertMethodDocumentation( ); } } + + // Verify exploded parameter dot notation. + foreach ($parser->getExplodedParameterDotNotation() as $annotation => $data) { + $this->assertSame($expected['params.exploded'][$annotation], $data, '`' . $annotation . '` mismatch'); + } + + foreach ($parser->getExplodedQueryParameterDotNotation() as $annotation => $data) { + $this->assertSame($expected['queryparams.exploded'][$annotation], $data, '`' . $annotation . '` mismatch'); + } } /** @@ -195,11 +199,12 @@ public function providerParseMethodDocumentation(): array 'expected' => [ 'label' => 'Get a single movie.', 'description' => $get_description, - 'capabilities.total' => 0, + 'group' => 'Movies', + 'vendor_tags.total' => 0, 'content_types.latest-version' => '1.1.2', 'content_types' => [ [ - 'content_type' => 'application/mill.example.movie', + 'content_type' => 'application/mill.example.movie+json', 'version' => '>=1.1.2' ], [ @@ -207,15 +212,45 @@ public function providerParseMethodDocumentation(): array 'version' => '<1.1.2' ] ], + 'path' => '/movies/+id', 'minimum_version' => false, + 'maximum_version' => false, 'responses.length' => 5, 'annotations' => [ - 'uri' => [ + 'error' => [ + [ + 'description' => 'If the movie could not be found.', + 'error_code' => false, + 'http_code' => '404 Not Found', + 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], + 'version' => false, + 'visible' => true + ], + [ + 'description' => 'For no reason.', + 'error_code' => false, + 'http_code' => '404 Not Found', + 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], + 'version' => '>=1.1.3', + 'visible' => true + ], + [ + 'description' => 'For some other reason.', + 'error_code' => false, + 'http_code' => '404 Not Found', + 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], + 'version' => '>=1.1.3', + 'visible' => true + ] + ], + 'path' => [ [ 'aliased' => true, 'aliases' => [], 'deprecated' => false, - 'namespace' => 'Movies', 'path' => '/movie/+id', 'visible' => false ], @@ -226,31 +261,23 @@ public function providerParseMethodDocumentation(): array 'aliased' => true, 'aliases' => [], 'deprecated' => false, - 'namespace' => 'Movies', 'path' => '/movie/+id', 'visible' => false ] ], 'deprecated' => false, - 'namespace' => 'Movies', 'path' => '/movies/+id', 'visible' => true ] ], - 'uriSegment' => [ - [ - 'description' => 'Movie ID', - 'field' => 'id', - 'type' => 'integer', - 'uri' => '/movie/+id', - 'values' => false - ], + 'pathparam' => [ [ 'description' => 'Movie ID', 'field' => 'id', + 'required' => true, + 'sample_data' => '1234', 'type' => 'integer', - 'uri' => '/movies/+id', - 'values' => false + 'values' => [] ] ], 'return' => [ @@ -271,37 +298,10 @@ public function providerParseMethodDocumentation(): array 'version' => false, 'visible' => true ] - ], - 'throws' => [ - [ - 'capability' => false, - 'description' => 'If the movie could not be found.', - 'error_code' => false, - 'http_code' => '404 Not Found', - 'representation' => '\Mill\Examples\Showtimes\Representations\Error', - 'version' => false, - 'visible' => true - ], - [ - 'capability' => false, - 'description' => 'For no reason.', - 'error_code' => false, - 'http_code' => '404 Not Found', - 'representation' => '\Mill\Examples\Showtimes\Representations\Error', - 'version' => '>=1.1.3', - 'visible' => true - ], - [ - 'capability' => false, - 'description' => 'For some other reason.', - 'error_code' => false, - 'http_code' => '404 Not Found', - 'representation' => '\Mill\Examples\Showtimes\Representations\Error', - 'version' => '>=1.1.3', - 'visible' => true - ] ] - ] + ], + 'params.exploded' => [], + 'queryparams.exploded' => [] ] ], 'PATCH' => [ @@ -309,11 +309,12 @@ public function providerParseMethodDocumentation(): array 'expected' => [ 'label' => 'Update a movie.', 'description' => 'Update a movies data.', - 'capabilities.total' => 0, + 'group' => 'Movies', + 'vendor_tags.total' => 0, 'content_types.latest-version' => '1.1.2', 'content_types' => [ [ - 'content_type' => 'application/mill.example.movie', + 'content_type' => 'application/mill.example.movie+json', 'version' => '>=1.1.2' ], [ @@ -321,188 +322,285 @@ public function providerParseMethodDocumentation(): array 'version' => '<1.1.2' ] ], + 'path' => '/movies/+id', 'minimum_version' => '1.1', + 'maximum_version' => false, 'responses.length' => 8, - 'uri.aliases' => [], + 'path.aliases' => [], 'annotations' => [ - 'uri' => [ + 'error' => [ [ - 'aliased' => false, - 'aliases' => [], - 'deprecated' => false, - 'namespace' => 'Movies', - 'path' => '/movies/+id', + 'description' => 'If there is a problem with the request.', + 'error_code' => false, + 'http_code' => '400 Bad Request', + 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], + 'version' => false, 'visible' => true - ] - ], - 'uriSegment' => [ + ], [ - 'description' => 'Movie ID', - 'field' => 'id', - 'type' => 'integer', - 'uri' => '/movies/+id', - 'values' => false + 'description' => 'If the IMDB URL could not be validated.', + 'error_code' => false, + 'http_code' => '400 Bad Request', + 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], + 'version' => false, + 'visible' => true + ], + [ + 'description' => 'If the movie could not be found.', + 'error_code' => false, + 'http_code' => '404 Not Found', + 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], + 'version' => false, + 'visible' => true + ], + [ + 'description' => 'If the trailer URL could not be validated.', + 'error_code' => false, + 'http_code' => '404 Not Found', + 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], + 'version' => '>=1.1.3', + 'visible' => true + ], + [ + 'description' => 'If something cool happened.', + 'error_code' => '1337', + 'http_code' => '403 Forbidden', + 'representation' => '\Mill\Examples\Showtimes\Representations\CodedError', + 'vendor_tags' => [], + 'version' => '>=1.1.3', + 'visible' => false + ], + [ + 'description' => 'If the user is not allowed to edit that movie.', + 'error_code' => '666', + 'http_code' => '403 Forbidden', + 'representation' => '\Mill\Examples\Showtimes\Representations\CodedError', + 'vendor_tags' => [], + 'version' => '>=1.1.3', + 'visible' => true ] ], - 'minVersion' => [ + 'minversion' => [ [ 'minimum_version' => '1.1' ] ], 'param' => [ 'cast' => [ - 'capability' => false, 'deprecated' => false, - 'description' => 'Array of names of the cast.', + 'description' => 'Array of cast members.', 'field' => 'cast', 'nullable' => false, 'required' => false, 'sample_data' => false, + 'subtype' => 'object', 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], + 'version' => false, + 'visible' => true + ], + 'cast.name' => [ + 'deprecated' => false, + 'description' => 'Cast member name.', + 'field' => 'cast.name', + 'nullable' => false, + 'required' => false, + 'sample_data' => 'Natasha Hovey', + 'subtype' => false, + 'type' => 'string', + 'values' => [], + 'vendor_tags' => [], + 'version' => false, + 'visible' => true + ], + 'cast.role' => [ + 'deprecated' => false, + 'description' => 'Cast member role.', + 'field' => 'cast.role', + 'nullable' => false, + 'required' => false, + 'sample_data' => 'Cheryl', + 'subtype' => false, + 'type' => 'string', + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ], 'content_rating' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'MPAA rating', 'field' => 'content_rating', 'nullable' => false, 'required' => false, - 'sample_data' => false, + 'sample_data' => 'NR', + 'subtype' => false, 'type' => 'enum', 'values' => [ - 'G' => '', - 'NC-17' => '', - 'NR' => '', - 'PG' => '', - 'PG-13' => '', - 'R' => '', - 'UR' => '', - 'X' => '' + 'G' => 'Rated G', + 'NC-17' => 'Rated NC-17', + 'NR' => 'Not rated', + 'PG' => 'Rated PG', + 'PG-13' => 'Rated PG-13', + 'R' => 'Rated R', + 'UR' => 'Unrated', + 'X' => 'Rated X' ], + 'vendor_tags' => [], 'version' => false, 'visible' => true ], 'description' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'Description, or tagline, for the movie.', 'field' => 'description', 'nullable' => false, 'required' => true, 'sample_data' => false, + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ], 'director' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'Name of the director.', 'field' => 'director', 'nullable' => false, 'required' => false, - 'sample_data' => false, + 'sample_data' => 'Lamberto Bava', + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ], 'is_kid_friendly' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'Is this movie kid friendly?', 'field' => 'is_kid_friendly', 'nullable' => false, 'required' => false, 'sample_data' => false, + 'subtype' => false, 'type' => 'boolean', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ], 'name' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'Name of the movie.', 'field' => 'name', 'nullable' => false, 'required' => true, - 'sample_data' => false, + 'sample_data' => 'Demons', + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ], 'genres' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'Array of movie genres.', 'field' => 'genres', 'nullable' => false, 'required' => false, 'sample_data' => false, + 'subtype' => false, 'type' => 'array', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ], 'imdb' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'IMDB URL', 'field' => 'imdb', 'nullable' => false, 'required' => false, - 'sample_data' => false, + 'sample_data' => 'https://www.imdb.com/title/tt0089013/', + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => '>=1.1.1', 'visible' => true ], 'rotten_tomatoes_score' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'Rotten Tomatoes score', 'field' => 'rotten_tomatoes_score', 'nullable' => false, 'required' => false, - 'sample_data' => false, + 'sample_data' => '56', + 'subtype' => false, 'type' => 'integer', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ], 'runtime' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'Movie runtime, in `HHhr MMmin` format.', 'field' => 'runtime', 'nullable' => false, 'required' => false, - 'sample_data' => false, + 'sample_data' => '1hr 20min', + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ], 'trailer' => [ - 'capability' => false, 'deprecated' => false, 'description' => 'Trailer URL', 'field' => 'trailer', 'nullable' => true, 'required' => false, - 'sample_data' => false, + 'sample_data' => 'https://www.youtube.com/watch?v=_cNjTdFHL8E', + 'subtype' => false, 'type' => 'string', - 'values' => false, + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ] ], + 'path' => [ + [ + 'aliased' => false, + 'aliases' => [], + 'deprecated' => false, + 'path' => '/movies/+id', + 'visible' => true + ] + ], + 'pathparam' => [ + [ + 'description' => 'Movie ID', + 'field' => 'id', + 'required' => true, + 'sample_data' => '1234', + 'type' => 'integer', + 'values' => [] + ] + ], 'return' => [ [ 'description' => false, @@ -526,64 +624,228 @@ public function providerParseMethodDocumentation(): array 'description' => false, 'scope' => 'edit' ] - ], - 'throws' => [ - [ - 'capability' => false, - 'description' => 'If there is a problem with the request.', - 'error_code' => false, - 'http_code' => '400 Bad Request', - 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + ] + ], + 'params.exploded' => [ + 'cast' => [ + '__NESTED_DATA__' => [ + 'deprecated' => false, + 'description' => 'Array of cast members.', + 'field' => 'cast', + 'nullable' => false, + 'required' => false, + 'sample_data' => false, + 'subtype' => 'object', + 'type' => 'array', + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true ], - [ - 'capability' => false, - 'description' => 'If the IMDB URL could not be validated.', - 'error_code' => false, - 'http_code' => '400 Bad Request', - 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'name' => [ + '__NESTED_DATA__' => [ + 'deprecated' => false, + 'description' => 'Cast member name.', + 'field' => 'cast.name', + 'nullable' => false, + 'required' => false, + 'sample_data' => 'Natasha Hovey', + 'subtype' => false, + 'type' => 'string', + 'values' => [], + 'vendor_tags' => [], + 'version' => false, + 'visible' => true + ] + ], + 'role' => [ + '__NESTED_DATA__' => [ + 'deprecated' => false, + 'description' => 'Cast member role.', + 'field' => 'cast.role', + 'nullable' => false, + 'required' => false, + 'sample_data' => 'Cheryl', + 'subtype' => false, + 'type' => 'string', + 'values' => [], + 'vendor_tags' => [], + 'version' => false, + 'visible' => true + ] + ], + ], + 'content_rating' => [ + '__NESTED_DATA__' => [ + 'deprecated' => false, + 'description' => 'MPAA rating', + 'field' => 'content_rating', + 'nullable' => false, + 'required' => false, + 'sample_data' => 'NR', + 'subtype' => false, + 'type' => 'enum', + 'values' => [ + 'G' => 'Rated G', + 'NC-17' => 'Rated NC-17', + 'NR' => 'Not rated', + 'PG' => 'Rated PG', + 'PG-13' => 'Rated PG-13', + 'R' => 'Rated R', + 'UR' => 'Unrated', + 'X' => 'Rated X' + ], + 'vendor_tags' => [], 'version' => false, 'visible' => true - ], - [ - 'capability' => false, - 'description' => 'If the movie could not be found.', - 'error_code' => false, - 'http_code' => '404 Not Found', - 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + ] + ], + 'description' => [ + '__NESTED_DATA__' => [ + 'deprecated' => false, + 'description' => 'Description, or tagline, for the movie.', + 'field' => 'description', + 'nullable' => false, + 'required' => true, + 'sample_data' => false, + 'subtype' => false, + 'type' => 'string', + 'values' => [], + 'vendor_tags' => [], 'version' => false, 'visible' => true - ], - [ - 'capability' => false, - 'description' => 'If the trailer URL could not be validated.', - 'error_code' => false, - 'http_code' => '404 Not Found', - 'representation' => '\Mill\Examples\Showtimes\Representations\Error', - 'version' => '>=1.1.3', + ] + ], + 'director' => [ + '__NESTED_DATA__' => [ + 'deprecated' => false, + 'description' => 'Name of the director.', + 'field' => 'director', + 'nullable' => false, + 'required' => false, + 'sample_data' => 'Lamberto Bava', + 'subtype' => false, + 'type' => 'string', + 'values' => [], + 'vendor_tags' => [], + 'version' => false, 'visible' => true - ], - [ - 'capability' => false, - 'description' => 'If something cool happened.', - 'error_code' => '1337', - 'http_code' => '403 Forbidden', - 'representation' => '\Mill\Examples\Showtimes\Representations\CodedError', - 'version' => '>=1.1.3', - 'visible' => false - ], - [ - 'capability' => false, - 'description' => 'If the user is not allowed to edit that movie.', - 'error_code' => '666', - 'http_code' => '403 Forbidden', - 'representation' => '\Mill\Examples\Showtimes\Representations\CodedError', - 'version' => '>=1.1.3', + ] + ], + 'is_kid_friendly' => [ + '__NESTED_DATA__' => [ + 'deprecated' => false, + 'description' => 'Is this movie kid friendly?', + 'field' => 'is_kid_friendly', + 'nullable' => false, + 'required' => false, + 'sample_data' => false, + 'subtype' => false, + 'type' => 'boolean', + 'values' => [], + 'vendor_tags' => [], + 'version' => false, + 'visible' => true + ] + ], + 'name' => [ + '__NESTED_DATA__' => [ + 'deprecated' => false, + 'description' => 'Name of the movie.', + 'field' => 'name', + 'nullable' => false, + 'required' => true, + 'sample_data' => 'Demons', + 'subtype' => false, + 'type' => 'string', + 'values' => [], + 'vendor_tags' => [], + 'version' => false, + 'visible' => true + ] + ], + 'genres' => [ + '__NESTED_DATA__' => [ + 'deprecated' => false, + 'description' => 'Array of movie genres.', + 'field' => 'genres', + 'nullable' => false, + 'required' => false, + 'sample_data' => false, + 'subtype' => false, + 'type' => 'array', + 'values' => [], + 'vendor_tags' => [], + 'version' => false, + 'visible' => true + ] + ], + 'imdb' => [ + '__NESTED_DATA__' => [ + 'deprecated' => false, + 'description' => 'IMDB URL', + 'field' => 'imdb', + 'nullable' => false, + 'required' => false, + 'sample_data' => 'https://www.imdb.com/title/tt0089013/', + 'subtype' => false, + 'type' => 'string', + 'values' => [], + 'vendor_tags' => [], + 'version' => '>=1.1.1', + 'visible' => true + ] + ], + 'rotten_tomatoes_score' => [ + '__NESTED_DATA__' => [ + 'deprecated' => false, + 'description' => 'Rotten Tomatoes score', + 'field' => 'rotten_tomatoes_score', + 'nullable' => false, + 'required' => false, + 'sample_data' => '56', + 'subtype' => false, + 'type' => 'integer', + 'values' => [], + 'vendor_tags' => [], + 'version' => false, + 'visible' => true + ] + ], + 'runtime' => [ + '__NESTED_DATA__' => [ + 'deprecated' => false, + 'description' => 'Movie runtime, in `HHhr MMmin` format.', + 'field' => 'runtime', + 'nullable' => false, + 'required' => false, + 'sample_data' => '1hr 20min', + 'subtype' => false, + 'type' => 'string', + 'values' => [], + 'vendor_tags' => [], + 'version' => false, + 'visible' => true + ] + ], + 'trailer' => [ + '__NESTED_DATA__' => [ + 'deprecated' => false, + 'description' => 'Trailer URL', + 'field' => 'trailer', + 'nullable' => true, + 'required' => false, + 'sample_data' => 'https://www.youtube.com/watch?v=_cNjTdFHL8E', + 'subtype' => false, + 'type' => 'string', + 'values' => [], + 'vendor_tags' => [], + 'version' => false, 'visible' => true ] ] - ] + ], + 'queryparams.exploded' => [] ] ], 'DELETE' => [ @@ -591,7 +853,8 @@ public function providerParseMethodDocumentation(): array 'expected' => [ 'label' => 'Delete a movie.', 'description' => 'Delete a movie.', - 'capabilities.total' => 1, + 'group' => 'Movies', + 'vendor_tags.total' => 1, 'content_types.latest-version' => null, 'content_types' => [ [ @@ -599,37 +862,50 @@ public function providerParseMethodDocumentation(): array 'version' => false ] ], + 'path' => '/movies/+id', 'minimum_version' => '1.1', + 'maximum_version' => '1.1.2', 'responses.length' => 2, - 'uri.aliases' => [], + 'path.aliases' => [], 'annotations' => [ - 'capability' => [ + 'error' => [ [ - 'capability' => 'DELETE_CONTENT' + 'description' => 'If the movie could not be found.', + 'error_code' => false, + 'http_code' => '404 Not Found', + 'representation' => '\Mill\Examples\Showtimes\Representations\Error', + 'vendor_tags' => [], + 'version' => false, + 'visible' => false ] ], - 'uri' => [ + 'maxversion' => [ + [ + 'maximum_version' => '1.1.2' + ] + ], + 'minversion' => [ + [ + 'minimum_version' => '1.1' + ] + ], + 'path' => [ [ 'aliased' => false, 'aliases' => [], 'deprecated' => false, - 'namespace' => 'Movies', 'path' => '/movies/+id', 'visible' => false ] ], - 'minVersion' => [ - [ - 'minimum_version' => '1.1' - ] - ], - 'uriSegment' => [ + 'pathparam' => [ [ 'description' => 'Movie ID', 'field' => 'id', + 'required' => true, + 'sample_data' => '1234', 'type' => 'integer', - 'uri' => '/movies/+id', - 'values' => false + 'values' => [] ] ], 'return' => [ @@ -648,18 +924,14 @@ public function providerParseMethodDocumentation(): array 'scope' => 'delete' ] ], - 'throws' => [ + 'vendortag' => [ [ - 'capability' => false, - 'description' => 'If the movie could not be found.', - 'error_code' => false, - 'http_code' => '404 Not Found', - 'representation' => '\Mill\Examples\Showtimes\Representations\Error', - 'version' => false, - 'visible' => false + 'vendor_tag' => 'tag:DELETE_CONTENT' ] ] - ] + ], + 'params.exploded' => [], + 'queryparams.exploded' => [] ] ] ]; @@ -668,22 +940,23 @@ public function providerParseMethodDocumentation(): array public function providerParsingOfSpecificUseCases(): array { return [ - 'with-aliased-uris' => [ + 'with-aliased-paths' => [ 'docblock' => '/** * @api-label Update a piece of content. + * @api-group Foo\Bar * - * @api-uri:public {Foo\Bar} /foo - * @api-uri:private:alias {Foo\Bar} /bar + * @api-path:public /foo + * @api-path:private:alias /bar * - * @api-contentType application/json + * @api-contenttype application/json * @api-scope public * * @api-return:public {ok} */', 'asserts' => [ - 'getUris' => [ + 'getPaths' => [ 'total' => 2, - 'annotation.name' => 'uri', + 'annotation.name' => 'path', 'data' => [ [ 'aliased' => false, @@ -692,13 +965,11 @@ public function providerParsingOfSpecificUseCases(): array 'aliased' => true, 'aliases' => [], 'deprecated' => false, - 'namespace' => 'Foo\Bar', 'path' => '/bar', 'visible' => false ] ], 'deprecated' => false, - 'namespace' => 'Foo\Bar', 'path' => '/foo', 'visible' => true ], @@ -706,7 +977,6 @@ public function providerParsingOfSpecificUseCases(): array 'aliased' => true, 'aliases' => [], 'deprecated' => false, - 'namespace' => 'Foo\Bar', 'path' => '/bar', 'visible' => false ] @@ -717,25 +987,25 @@ public function providerParsingOfSpecificUseCases(): array 'with-multiple-visibilities' => [ 'docblock' => '/** * @api-label Update a piece of content. + * @api-group Foo\Bar * - * @api-uri:public {Foo\Bar} /foo - * @api-uri:private {Foo\Bar} /bar + * @api-path:public /foo + * @api-path:private /bar * - * @api-contentType application/json + * @api-contenttype application/json * @api-scope public * * @api-return:public {ok} */', 'asserts' => [ - 'getUris' => [ + 'getPaths' => [ 'total' => 2, - 'annotation.name' => 'uri', + 'annotation.name' => 'path', 'data' => [ [ 'aliased' => false, 'aliases' => [], 'deprecated' => false, - 'namespace' => 'Foo\Bar', 'path' => '/foo', 'visible' => true ], @@ -743,7 +1013,6 @@ public function providerParsingOfSpecificUseCases(): array 'aliased' => false, 'aliases' => [], 'deprecated' => false, - 'namespace' => 'Foo\Bar', 'path' => '/bar', 'visible' => false ] @@ -754,22 +1023,23 @@ public function providerParsingOfSpecificUseCases(): array 'with-capabilities' => [ 'docblock' => '/** * @api-label Delete a piece of content. + * @api-group Foo\Bar * - * @api-uri:private {Foo\Bar} /foo + * @api-path:private /foo * - * @api-contentType application/json + * @api-contenttype application/json * @api-scope delete - * @api-capability DELETE_CONTENT + * @api-vendortag tag:DELETE_CONTENT * * @api-return:private {deleted} */', 'asserts' => [ - 'getCapabilities' => [ + 'getVendorTags' => [ 'total' => 1, - 'annotation.name' => 'capability', + 'annotation.name' => 'vendortag', 'data' => [ [ - 'capability' => 'DELETE_CONTENT' + 'vendor_tag' => 'tag:DELETE_CONTENT' ] ] ] @@ -790,7 +1060,7 @@ public function providerMethodsThatWillFailParsing(): array 'docblock' => '/** * Test throwing an exception when a required `@api-label` annotation is missing. * - * @api-uri {Something} /some/page + * @api-path /some/page */', 'expected.exception' => '\Mill\Exceptions\Annotations\RequiredAnnotationException', 'expected.exception.asserts' => [ @@ -811,14 +1081,15 @@ public function providerMethodsThatWillFailParsing(): array ], 'missing-required-content-type-annotation' => [ 'docblock' => '/** - * Test throwing an exception when a required `@api-contentType` annotation is missing. + * Test throwing an exception when a required `@api-contenttype` annotation is missing. * * @api-label Test Method - * @api-uri {Something} /some/page + * @api-group Something + * @api-path /some/page */', 'expected.exception' => '\Mill\Exceptions\Annotations\RequiredAnnotationException', 'expected.exception.asserts' => [ - 'getAnnotation' => 'contentType' + 'getAnnotation' => 'contenttype' ] ], 'missing-required-visibility-decorator' => [ @@ -826,13 +1097,14 @@ public function providerMethodsThatWillFailParsing(): array * Test throwing an exception when a required visibility decorator is missing on an annotation. * * @api-label Test method - * @api-uri {Root} / - * @api-contentType application/json + * @api-group Root + * @api-path / + * @api-contenttype application/json * @api-return:public {collection} \Mill\Examples\Showtimes\Representations\Representation */', 'expected.exception' => '\Mill\Exceptions\Resource\MissingVisibilityDecoratorException', 'expected.exception.asserts' => [ - 'getAnnotation' => 'uri' + 'getAnnotation' => 'path' ] ], 'unsupported-decorator' => [ @@ -840,27 +1112,29 @@ public function providerMethodsThatWillFailParsing(): array * Test throwing an exception when an unsupported decorator is found. * * @api-label Test method - * @api-uri:special {Root} / - * @api-contentType application/json + * @api-group Root + * @api-path:special / + * @api-contenttype application/json * @api-return {collection} \Mill\Examples\Showtimes\Representations\Representation */', 'expected.exception' => '\Mill\Exceptions\Resource\UnsupportedDecoratorException', 'expected.exception.asserts' => [ 'getDecorator' => 'special', - 'getAnnotation' => 'uri' + 'getAnnotation' => 'path' ] ], - 'required-uri-annotation-missing' => [ + 'required-path-annotation-missing' => [ 'docblock' => '/** - * Test throwing an exception when a required `@api-uri` annotation is missing. + * Test throwing an exception when a required `@api-path` annotation is missing. * * @api-label Test method - * @api-contentType application/json + * @api-group Something + * @api-contenttype application/json * @api-param:public {page} */', 'expected.exception' => '\Mill\Exceptions\Annotations\RequiredAnnotationException', 'expected.exception.asserts' => [ - 'getAnnotation' => 'uri' + 'getAnnotation' => 'path' ] ], 'public-annotations-on-a-private-action' => [ @@ -868,34 +1142,34 @@ public function providerMethodsThatWillFailParsing(): array * Test throwing an exception when there are private annotations on a private action. * * @api-label Test method - * @api-uri:private {Search} /search - * @api-contentType application/json + * @api-group Search + * @api-path:private /search + * @api-contenttype application/json * @api-scope public * @api-return:private {collection} \Mill\Examples\Showtimes\Representations\Representation - * @api-throws:public {403} \Mill\Examples\Showtimes\Representations\CodedError - * (Mill\Examples\Showtimes\Representations\CodedError::DISALLOWED) If the user isn\'t allowed to - * do something. + * @api-error:public 403 (\Mill\Examples\Showtimes\Representations\CodedError<666>) - If the user + * isn\'t allowed to do something. */', 'expected.exception' => '\Mill\Exceptions\Resource\PublicDecoratorOnPrivateActionException', 'expected.exception.asserts' => [ - 'getAnnotation' => 'throws' + 'getAnnotation' => 'error' ] ], 'too-many-aliases' => [ 'docblock' => '/** - * Test throwing an exception when there are private annotations on a private action. + * Test throwing an exception when there is no canonical path and only path aliases. * * @api-label Test method - * @api-uri:private:alias {Search} /search - * @api-uri:private:alias {Search} /search2 - * @api-contentType application/json + * @api-group Search + * @api-path:private:alias /search + * @api-path:private:alias /search2 + * @api-contenttype application/json * @api-scope public * @api-return:private {collection} \Mill\Examples\Showtimes\Representations\Representation - * @api-throws:public {403} \Mill\Examples\Showtimes\Representations\CodedError - * (Mill\Examples\Showtimes\Representations\CodedError::DISALLOWED) If the user isn\'t allowed to - * do something. + * @api-error:public 403 (\Mill\Examples\Showtimes\Representations\CodedError<666>) - If the user + * isn\'t allowed to do something. */', - 'expected.exception' => '\Mill\Exceptions\Resource\TooManyAliasedUrisException', + 'expected.exception' => '\Mill\Exceptions\Resource\TooManyAliasedPathsException', 'expected.exception.asserts' => [] ] ]; diff --git a/tests/ParserTest.php b/tests/ParserTest.php index b8deb45..31aad5b 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -58,11 +58,12 @@ public function testParsingADeprecatedDecorator(): void { $this->overrideReadersWithFakeDocblockReturn('/** * @api-label Update a piece of content. + * @api-group Foo\Bar * - * @api-uri:public {Foo\Bar} /foo - * @api-uri:private:deprecated {Foo\Bar} /bar + * @api-path:public /foo + * @api-path:private:deprecated /bar * - * @api-contentType application/json + * @api-contenttype application/json * @api-scope public * * @api-return:public {ok} @@ -70,9 +71,9 @@ public function testParsingADeprecatedDecorator(): void $annotations = (new Parser(__CLASS__))->getAnnotations(__METHOD__); - $this->assertArrayHasKey('uri', $annotations); - $this->assertFalse($annotations['uri'][0]->isDeprecated()); - $this->assertTrue($annotations['uri'][1]->isDeprecated()); + $this->assertArrayHasKey('path', $annotations); + $this->assertFalse($annotations['path'][0]->isDeprecated()); + $this->assertTrue($annotations['path'][1]->isDeprecated()); } public function testParseAnnotationsOnClassMethodThatDoesntExist(): void @@ -93,7 +94,7 @@ public function providerParseAnnotationsOnClassMethod(): array 'GET' => [ 'method' => 'GET', 'expected' => [ - 'contentType' => [ + 'contenttype' => [ 'class' => Parser\Annotations\ContentTypeAnnotation::class, 'count' => 2 ], @@ -101,28 +102,32 @@ public function providerParseAnnotationsOnClassMethod(): array 'class' => Parser\Annotations\DescriptionAnnotation::class, 'count' => 1 ], + 'error' => [ + 'class' => Parser\Annotations\ErrorAnnotation::class, + 'count' => 3 + ], + 'group' => [ + 'class' => Parser\Annotations\GroupAnnotation::class, + 'count' => 1 + ], 'label' => [ 'class' => Parser\Annotations\LabelAnnotation::class, 'count' => 1 ], - 'minVersion' => [ + 'minversion' => [ 'class' => Parser\Annotations\MinVersionAnnotation::class, 'count' => 1 ], - 'return' => [ - 'class' => Parser\Annotations\ReturnAnnotation::class, + 'path' => [ + 'class' => Parser\Annotations\PathAnnotation::class, 'count' => 2 ], - 'throws' => [ - 'class' => Parser\Annotations\ThrowsAnnotation::class, - 'count' => 3 - ], - 'uri' => [ - 'class' => Parser\Annotations\UriAnnotation::class, - 'count' => 2 + 'pathparam' => [ + 'class' => Parser\Annotations\PathParamAnnotation::class, + 'count' => 1 ], - 'uriSegment' => [ - 'class' => Parser\Annotations\UriSegmentAnnotation::class, + 'return' => [ + 'class' => Parser\Annotations\ReturnAnnotation::class, 'count' => 2 ] ] @@ -130,7 +135,7 @@ public function providerParseAnnotationsOnClassMethod(): array 'PATCH' => [ 'method' => 'PATCH', 'expected' => [ - 'contentType' => [ + 'contenttype' => [ 'class' => Parser\Annotations\ContentTypeAnnotation::class, 'count' => 2 ], @@ -138,17 +143,33 @@ public function providerParseAnnotationsOnClassMethod(): array 'class' => Parser\Annotations\DescriptionAnnotation::class, 'count' => 1 ], + 'error' => [ + 'class' => Parser\Annotations\ErrorAnnotation::class, + 'count' => 6 + ], + 'group' => [ + 'class' => Parser\Annotations\GroupAnnotation::class, + 'count' => 1 + ], 'label' => [ 'class' => Parser\Annotations\LabelAnnotation::class, 'count' => 1 ], - 'minVersion' => [ + 'minversion' => [ 'class' => Parser\Annotations\MinVersionAnnotation::class, 'count' => 1 ], 'param' => [ 'class' => Parser\Annotations\ParamAnnotation::class, - 'count' => 11 + 'count' => 13 + ], + 'path' => [ + 'class' => Parser\Annotations\PathAnnotation::class, + 'count' => 1 + ], + 'pathparam' => [ + 'class' => Parser\Annotations\PathParamAnnotation::class, + 'count' => 1 ], 'return' => [ 'class' => Parser\Annotations\ReturnAnnotation::class, @@ -157,29 +178,13 @@ public function providerParseAnnotationsOnClassMethod(): array 'scope' => [ 'class' => Parser\Annotations\ScopeAnnotation::class, 'count' => 1 - ], - 'throws' => [ - 'class' => Parser\Annotations\ThrowsAnnotation::class, - 'count' => 6 - ], - 'uri' => [ - 'class' => Parser\Annotations\UriAnnotation::class, - 'count' => 1 - ], - 'uriSegment' => [ - 'class' => Parser\Annotations\UriSegmentAnnotation::class, - 'count' => 1 ] ] ], 'DELETE' => [ 'method' => 'DELETE', 'expected' => [ - 'capability' => [ - 'class' => Parser\Annotations\CapabilityAnnotation::class, - 'count' => 1 - ], - 'contentType' => [ + 'contenttype' => [ 'class' => Parser\Annotations\ContentTypeAnnotation::class, 'count' => 1 ], @@ -187,32 +192,44 @@ public function providerParseAnnotationsOnClassMethod(): array 'class' => Parser\Annotations\DescriptionAnnotation::class, 'count' => 1 ], + 'error' => [ + 'class' => Parser\Annotations\ErrorAnnotation::class, + 'count' => 1 + ], + 'group' => [ + 'class' => Parser\Annotations\GroupAnnotation::class, + 'count' => 1 + ], 'label' => [ 'class' => Parser\Annotations\LabelAnnotation::class, 'count' => 1 ], - 'minVersion' => [ + 'maxversion' => [ + 'class' => Parser\Annotations\MaxVersionAnnotation::class, + 'count' => 1 + ], + 'minversion' => [ 'class' => Parser\Annotations\MinVersionAnnotation::class, 'count' => 1 ], - 'return' => [ - 'class' => Parser\Annotations\ReturnAnnotation::class, + 'pathparam' => [ + 'class' => Parser\Annotations\PathParamAnnotation::class, 'count' => 1 ], - 'scope' => [ - 'class' => Parser\Annotations\ScopeAnnotation::class, + 'path' => [ + 'class' => Parser\Annotations\PathAnnotation::class, 'count' => 1 ], - 'throws' => [ - 'class' => Parser\Annotations\ThrowsAnnotation::class, + 'return' => [ + 'class' => Parser\Annotations\ReturnAnnotation::class, 'count' => 1 ], - 'uri' => [ - 'class' => Parser\Annotations\UriAnnotation::class, + 'scope' => [ + 'class' => Parser\Annotations\ScopeAnnotation::class, 'count' => 1 ], - 'uriSegment' => [ - 'class' => Parser\Annotations\UriSegmentAnnotation::class, + 'vendortag' => [ + 'class' => Parser\Annotations\VendorTagAnnotation::class, 'count' => 1 ] ] diff --git a/tests/_fixtures/Representations/RepresentationWithVersioningAcrossMultipleAnnotations.php b/tests/_fixtures/Representations/RepresentationWithVersioningAcrossMultipleAnnotations.php index 6ac38dc..094cf75 100644 --- a/tests/_fixtures/Representations/RepresentationWithVersioningAcrossMultipleAnnotations.php +++ b/tests/_fixtures/Representations/RepresentationWithVersioningAcrossMultipleAnnotations.php @@ -18,7 +18,7 @@ public function create(): void */ /** - * @api-data connections.things (object, FEATURE_FLAG) - Information about this thing. + * @api-data connections.things (object, tag:FEATURE_FLAG) - Information about this thing. * @api-scope public * @api-see self::someMethod connections.things */ @@ -32,7 +32,7 @@ public function someMethod(): void */ /** - * @api-data name (string, MOVIE_RATINGS) - Name of a thing. + * @api-data name (string, tag:MOVIE_RATINGS) - Name of a thing. */ } } diff --git a/tests/_fixtures/mill.test.xml b/tests/_fixtures/mill.test.xml index d8e2b3f..389c289 100644 --- a/tests/_fixtures/mill.test.xml +++ b/tests/_fixtures/mill.test.xml @@ -3,6 +3,24 @@ name="Mill unit test API, Showtimes" bootstrap="vendor/autoload.php" > + + + + + + + + + + + + + + + @@ -13,15 +31,13 @@ - - - - - - - - + + + + + + @@ -53,30 +69,41 @@ - - - - - - + + + - - - - - - + + + + + + + + + + + + + page (integer, optional) - The page number to show. per_page (integer, optional) - Number of items to show on each page. Max 100. - filter (string, optional) - Filter to apply to the results. + filter (enum, optional) - Filter to apply to the results. - + - + + + + + + + + diff --git a/website/i18n/en.json b/website/i18n/en.json index 52ffcc8..26afe9b 100644 --- a/website/i18n/en.json +++ b/website/i18n/en.json @@ -3,23 +3,26 @@ "localized-strings": { "next": "Next", "previous": "Previous", - "tagline": "A small annotation DSL for documenting a REST API.", + "tagline": "An annotation-based DSL for documenting a REST API", + "compile-documentation": "Compiling documentation", "configuration": "Configuration", "generate-changelogs": "Generating changelogs", - "generate-documentation": "Generating documentation", "installation": "Installation", - "api-capability": "@api-capability", "api-contenttype": "@api-contenttype", "api-data": "@api-data", + "api-error": "@api-error", + "api-group": "@api-group", "api-label": "@api-label", + "api-maxversion": "@api-maxversion", "api-minversion": "@api-minversion", "api-param": "@api-param", + "api-path": "@api-path", + "api-pathparam": "@api-pathparam", + "api-queryparam": "@api-queryparam", "api-return": "@api-return", "api-scope": "@api-scope", "api-see": "@api-see", - "api-throws": "@api-throws", - "api-uri": "@api-uri", - "api-urisegment": "@api-urisegment", + "api-vendortag": "@api-vendortag", "deprecation": "Deprecation", "representations": "Representations", "resource-actions": "Resource Actions", diff --git a/website/package-lock.json b/website/package-lock.json index fea4e93..41aed6b 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -12,6 +12,12 @@ "negotiator": "0.6.1" } }, + "address": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/address/-/address-1.0.3.tgz", + "integrity": "sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg==", + "dev": true + }, "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", @@ -30,6 +36,21 @@ "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", "dev": true }, + "ansi-escapes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "dev": true + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, "ansi-red": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", @@ -57,6 +78,23 @@ "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", "dev": true }, + "archive-type": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/archive-type/-/archive-type-3.2.0.tgz", + "integrity": "sha1-nNnABpV+vpX62tW9YJiUKoE3N/Y=", + "dev": true, + "requires": { + "file-type": "3.9.0" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", + "dev": true + } + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -66,12 +104,78 @@ "sprintf-js": "1.0.3" } }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, + "array-filter": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, + "array-map": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", + "dev": true + }, + "array-reduce": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -90,6 +194,12 @@ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, + "async-each-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-1.1.0.tgz", + "integrity": "sha1-9C/YFV048hpbjqB8KOBj7RcAsTg=", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -109,7 +219,7 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000833", + "caniuse-db": "1.0.30000856", "normalize-range": "0.1.2", "num2fraction": "1.2.2", "postcss": "5.2.18", @@ -122,8 +232,8 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000833", - "electron-to-chromium": "1.3.45" + "caniuse-db": "1.0.30000856", + "electron-to-chromium": "1.3.48" } } } @@ -779,7 +889,7 @@ "dev": true, "requires": { "babel-runtime": "6.26.0", - "core-js": "2.5.5", + "core-js": "2.5.7", "regenerator-runtime": "0.10.5" }, "dependencies": { @@ -792,9 +902,9 @@ } }, "babel-preset-env": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", - "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", + "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", "dev": true, "requires": { "babel-plugin-check-es2015-constants": "6.22.0", @@ -824,7 +934,7 @@ "babel-plugin-transform-es2015-unicode-regex": "6.24.1", "babel-plugin-transform-exponentiation-operator": "6.24.1", "babel-plugin-transform-regenerator": "6.26.0", - "browserslist": "2.11.3", + "browserslist": "3.2.8", "invariant": "2.2.4", "semver": "5.5.0" } @@ -860,7 +970,7 @@ "requires": { "babel-core": "6.26.3", "babel-runtime": "6.26.0", - "core-js": "2.5.5", + "core-js": "2.5.7", "home-or-tmp": "2.0.0", "lodash": "4.17.10", "mkdirp": "0.5.1", @@ -873,7 +983,7 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.5", + "core-js": "2.5.7", "regenerator-runtime": "0.11.1" } }, @@ -941,6 +1051,149 @@ "tweetnacl": "0.14.5" } }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", + "dev": true + }, + "bin-build": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/bin-build/-/bin-build-2.2.0.tgz", + "integrity": "sha1-EfjdYfcP/Por3KpbRvXo/t1CIcw=", + "dev": true, + "requires": { + "archive-type": "3.2.0", + "decompress": "3.0.0", + "download": "4.4.3", + "exec-series": "1.0.3", + "rimraf": "2.6.2", + "tempfile": "1.1.1", + "url-regex": "3.2.0" + }, + "dependencies": { + "tempfile": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-1.1.1.tgz", + "integrity": "sha1-W8xOrsxKsscH2LwR2ZzMmiyyh/I=", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2", + "uuid": "2.0.3" + } + }, + "uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", + "dev": true + } + } + }, + "bin-check": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-2.0.0.tgz", + "integrity": "sha1-hvjm9CU4k99g3DFpV/WvAqywWTA=", + "dev": true, + "requires": { + "executable": "1.1.0" + } + }, + "bin-version": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-1.0.4.tgz", + "integrity": "sha1-nrSY7m/Xb3q5p8FgQ2+JV5Q1144=", + "dev": true, + "requires": { + "find-versions": "1.2.1" + } + }, + "bin-version-check": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-2.1.0.tgz", + "integrity": "sha1-5OXfKQuQaffRETJAMe/BP90RpbA=", + "dev": true, + "requires": { + "bin-version": "1.0.4", + "minimist": "1.2.0", + "semver": "4.3.6", + "semver-truncate": "1.1.2" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", + "dev": true + } + } + }, + "bin-wrapper": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bin-wrapper/-/bin-wrapper-3.0.2.tgz", + "integrity": "sha1-Z9MwYmLksaXy+I7iNGT2plVneus=", + "dev": true, + "requires": { + "bin-check": "2.0.0", + "bin-version-check": "2.1.0", + "download": "4.4.3", + "each-async": "1.1.1", + "lazy-req": "1.1.0", + "os-filter-obj": "1.0.3" + } + }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "dev": true, + "requires": { + "readable-stream": "2.3.6", + "safe-buffer": "5.1.2" + } + }, + "body": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", + "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", + "dev": true, + "requires": { + "continuable-cache": "0.3.1", + "error": "7.0.2", + "raw-body": "1.1.7", + "safe-json-parse": "1.0.1" + }, + "dependencies": { + "bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", + "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", + "dev": true + }, + "raw-body": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", + "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=", + "dev": true, + "requires": { + "bytes": "1.0.0", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, "body-parser": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", @@ -967,14 +1220,11 @@ } } }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "dev": true, - "requires": { - "hoek": "4.2.1" - } + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true }, "brace-expansion": { "version": "1.1.11", @@ -986,20 +1236,91 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" + } + }, "browserslist": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", - "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", "dev": true, "requires": { - "caniuse-lite": "1.0.30000833", - "electron-to-chromium": "1.3.45" + "caniuse-lite": "1.0.30000856", + "electron-to-chromium": "1.3.48" } }, - "buffer-from": { + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "1.1.0", + "buffer-fill": "1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "buffer-fill": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", - "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "buffer-from": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", + "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==", + "dev": true + }, + "buffer-to-vinyl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-to-vinyl/-/buffer-to-vinyl-1.1.0.tgz", + "integrity": "sha1-APFfruOreh3aLN5tkSG//dB7ImI=", + "dev": true, + "requires": { + "file-type": "3.9.0", + "readable-stream": "2.3.6", + "uuid": "2.0.3", + "vinyl": "1.2.0" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", + "dev": true + }, + "uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", + "dev": true + } + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, "bytes": { @@ -1008,6 +1329,22 @@ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "2.1.1", + "map-obj": "1.0.1" + } + }, "caniuse-api": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", @@ -1015,7 +1352,7 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000833", + "caniuse-db": "1.0.30000856", "lodash.memoize": "4.1.2", "lodash.uniq": "4.5.0" }, @@ -1026,22 +1363,28 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000833", - "electron-to-chromium": "1.3.45" + "caniuse-db": "1.0.30000856", + "electron-to-chromium": "1.3.48" } } } }, "caniuse-db": { - "version": "1.0.30000833", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000833.tgz", - "integrity": "sha1-K9e+cqQBZY0svLj012AN7r6xxnY=", + "version": "1.0.30000856", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000856.tgz", + "integrity": "sha1-++u5mr4VpWVPx3R+u1MVvf3jNY8=", "dev": true }, "caniuse-lite": { - "version": "1.0.30000833", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000833.tgz", - "integrity": "sha512-tKNuKu4WLImh4NxoTgntxFpDrRiA0Q6Q1NycNhuMST0Kx+Pt8YnRDW6V8xsyH6AtO2CpAoibatEk5eaEhP3O1g==", + "version": "1.0.30000856", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000856.tgz", + "integrity": "sha512-x3mYcApHMQemyaHuH/RyqtKCGIYTgEA63fdi+VBvDz8xUSmRiVWTLeyKcoGQCGG6UPR9/+4qG4OKrTa6aSQRKg==", + "dev": true + }, + "capture-stack-trace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", + "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", "dev": true }, "caseless": { @@ -1050,6 +1393,32 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, + "caw": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/caw/-/caw-1.2.0.tgz", + "integrity": "sha1-/7Im/n78VHKI3GLuPpcHPCEtEDQ=", + "dev": true, + "requires": { + "get-proxy": "1.1.0", + "is-obj": "1.0.1", + "object-assign": "3.0.0", + "tunnel-agent": "0.4.3" + }, + "dependencies": { + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "dev": true + } + } + }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -1067,7 +1436,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "1.9.2" } }, "supports-color": { @@ -1081,6 +1450,12 @@ } } }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, "clap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", @@ -1106,17 +1481,50 @@ } }, "classnames": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz", - "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0=", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, + "clipboard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.1.tgz", + "integrity": "sha512-7yhQBmtN+uYZmfRjjVjKa0dZdWuabzpSKGtyQZN+9C8xlC788SSJjOHWh7tzurfwTqTD5UDYAhIv5fRJg3sHjQ==", + "dev": true, + "optional": true, + "requires": { + "good-listener": "1.2.2", + "select": "1.1.2", + "tiny-emitter": "2.0.2" + } + }, "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", "dev": true }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -1144,23 +1552,23 @@ "integrity": "sha512-ubUCVVKfT7r2w2D3qtHakj8mbmKms+tThR8gI8zEYCbUBl8/voqFGt3kgBqGwXAopgXybnkuOq+qMYCRrp4cXw==", "dev": true, "requires": { - "color-convert": "1.9.1", + "color-convert": "1.9.2", "color-string": "1.5.2" } }, "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", + "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "1.1.1" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", "dev": true }, "color-string": { @@ -1169,10 +1577,16 @@ "integrity": "sha1-JuRYFLw8mny9Z1FkikFDRRSnc6k=", "dev": true, "requires": { - "color-name": "1.1.3", + "color-name": "1.1.1", "simple-swizzle": "0.2.2" } }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, "colormin": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", @@ -1181,7 +1595,7 @@ "requires": { "color": "0.11.4", "css-color-names": "0.0.4", - "has": "1.0.1" + "has": "1.0.3" }, "dependencies": { "color": { @@ -1191,7 +1605,7 @@ "dev": true, "requires": { "clone": "1.0.4", - "color-convert": "1.9.1", + "color-convert": "1.9.2", "color-string": "0.3.0" } }, @@ -1201,7 +1615,7 @@ "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "1.1.1" } } } @@ -1239,12 +1653,18 @@ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { - "buffer-from": "1.0.0", + "buffer-from": "1.1.0", "inherits": "2.0.3", "readable-stream": "2.3.6", "typedarray": "0.0.6" } }, + "console-stream": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/console-stream/-/console-stream-0.1.1.tgz", + "integrity": "sha1-oJX+B7IEZZVfL6/Si11yvM2UnUQ=", + "dev": true + }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -1257,6 +1677,12 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "dev": true }, + "continuable-cache": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", + "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", + "dev": true + }, "convert-source-map": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", @@ -1276,9 +1702,9 @@ "dev": true }, "core-js": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz", - "integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs=", + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", "dev": true }, "core-util-is": { @@ -1287,15 +1713,24 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, - "create-react-class": { - "version": "15.6.3", - "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz", - "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==", + "create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1" + "capture-stack-trace": "1.0.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "4.1.3", + "shebang-command": "1.2.0", + "which": "1.3.1" } }, "crowdin-cli": { @@ -1304,37 +1739,57 @@ "integrity": "sha1-6smYmm/n/qrzMJA5evwYfGe0YZE=", "dev": true, "requires": { - "request": "2.85.0", + "request": "2.87.0", "yamljs": "0.2.10", "yargs": "2.3.0" } }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "dev": true, - "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "dev": true, - "requires": { - "hoek": "4.2.1" - } - } - } - }, "css-color-names": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", "dev": true }, + "css-select": { + "version": "1.3.0-rc0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.3.0-rc0.tgz", + "integrity": "sha1-b5MZaqrnN2ZuoQNqjLFKj8t6kjE=", + "dev": true, + "requires": { + "boolbase": "1.0.0", + "css-what": "2.1.0", + "domutils": "1.5.1", + "nth-check": "1.0.1" + } + }, + "css-select-base-adapter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.0.tgz", + "integrity": "sha1-AQKz0UYw34bD65+p9UVicBBs+ZA=", + "dev": true + }, + "css-tree": { + "version": "1.0.0-alpha25", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha25.tgz", + "integrity": "sha512-XC6xLW/JqIGirnZuUWHXCHRaAjje2b3OIB0Vj5RIJo6mIi/AdJo30quQl5LxUl0gkXDIrTrFGbMlcZjyFplz1A==", + "dev": true, + "requires": { + "mdn-data": "1.1.4", + "source-map": "0.5.7" + } + }, + "css-url-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/css-url-regex/-/css-url-regex-1.1.0.tgz", + "integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=", + "dev": true + }, + "css-what": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", + "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", + "dev": true + }, "cssnano": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", @@ -1344,7 +1799,7 @@ "autoprefixer": "6.7.7", "decamelize": "1.2.0", "defined": "1.0.0", - "has": "1.0.1", + "has": "1.0.3", "object-assign": "4.1.1", "postcss": "5.2.18", "postcss-calc": "5.3.1", @@ -1355,7 +1810,7 @@ "postcss-discard-empty": "2.1.0", "postcss-discard-overridden": "0.1.1", "postcss-discard-unused": "2.2.3", - "postcss-filter-plugins": "2.0.2", + "postcss-filter-plugins": "2.0.3", "postcss-merge-idents": "2.1.7", "postcss-merge-longhand": "2.0.2", "postcss-merge-rules": "2.1.2", @@ -1385,7 +1840,16 @@ "source-map": "0.5.7" } }, - "dashdash": { + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "1.0.2" + } + }, + "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", @@ -1394,6 +1858,12 @@ "assert-plus": "1.0.0" } }, + "dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "dev": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1409,12 +1879,187 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decompress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-3.0.0.tgz", + "integrity": "sha1-rx3VDQbjv8QyRh033hGzjA2ZG+0=", + "dev": true, + "requires": { + "buffer-to-vinyl": "1.1.0", + "concat-stream": "1.6.2", + "decompress-tar": "3.1.0", + "decompress-tarbz2": "3.1.0", + "decompress-targz": "3.1.0", + "decompress-unzip": "3.4.0", + "stream-combiner2": "1.1.1", + "vinyl-assign": "1.2.1", + "vinyl-fs": "2.4.4" + } + }, + "decompress-tar": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-3.1.0.tgz", + "integrity": "sha1-IXx4n5uURQ76rcXF5TeXj8MzxGY=", + "dev": true, + "requires": { + "is-tar": "1.0.0", + "object-assign": "2.1.1", + "strip-dirs": "1.1.1", + "tar-stream": "1.6.1", + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "dev": true + }, + "object-assign": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", + "dev": true + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "dev": true, + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "decompress-tarbz2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-3.1.0.tgz", + "integrity": "sha1-iyOTVoE1X58YnYclag+L3ZbZZm0=", + "dev": true, + "requires": { + "is-bzip2": "1.0.0", + "object-assign": "2.1.1", + "seek-bzip": "1.0.5", + "strip-dirs": "1.1.1", + "tar-stream": "1.6.1", + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "dev": true + }, + "object-assign": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", + "dev": true + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "dev": true, + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "decompress-targz": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-3.1.0.tgz", + "integrity": "sha1-ssE9+YFmJomRtxXWRH9kLpaW9aA=", + "dev": true, + "requires": { + "is-gzip": "1.0.0", + "object-assign": "2.1.1", + "strip-dirs": "1.1.1", + "tar-stream": "1.6.1", + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "dev": true + }, + "object-assign": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", + "dev": true + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "dev": true, + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "decompress-unzip": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-3.4.0.tgz", + "integrity": "sha1-YUdbQVIGa74/7hL51inRX+ZHjus=", + "dev": true, + "requires": { + "is-zip": "1.0.0", + "read-all-stream": "3.1.0", + "stat-mode": "0.2.2", + "strip-dirs": "1.1.1", + "through2": "2.0.3", + "vinyl": "1.2.0", + "yauzl": "2.9.2" + }, + "dependencies": { + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.6", + "xtend": "4.0.1" + } + } + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.2.tgz", "integrity": "sha1-nO1l6gvAsJ9CptecGxkD+dkTzBg=", "dev": true }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "dev": true, + "requires": { + "foreach": "2.0.5", + "object-keys": "1.0.11" + } + }, "defined": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", @@ -1427,6 +2072,13 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", + "dev": true, + "optional": true + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -1448,6 +2100,16 @@ "repeating": "2.0.1" } }, + "detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "dev": true, + "requires": { + "address": "1.0.3", + "debug": "2.6.9" + } + }, "diacritics-map": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz", @@ -1455,21 +2117,21 @@ "dev": true }, "docusaurus": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/docusaurus/-/docusaurus-1.0.14.tgz", - "integrity": "sha512-veTxpjniDoEA7K5OlCFVXha2ak7d/J3/1e2sYFg/9n2qdfuLMGfpHQhAWyowXHr0Cbv3BjoOk5bXFFsiOUVt4A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/docusaurus/-/docusaurus-1.2.0.tgz", + "integrity": "sha512-CRIjFbZiQDWUxxjocHTVuJ3FXhbnczP66NPbqVGUZRalHB7u47OlEZvJztfStXjWocjftFyChaSBTGOSskaF+g==", "dev": true, "requires": { "babel-plugin-transform-class-properties": "6.24.1", "babel-plugin-transform-object-rest-spread": "6.26.0", "babel-polyfill": "6.26.0", - "babel-preset-env": "1.6.1", + "babel-preset-env": "1.7.0", "babel-preset-react": "6.24.1", "babel-register": "6.26.0", "babel-traverse": "6.26.0", "babylon": "6.18.0", "chalk": "2.4.1", - "classnames": "2.2.5", + "classnames": "2.2.6", "color": "2.0.1", "commander": "2.15.1", "crowdin-cli": "0.3.0", @@ -1478,18 +2140,121 @@ "express": "4.16.3", "feed": "1.1.1", "fs-extra": "5.0.0", + "gaze": "1.1.3", "glob": "7.1.2", "highlight.js": "9.12.0", + "imagemin": "5.3.1", + "imagemin-gifsicle": "5.2.0", + "imagemin-jpegtran": "5.0.2", + "imagemin-optipng": "5.2.1", + "imagemin-svgo": "6.0.0", "markdown-toc": "1.2.0", "mkdirp": "0.5.1", - "react": "15.6.2", - "react-dom": "15.6.2", - "react-dom-factories": "1.0.2", + "prismjs": "1.15.0", + "react": "16.4.1", + "react-dev-utils": "5.0.1", + "react-dom": "16.4.1", "remarkable": "1.7.1", - "request": "2.85.0", + "request": "2.87.0", "shelljs": "0.7.8", "sitemap": "1.13.0", - "tcp-port-used": "0.1.2" + "tcp-port-used": "0.1.2", + "tiny-lr": "1.1.1", + "tree-node-cli": "1.2.2" + } + }, + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "dev": true, + "requires": { + "domelementtype": "1.1.3", + "entities": "1.1.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", + "dev": true + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0.1.0", + "domelementtype": "1.3.0" + } + }, + "download": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/download/-/download-4.4.3.tgz", + "integrity": "sha1-qlX9rTktldS2jowr4D4MKqIbqaw=", + "dev": true, + "requires": { + "caw": "1.2.0", + "concat-stream": "1.6.2", + "each-async": "1.1.1", + "filenamify": "1.2.1", + "got": "5.7.1", + "gulp-decompress": "1.2.0", + "gulp-rename": "1.3.0", + "is-url": "1.2.4", + "object-assign": "4.1.1", + "read-all-stream": "3.1.0", + "readable-stream": "2.3.6", + "stream-combiner2": "1.1.1", + "vinyl": "1.2.0", + "vinyl-fs": "2.4.4", + "ware": "1.3.0" + } + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "2.3.6" + } + }, + "duplexify": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", + "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "dev": true, + "requires": { + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "stream-shift": "1.0.0" + } + }, + "each-async": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/each-async/-/each-async-1.1.1.tgz", + "integrity": "sha1-3uUim98KtrogEqOV4bhpq/iBNHM=", + "dev": true, + "requires": { + "onetime": "1.1.0", + "set-immediate-shim": "1.0.1" } }, "ecc-jsbn": { @@ -1509,9 +2274,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.45", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.45.tgz", - "integrity": "sha1-RYrBscXHYM6IEaFtK/vZfsMLr7g=", + "version": "1.3.48", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.48.tgz", + "integrity": "sha1-07DYWTgUBE4JLs4hCPw6ya6kuQA=", "dev": true }, "encodeurl": { @@ -1529,6 +2294,72 @@ "iconv-lite": "0.4.19" } }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "1.4.0" + } + }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", + "dev": true + }, + "error": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz", + "integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=", + "dev": true, + "requires": { + "string-template": "0.2.1", + "xtend": "4.0.1" + } + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + } + } + }, + "es-abstract": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", + "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", + "dev": true, + "requires": { + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.3", + "is-callable": "1.1.3", + "is-regex": "1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "1.1.3", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1559,13 +2390,95 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, + "eventsource": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", + "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", + "dev": true, + "requires": { + "original": "1.0.1" + } + }, + "exec-buffer": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz", + "integrity": "sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA==", + "dev": true, + "requires": { + "execa": "0.7.0", + "p-finally": "1.0.0", + "pify": "3.0.0", + "rimraf": "2.6.2", + "tempfile": "2.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "exec-series": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/exec-series/-/exec-series-1.0.3.tgz", + "integrity": "sha1-bSV6m+rEgqhyx3g7yGFYOfx3FDo=", + "dev": true, + "requires": { + "async-each-series": "1.1.0", + "object-assign": "4.1.1" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + } + }, + "executable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/executable/-/executable-1.1.0.tgz", + "integrity": "sha1-h3mA6REvM5EGbaNyZd562ENKtNk=", + "dev": true, + "requires": { + "meow": "3.7.0" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "0.1.1" + } + }, "expand-range": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "2.2.3" + "fill-range": "2.2.4" + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "1.0.1" } }, "express": { @@ -1635,12 +2548,51 @@ "is-extendable": "0.1.1" } }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "0.4.2", + "iconv-lite": "0.4.19", + "tmp": "0.0.33" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + } + } + }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, + "fancy-log": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", + "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", + "dev": true, + "requires": { + "ansi-gray": "0.1.1", + "color-support": "1.1.3", + "time-stamp": "1.1.0" + } + }, "fast-deep-equal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", @@ -1653,10 +2605,19 @@ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", "dev": true }, + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "dev": true, + "requires": { + "websocket-driver": "0.7.0" + } + }, "fbjs": { - "version": "0.8.16", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", - "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=", + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", "dev": true, "requires": { "core-js": "1.2.7", @@ -1676,6 +2637,15 @@ } } }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "1.2.0" + } + }, "feed": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/feed/-/feed-1.1.1.tgz", @@ -1685,15 +2655,60 @@ "xml": "1.0.1" } }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" + } + }, + "file-type": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz", + "integrity": "sha1-G2AOX8ofvcboDApwxxyNul95BsU=", + "dev": true + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "filename-reserved-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz", + "integrity": "sha1-5hz4BfDeHJhFZ9A4bcXfUO5a9+Q=", + "dev": true + }, + "filenamify": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.1.tgz", + "integrity": "sha1-qfL/0RxQO+0wABUCknI3jx8TZaU=", + "dev": true, + "requires": { + "filename-reserved-regex": "1.0.0", + "strip-outer": "1.0.1", + "trim-repeated": "1.0.0" + } + }, + "filesize": { + "version": "3.5.11", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.5.11.tgz", + "integrity": "sha512-ZH7loueKBoDb7yG9esn1U+fgq7BzlzW6NRi5/rMdxIZ05dj7GFD/Xc5rq2CDt5Yq86CyfSYVyx4242QQNZbx1g==", + "dev": true + }, "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { "is-number": "2.1.0", "isobject": "2.1.0", - "randomatic": "1.1.7", + "randomatic": "3.0.0", "repeat-element": "1.1.2", "repeat-string": "1.6.1" } @@ -1713,6 +2728,34 @@ "unpipe": "1.0.0" } }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "find-versions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-1.2.1.tgz", + "integrity": "sha1-y96fEuOFdaCvG+G5osXV/Y8Ya2I=", + "dev": true, + "requires": { + "array-uniq": "1.0.3", + "get-stdin": "4.0.1", + "meow": "3.7.0", + "semver-regex": "1.0.0" + } + }, + "first-chunk-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=", + "dev": true + }, "flatten": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", @@ -1725,6 +2768,21 @@ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -1754,6 +2812,12 @@ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", "dev": true }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, "fs-extra": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", @@ -1777,6 +2841,36 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "requires": { + "globule": "1.2.1" + } + }, + "get-proxy": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-1.1.0.tgz", + "integrity": "sha1-iUhUSRvFkbDxR9euVw9cZ4tyVus=", + "dev": true, + "requires": { + "rc": "1.2.8" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -1786,6 +2880,17 @@ "assert-plus": "1.0.0" } }, + "gifsicle": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/gifsicle/-/gifsicle-3.0.4.tgz", + "integrity": "sha1-9Fy17RAWW2ZdySng6TKLbIId+js=", + "dev": true, + "requires": { + "bin-build": "2.2.0", + "bin-wrapper": "3.0.2", + "logalot": "2.1.0" + } + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -1800,18 +2905,191 @@ "path-is-absolute": "1.0.1" } }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "2.0.0", + "is-glob": "2.0.1" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "2.0.1" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + } + }, + "glob-stream": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", + "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", + "dev": true, + "requires": { + "extend": "3.0.1", + "glob": "5.0.15", + "glob-parent": "3.1.0", + "micromatch": "2.3.11", + "ordered-read-streams": "0.3.0", + "through2": "0.6.5", + "to-absolute-glob": "0.1.1", + "unique-stream": "2.2.1" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + } + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "1.0.2", + "is-windows": "1.0.2", + "resolve-dir": "1.0.1" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "2.0.2", + "homedir-polyfill": "1.0.1", + "ini": "1.3.5", + "is-windows": "1.0.2", + "which": "1.3.1" + } + }, "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "globule": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", + "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "dev": true, + "requires": { + "glob": "7.1.2", + "lodash": "4.17.10", + "minimatch": "3.0.4" + } + }, + "glogg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.1.tgz", + "integrity": "sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw==", + "dev": true, + "requires": { + "sparkles": "1.0.1" + } + }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "dev": true, + "optional": true, + "requires": { + "delegate": "3.2.0" + } + }, + "got": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", + "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", + "dev": true, + "requires": { + "create-error-class": "3.0.2", + "duplexer2": "0.1.4", + "is-redirect": "1.0.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "lowercase-keys": "1.0.1", + "node-status-codes": "1.0.0", + "object-assign": "4.1.1", + "parse-json": "2.2.0", + "pinkie-promise": "2.0.1", + "read-all-stream": "3.1.0", + "readable-stream": "2.3.6", + "timed-out": "3.1.3", + "unzip-response": "1.0.2", + "url-parse-lax": "1.0.0" + } + }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, "gray-matter": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz", @@ -1821,7 +3099,7 @@ "ansi-red": "0.1.1", "coffee-script": "1.12.7", "extend-shallow": "2.0.1", - "js-yaml": "3.11.0", + "js-yaml": "3.12.0", "toml": "2.3.3" }, "dependencies": { @@ -1832,9 +3110,9 @@ "dev": true }, "js-yaml": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", - "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", "dev": true, "requires": { "argparse": "1.0.10", @@ -1843,6 +3121,147 @@ } } }, + "gulp-decompress": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gulp-decompress/-/gulp-decompress-1.2.0.tgz", + "integrity": "sha1-jutlpeAV+O2FMsr+KEVJYGJvDcc=", + "dev": true, + "requires": { + "archive-type": "3.2.0", + "decompress": "3.0.0", + "gulp-util": "3.0.8", + "readable-stream": "2.3.6" + } + }, + "gulp-rename": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.3.0.tgz", + "integrity": "sha512-nEuZB7/9i0IZ8AXORTizl2QLP9tcC9uWc/s329zElBLJw1CfOhmMXBxwVlCRKjDyrWuhVP0uBKl61KeQ32TiCg==", + "dev": true + }, + "gulp-sourcemaps": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", + "integrity": "sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw=", + "dev": true, + "requires": { + "convert-source-map": "1.5.1", + "graceful-fs": "4.1.11", + "strip-bom": "2.0.0", + "through2": "2.0.3", + "vinyl": "1.2.0" + }, + "dependencies": { + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.6", + "xtend": "4.0.1" + } + } + } + }, + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "dev": true, + "requires": { + "array-differ": "1.0.0", + "array-uniq": "1.0.3", + "beeper": "1.1.1", + "chalk": "1.1.3", + "dateformat": "2.2.0", + "fancy-log": "1.3.2", + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash._reescape": "3.0.0", + "lodash._reevaluate": "3.0.0", + "lodash._reinterpolate": "3.0.0", + "lodash.template": "3.6.2", + "minimist": "1.2.0", + "multipipe": "0.1.2", + "object-assign": "3.0.0", + "replace-ext": "0.0.1", + "through2": "2.0.3", + "vinyl": "0.5.3" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.6", + "xtend": "4.0.1" + } + }, + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "dev": true, + "requires": { + "clone": "1.0.4", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "requires": { + "glogg": "1.0.1" + } + }, + "gzip-size": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", + "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=", + "dev": true, + "requires": { + "duplexer": "0.1.1" + } + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -1860,9 +3279,9 @@ } }, "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { "function-bind": "1.1.1" @@ -1883,16 +3302,13 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", "dev": true, "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.1", - "sntp": "2.1.0" + "sparkles": "1.0.1" } }, "highlight.js": { @@ -1901,12 +3317,6 @@ "integrity": "sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4=", "dev": true }, - "hoek": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", - "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", - "dev": true - }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", @@ -1917,6 +3327,21 @@ "os-tmpdir": "1.0.2" } }, + "homedir-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "dev": true, + "requires": { + "parse-passwd": "1.0.0" + } + }, + "hosted-git-info": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", + "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", + "dev": true + }, "html-comment-regex": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", @@ -1935,6 +3360,12 @@ "statuses": "1.4.0" } }, + "http-parser-js": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.13.tgz", + "integrity": "sha1-O9bW/ebjFyyTNMOzO2wZPYD+ETc=", + "dev": true + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -1943,7 +3374,7 @@ "requires": { "assert-plus": "1.0.0", "jsprim": "1.4.1", - "sshpk": "1.14.1" + "sshpk": "1.14.2" } }, "iconv-lite": { @@ -1952,6 +3383,149 @@ "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", "dev": true }, + "imagemin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/imagemin/-/imagemin-5.3.1.tgz", + "integrity": "sha1-8Zwu7h5xumxlWMUV+fyWaAGJptQ=", + "dev": true, + "requires": { + "file-type": "4.4.0", + "globby": "6.1.0", + "make-dir": "1.3.0", + "p-pipe": "1.2.0", + "pify": "2.3.0", + "replace-ext": "1.0.0" + } + }, + "imagemin-gifsicle": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/imagemin-gifsicle/-/imagemin-gifsicle-5.2.0.tgz", + "integrity": "sha512-K01m5QuPK+0en8oVhiOOAicF7KjrHlCZxS++mfLI2mV/Ksfq/Y9nCXCWDz6jRv13wwlqe5T7hXT+ji2DnLc2yQ==", + "dev": true, + "requires": { + "exec-buffer": "3.2.0", + "gifsicle": "3.0.4", + "is-gif": "1.0.0" + } + }, + "imagemin-jpegtran": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/imagemin-jpegtran/-/imagemin-jpegtran-5.0.2.tgz", + "integrity": "sha1-5ogiY7j3kW/duABkDPddLpcNKtY=", + "dev": true, + "requires": { + "exec-buffer": "3.2.0", + "is-jpg": "1.0.1", + "jpegtran-bin": "3.2.0" + } + }, + "imagemin-optipng": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/imagemin-optipng/-/imagemin-optipng-5.2.1.tgz", + "integrity": "sha1-0i2kEsCfX/AKQzmWC5ioix2+hpU=", + "dev": true, + "requires": { + "exec-buffer": "3.2.0", + "is-png": "1.1.0", + "optipng-bin": "3.1.4" + } + }, + "imagemin-svgo": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/imagemin-svgo/-/imagemin-svgo-6.0.0.tgz", + "integrity": "sha512-xwjBZQKpbkklHtJYnCOwRJjTRJA/nR0hQzKMh+CUZRvm/L0QwKKPJQ9tkPWQHrg+cydPu2i1vLgHuy2E0hKEkg==", + "dev": true, + "requires": { + "buffer-from": "0.1.2", + "is-svg": "2.1.0", + "svgo": "1.0.5" + }, + "dependencies": { + "buffer-from": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", + "integrity": "sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==", + "dev": true + }, + "coa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.1.tgz", + "integrity": "sha512-5wfTTO8E2/ja4jFSxePXlG5nRu5bBtL/r1HCIpJW/lzT6yDtKl0u0Z4o/Vpz32IpKmBn7HerheEZQgA9N2DarQ==", + "dev": true, + "requires": { + "q": "1.5.1" + } + }, + "csso": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz", + "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==", + "dev": true, + "requires": { + "css-tree": "1.0.0-alpha.29" + }, + "dependencies": { + "css-tree": { + "version": "1.0.0-alpha.29", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz", + "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==", + "dev": true, + "requires": { + "mdn-data": "1.1.4", + "source-map": "0.5.7" + } + } + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "js-yaml": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", + "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.0" + } + }, + "svgo": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.0.5.tgz", + "integrity": "sha512-nYrifviB77aNKDNKKyuay3M9aYiK6Hv5gJVDdjj2ZXTQmI8WZc8+UPLR5IpVlktJfSu3co/4XcWgrgI6seGBPg==", + "dev": true, + "requires": { + "coa": "2.0.1", + "colors": "1.1.2", + "css-select": "1.3.0-rc0", + "css-select-base-adapter": "0.1.0", + "css-tree": "1.0.0-alpha25", + "css-url-regex": "1.1.0", + "csso": "3.5.1", + "js-yaml": "3.10.0", + "mkdirp": "0.5.1", + "object.values": "1.0.4", + "sax": "1.2.4", + "stable": "0.1.8", + "unquote": "1.1.1", + "util.promisify": "1.0.0" + } + } + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "2.0.1" + } + }, "indexes-of": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", @@ -1974,6 +3548,60 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "3.1.0", + "chalk": "2.4.1", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "2.2.0", + "figures": "2.0.0", + "lodash": "4.17.10", + "mute-stream": "0.0.7", + "run-async": "2.3.0", + "rx-lite": "4.0.8", + "rx-lite-aggregates": "4.0.8", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "through": "2.3.8" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, "interpret": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", @@ -1989,12 +3617,27 @@ "loose-envify": "1.3.1" } }, + "ip-regex": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-1.0.3.tgz", + "integrity": "sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0=", + "dev": true + }, "ipaddr.js": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=", "dev": true }, + "is-absolute": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "integrity": "sha1-hHSREZ/MtftDYhfMc39/qtUPYD8=", + "dev": true, + "requires": { + "is-relative": "0.1.3" + } + }, "is-absolute-url": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", @@ -2013,12 +3656,60 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-bzip2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-bzip2/-/is-bzip2-1.0.0.tgz", + "integrity": "sha1-XuWOqlounIDiFAe+3yOuWsCRs/w=", + "dev": true + }, + "is-callable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "2.0.0" + } + }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, "is-finite": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", @@ -2028,6 +3719,45 @@ "number-is-nan": "1.0.1" } }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-gif": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-gif/-/is-gif-1.0.0.tgz", + "integrity": "sha1-ptKumIkwB7/6l6HYwB1jIFgyCX4=", + "dev": true + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-gzip": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-gzip/-/is-gzip-1.0.0.tgz", + "integrity": "sha1-bKiwe5nHeZgCWQDlVc7Y7YCHmoM=", + "dev": true + }, + "is-jpg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-jpg/-/is-jpg-1.0.1.tgz", + "integrity": "sha1-KW1X/dmc4BBDSnKD40armhA16XU=", + "dev": true + }, + "is-natural-number": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.1.1.tgz", + "integrity": "sha1-fUxXKDd+84bD4ZSpkRv1fG3DNec=", + "dev": true + }, "is-number": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", @@ -2037,6 +3767,12 @@ "kind-of": "3.2.2" } }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -2060,6 +3796,63 @@ } } }, + "is-png": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-png/-/is-png-1.1.0.tgz", + "integrity": "sha1-1XSxK/J1wDUEVVcLDltXqwYgd84=", + "dev": true + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "1.0.3" + } + }, + "is-relative": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz", + "integrity": "sha1-kF/uiuhvRbPsYUvDwVyGnfCHboI=", + "dev": true + }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", + "dev": true + }, + "is-root": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-1.0.0.tgz", + "integrity": "sha1-B7bCM7w5TNnQK6FclmvWZg1jQtU=", + "dev": true + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -2075,12 +3868,60 @@ "html-comment-regex": "1.1.1" } }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, + "is-tar": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-tar/-/is-tar-1.0.0.tgz", + "integrity": "sha1-L2suF5LB9bs2UZrKqdZcDSb+hT0=", + "dev": true + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-valid-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", + "integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "is-zip": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-zip/-/is-zip-1.0.0.tgz", + "integrity": "sha1-R7Co/004p2QxzP2ZqOFaTIa6IyU=", + "dev": true + }, "is2": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/is2/-/is2-0.0.9.tgz", @@ -2096,6 +3937,12 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, "isobject": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", @@ -2121,10 +3968,21 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, + "jpegtran-bin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jpegtran-bin/-/jpegtran-bin-3.2.0.tgz", + "integrity": "sha1-9g7PSumZwL2tLp+83ytvCYHnops=", + "dev": true, + "requires": { + "bin-build": "2.2.0", + "bin-wrapper": "3.0.2", + "logalot": "2.1.0" + } + }, "js-base64": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz", - "integrity": "sha512-H7ErYLM34CvDMto3GbD6xD0JLUGYXR3QTcH6B/tr4Hi/QpSThnCsIp+Sy5FRTw3B0d6py4HcNkW7nO/wdtGWEw==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.5.tgz", + "integrity": "sha512-aUnNwqMOXw3yvErjMPSQu6qIIzUmT1e5KcU1OZxRDU1g/am6mzBvcrmLAYwzmB59BHPrh5/tKaiF4OPhqRWESQ==", "dev": true }, "js-tokens": { @@ -2168,12 +4026,27 @@ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", "dev": true }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, "json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", @@ -2189,6 +4062,12 @@ "graceful-fs": "4.1.11" } }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -2219,6 +4098,21 @@ "set-getter": "0.1.0" } }, + "lazy-req": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/lazy-req/-/lazy-req-1.1.0.tgz", + "integrity": "sha1-va6+rTD42CQDnODOFJ1Nqge6H6w=", + "dev": true + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "dev": true, + "requires": { + "readable-stream": "2.3.6" + } + }, "list-item": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz", @@ -2231,24 +4125,184 @@ "repeat-string": "1.6.1" } }, + "livereload-js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.3.0.tgz", + "integrity": "sha512-j1R0/FeGa64Y+NmqfZhyoVRzcFlOZ8sNlKzHjh4VvLULFACZhn68XrX5DFg2FhMvSMJmROuFxRSa560ECWKBMg==", + "dev": true + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + } + }, "lodash": { "version": "4.17.10", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", "dev": true }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "dev": true + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash._reescape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", + "dev": true + }, + "lodash._reevaluate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "dev": true, + "requires": { + "lodash._root": "3.0.1" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "dev": true }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "dev": true + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "dev": true, + "requires": { + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "dev": true, + "requires": { + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" + } + }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", "dev": true }, + "logalot": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/logalot/-/logalot-2.1.0.tgz", + "integrity": "sha1-X46MkNME7fElMJUaVVSruMXj9VI=", + "dev": true, + "requires": { + "figures": "1.7.0", + "squeak": "1.3.0" + } + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, "loose-envify": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", @@ -2258,10 +4312,65 @@ "js-tokens": "3.0.2" } }, - "macaddress": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", - "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=", + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.2" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lpad-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lpad-align/-/lpad-align-1.1.2.tgz", + "integrity": "sha1-IfYArBwwlcPG5JfuZyce4ISB/p4=", + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "indent-string": "2.1.0", + "longest": "1.0.1", + "meow": "3.7.0" + } + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", "dev": true }, "markdown-link": { @@ -2304,24 +4413,109 @@ "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", "dev": true }, + "math-random": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", + "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", + "dev": true + }, + "mdn-data": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz", + "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==", + "dev": true + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.0", + "normalize-package-data": "2.4.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "2.3.6" + } + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", "dev": true }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + } + } + }, "mime": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", @@ -2343,6 +4537,12 @@ "mime-db": "1.33.0" } }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -2394,6 +4594,56 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "dev": true, + "requires": { + "duplexer2": "0.0.2" + }, + "dependencies": { + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "dev": true, + "requires": { + "readable-stream": "1.1.14" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -2410,6 +4660,33 @@ "is-stream": "1.1.0" } }, + "node-status-codes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", + "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=", + "dev": true + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "2.6.0", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.3" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "1.1.0" + } + }, "normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", @@ -2428,6 +4705,24 @@ "sort-keys": "1.1.2" } }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "2.0.1" + } + }, + "nth-check": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", + "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", + "dev": true, + "requires": { + "boolbase": "1.0.0" + } + }, "num2fraction": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", @@ -2452,6 +4747,32 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", + "dev": true + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "es-abstract": "1.12.0" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "0.1.5", + "is-extendable": "0.1.1" + } + }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -2469,6 +4790,18 @@ } } }, + "object.values": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.0.4.tgz", + "integrity": "sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo=", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "es-abstract": "1.12.0", + "function-bind": "1.1.1", + "has": "1.0.3" + } + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -2487,6 +4820,57 @@ "wrappy": "1.0.2" } }, + "onetime": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "opn": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.2.0.tgz", + "integrity": "sha512-Jd/GpzPyHF4P2/aNOVmS3lfMSWV9J7cOhCG1s08XCEAsPkB7lp6ddiU0J7XzyQRDUh8BqJ7PchfINjR8jyofRQ==", + "dev": true, + "requires": { + "is-wsl": "1.1.0" + } + }, + "optipng-bin": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/optipng-bin/-/optipng-bin-3.1.4.tgz", + "integrity": "sha1-ldNPLEiHBPb9cGBr/qDGWfHZXYQ=", + "dev": true, + "requires": { + "bin-build": "2.2.0", + "bin-wrapper": "3.0.2", + "logalot": "2.1.0" + } + }, + "ordered-read-streams": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", + "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", + "dev": true, + "requires": { + "is-stream": "1.1.0", + "readable-stream": "2.3.6" + } + }, + "original": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.1.tgz", + "integrity": "sha512-IEvtB5vM5ULvwnqMxWBLxkS13JIEXbakizMSo3yoPNPCIWzg8TG3Usn/UhXoZFM/m+FuEA20KdzPSFq/0rS+UA==", + "dev": true, + "requires": { + "url-parse": "1.4.1" + } + }, + "os-filter-obj": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-1.0.3.tgz", + "integrity": "sha1-WRUzDZDs7VV9LZOKMcbdIU2cY60=", + "dev": true + }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -2499,18 +4883,95 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-pipe": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-1.2.0.tgz", + "integrity": "sha1-SxoROZoRUgpneQ7loMHViB1r7+k=", + "dev": true + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + } + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "1.3.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, "parseurl": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", "dev": true }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, "path-parse": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", @@ -2523,12 +4984,50 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", "dev": true }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, "postcss": { "version": "5.2.18", "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", @@ -2536,7 +5035,7 @@ "dev": true, "requires": { "chalk": "1.1.3", - "js-base64": "2.4.3", + "js-base64": "2.4.5", "source-map": "0.5.7", "supports-color": "3.2.3" }, @@ -2658,13 +5157,12 @@ } }, "postcss-filter-plugins": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", - "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz", + "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==", "dev": true, "requires": { - "postcss": "5.2.18", - "uniqid": "4.1.1" + "postcss": "5.2.18" } }, "postcss-merge-idents": { @@ -2673,7 +5171,7 @@ "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", "dev": true, "requires": { - "has": "1.0.1", + "has": "1.0.3", "postcss": "5.2.18", "postcss-value-parser": "3.3.0" } @@ -2706,8 +5204,8 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000833", - "electron-to-chromium": "1.3.45" + "caniuse-db": "1.0.30000856", + "electron-to-chromium": "1.3.48" } } } @@ -2758,7 +5256,7 @@ "dev": true, "requires": { "alphanum-sort": "1.0.2", - "has": "1.0.1", + "has": "1.0.3", "postcss": "5.2.18", "postcss-selector-parser": "2.2.3" } @@ -2819,7 +5317,7 @@ "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", "dev": true, "requires": { - "has": "1.0.1", + "has": "1.0.3", "postcss": "5.2.18", "postcss-value-parser": "3.3.0" } @@ -2870,7 +5368,7 @@ "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", "dev": true, "requires": { - "has": "1.0.1", + "has": "1.0.3", "postcss": "5.2.18", "uniqs": "2.0.0" } @@ -2881,6 +5379,21 @@ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "prismjs": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.15.0.tgz", + "integrity": "sha512-Lf2JrFYx8FanHrjoV5oL8YHCclLQgbJcVZR+gikGGMqz6ub5QVWDTM6YIwm3BuPxM/LOV+rKns3LssXNLIf+DA==", + "dev": true, + "requires": { + "clipboard": "2.0.1" + } + }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -2908,7 +5421,7 @@ "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==", "dev": true, "requires": { - "fbjs": "0.8.16", + "fbjs": "0.8.17", "loose-envify": "1.3.1", "object-assign": "4.1.1" } @@ -2923,6 +5436,12 @@ "ipaddr.js": "1.6.0" } }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -2951,44 +5470,34 @@ "strict-uri-encode": "1.1.0" } }, + "querystringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz", + "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==", + "dev": true + }, "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", + "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "4.0.0", + "kind-of": "6.0.2", + "math-random": "1.0.1" }, "dependencies": { "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true }, "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true } } }, @@ -3036,37 +5545,128 @@ } } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "0.6.0", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "react": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz", - "integrity": "sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=", + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/react/-/react-16.4.1.tgz", + "integrity": "sha512-3GEs0giKp6E0Oh/Y9ZC60CmYgUPnp7voH9fbjWsvXtYFb4EWtgQub0ADSq0sJR0BbHc4FThLLtzlcFaFXIorwg==", "dev": true, "requires": { - "create-react-class": "15.6.3", - "fbjs": "0.8.16", + "fbjs": "0.8.17", "loose-envify": "1.3.1", "object-assign": "4.1.1", "prop-types": "15.6.1" } }, + "react-dev-utils": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-5.0.1.tgz", + "integrity": "sha512-+y92rG6pmXt3cpcg/NGmG4w/W309tWNSmyyPL8hCMxuCSg2UP/hUg3npACj2UZc8UKVSXexyLrCnxowizGoAsw==", + "dev": true, + "requires": { + "address": "1.0.3", + "babel-code-frame": "6.26.0", + "chalk": "1.1.3", + "cross-spawn": "5.1.0", + "detect-port-alt": "1.1.6", + "escape-string-regexp": "1.0.5", + "filesize": "3.5.11", + "global-modules": "1.0.0", + "gzip-size": "3.0.0", + "inquirer": "3.3.0", + "is-root": "1.0.0", + "opn": "5.2.0", + "react-error-overlay": "4.0.0", + "recursive-readdir": "2.2.1", + "shell-quote": "1.6.1", + "sockjs-client": "1.1.4", + "strip-ansi": "3.0.1", + "text-table": "0.2.0" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + } + } + }, "react-dom": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.2.tgz", - "integrity": "sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=", + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.4.1.tgz", + "integrity": "sha512-1Gin+wghF/7gl4Cqcvr1DxFX2Osz7ugxSwl6gBqCMpdrxHjIFUS7GYxrFftZ9Ln44FHw0JxCFD9YtZsrbR5/4A==", "dev": true, "requires": { - "fbjs": "0.8.16", + "fbjs": "0.8.17", "loose-envify": "1.3.1", "object-assign": "4.1.1", "prop-types": "15.6.1" } }, - "react-dom-factories": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/react-dom-factories/-/react-dom-factories-1.0.2.tgz", - "integrity": "sha1-63cFxNs2+1AbOqOP91lhaqD/luA=", + "react-error-overlay": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-4.0.0.tgz", + "integrity": "sha512-FlsPxavEyMuR6TjVbSSywovXSEyOg6ZDj5+Z8nbsRl9EkOzAhEIcS+GLoQDC5fz/t9suhUXWmUrOBrgeUvrMxw==", "dev": true }, + "read-all-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", + "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=", + "dev": true, + "requires": { + "pinkie-promise": "2.0.1", + "readable-stream": "2.3.6" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } + }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", @@ -3082,13 +5682,43 @@ "util-deprecate": "1.0.2" } }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "1.8.0" + } + }, + "recursive-readdir": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.1.tgz", + "integrity": "sha1-kO8jHQd4xc4JPJpI105cVCLROpk=", + "dev": true, + "requires": { + "minimatch": "3.0.3" + }, + "dependencies": { + "minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + } + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, "requires": { - "resolve": "1.7.1" + "indent-string": "2.1.0", + "strip-indent": "1.0.1" } }, "reduce-css-calc": { @@ -3128,9 +5758,9 @@ } }, "regenerate": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", - "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", "dev": true }, "regenerator-runtime": { @@ -3150,13 +5780,22 @@ "private": "0.1.8" } }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "0.1.3" + } + }, "regexpu-core": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", "dev": true, "requires": { - "regenerate": "1.3.3", + "regenerate": "1.4.0", "regjsgen": "0.2.0", "regjsparser": "0.1.5" } @@ -3198,6 +5837,12 @@ } } }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, "repeat-element": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", @@ -3219,10 +5864,16 @@ "is-finite": "1.0.2" } }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, "request": { - "version": "2.85.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", - "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", + "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", "dev": true, "requires": { "aws-sign2": "0.7.0", @@ -3233,7 +5884,6 @@ "forever-agent": "0.6.1", "form-data": "2.3.2", "har-validator": "5.0.3", - "hawk": "6.0.2", "http-signature": "1.2.0", "is-typedarray": "1.0.0", "isstream": "0.1.2", @@ -3243,39 +5893,162 @@ "performance-now": "2.1.0", "qs": "6.5.2", "safe-buffer": "5.1.2", - "stringstream": "0.0.5", "tough-cookie": "2.3.4", "tunnel-agent": "0.6.0", "uuid": "3.2.1" } }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, "resolve": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", - "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.0.tgz", + "integrity": "sha512-MNcwJ8/K9iJqFDBDyhcxZuDWvf/ai0GcAJWetx2Cvvcz4HLfA8j0KasWR5Z6ChcbjYZ+FaczcXjN2jrCXCjQ4w==", "dev": true, "requires": { "path-parse": "1.0.5" } }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "2.0.2", + "global-modules": "1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "2.0.1", + "signal-exit": "3.0.2" + }, + "dependencies": { + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "1.2.0" + } + } + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "2.1.0" + } + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "4.0.8" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "safe-json-parse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", + "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "seek-bzip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", + "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", + "dev": true, + "requires": { + "commander": "2.8.1" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "dev": true, + "requires": { + "graceful-readlink": "1.0.1" + } + } + } + }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", + "dev": true, + "optional": true + }, "semver": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, + "semver-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-1.0.0.tgz", + "integrity": "sha1-kqSWkGX5xwxpR1PVUkj8aPj2Usk=", + "dev": true + }, + "semver-truncate": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-1.1.2.tgz", + "integrity": "sha1-V/Qd5pcHpicJp+AQS6IRcQnqR+g=", + "dev": true, + "requires": { + "semver": "5.5.0" + } + }, "send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -3318,6 +6091,12 @@ "to-object-path": "0.3.0" } }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -3330,6 +6109,33 @@ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", "dev": true }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shell-quote": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "dev": true, + "requires": { + "array-filter": "0.0.1", + "array-map": "0.0.0", + "array-reduce": "0.0.0", + "jsonify": "0.0.0" + } + }, "shelljs": { "version": "0.7.8", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", @@ -3341,6 +6147,12 @@ "rechoir": "0.6.2" } }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -3366,13 +6178,18 @@ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", "dev": true }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "sockjs-client": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", + "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", "dev": true, "requires": { - "hoek": "4.2.1" + "debug": "2.6.9", + "eventsource": "0.1.6", + "faye-websocket": "0.11.1", + "inherits": "2.0.3", + "json3": "3.3.2", + "url-parse": "1.4.1" } }, "sort-keys": { @@ -3399,16 +6216,80 @@ "source-map": "0.5.7" } }, + "sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "dev": true + }, + "spdx-correct": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", + "dev": true, + "requires": { + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", + "dev": true + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "squeak": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/squeak/-/squeak-1.3.0.tgz", + "integrity": "sha1-MwRQN7ZDiLVnZ0uEMiplIQc5FsM=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "console-stream": "0.1.1", + "lpad-align": "1.1.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + } + } + }, "sshpk": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", - "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "dev": true, "requires": { "asn1": "0.2.3", @@ -3418,50 +6299,214 @@ "ecc-jsbn": "0.1.1", "getpass": "0.1.7", "jsbn": "0.1.1", + "safer-buffer": "2.1.2", "tweetnacl": "0.14.5" } }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "stat-mode": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.2.2.tgz", + "integrity": "sha1-5sgLYjEj19gM8TLOU480YokHJQI=", + "dev": true + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, + "requires": { + "duplexer2": "0.1.4", + "readable-stream": "2.3.6" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-template": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", + "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-bom-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", + "dev": true, + "requires": { + "first-chunk-stream": "1.0.0", + "strip-bom": "2.0.0" + } + }, + "strip-color": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz", + "integrity": "sha1-EG9l09PmotlAHKwOsM6LinArT3s=", + "dev": true + }, + "strip-dirs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "integrity": "sha1-lgu9EoeETzl1pFWKoQOoJV4kVqA=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "get-stdin": "4.0.1", + "is-absolute": "0.1.7", + "is-natural-number": "2.1.1", + "minimist": "1.2.0", + "sum-up": "1.0.3" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, "requires": { - "safe-buffer": "5.1.2" + "get-stdin": "4.0.1" } }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "escape-string-regexp": "1.0.5" } }, - "strip-color": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz", - "integrity": "sha1-EG9l09PmotlAHKwOsM6LinArT3s=", - "dev": true + "sum-up": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.3.tgz", + "integrity": "sha1-HGYfZnBX9jvLeHWqFDi8FiUlFW4=", + "dev": true, + "requires": { + "chalk": "1.1.3" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + } + } }, "supports-color": { "version": "2.0.0", @@ -3484,6 +6529,21 @@ "whet.extend": "0.9.9" } }, + "tar-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz", + "integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==", + "dev": true, + "requires": { + "bl": "1.2.2", + "buffer-alloc": "1.2.0", + "end-of-stream": "1.4.1", + "fs-constants": "1.0.0", + "readable-stream": "2.3.6", + "to-buffer": "1.1.1", + "xtend": "4.0.1" + } + }, "tcp-port-used": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-0.1.2.tgz", @@ -3509,6 +6569,169 @@ } } }, + "temp-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", + "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", + "dev": true + }, + "tempfile": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", + "integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=", + "dev": true, + "requires": { + "temp-dir": "1.0.0", + "uuid": "3.2.1" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "through2-filter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", + "dev": true, + "requires": { + "through2": "2.0.3", + "xtend": "4.0.1" + }, + "dependencies": { + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.6", + "xtend": "4.0.1" + } + } + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true + }, + "timed-out": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", + "integrity": "sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc=", + "dev": true + }, + "tiny-emitter": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz", + "integrity": "sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==", + "dev": true, + "optional": true + }, + "tiny-lr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", + "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", + "dev": true, + "requires": { + "body": "5.1.0", + "debug": "3.1.0", + "faye-websocket": "0.10.0", + "livereload-js": "2.3.0", + "object-assign": "4.1.1", + "qs": "6.5.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": "0.7.0" + } + } + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } + }, + "to-absolute-glob": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", + "integrity": "sha1-HN+kcqnvUMI57maZm2YsoOs5k38=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1" + } + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "dev": true + }, "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", @@ -3539,6 +6762,30 @@ "punycode": "1.4.1" } }, + "tree-node-cli": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-node-cli/-/tree-node-cli-1.2.2.tgz", + "integrity": "sha512-Q21gG3xLXQAVSWMiSblNLRaAlg/tPzW7Tgs7/rDFjBOd7BDUfCdxb1oBGE6EojJWAArsbEb4Ix8voqgDQPQ8IQ==", + "dev": true, + "requires": { + "commander": "2.15.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5" + } + }, "trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", @@ -3601,21 +6848,22 @@ "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", "dev": true }, - "uniqid": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", - "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", - "dev": true, - "requires": { - "macaddress": "0.2.8" - } - }, "uniqs": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", "dev": true }, + "unique-stream": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", + "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "dev": true, + "requires": { + "json-stable-stringify": "1.0.1", + "through2-filter": "2.0.0" + } + }, "universalify": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", @@ -3628,18 +6876,68 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", + "dev": true + }, + "unzip-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", + "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=", + "dev": true + }, "url-join": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/url-join/-/url-join-1.1.0.tgz", "integrity": "sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg=", "dev": true }, + "url-parse": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.1.tgz", + "integrity": "sha512-x95Td74QcvICAA0+qERaVkRpTGKyBHHYdwL2LXZm5t/gBtCB9KQSO/0zQgSTYEV1p0WcvSg79TLNPSvd5IDJMQ==", + "dev": true, + "requires": { + "querystringify": "2.0.0", + "requires-port": "1.0.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true, + "requires": { + "prepend-http": "1.0.4" + } + }, + "url-regex": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/url-regex/-/url-regex-3.2.0.tgz", + "integrity": "sha1-260eDJ4p4QXdCx8J9oYvf9tIJyQ=", + "dev": true, + "requires": { + "ip-regex": "1.0.3" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "object.getownpropertydescriptors": "2.0.3" + } + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -3652,6 +6950,22 @@ "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", "dev": true }, + "vali-date": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", + "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", + "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", + "dev": true, + "requires": { + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" + } + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3675,6 +6989,97 @@ "extsprintf": "1.3.0" } }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "1.0.4", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + }, + "dependencies": { + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + } + } + }, + "vinyl-assign": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/vinyl-assign/-/vinyl-assign-1.2.1.tgz", + "integrity": "sha1-TRmIkbVRWRHXcajNnFSApGoHSkU=", + "dev": true, + "requires": { + "object-assign": "4.1.1", + "readable-stream": "2.3.6" + } + }, + "vinyl-fs": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", + "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", + "dev": true, + "requires": { + "duplexify": "3.6.0", + "glob-stream": "5.3.5", + "graceful-fs": "4.1.11", + "gulp-sourcemaps": "1.6.0", + "is-valid-glob": "0.3.0", + "lazystream": "1.0.0", + "lodash.isequal": "4.5.0", + "merge-stream": "1.0.1", + "mkdirp": "0.5.1", + "object-assign": "4.1.1", + "readable-stream": "2.3.6", + "strip-bom": "2.0.0", + "strip-bom-stream": "1.0.0", + "through2": "2.0.3", + "through2-filter": "2.0.0", + "vali-date": "1.0.0", + "vinyl": "1.2.0" + }, + "dependencies": { + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.6", + "xtend": "4.0.1" + } + } + } + }, + "ware": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz", + "integrity": "sha1-0bFPOdLiy0q4xAmPdW/ksWTkc9Q=", + "dev": true, + "requires": { + "wrap-fn": "0.1.5" + } + }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "dev": true, + "requires": { + "http-parser-js": "0.4.13", + "websocket-extensions": "0.1.3" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, "whatwg-fetch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", @@ -3687,12 +7092,38 @@ "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", "dev": true }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, "wordwrap": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", "dev": true }, + "wrap-fn": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/wrap-fn/-/wrap-fn-0.1.5.tgz", + "integrity": "sha1-8htuQQFv9KfjFyDbxjoJAWvfmEU=", + "dev": true, + "requires": { + "co": "3.1.0" + }, + "dependencies": { + "co": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", + "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=", + "dev": true + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3705,6 +7136,18 @@ "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=", "dev": true }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, "yamljs": { "version": "0.2.10", "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.2.10.tgz", @@ -3723,6 +7166,16 @@ "requires": { "wordwrap": "0.0.2" } + }, + "yauzl": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.9.2.tgz", + "integrity": "sha1-T7G8euH8L1cDe1SvasyP4QMcW3c=", + "dev": true, + "requires": { + "buffer-crc32": "0.2.13", + "fd-slicer": "1.1.0" + } } } } diff --git a/website/pages/en/index.js b/website/pages/en/index.js index c76fd5a..887d778 100755 --- a/website/pages/en/index.js +++ b/website/pages/en/index.js @@ -64,22 +64,30 @@ const Block = props => ( ) const Features = props => ( - - {[ - { - content: 'Generate API Blueprint specifications from your documentation.', - image: imgUrl('api_blueprint.png'), - imageAlign: 'top', - title: 'API Blueprint' - }, - { - content: 'Automatically compiled, Keep a Changelog-friendly, changelogs of your documentation.', - image: imgUrl('keep-a-changelog.svg'), - imageAlign: 'top', - title: 'Changelogs' - } - ]} - +
+ + {[ + { + content: 'Compile OpenAPI 3.0 specifications from your documentation.', + image: imgUrl('openapi.png'), + imageAlign: 'top', + title: 'OpenAPI 3.0 ready' + }, + { + content: 'Automatically compiled, Keep a Changelog-friendly, changelogs of your documentation.', + image: imgUrl('keep-a-changelog.svg'), + imageAlign: 'top', + title: 'Automatic changelogs' + }, + { + content: 'Compile API Blueprint specifications for simple API design.', + image: imgUrl('api_blueprint.png'), + imageAlign: 'top', + title: 'API Blueprint compatible' + } + ]} + +
) class Index extends React.Component { diff --git a/website/sidebars.json b/website/sidebars.json index a7d5b57..f53d2eb 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -4,7 +4,7 @@ "installation", "configuration", "writing-documentation", - "generate-documentation", + "compile-documentation", "generate-changelogs" ], "Concepts": [ @@ -16,18 +16,21 @@ "representations" ], "API Reference": [ - "api-capability", "api-contenttype", "api-data", + "api-error", + "api-group", "api-label", + "api-maxversion", "api-minversion", "api-param", + "api-path", + "api-pathparam", + "api-queryparam", "api-return", "api-scope", "api-see", - "api-throws", - "api-uri", - "api-urisegment" + "api-vendortag" ] } } diff --git a/website/siteConfig.js b/website/siteConfig.js index 5a89553..4dc8b67 100644 --- a/website/siteConfig.js +++ b/website/siteConfig.js @@ -1,6 +1,6 @@ const siteConfig = { title: 'Mill', - tagline: 'A small annotation DSL for documenting a REST API.', + tagline: 'An annotation-based DSL for documenting a REST API', url: 'https://vimeo.github.io', baseUrl: '/mill/', diff --git a/website/static/img/openapi.png b/website/static/img/openapi.png new file mode 100644 index 0000000..19369ab Binary files /dev/null and b/website/static/img/openapi.png differ