## Documenting tools * [Apiary](https://apiary.io/) * [API blueprint](https://apiblueprint.org/) * [RAML](http://raml.org/) * [Swagger](http://swagger.io/)
## [Open API initiative](https://openapis.org/) (November 5, 2015) The Open API Initiative (OAI) was created by a consortium of forward-looking industry experts who recognize the immense value of standardizing on how REST APIs are described. As an open governance structure under the Linux Foundation, the OAI is focused on creating, evolving and promoting a vendor neutral description format. SmartBear Software is donating the Swagger Specification directly to the OAI as the basis of this Open Specification.
## [OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md) * Meta * Path, operation, and response * Parameters * Definitions * Security

Meta

title: Swagger Sample App
description: This is a sample server Petstore server.
termsOfService: http://swagger.io/terms/
contact:
  name: API Support
  url: http://www.swagger.io/support
  email: support@swagger.io
license:
  name: Apache 2.0
  url: http://www.apache.org/licenses/LICENSE-2.0.html
version: 1.0.1

Security

securityDefinitions:
  api_key:
    type: apiKey
    name: Authorization
    in: header
    description: >-
      ## Authentication

      All API calls but secured with api token. Client **must**
      provide `Authorization` header with valid user token and
      email to make request. All requests **must** have HTTP
      header with following format.

          Token token="O70Z9WsG7tZkl6I5I9dfmwUWzVq/7AVurbX77gEScbUPUB+Yuu4b1ph1AfEh9rIUdTrXUhkDwWciARZjbhjfzw==", email="user@email.com"

Definitions

User:
  type: object
  required:
  - id
  - email
  - name
  properties:
    id:
      type: integer
    email:
      type: string
    name:
      type: string

Request

GET /pets/1/photos?month=january

Path

paths:
  /pets:
    - ...
  /users:
    - ...
  /photos
    - ...

Method

/pets:
  get:
    description: Returns all pets from the system that the user has access to
    produces:
    - application/json
    responses:
      - ...
  post:
    description: Creates new pets instance
    produces:
    - application/json
    responses:
      - ...

Response

responses:
  '200':
    description: A list of pets.
    schema:
      type: array
      items:
        $ref: '#/definitions/pet'

  '503':
    description: Server error
    schema:
      type: object
      properties:
        $ref: '#/definitions/error'

Parameters

parameters:
- name: petId
  in: path
  description: ID of pet that needs to be updated
  required: true
  type: string
- name: name
  in: formData
  description: Updated name of the pet
  required: false
  type: string

All together

paths:
  /pets/{petId}:
    get:
      parameters:
      - name: petId
        in: path
        description: ID of pet that needs to be updated
        required: true
        type: string
      responses:
        '200':
          description:
          schema:
            type: object
            properties:
              $ref: '#/definitions/pet'

## Swagger tools * [Official](http://swagger.io/tools/) * [Community](http://swagger.io/open-source-integrations/)
## Official * [Swagger UI](http://petstore.swagger.io/) * [Swagger Editor](http://editor.swagger.io/)
## Community **Languages:** Clojure, D, Erlang, Go, Haskell, Java, Javascript, Typescript, .NET, Perl, PHP, Python, Ruby, Scala **Tools:** linters, generators, UIs, Confluence publisher, and everything.
## Testing requirements * YAML parser * JSON schema validator * Testing framework * Some magic

Helper

RSpec::Matchers.define :match_response_schema do |path, meth, status|
  match do |response|
    swagger_yaml = YAML.load_file(File.join(Rails.root, 'api.swagger.yml'))
    full_schema = swagger_yaml.resolve_model_definitions!(swagger_yaml['definitions'])
    schema = full_schema['paths'][path][meth.to_s]['responses'][status.to_s]['schema']
    JSON::Validator.validate!(schema, response.body, strict: true)
  end
end

class Hash
  def resolve_model_definitions!(definitions)
    if self.key?('$ref')
      model = self['$ref'].split('/').last
      definition = definitions[model]
      delete('$ref')
      definition.each_pair do |key, value|
        self[key] = value
      end
    end

    each_pair do |_, value|
      value.resolve_model_definitions!(definitions) if value.is_a? Hash
    end

    self
  end
end

Request specs

describe 'Authenticate API user' do
  let(:user) { FactoryGirl.create :user }

  context 'with correct credentials' do
    before { post '/api/v1/sessions', user: { email: user.email, password: user.password } }

    it { expect(response).to have_http_status 201 }
    it { expect(response).to match_response_schema('/sessions/', :post, 201) }
  end

  context 'with wrong credentials' do
    before { post '/api/v1/sessions', user: { email: user.email, password: FFaker::Lorem.word } }

    it { expect(response).to have_http_status 401 }
    it { expect(response.body).to eq({ error: 'Wrong credentials' }.to_json) }
    it { expect(response).to match_response_schema('/sessions/', :post, 401) }
  end
end