From e0de3ac08d6ee5ce93787cd62de532835535c75c Mon Sep 17 00:00:00 2001 From: LeFnord Date: Mon, 10 Oct 2016 18:34:03 +0200 Subject: [PATCH] removes limit on model names - updates README - adds changelog entry --- CHANGELOG.md | 1 + README.md | 408 +++++++++++---------- lib/grape-swagger/endpoint.rb | 17 +- spec/issues/430_entity_definitions_spec.rb | 28 +- 4 files changed, 242 insertions(+), 212 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac67d9b9..4e6fb99f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ #### Fixes +* [#515](https://github.com/ruby-grape/grape-swagger/pull/515): Removes limit on model names, updates README - [@LeFnord](https://github.com/LeFnord). * Your contribution here. ### 0.24.0 (September 23, 2016) diff --git a/README.md b/README.md index dbbd18c1..5cad3f6b 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,9 @@ * [Model Parsers](#model_parsers) * [Configure](#configure) * [Routes Configuration](#routes) +* [Using Grape Entities](#grape-entity) * [Securing the Swagger UI](#oauth) * [Markdown](#md_usage) -* [Response documentation](#response) -* [Extensions](#extensions) * [Example](#example) * [Rake Tasks](#rake) @@ -309,7 +308,7 @@ add_swagger_documentation \ #### security_definitions: Specify the [Security Definitions Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-definitions-object) -_NOTE: [Swagger-UI is supporting only implicit flow yet](https://github.com/swagger-api/swagger-ui/issues/2406#issuecomment-248651879)_ +_NOTE: [Swagger-UI is supporting only implicit flow yet](https://github.com/swagger-api/swagger-ui/issues/2406#issuecomment-248651879)_ ```ruby add_swagger_documentation \ @@ -390,14 +389,19 @@ add_swagger_documentation \ * [Swagger Header Parameters](#headers) * [Hiding an Endpoint](#hiding) * [Overriding Auto-Generated Nicknames](#overriding-auto-generated-nicknames) +* [Specify endpoint details](#details) +* [Overriding the route summary](#summary) * [Defining an endpoint as an array](#array) * [Using an options hash](#options) -* [Specify endpoint details](#details) * [Overriding param type](#overriding-param-type) -* [Overriding type](#overriding-type) -* [Multi types](#multi-types) +* [Overriding param types](#overriding-param-types) +* [Multiple param types](#multiple-types) +* [Array type](#array-type) +* [Collection Format](#collection-format) * [Hiding parameters](#hiding-parameters) +* [Setting a Swagger default value](#default-value) * [Response documentation](#response) +* [Extensions](#extensions) @@ -438,6 +442,7 @@ desc 'Conditionally hide this endpoint', hidden: lambda { ENV['EXPERIMENTAL'] != ``` + #### Overriding Auto-Generated Nicknames You can specify a swagger nickname to use instead of the auto generated name by adding `:nickname 'string'``` in the description of the endpoint. @@ -447,6 +452,35 @@ desc 'Get a full list of pets', nickname: 'getAllPets' ``` + +#### Specify endpoint details + +To specify further details for an endpoint, use the `detail` option within a block passed to `desc`: + +```ruby +desc 'Get all kittens!' do + detail 'this will expose all the kittens' +end +get '/kittens' do +``` + + + +#### Overriding the route summary + +To override the summary, add `summary: '[string]'` after the description. + +```ruby +namespace 'order' do + desc 'This will be your summary', + summary: 'Now this is your summary!' + get :order_id do + ... + end +end +``` + + #### Defining an endpoint as an array @@ -468,8 +502,8 @@ desc 'Get all kittens!', { hidden: true, is_array: true, nickname: 'getKittens', - entity: Entities::Kitten, # or success - http_codes: [[401, 'KittenBitesError', Entities::BadKitten]] # or failure + success: Entities::Kitten, # or success + failures: [[401, 'KittenBitesError', Entities::BadKitten]] # or failure # also explicit as hash: [{ code: 401, mssage: 'KittenBitesError', model: Entities::BadKitten }] produces: [ "array", "of", "mime_types" ], consumes: [ "array", "of", "mime_types" ] @@ -478,19 +512,7 @@ get '/kittens' do ``` - -#### Specify endpoint details - -To specify further details for an endpoint, use the `detail` option within a block passed to `desc`: - -```ruby -desc 'Get all kittens!' do - detail 'this will expose all the kittens' -end -get '/kittens' do -``` - - + #### Overriding param type You can override paramType in POST|PUT methods to query, using the documentation hash. @@ -504,7 +526,8 @@ post :act do end ``` -#### Overriding type + +#### Overriding param types You can override type, using the documentation hash. @@ -527,6 +550,31 @@ end } ``` + + +#### Multi types + +By default when you set multi types, the first type is selected as swagger type + +```ruby +params do + requires :action, types: [String, Integer] +end +post :act do + ... +end +``` + +```json +{ + "in": "formData", + "name": "action", + "type": "string", + "required": true +} +``` + + #### Array type Array types are also supported. @@ -552,6 +600,8 @@ end } ``` + + #### Collection format of arrays You can set the collection format of an array, using the documentation hash. @@ -585,28 +635,8 @@ end } ``` -#### Multi types - -By default when you set multi types, the first type is selected as swagger type - -```ruby -params do - requires :action, types: [String, Integer] -end -post :act do - ... -end -``` - -```json -{ - "in": "formData", - "name": "action", - "type": "string", - "required": true -} -``` + #### Hiding parameters Exclude single optional parameter from the documentation @@ -621,28 +651,27 @@ post :act do end ``` -#### Overriding the route summary -By default, the route summary is filled with the value supplied to `desc`. + +#### Setting a Swagger default value + +Grape allows for an additional documentation hash to be passed to a parameter. ```ruby -namespace 'order' do - desc 'This will be your summary' - get :order_id do - ... - end +params do + requires :id, type: Integer, desc: 'Coffee ID' + requires :temperature, type: Integer, desc: 'Temperature of the coffee in celcius', documentation: { default: 72 } end ``` -To override the summary, add `summary: '[string]'` after the description. +The example parameter will populate the Swagger UI with the example value, and can be used for optional or required parameters. + +Grape uses the option `default` to set a default value for optional parameters. This is different in that Grape will set your parameter to the provided default if the parameter is omitted, whereas the example value above will only set the value in the UI itself. This will set the Swagger `defaultValue` to the provided value. Note that the example value will override the Grape default value. ```ruby -namespace 'order' do - desc 'This will be your summary', - summary: 'Now this is your summary!' - get :order_id do - ... - end +params do + requires :id, type: Integer, desc: 'Coffee ID' + optional :temperature, type: Integer, desc: 'Temperature of the coffee in celcius', default: 72 end ``` @@ -694,40 +723,134 @@ end ``` - -## Additional documentation - -* [Markdown in Detail](#md_usage) -* [Response documentation](#response) + +#### Response documentation -### Setting a Swagger defaultValue +You can also document the HTTP status codes with a description and a specified model, as ref in the schema to the definitions, that your API returns with one of the following syntax. -Grape allows for an additional documentation hash to be passed to a parameter. +In the following cases, the schema ref would be taken from route. ```ruby -params do - requires :id, type: Integer, desc: 'Coffee ID' - requires :temperature, type: Integer, desc: 'Temperature of the coffee in celcius', documentation: { default: 72 } +desc 'thing', failures: [ { code: 400, message: "Invalid parameter entry" } ] +get '/thing' do + ... end ``` -The example parameter will populate the Swagger UI with the example value, and can be used for optional or required parameters. +```ruby +desc 'thing' do + params Entities::Something.documentation + failures [ { code: 400, message: "Invalid parameter entry" } ] +end +get '/thing' do + ... +end +``` -Grape uses the option `default` to set a default value for optional parameters. This is different in that Grape will set your parameter to the provided default if the parameter is omitted, whereas the example value above will only set the value in the UI itself. This will set the Swagger `defaultValue` to the provided value. Note that the example value will override the Grape default value. +```ruby +get '/thing', failures: [ + { code: 200, message: 'Ok' }, + { code: 400, message: "Invalid parameter entry" } +] do + ... +end +``` +By adding a `model` key, e.g. this would be taken. ```ruby -params do - requires :id, type: Integer, desc: 'Coffee ID' - optional :temperature, type: Integer, desc: 'Temperature of the coffee in celcius', default: 72 +get '/thing', failures: [ + { code: 200, message: 'Ok' }, + { code: 422, message: "Invalid parameter entry", model: Entities::ApiError } +] do + ... end ``` +If no status code is defined [defaults](/lib/grape-swagger/endpoint.rb#L121) would be taken. +The result is then something like following: -### Grape Entities +```json +"responses": { + "200": { + "description": "get Horses", + "schema": { + "$ref": "#/definitions/Thing" + } + }, + "401": { + "description": "HorsesOutError", + "schema": { + "$ref": "#/definitions/ApiError" + } + } +}, +``` -Add the [grape-entity](https://github.com/ruby-grape/grape-entity) gem to your Gemfile. + +#### Extensions + +Swagger spec2.0 supports extensions on different levels, for the moment, +the documentation on `verb`, `path` and `definition` level would be supported. +The documented key would be generated from the `x` + `-` + key of the submitted hash, +for possibilities refer to the [extensions spec](spec/lib/extensions_spec.rb). +To get an overview *how* the extensions would be defined on grape level, see the following examples: + +- `verb` extension, add a `x` key to the `desc` hash: +```ruby +desc 'This returns something with extension on verb level', + x: { some: 'stuff' } +``` +this would generate: +```json +"/path":{ + "get":{ + "…":"…", + "x-some":"stuff" + } +} +``` + +- `path` extension, by setting via route settings: +```ruby +route_setting :x_path, { some: 'stuff' } +``` +this would generate: +```json +"/path":{ + "x-some":"stuff", + "get":{ + "…":"…", + } +} +``` + +- `definition` extension, again by setting via route settings, +here the status code must be provided, for which definition the extensions should be: +```ruby +route_setting :x_def, { for: 422, other: 'stuff' } +``` +this would generate: +```json +"/definitions":{ + "ApiError":{ + "x-other":"stuff", + "…":"…", + } +} +``` +or, for more definitions: +```ruby +route_setting :x_def, [{ for: 422, other: 'stuff' }, { for: 200, some: 'stuff' }] +``` + + + +## Using Grape Entities + +Add the [grape-entity](https://github.com/ruby-grape/grape-entity) and [grape-swagger-entity](https://github.com/ruby-grape/grape-swagger-entity) gem to your Gemfile. The following example exposes statuses. And exposes statuses documentation adding :type and :desc. +The documented class/definition name could be set via `#entity_name`. ```ruby module API @@ -741,6 +864,11 @@ module API class Link < Grape::Entity expose :href, documentation: { type: 'url' } expose :rel, documentation: { type: 'string'} + + def self.entity_name + 'LinkedStatus' + end + end end @@ -766,12 +894,12 @@ end ``` -#### Relationships +### Relationships You may safely omit `type` from relationships, as it can be inferred. However, if you need to specify or override it, use the full name of the class leaving out any modules named `Entities` or `Entity`. -##### 1xN +#### 1xN ```ruby module API @@ -801,7 +929,7 @@ end ``` -##### 1x1 +#### 1x1 Note: `is_array` is `false` by default. @@ -840,7 +968,7 @@ The Swagger UI on Grape could be secured from unauthorized access using any midd - some guard method, which could receive as argument a string or an array of authorization scopes; - a *before* method to be run in the Grape controller for authorization purpose; -- a set of methods which will process the access token received in the HTTP request headers (usually in the +- a set of methods which will process the access token received in the HTTP request headers (usually in the 'HTTP_AUTHORIZATION' header) and try to return the owner of the token. Below are some examples of securing the Swagger UI on Grape installed along with Ruby on Rails: @@ -864,8 +992,8 @@ This is how to configure the grape_swagger documentation: The guard method should inject the Security Requirement Object into the endpoint's route settings (see Grape::DSL::Settings.route_setting method). -The 'oauth2 false' added to swagger_documentation is making the main Swagger endpoint protected with OAuth, i.e. the -access_token is being retreiving from the HTTP request, but the 'false' scope is for skipping authorization and +The 'oauth2 false' added to swagger_documentation is making the main Swagger endpoint protected with OAuth, i.e. the +access_token is being retreiving from the HTTP request, but the 'false' scope is for skipping authorization and showing the UI for everyone. If the scope would be set to something else, like 'oauth2 admin', for example, than the UI wouldn't be displayed at all to unauthorized users. @@ -977,126 +1105,6 @@ end ``` - -## Response documentation - -You can also document the HTTP status codes with a description and a specified model, as ref in the schema to the definitions, that your API returns with one of the following syntax. - -In the following cases, the schema ref would be taken from route. - -```ruby -desc 'thing', http_codes: [ { code: 400, message: "Invalid parameter entry" } ] -get '/thing' do - ... -end -``` - -```ruby -desc 'thing' do - params Entities::Something.documentation - http_codes [ { code: 400, message: "Invalid parameter entry" } ] -end -get '/thing' do - ... -end -``` - -```ruby -get '/thing', http_codes: [ - { code: 200, message: 'Ok' }, - { code: 400, message: "Invalid parameter entry" } -] do - ... -end -``` - -By adding a `model` key, e.g. this would be taken. -```ruby -get '/thing', http_codes: [ - { code: 200, message: 'Ok' }, - { code: 422, message: "Invalid parameter entry", model: Entities::ApiError } -] do - ... -end -``` -If no status code is defined [defaults](/lib/grape-swagger/endpoint.rb#L121) would be taken. - -The result is then something like following: - -```json -"responses": { - "200": { - "description": "get Horses", - "schema": { - "$ref": "#/definitions/Thing" - } - }, - "401": { - "description": "HorsesOutError", - "schema": { - "$ref": "#/definitions/ApiError" - } - } -}, -``` - - -## Extensions - -Swagger spec2.0 supports extensions on different levels, for the moment, -the documentation on `verb`, `path` and `definition` level would be supported. -The documented key would be generated from the `x` + `-` + key of the submitted hash, -for possibilities refer to the [extensions spec](spec/lib/extensions_spec.rb). -To get an overview *how* the extensions would be defined on grape level, see the following examples: - -- `verb` extension, add a `x` key to the `desc` hash: -```ruby -desc 'This returns something with extension on verb level', - x: { some: 'stuff' } -``` -this would generate: -```json -"/path":{ - "get":{ - "…":"…", - "x-some":"stuff" - } -} -``` - -- `path` extension, by setting via route settings: -```ruby -route_setting :x_path, { some: 'stuff' } -``` -this would generate: -```json -"/path":{ - "x-some":"stuff", - "get":{ - "…":"…", - } -} -``` - -- `definition` extension, again by setting via route settings, -here the status code must be provided, for which definition the extensions should be: -```ruby -route_setting :x_def, { for: 422, other: 'stuff' } -``` -this would generate: -```json -"/definitions":{ - "ApiError":{ - "x-other":"stuff", - "…":"…", - } -} -``` -or, for more definitions: -```ruby -route_setting :x_def, [{ for: 422, other: 'stuff' }, { for: 200, some: 'stuff' }] -``` - ## Example diff --git a/lib/grape-swagger/endpoint.rb b/lib/grape-swagger/endpoint.rb index 4e667501..a3f5199a 100644 --- a/lib/grape-swagger/endpoint.rb +++ b/lib/grape-swagger/endpoint.rb @@ -287,17 +287,14 @@ def expose_params_from_model(model) model_name end - def model_name(name) - if name.respond_to?(:entity_name) - name.entity_name - elsif name.to_s.end_with?('Entity', 'Entities') - length = 0 - name.to_s.split('::')[0..-2].reverse.take_while do |x| - length += x.length - length < 42 - end.reverse.join + def model_name(entity) + if entity.respond_to?(:entity_name) + entity.entity_name + elsif entity.to_s.end_with?('::Entity', '::Entities') + entity.to_s.split('::')[-2] else - name.name.demodulize.camelize + entity.name.demodulize.camelize + # entity.respond_to?(:name) ? entity.name.demodulize.camelize : entity.split('::').last end end diff --git a/spec/issues/430_entity_definitions_spec.rb b/spec/issues/430_entity_definitions_spec.rb index f6b193ea..5eda5bc8 100644 --- a/spec/issues/430_entity_definitions_spec.rb +++ b/spec/issues/430_entity_definitions_spec.rb @@ -15,6 +15,12 @@ class Entity < Grape::Entity end class Class2 + class Entities < Grape::Entity + expose :one_thing + end + end + + class Class3 class Entity < Grape::Entity expose :another_thing @@ -23,6 +29,18 @@ def self.entity_name end end end + + class Class4 + class FourthEntity < Grape::Entity + expose :another_thing + end + end + + class Class5 + class FithEntity < Class4::FourthEntity + expose :another_thing + end + end end end end @@ -30,7 +48,10 @@ def self.entity_name class NameApi < Grape::API add_swagger_documentation models: [ DummyEntities::WithVeryLongName::AnotherGroupingModule::Class1::Entity, - DummyEntities::WithVeryLongName::AnotherGroupingModule::Class2::Entity + DummyEntities::WithVeryLongName::AnotherGroupingModule::Class2::Entities, + DummyEntities::WithVeryLongName::AnotherGroupingModule::Class3::Entity, + DummyEntities::WithVeryLongName::AnotherGroupingModule::Class4::FourthEntity, + DummyEntities::WithVeryLongName::AnotherGroupingModule::Class5::FithEntity ] end end @@ -43,6 +64,9 @@ class NameApi < Grape::API JSON.parse(last_response.body)['definitions'] end - specify { expect(subject).to include 'AnotherGroupingModuleClass1' } + specify { expect(subject).to include 'Class1' } + specify { expect(subject).to include 'Class2' } specify { expect(subject).to include 'FooKlass' } + specify { expect(subject).to include 'FourthEntity' } + specify { expect(subject).to include 'FithEntity' } end