-
Notifications
You must be signed in to change notification settings - Fork 184
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes #243
- Loading branch information
Showing
1 changed file
with
204 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
# Reusing swagger schemas | ||
|
||
When building an API it can be common to have schemas that are common to multiple actions. | ||
|
||
## Scenario | ||
|
||
For example, let's say we've got two endpoints: | ||
- `GET /projects` | ||
- `GET /books` | ||
|
||
Both of these endpoints can have an erroneous responses like `401 Unauthorized`. | ||
We don't want to define our new `Error` schema for both of these endpoints. This will create | ||
duplicate code and we just want to keep our code [DRY](https://cs.wikipedia.org/wiki/Don%27t_repeat_yourself). | ||
|
||
Here's what the swagger spec in our `ProjectsController` and `BookController` would look like: | ||
|
||
```elixir | ||
defmodule ProjectsController do | ||
use HelloWeb, :controller | ||
use PhoenixSwagger | ||
|
||
swagger_path :index do | ||
get "/projects" | ||
produces "application/json" | ||
parameter("Authorization", :header, :string, "OAuth2 access token", required: true) | ||
parameters do | ||
sort_by :query, :string, "The property to sort by" | ||
sort_direction :query, :string, "The sort direction", enum: [:asc, :desc], default: :asc | ||
company_id :string, :query, "The company id" | ||
end | ||
response(200, "OK", Schema.ref(:ListOfProjects)) | ||
response(401, "Unauthorized", Schema.ref(:Error)) | ||
end | ||
|
||
|
||
|
||
@doc false | ||
def swagger_definitions do | ||
%{ | ||
ListOfProjects: ...schema definition..., | ||
Error: | ||
swagger_schema do | ||
properties do | ||
code(:string, "Error code", required: true) | ||
message(:string, "Error message", required: true) | ||
end | ||
end | ||
} | ||
end | ||
end | ||
``` | ||
|
||
```elixir | ||
defmodule BooksController do | ||
use HelloWeb, :controller | ||
use PhoenixSwagger | ||
|
||
swagger_path :index do | ||
get "/books" | ||
produces "application/json" | ||
parameter("Authorization", :header, :string, "OAuth2 access token", required: true) | ||
parameters do | ||
sort_by :query, :string, "The property to sort by" | ||
sort_direction :query, :string, "The sort direction", enum: [:asc, :desc], default: :asc | ||
company_id :string, :query, "The company id" | ||
end | ||
response(200, "OK", Schema.ref(:ListOfBooks)) | ||
response(401, "Unauthorized", Schema.ref(:Error)) | ||
end | ||
|
||
|
||
|
||
@doc false | ||
def swagger_definitions do | ||
%{ | ||
ListOfBooks: ...schema definition..., | ||
Error: | ||
swagger_schema do | ||
properties do | ||
code(:string, "Error code", required: true) | ||
message(:string, "Error message", required: true) | ||
end | ||
end | ||
} | ||
end | ||
end | ||
``` | ||
|
||
Our ProjectsController and BooksControllers have now identical `Error` schema defined in their modules. | ||
|
||
|
||
## Extracting schemas into a module for reuse | ||
|
||
We can easily extract common schemas into an ordinary elixir module. This module has to implement `PhoenixSwagger` | ||
behaviour which means implementing the `swagger_definitions/0` function. | ||
|
||
```elixir | ||
defmodule CommonSchemas do | ||
@moduledoc "Common schema declarations for phoenix swagger" | ||
|
||
use PhoenixSwagger | ||
|
||
@doc false | ||
def swagger_definitions do | ||
%{ | ||
Error: | ||
swagger_schema do | ||
properties do | ||
code(:string, "Error code", required: true) | ||
message(:string, "Error message", required: true) | ||
end | ||
end, | ||
Errors: | ||
swagger_schema do | ||
properties do | ||
errors( | ||
Schema.new do | ||
title("Errors") | ||
description("A collection of Errors") | ||
type(:array) | ||
items(Schema.ref(:Error)) | ||
end | ||
) | ||
end | ||
end | ||
} | ||
end | ||
end | ||
``` | ||
|
||
As you can see, it is also possible to reference other common schemas defined inside the `swagger_definitions/0`. | ||
|
||
## Reusing the common schemas | ||
|
||
Now, instead of defining the `Error` schema in every controller, we can just `alias` CommonSchema and | ||
reference the `Error` schema by atom. It is also possible to call `CommonSchemas.swagger_definitions()[:Error]` to | ||
get the reference to the Error schema, but it's verbose and unnecessary. | ||
|
||
```elixir | ||
defmodule ProjectsController do | ||
use HelloWeb, :controller | ||
use PhoenixSwagger | ||
alias CommonSchemas | ||
|
||
swagger_path :index do | ||
get "/projects" | ||
produces "application/json" | ||
parameter("Authorization", :header, :string, "OAuth2 access token", required: true) | ||
parameters do | ||
sort_by :query, :string, "The property to sort by" | ||
sort_direction :query, :string, "The sort direction", enum: [:asc, :desc], default: :asc | ||
company_id :string, :query, "The company id" | ||
end | ||
response(200, "OK", Schema.ref(:ListOfProjects)) | ||
response(401, "Unauthorized", Schema.ref(:Error)) | ||
end | ||
|
||
|
||
|
||
@doc false | ||
def swagger_definitions do | ||
%{ | ||
ListOfProjects: ...schema definition... | ||
} | ||
end | ||
end | ||
``` | ||
|
||
```elixir | ||
defmodule BooksController do | ||
use HelloWeb, :controller | ||
use PhoenixSwagger | ||
|
||
alias CommonSchemas | ||
|
||
swagger_path :index do | ||
get "/books" | ||
produces "application/json" | ||
parameter("Authorization", :header, :string, "OAuth2 access token", required: true) | ||
parameters do | ||
sort_by :query, :string, "The property to sort by" | ||
sort_direction :query, :string, "The sort direction", enum: [:asc, :desc], default: :asc | ||
company_id :string, :query, "The company id" | ||
end | ||
response(200, "OK", Schema.ref(:ListOfBooks)) | ||
response(401, "Unauthorized", Schema.ref(:Error)) | ||
end | ||
|
||
|
||
|
||
@doc false | ||
def swagger_definitions do | ||
%{ | ||
ListOfBooks: ...schema definition..., | ||
} | ||
end | ||
end | ||
``` | ||
|
||
To avoid aliasing the `CommonSchemas` in every one of your controllers, find a `HelloWeb` module and add the alias | ||
inside the `controller/0` function. This module will be called differently in your project depending on the the name of your | ||
Phoenix project name: `<ProjectName>Web`. This file is the entrypoint for defining your web interface and is part | ||
of every Phoenix installation. | ||
|