erunion / mill Goto Github PK
View Code? Open in Web Editor NEW☴ An annotation-based DSL for documenting a REST API.
License: MIT License
☴ An annotation-based DSL for documenting a REST API.
License: MIT License
Right now, SemVer "patch" versions, like "1.1.1" are not compiled.
We should treat them just like every other version, so they'd get their own directory in the output, and the generator should have them split apart as well.
Right now, @api-throws
has support for a dictionary-like type and subtype system where you can do {User}
as your throws description to generate a description of "If the User cannot be found.".
It might be worth investigating at looking to replace that with a configurable dictionary of these types of common return, error, and parameter descriptions that you could easily drop in.
For example:
@api-throws:private {403} \ErrorRepresentation If the user was not found.
Imagine setting up a dictionary in your config like:
<dictionaries>
<description id="user.not_found">If the user was not found.</description>
</dictionaries>
And then doing:
@api-throws:private {403} \ErrorRepresentation {user.not_found}
We should also retain the current sprintf-like functionality and support something like:
<dictionaries>
<description id="common.not_found">If the %s was not found.</description>
</dictionaries>
@api-throws:private {403} \ErrorRepresentation {common.not_found:"user"}
Just an idea to consolidate and have consistent documentation error messaging.
There are some instances in the Vimeo API where we have data within a representation that depends upon a service call, and that call may fail from time to time. The problem right now is that when that call fails, we don't then want the entire endpoint to fail.
Instead, we've come up with a solution to return an array of "warnings" with information on what data in the representation we were unable to fill out for the developer.
The way to document this, I believe, is with a new @api-warning
annotation. It'll look the same as an @api-throws
, just without an HTTP status code.
@api-warning \Warning\Representation (\Warning\Error:CODE) Some warning message.
This annotation will only be available on representations:
/**
* @api-data metadata.connections.watched_videos (object) - Information about the
* videos this user has watched.
* @api-see \App\Representation\Connection::create metadata.connections.watched_videos
* @api-warning \App\Representation\Warning
* (\App\Warnings\PlayProgress::WATCHED_VIDEOS_UNAVAILABLE) Watched videos
* are currently unavailable.
*/
For configuration, I think we can go with the same setup that we have for error representations since this is a little similar:
<representations>
<filter>
...
</filter>
<errors>
...
</errors>
<warnings>
<class name="\App\Representation\Warning" method="create" />
</warnings>
</representations>
@api-warning
annotation to be used within representations.
@api-throws
annotations, there should be validation in place to verify that the attached warning representation is valid, and the (required) error code is present.
@api-throws
, error codes should be able to be strings or a FQN static callable. If it is not callable, we should throw a validation error.+ Response
and display them in a bulleted list similar to how we're currently surfacing multiple @api-throws
and error codes.Not exactly sure how we're going to handle this because those descriptions can contain Markdown, and it'll be very difficult to merge that with the Markdown in the Blueprint files. Nonetheless we should figure something out, even if it's just converting Markdown headers into bold elements or something.
For the API Blueprint files (and coming OAI work planned for in 2.0, #16), to be useful in a mock server environment like Drakov, we need some sort of way to document @api-param
and representation data examples.
I think documenting them on representations would be easily accomplished with an @api-example
annotation:
/**
* @api-label Director
* @api-field director
* @api-type string
* @api-example JJ Abrams
*/
Thankfully, this isn't really necessary for enum's as Drakov picks the first item in the enum as the example. For parameters, however, it's a bit more complicated because of the existing language syntax in place for documenting them.
The current idea that I've been running with is to document examples within backticks.
@api-param:public {string} director (optional) `JJ Abrams` Name of the director.
And compiled into the Blueprint, it would appear as:
- `director`: `JJ Abrams` (string) - Director
<exclude>
and <ignores>
in the config file are basically doing the same thing: ignoring classes from being parsed for documentation. Let's merge them and go with just <exclude>
.
Also replace the isRepresentationIgnored
method in the config class to pull from this merged array.
In the Vimeo API, we have:
/**
* @api-label Upload URL (secure)
* @api-field upload_link_secure
* @api-type string
* @api-version <=3.3
*/
However, on Blueprint generation, it's not appearing on any compiled version Markdown, including v3.3.
Now that Psalm has been open sourced (https://github.com/vimeo/psalm), it'd be neat to create a Psalm plugin that can verify that the actual type produced from an expression or closure matches the @api-type
set on it.
@api-contentType
currently lacks support for versioning, which might be useful if you once had an endpoint returning application/json
, but now returns a custom mime-type like application/vnd.vimeo.channel
.
With the new MSON work being done in mill#42, the current parameter token system is no longer viable.
The way it was originally constructed was that it would str_replace
content from the token, "{string} direction (optional) The direction that the results are sorted." onto the @api-param
annotation: "@api-param:private {direction} [asc|default|desc] Sort direction". This has resulted in funky API Blueprint content being generated:
- `direction` (enum[string]) - The direction that the results are sorted. Sort direction
+ Members
+ `asc`
+ `default`
+ `desc`
With he move to MSON for parameters and representations, this format will no longer be viable because of the wide difference between MSON content and how a string replace would work.
So the solution to fix this is to construct a new "trait" system.
The basic idea is that we'd replace the parameterTokens
config with traits
. You would load traits with a @api-trait
annotation, and then specify additional data to override that trait with, like a description, or new enum values.
<parameterTokens>
config XML declaration with a <traits>
config.<trait>
blocks:<trait name="filter_playable">
`true` (string, default, optional) - Choose between only videos that are playable, and only videos that are not playable.
+ Members
- `true`
- `false`
</trait>
@api-trait filter_playable
@api-trait
annotation:@api-trait filter_playable - This is an overloaded description
+ Default: `yes`
+ Members
- `yes`
- `no`
@api-traitEnabled trait_name
annotation.
For some additional background, see:
The goal is to be able to indicate that a field, endpoint, parameter etc. is "the wrong way", and has been replaced with a better way.
eg from the Vimeo API: The upload_link
parameter is now HTTPS, which means there is no need for the upload_link_secure
parameter because the urls are now identical. We should have the option to exclude upload_link_secure
from most docs, and include it in others with a clear warning that upload_link_secure
should no longer be used, and will be removed in future versions.
Ideally we would be able to explain what the better way is alongside the deprecation annotation.
<parameterTokens>
<token name="direction">{string} direction (optional) The direction that the results are sorted.</token>
</parameterTokens>
@api-param:private {direction} [asc|default|desc] Sort direction
Is compiled into API Blueprint as:
- `direction` (enum[string]) - The direction that the results are sorted. Sort direction
+ Members
+ `asc`
+ `default`
+ `desc`
The problem with fixing this is that right now we're doing a straight string replace for the token, and that doesn't care about the existing data.
I don't exactly know how to fix this other than running a quick parse of the existing parameter (pre-token replacement) to parse out its data, then drop the token in, re-parse and replace the tokens' data with the previously parsed content.
This is also going to be problematic with the coming MOSN work (#42), but that's going to most likely require revisiting the entire concept of parameter tokens.
@api-see
right now only lets you reference classes by their full FQN, @api-see \Full\Class::method
.
We should change this so you can do things like @api-see static::method
so you can self-reference methods within the response you're using. This is helpful if you have documentation in a base class, but want to include docs that are available within an abstract method.
Programmatically, I think we can do a detection on the see
annotation and if it has static::
or self::
, swap that out with the FQN of the class representation that that exists within.
Should be fairly easy.
@api-param {string} content_rating +MOVIE_RATINGS+
[MPAA rating](http://www.mpaa.org/film-ratings/)
Having a parameter description with a Markdown link causes the link to get parsed without the link content.
[
'capability' => 'MOVIE_RATINGS'
'deprecated' => false
'description' => '(http://www.mpaa.org/film-ratings/)'
'field' => 'content_rating'
'required' => true
'type' => 'string'
'values' => Array &1 (
0 => 'MPAA rating'
)
'version' => false
'visible' => true
]
Right now we have support for neither documenting @api-param
or @api-options
enumerated values.
@api-param:public {sort} [relevant|date|alphabetical|plays|likes|comments|duration]
@api-options [by|by-sa|by-nd|by-nc|by-nc-sa|by-nc-nd]
I'm imagining something like this:
@api-param:public {sort} [relevant|date|alphabetical|plays|likes|comments|duration]
@api-paramEnumValue {sort} comments This sorts by the amount of comments on the object.
@api-options [by|by-sa|by-nd|by-nc|by-nc-sa|by-nc-nd]
@api-optionsEnumValue {by-sa} Attribution-ShareAlike 2.0 Generic
This could also allow us to start documenting the default value in an enum:
@api-paramEnumValue {sort} date *default* Date that a video was uploaded.
https://github.com/apiaryio/mson
Basically, we're running into issues adding new things (like examples) into the Mill DSL for @api-param
and representation annotations (see #41). Instead of trying to reinvent the wheel ourselves, we should just move over to MSON and be done with it.
Advantages of MSON over our existing description DSL:
Disadvantages:
Solving this would be three parts:
@api-param
syntax with MSON@api-field
, @api-type
, @api-label
, and @api-options
in representations with a single new @api-data
annotation, that also uses MSON for describing the piece of data.Since we require capabilities, and writing enum/options/members on the same line as the parameter, we'll need to have our own MSON flavor:
@api-param:visibility field `example` (type, required, capability) - Description
+ Members
- `option1` - "option1" description
- `option2` - "option2" description
@api-data field `example` (type, required, capability) - Description
+ Members
- `option1` - "option1" description
- `option2` - "option2" description
This should allow us to retain the core parts of MSON portability, while also retaining some flexibility for Mill-specific features like capabilities, and single line definitions.
Unfortunately, this means we'll need to build our own parser, but at least the spec part of this is easy to understand, and will be easy to implement.
Swapping out the @api-param
DSL for MSON would be relatively easy (pending an MSON parser) as all of that logic is contained within ParamAnnotation::parse
. Instead of manually parsing the docblock with regex, we'd feed it into the MSON parser and get back our data, and then send that along to the overall Mill parsing engine.
For example:
@api-param:public {page}
@api-param:public {per_page}
@api-param:public {string} query Search query.
@api-param:public {sort} [relevant|date|alphabetical]
@api-param:public {direction} [asc|desc]
Will become:
@api-param:public page
@api-param:public per_page
@api-param:public query (string) - Search query.
@api-param:public sort [relevant|date|alphabetical]
@api-param:public direction [asc|desc]
Doing the same will also be simple, since everything is contained within the FieldAnnotation:parser
method.
@api-label Director
@api-field director
@api-type string
@api-version >=3.3
Will become:
@api-data director `JJ Abrams` (string) - Director
@api-version >=3.3
This'll allow us to cut back on the annotation complexity of representation data by killing @api-label
, @api-field
, @api-type
, and @api-options
.
I'd like to heavily use ::class
in unit tests to enable better IDE support when building the library, and can't do this and also support PHP 5.4
SemVer patch version schemas like 1.2.3 currently fail version parsing.
Throw an exception when options are supplied to a @api-type
annotation that doesn't allow options.
Implementers draft hits on February 28th.
https://www.openapis.org/blog/2017/01/24/a-new-year-a-new-specification
Follow the format defined here: https://github.com/olivierlacan/keep-a-changelog/blob/master/CHANGELOG.md
When using @api-version
alongside a @api-see
element, the specified version is not being applied to the loaded annotations from @api-see
, but instead all versions.
Example:
/**
* @api-label Play representation
* @api-field play
* @api-type representation
* @api-version >=3.3
* @api-see \PlayResponse::_json play
*/
We're loading the play
representation behind >=3.3
, but what happens when you compile this is that the >=3.3
Blueprint files get the "Play representation" label, and content, while other versioned Blueprint files get everything, except for "Play representation".
What should be happening is that only >=3.3
gets the play
representation content. All other versions should have no knowledge of this data.
We should have a lint
command, that does everything that generate
does when compiling your documentation. Right now, validation is halted at the first error, so instead of that, this command should build up a list of all the errors in your documentation and spit out a failure or success at the end.
Having this, it'll allow people to integrate this into their build processes alongside things like PHPUnit, PHPCS, or static analysis checkers.
Currently there's a list of return code types in ReturnAnnotation::findReturnCodeForType
that should be converted into class constants on that annotation class.
latestApiVersion
declaration into mill.xml
so people can tell Mill what their latest API version is, and also use that to pull their own latest API docs.Down the line, we can then use this to do validation on their API versioning to prevent issues where might accidentally typo a version and then blowout their docs.
Right now, there is no way to document the contents of a non-enum array parameter.
How can we build something where you can document something like:
@api-param:public {array[floats]} time_codes Array of all timecodes in this video with associated notes.
When you're doing a DELETE, you usually aren't returning any data from the request, so it doesn't make sense to document it with a representation.
/**
* Delete a movie.
*
* @api-label Delete a movie.
*
* @api-uri:private {Movies} /movies/+id
* @api-uriSegment {/movies/+id} {integer} id Movie ID
*
* @api-contentType application/json
* @api-scope delete
*
* @api-return:private {deleted}
*
* @api-throws:private {404} \Vimeo\Mill\Examples\Showtimes\Representations\Error If the movie could not be found.
*/
If an @api-minVersion
is set on a resource, but there are @api-version
annotations on that same resource that are less than the minimum version, we should throw an error during generation.
/**
* @api-label LABEL
*
* @api-uri:private {Group} /uri
*
* @api-contentType application/json
* @api-minVersion 2.0
*
* @api-return:public {collection} \Representation
*
* @api-version 1.0
* @api-return:public {object} \Representation
*/
The last version of PHPUnit to support PHP 5.4 was 4.8.*, but I'm having trouble getting that installed locally:
$ composer require --dev phpunit/phpunit 4.8.34
You are running composer with xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.
Problem 1
- phpunit/phpunit 4.8.34 conflicts with phpunit/phpunit-mock-objects[3.4.3].
- phpunit/phpunit 4.8.34 conflicts with phpunit/phpunit-mock-objects[3.4.3].
- phpunit/phpunit 4.8.34 conflicts with phpunit/phpunit-mock-objects[3.4.3].
- Installation request for phpunit/phpunit 4.8.34 -> satisfiable by phpunit/phpunit[4.8.34].
- Installation request for phpunit/phpunit-mock-objects (locked at 3.4.3) -> satisfiable by phpunit/phpunit-mock-objects[3.4.3].
Installation failed, reverting ./composer.json to its original content.
Until we drop support for 5.4 (#13), we should at least get tests running against it.
This is partly related to #21.
Right now, the Mill deprecated
annotation decorator makes it awful to document why something was deprecated. There is also no way to designate that a URI was deprecated a new version as URI's cannot be versioned.
You also cannot deprecate representation datasets, which is kind of silly.
Creating a mill.xml
file is a bit... complicated. And tedious.
Build an init
command that will walk you through, wizard-style, everything to set it up.
Parameters with values that are broken up on multiple lines end up having unnecessary/unwanted whitespace surrounding them when generated into API Blueprint files.
* @api-param:public {filter} [CC
* |CC0
* |CC-BY
* |CC-BY-SA
* |CC-BY-ND
* |CC-BY-NC
* |CC-BY-NC-SA
* |CC-BY-NC-ND
* |in-progress
* |upload_date
* |duration
* |minimum_likes
* |categories
* |trending]
* The CC filters will show only those videos with the applicable creative commons licenses.
* See our <a href="https://vimeo.com/creativecommons">Creative Commons</a> page for more.
- `filter` (enum[string]) - Filter to apply to the results. The CC filters will show only those videos with the applicable creative commons licenses. See our <a href="https://vimeo.com/creativecommons">Creative Commons</a> page for more.
+ Members
+ `CC `
+ `CC-BY `
+ `CC-BY-NC `
+ `CC-BY-NC-ND `
+ `CC-BY-NC-SA `
+ `CC-BY-ND `
+ `CC-BY-SA `
+ `CC0 `
+ `categories `
+ `duration `
+ `in-progress `
+ `minimum_likes `
+ `trending`
+ `upload_date `
+ Headers
header.+ Request
+ Headers
Content-Type: application/json
/**
* @api-data pictures (\App\PictureRepresentation) - The active picture for this video.
*/
/**
* @api-data pictures (array<object>) - The video's thumbnail images
* @api-version <3.2
*/
Problem stems from us storing representation data in an identifier-keyed array when parsing it out in the RepresentationParser
.
We should throw an exception when a representation is detected as having an error code when it is configured to not require it.
Only representations that need error codes should be present with them.
It would be amazing to have a changelog
command that, when run, would spit out a changelog (in Markdown, probably) for the life of your API.
Starting at the configured sinceApiVersion
, it'll run through every version it knows about and do diffs between the previous/next version.
Unclear right now on the verbiage that the changelog would have, but it'd be cool to get something functional first.
Parameter:
@api-param:public {boolean} review_link (optional) Enable or disable the review page
Blueprint:
- `review_link` (string) - Enable or disable the review page
https://github.com/Aconex/api-blueprint-validator-module is an NPM package that we use at Vimeo for validating our Blueprint files after Mill generates them, so it only makes sense to pull that into Mill to have the Blueprints that Mill can generate validate before we tag releases.
After this is in, also update the CONTRIBUTING file with details for anyone who wants to help out on the project.
There are instances where you have a resource action that is serving multiple URI endpoints:
* @api-uri:private {Albums\Videos} /albums/#album_id/videos/#clip_id
* @api-uriSegment {/albums/#album_id/videos/#clip_id} {integer} album_id Album ID
* @api-uriSegment {/albums/#album_id/videos/#clip_id} {integer} clip_id Video ID
*
* @api-uri:public {Albums\Me\Albums\Videos} /me/albums/#album_id/videos/!clip_id
* @api-uriSegment {/me/albums/#album_id/videos/!clip_id} {integer} album_id Album ID
* @api-uriSegment {/me/albums/#album_id/videos/!clip_id} {integer} clip_id Video ID
*
* @api-uri:public {Albums\Users\Albums\Videos} /users/+user_id/albums/#album_id/videos/!clip_id
* @api-uriSegment {/users/+user_id/albums/#album_id/videos/!clip_id} {integer} user_id User ID
* @api-uriSegment {/users/+user_id/albums/#album_id/videos/!clip_id} {integer} album_id Album ID
* @api-uriSegment {/users/+user_id/albums/#album_id/videos/!clip_id} {integer} clip_id Video ID
However, you may, in your custom rendering of documentation to smash all three of those into a single entry. I think we can solve this with a new alias decorator system for @api-uri
annotations. Designate a URI as an alias, and it'll be absorbed into the other URI for your own use.
* @api-uri:private:alias {Albums\Videos} /albums/#album_id/videos/#clip_id
* @api-uriSegment {/albums/#album_id/videos/#clip_id} {integer} album_id Album ID
* @api-uriSegment {/albums/#album_id/videos/#clip_id} {integer} clip_id Video ID
*
* @api-uri:public:alias {Me\Albums\Videos} /me/albums/#album_id/videos/!clip_id
* @api-uriSegment {/me/albums/#album_id/videos/!clip_id} {integer} album_id Album ID
* @api-uriSegment {/me/albums/#album_id/videos/!clip_id} {integer} clip_id Video ID
*
* @api-uri:public {Users\Albums\Videos} /users/+user_id/albums/#album_id/videos/!clip_id
* @api-uriSegment {/users/+user_id/albums/#album_id/videos/!clip_id} {integer} user_id User ID
* @api-uriSegment {/users/+user_id/albums/#album_id/videos/!clip_id} {integer} album_id Album ID
* @api-uriSegment {/users/+user_id/albums/#album_id/videos/!clip_id} {integer} clip_id Video ID
:alias
decorator that's only supported on URI annotations.Depending on code organization, uri annotations can become redundant when applied to every resource action. For example, a class that only defines the GET
and POST
actions for the /todos
resource would end up using the same uri annotations for both methods. In those cases it could be cleaner to throw the annotation in the class's docblock.
With the new MSON work in #42, the syntax for referencing another representation in a representation data is:
@api-data director (\Mill\Examples\Showtimes\Representations\Person) - Director
This is fine, but it's a bit... wordy. It would be nice if instead of that you could just do:
@api-data director (Person) - Director
Where Person
is being pulled from the @api-label
annotation atop the \Mill\Examples\Showtimes\Representations\Person
class.
The major problem I see with handling this is that this would require that we parse out representations first, so we have that label data in memory for lookups.
...
* @api-param:public {string} name (optional) The new title for the video
* @api-param:public {string} license [by|by-sa|by-nd|by-nc|by-nc-sa|by-nc-nd|cc0] (optional) Set the Creative Commons license
* @api-param:public {string} description (optional) The new description for the video
...
When generated into Blueprint, or even just from the generator array, these parameters are in the order that they were typed.
To keep things clean, and friendly to the person actually consuming documentation, Mill should alphabetize (recursively if necessary on dot-notation field) parameters.
Currently mill requires your PHP method to be named after an HTTP method. This seems like an unnecessary restriction. Lets discuss possible solutions in comments below.
Everywhere we're currently reporting errors back as __CLASS__::__METHOD__
. This is fine, except that __METHOD__
already includes the class that it's part of, so our error messages end up being __CLASS::__CLASS__::__METHOD__
.
__METHOD__
.A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.