### Install Dependencies and Run Phoenix App Source: https://github.com/open-api-spex/open_api_spex/blob/master/examples/phoenix_app/README.md Commands to fetch dependencies, generate the OpenAPI spec, set up the database, and start the Phoenix server. ```bash mix deps.get mix phoenix_app_web.open_api_spec mix ecto.create mix ecto.migrate mix phx.server ``` -------------------------------- ### Example MediaType Struct Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/10-utilities-and-helpers.md Illustrates the creation of a MediaType struct with schema and examples. ```elixir %MediaType{ schema: UserSchema, example: %{"id" => 1, "name" => "John"}, examples: %{ "user1" => %Example{ value: %{"id" => 1, "name" => "John"} }, "user2" => %Example{ value: %{"id" => 2, "name" => "Jane"} } } } ``` -------------------------------- ### Example of Constructing a Path Parameter Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/03-operations-and-controller-specs.md Demonstrates how to use Operation.parameter/5 to create a parameter for the 'user_id' in the path, specifying its type as integer and providing a description and example. ```elixir Operation.parameter(:user_id, :path, :integer, "The user ID", example: 123) # => %Parameter{name: :user_id, in: :path, schema: %Schema{type: :integer}, ...} ``` -------------------------------- ### Generate Example Data from Schema Source: https://github.com/open-api-spex/open_api_spex/blob/master/README.md Use OpenApiSpex.Schema.example/1 to generate example data from a schema, useful for controller/plug tests. This example shows generating a request body for a user. ```elixir use MyAppWeb.ConnCase test "create/2", %{conn: conn} do request_body = OpenApiSpex.Schema.example(MyAppWeb.Schemas.UserRequest.schema()) json = conn |> post(user_path(conn), request_body) |> json_response(200) end ``` -------------------------------- ### Generate Schema Example Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/02-schema-definitions.md Generates a realistic example value that conforms to the provided schema. Useful for testing and documentation. ```elixir def example(schema = %Schema{}) ``` -------------------------------- ### Install and Run PlugApp Source: https://github.com/open-api-spex/open_api_spex/blob/master/examples/plug_app/README.md Commands to fetch dependencies, set up the database, and run the Plug application. ```bash mix deps.get mix ecto.create mix ecto.migrate mix run --no-halt ``` -------------------------------- ### Generate Schema Example Value Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/10-utilities-and-helpers.md Creates example data from a schema definition, supporting 'example' fields, generated values, and nested objects. Useful for documentation and testing. ```elixir @spec example(Schema.t()) :: any def example(schema = %Schema{}) ``` ```elixir schema = MyAppWeb.Schemas.User.schema() example = Schema.example(schema) # => %{ # "id" => 1, # "name" => "example", # "email" => "user@example.com", # "created_at" => "2023-01-15T10:30:00Z" # } ``` -------------------------------- ### Server Configuration with Variables Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/08-configuration.md Example of configuring multiple servers, including one with environment-specific variables. ```elixir servers: [ %Server{url: "https://api.example.com"}, %Server{url: "https://{environment}.example.com", variables: %{ "environment" => %ServerVariable{ default: "production", enum: ["production", "staging", "development"], description: "API environment" } } } ] ``` -------------------------------- ### Elixir Test Example for assert_schema/3 with Example Values Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/07-test-assertions.md Demonstrates how to use `assert_schema/3` to validate an example user schema within an ExUnit test. Ensure `MyAppWeb.ApiSpec.spec()` is available. ```elixir defmodule MyAppWeb.UserTest do use ExUnit.Case import OpenApiSpex.TestAssertions setup do {:ok, api_spec: MyAppWeb.ApiSpec.spec()} end test "user schema example is valid", %{api_spec: spec} do example = MyAppWeb.Schemas.User.schema().example assert_schema(example, "User", spec) end end ``` -------------------------------- ### Formatter Setup Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/QUICK-REFERENCE.md Configure the formatter for OpenAPI Spex dependencies in `.formatter.exs`. ```elixir [ import_deps: [:open_api_spex] ] ``` -------------------------------- ### Define Example Data Structure Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/10-utilities-and-helpers.md Defines an example object for use in OpenAPI specifications. This can include a summary, description, a direct value, or an external URL for the example. ```elixir @type t :: %__MODULE__{ summary: String.t() | nil, description: String.t() | nil, value: any, externalValue: String.t() | nil, extensions: %{String.t() => any()} | nil } ``` ```elixir %Example{ summary: "Valid user", value: %{ id: 123, name: "John Doe", email: "john@example.com" } } ``` ```elixir # Usage in operation: %Operation{ responses: %{ 200 => %Response{ content: %{ "application/json" => %MediaType{ schema: UserSchema, examples: %{ "user" => %Example{ summary: "A user", value: %{id: 1, name: "John", email: "john@example.com"} } } } } } } } ``` -------------------------------- ### Assert Example Against Schema Source: https://github.com/open-api-spex/open_api_spex/blob/master/README.md Validate that an example data structure conforms to a given schema using OpenApiSpex.TestAssertions.assert_schema/3. This is useful for ensuring examples remain valid as schemas evolve. ```elixir use ExUnit.Case import OpenApiSpex.TestAssertions test "UsersResponse example matches schema" do api_spec = MyAppWeb.ApiSpec.spec() schema = MyAppWeb.Schemas.UsersResponse.schema() assert_schema(schema.example, "UsersResponse", api_spec) end ``` -------------------------------- ### Testing Schema Examples for User and Error Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/07-test-assertions.md This snippet shows how to verify that the example values defined within your API schemas (e.g., User, Error) actually conform to their respective schema definitions using `assert_schema`. ```elixir defmodule MyAppWeb.Schemas.UserTest do use ExUnit.Case import OpenApiSpex.TestAssertions test "User schema example is valid" do api_spec = MyAppWeb.ApiSpec.spec() schema = MyAppWeb.Schemas.User.schema() assert_schema(schema.example, "User", api_spec) end test "Error schema example is valid" do api_spec = MyAppWeb.ApiSpec.spec() schema = MyAppWeb.Schemas.Error.schema() assert_schema(schema.example, "Error", api_spec) end end ``` -------------------------------- ### Define a Simple Object Schema Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/02-schema-definitions.md Example of defining a reusable 'User' schema as an object with properties, required fields, and an example. ```Elixir defmodule MyAppWeb.Schemas.User do require OpenApiSpex alias OpenApiSpex.Schema OpenApiSpex.schema(%{ title: "User", description: "A user account", type: :object, properties: %{ id: %Schema{type: :integer, description: "User ID"}, name: %Schema{type: :string, description: "User name"}, email: %Schema{type: :string, format: :email}, age: %Schema{type: :integer, minimum: 0, maximum: 150} }, required: [:name, :email], example: %{ "id" => 123, "name" => "John Doe", "email" => "john@example.com", "age" => 30 } }) end ``` -------------------------------- ### Type Casting Examples Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/11-quick-start-architecture.md Shows examples of automatic type conversion for incoming JSON data based on the OpenAPI schema definitions. ```elixir "123" (string) → 123 (integer) "2023-01-15" (string) → ~D[2023-01-15] (Date) %{...} (map) → %User{...} (struct, if x-struct set) ``` -------------------------------- ### Example Operation with Link Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/10-utilities-and-helpers.md Illustrates an Operation response that includes a Link to another operation, using operationId and parameters. ```elixir %Operation{ responses: %{ 201 => %Response{ description: "User created", links: %{ "GetUser" => %Link{ operationId: "getUser", parameters: %{ "userId" => "$response.body#/id" } } } } } } ``` -------------------------------- ### Example Request Body with Encoding Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/10-utilities-and-helpers.md Shows an example of a RequestBody with multipart form data, specifying encoding for a file field. ```elixir %RequestBody{ content: %{ "multipart/form-data" => %MediaType{ schema: %Schema{ type: :object, properties: %{ file: %Schema{type: :string, format: :binary} } }, encoding: %{ "file" => %Encoding{ contentType: "application/octet-stream" } } } } } ``` -------------------------------- ### Configure Production Cache Adapter Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/08-configuration.md Example configuration for the cache adapter in the production environment, suggesting the use of the fastest cache for optimal performance. ```elixir # config/prod.exs # Default PersistentTermCache is optimal - no config needed # Or: # config :open_api_spex, :cache_adapter, OpenApiSpex.Plug.AppEnvCache ``` -------------------------------- ### Example Response Headers Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/10-utilities-and-helpers.md Demonstrates how to define response headers using the Header struct, including rate limiting and request ID headers. ```elixir %Response{ description: "Success", headers: %{ "X-Rate-Limit" => %Header{ description: "Requests per hour", schema: %Schema{type: :integer} }, "X-Request-Id" => %Header{ description: "Request identifier", schema: %Schema{type: :string, format: :uuid} } } } ``` -------------------------------- ### Define Response Definitions Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/08-configuration.md Example of defining multiple response types (ok, created, not_found) with descriptions, content types, and schemas. ```elixir responses: ok: {"Success", "application/json", UserSchema}, created: {"Created", "application/json", UserSchema}, not_found: {"Not found", "application/json", ErrorSchema} ``` -------------------------------- ### Define Path Parameter Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/08-configuration.md Example of defining a required integer path parameter named 'id' with a description. ```elixir parameters: id: [in: :path, type: :integer, required: true, description: "User ID"] ``` -------------------------------- ### Configure Authentication Schemes Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/11-quick-start-architecture.md Set up security schemes like bearer tokens globally or per-endpoint. This example shows defining a 'bearer' scheme and applying it to an operation. ```elixir # In ApiSpec: components: %Components{ securitySchemes: %{ "bearer" => %SecurityScheme{type: "http", scheme: "bearer"} } } # Global (all endpoints): security: [%{"bearer" => []}] # Per-endpoint: operation :update, security: [%{"bearer" => ["users:write"]}] ``` -------------------------------- ### Complete Test Example for User Controller Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/07-test-assertions.md This snippet demonstrates a comprehensive test suite for a User controller, covering creation, retrieval, update, and deletion of users. It utilizes `OpenApiSpex.TestAssertions` to validate response schemas and data. ```elixir defmodule MyAppWeb.UserControllerTest do use MyAppWeb.ConnCase import OpenApiSpex.TestAssertions setup do {:ok, api_spec: MyAppWeb.ApiSpec.spec()} end describe "POST /users" do test "creates user with valid params", %{conn: conn, api_spec: spec} do user_data = %{ "name" => "John Doe", "email" => "john@example.com" } response = conn |> post(~p"/users", user: user_data) |> json_response(201) # Validate response structure assert_schema(response, "User", spec) # Additional assertions assert response["name"] == "John Doe" assert response["email"] == "john@example.com" end test "rejects invalid email", %{conn: conn} do user_data = %{ "name" => "John Doe", "email" => "not_an_email" } response = conn |> post(~p"/users", user: user_data) |> json_response(422) # Validate error response structure assert response["errors"] end end describe "GET /users" do test "returns list of users", %{conn: conn, api_spec: spec} do insert(:user, name: "Alice") insert(:user, name: "Bob") response = conn |> get(~p"/users") |> json_response(200) # Validate response is list assert is_list(response["data"]) assert Enum.count(response["data"]) == 2 # Validate each user matches schema Enum.each(response["data"], fn user -> assert_schema(user, "User", spec) end) end end describe "GET /users/:id" do test "returns user by id", %{conn: conn, api_spec: spec} do user = insert(:user) response = conn |> get(~p"/users/#{user.id}") |> json_response(200) assert_schema(response, "User", spec) assert response["id"] == user.id end test "returns 404 for nonexistent user", %{conn: conn, api_spec: spec} do response = conn |> get(~p"/users/0") |> json_response(404) assert_schema(response, "Error", spec) assert response["message"] =~ "not found" end end describe "PUT /users/:id" do test "updates user", %{conn: conn, api_spec: spec} do user = insert(:user) response = conn |> put(~p"/users/#{user.id}", user: %{name: "Jane Doe"}) |> json_response(200) assert_schema(response, "User", spec) assert response["name"] == "Jane Doe" end end describe "DELETE /users/:id" do test "deletes user", %{conn: conn, api_spec: spec} do user = insert(:user) conn |> delete(~p"/users/#{user.id}") |> response(204) # Verify deletion conn |> get(~p"/users/#{user.id}") |> response(404) end end end ``` -------------------------------- ### Responses Map Example Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/04-types-reference.md An example of a map defining responses for different HTTP status codes. Each key is a status code string, and the value is a Response struct or a Reference. ```elixir %{ "200" => %Response{description: "Success"}, "404" => %Response{description: "Not found"}, "default" => %Response{description: "Error"} } ``` -------------------------------- ### String Type Casting Examples Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/06-casting-validation.md Demonstrates casting various Elixir types (numbers, atoms) to strings, and passing through existing strings. ```elixir schema = %Schema{type: :string} # Numbers converted to strings {:ok, "123"} = Cast.cast(schema, 123) {:ok, "3.14"} = Cast.cast(schema, 3.14) # Atoms converted to strings {:ok, "atom"} = Cast.cast(schema, :atom) # Strings pass through {:ok, "hello"} = Cast.cast(schema, "hello") ``` -------------------------------- ### Example of Constructing a JSON Request Body Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/03-operations-and-controller-specs.md Shows how to use Operation.request_body/4 to define a request body for user data, specifying the description, content type as 'application/json', and referencing the User schema from MyAppWeb.Schemas. ```elixir Operation.request_body( "User data", "application/json", MyAppWeb.Schemas.User ) ``` -------------------------------- ### Cast and Validate with Options Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/08-configuration.md Example of using OpenApiSpex.cast_and_validate/5 with custom options like `replace_params` and `apply_defaults`. ```elixir OpenApiSpex.cast_and_validate(spec, operation, conn, nil, [ replace_params: true, apply_defaults: true, read_write_scope: nil ]) ``` -------------------------------- ### Define Request Body with Multiple Content Types and Example Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/08-configuration.md Defines a request body supporting multiple content types (JSON and XML) with associated schemas and an example for JSON. ```elixir request_body: {"Description", %{"application/json" => [...], "application/xml" => [...]}, SchemaModule, example: %{"name" => "John"}} ``` -------------------------------- ### OpenApiSpex Schema Module Functions Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/QUICK-REFERENCE.md Utilize functions from OpenApiSpex.Schema for type conversion, validation, and example generation. ```elixir # From OpenApiSpex.Schema Schema.cast(schema, value, schemas) # Type convert Schema.validate(schema, value, schemas) # Check rules Schema.example(schema) # Generate example Schema.properties(schema) # Get property names ``` -------------------------------- ### Number Type Casting Examples Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/06-casting-validation.md Illustrates casting strings and integers to numbers (floats), passing through floats, and handling invalid string inputs. ```elixir schema = %Schema{type: :number} # String numbers parsed {:ok, 3.14} = Cast.cast(schema, "3.14") # Integers converted {:ok, 123.0} = Cast.cast(schema, 123) # Numbers pass through {:ok, 2.5} = Cast.cast(schema, 2.5) # Invalid strings fail {:error, _} = Cast.cast(schema, "abc") ``` -------------------------------- ### Define Controller Operation with Summary and Parameters Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/08-configuration.md Use the `operation/2` macro within `controller_spec` to define an API operation. This example shows how to specify a summary, description, parameters, request body, and responses. ```elixir operation :create, summary: "Create user", description: "Create a new user", parameters: [...], request_body: {}, responses: [...] ``` -------------------------------- ### Configure No Caching for Test Environment Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/08-configuration.md Sets the cache adapter to OpenApiSpex.Plug.NoneCache for the test environment, typically mirroring the development setup. ```elixir # config/test.exs config :open_api_spex, :cache_adapter, OpenApiSpex.Plug.NoneCache ``` -------------------------------- ### Security Requirement with Multiple Schemes Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/04-types-reference.md Example demonstrating OR logic between multiple security schemes, such as OAuth2 and API Key. An operation can be accessed if either scheme is satisfied. ```elixir # Multiple schemes (OR logic) [ %{"oauth2" => ["read:users"]}, %{"api_key" => []} ] ``` -------------------------------- ### Operation Definition Example Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/03-operations-and-controller-specs.md Defines an OpenAPI operation for a controller action, including summary, description, parameters, request body, and responses. ```APIDOC ## POST /users/{id} ### Description Updates a user record. ### Method POST ### Endpoint /users/{id} ### Parameters #### Path Parameters - **id** (integer) - Required - User ID #### Request Body - **User data** (application/json) - Schema: UserParams ### Response #### Success Response (200) - **Updated user** (application/json) - Schema: UserResponse #### Error Response (404) - **User not found** (application/json) - Schema: ErrorResponse ``` -------------------------------- ### Integer Type Casting Examples Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/06-casting-validation.md Shows casting strings to integers, truncating floats, passing through integers, and handling invalid string inputs. ```elixir schema = %Schema{type: :integer} # String numbers parsed {:ok, 123} = Cast.cast(schema, "123") {:ok, -456} = Cast.cast(schema, "-456") # Floats truncated {:ok, 3} = Cast.cast(schema, 3.14) {:ok, 3} = Cast.cast(schema, 3.99) # Integers pass through {:ok, 42} = Cast.cast(schema, 42) # Invalid strings fail {:error, _} = Cast.cast(schema, "not_a_number") ``` -------------------------------- ### Object Type Casting Example Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/06-casting-validation.md Demonstrates recursive casting of map properties within an object schema, and how non-map inputs result in errors. ```elixir schema = %Schema{ type: :object, properties: %{ id: %Schema{type: :integer}, name: %Schema{type: :string} } } # Map properties are recursively cast {:ok, %{"id" => 123, "name" => "John"}} = Cast.cast(schema, %{"id" => "123", "name" => "John"}) # Non-maps fail {:error, _} = Cast.cast(schema, "not_a_map") ``` -------------------------------- ### Info Struct Definition Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/08-configuration.md Example of defining the Info object for an OpenAPI specification, including title, version, and contact details. ```elixir %Info{ title: "My API", version: "1.0.0", description: "API description", termsOfService: "https://example.com/terms", contact: %Contact{name: "Support", email: "support@example.com"}, license: %License{name: "MIT"} } ``` -------------------------------- ### Boolean Type Casting Examples Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/06-casting-validation.md Demonstrates casting various string representations of true and false to Elixir booleans, and passing through existing booleans. ```elixir schema = %Schema{type: :boolean} # String true values {:ok, true} = Cast.cast(schema, "true") {:ok, true} = Cast.cast(schema, "1") {:ok, true} = Cast.cast(schema, "yes") {:ok, true} = Cast.cast(schema, "on") # String false values {:ok, false} = Cast.cast(schema, "false") {:ok, false} = Cast.cast(schema, "0") {:ok, false} = Cast.cast(schema, "no") {:ok, false} = Cast.cast(schema, "off") # Booleans pass through {:ok, true} = Cast.cast(schema, true) {:ok, false} = Cast.cast(schema, false) ``` -------------------------------- ### Array Type Casting Example Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/06-casting-validation.md Shows how array items are recursively cast to the specified item type, and how non-array inputs result in errors. ```elixir schema = %Schema{ type: :array, items: %Schema{type: :integer} } # Array items are recursively cast {:ok, [1, 2, 3]} = Cast.cast(schema, ["1", "2", "3"]) # Non-arrays fail {:error, _} = Cast.cast(schema, "not_an_array") ``` -------------------------------- ### Elixir Assertion Failure Example Message Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/07-test-assertions.md Illustrates the detailed error message provided by `assert_schema/3` when a value does not conform to the specified schema. ```text Assertion failed: Value does not conform to schema User: Expected properties: [id, name, email] Missing required: [name] Type mismatch at /email: expected string, got integer ``` -------------------------------- ### Define a Schema Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/README.md Use the `OpenApiSpex.schema/2` macro to define a schema for your data structures. This example defines a User schema with an integer ID, a string name, and marks the name as required. ```elixir defmodule MyAppWeb.Schemas.User do require OpenApiSpex OpenApiSpex.schema(%{ type: :object, properties: %{ id: %Schema{type: :integer}, name: %Schema{type: :string} }, required: [:name] }) end ``` -------------------------------- ### API Key Security Scheme Example Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/04-types-reference.md Defines an API key security scheme, specifying the key name and its location (e.g., header). Use this for API key authentication. ```elixir %SecurityScheme{ type: "apiKey", name: "api_key", in: "header" } ``` -------------------------------- ### Elixir Test Example for assert_schema/4 Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/07-test-assertions.md Demonstrates using `assert_schema/4` to validate a 'create user' API response against the specific operation and a 201 status code. Requires `get_operation/3` to retrieve the operation definition. ```elixir test "create user returns 201", %{conn: conn, api_spec: spec} do operation = get_operation(spec, "/users", :post) response = conn |> post(~p"/users", user: @valid_attrs) |> json_response(201) assert_schema(response, spec, operation, 201) end ``` -------------------------------- ### Phoenix Controller with CastAndValidate Plug Source: https://github.com/open-api-spex/open_api_spex/blob/master/README.md Example of integrating the OpenApiSpex.Plug.CastAndValidate plug into a Phoenix controller. It demonstrates defining an operation with parameters, request body, and responses, including custom error responses. ```elixir defmodule MyAppWeb.UserController do use MyAppWeb, :controller use OpenApiSpex.ControllerSpecs alias MyAppWeb.Schemas.{UserParams, UserResponse} plug OpenApiSpex.Plug.CastAndValidate, json_render_error_v2: true operation :update, summary: "Update user", description: "Updates with the given params.\nThis is another line of text in the description.", parameters: [ id: [in: :path, type: :integer, description: "user ID"], vsn: [in: :query, type: :integer, description: "API version number"], "api-version": [in: :header, type: :integer, description: "API version number"] ], request_body: {"The user attributes", "application/json", UserParams}, responses: %{ 201 => {"User", "application/json", UserResponse}, 422 => OpenApiSpex.JsonErrorResponse.response() } def update( conn = %{ body_params: %UserParams{ name: name, email: email, birthday: %Date{} = birthday } }, %{id: id} ) do # conn.body_params cast to UserRequest struct # conn.params combines path params, query params and header params # conn.params.id cast to integer # conn.params.vsn cast to integer # conn.params[:"api-version"] cast to integer # params is the same as conn.params # params.id cast to integer # Note: Using pattern-matching in the action function's arguments can # cause Dialyzer to complain. This is because Dialyzer expects the # `conn` and `params` arguments to have string keys, not atom keys. # To resolve this, fetch the `:body_params` with # `body_params = Map.get(conn, :body_params)`. end end ``` -------------------------------- ### Elixir Test Example for assert_schema/3 with API Responses Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/07-test-assertions.md Shows how to use `assert_schema/3` to validate a JSON API response against the 'User' schema. This is useful for testing controller actions. ```elixir defmodule MyAppWeb.UserControllerTest do use MyAppWeb.ConnCase import OpenApiSpex.TestAssertions setup do {:ok, api_spec: MyAppWeb.ApiSpec.spec()} end test "GET /users/:id returns user", %{conn: conn, api_spec: spec} do user = insert(:user) response = conn |> get(~p"/users/#{user.id}") |> json_response(200) assert_schema(response, "User", spec) end test "GET /users returns list of users", %{conn: conn, api_spec: spec} do insert(:user) insert(:user) response = conn |> get(~p"/users") |> json_response(200) assert_schema(response, "UserList", spec) end end ``` -------------------------------- ### Validate API Response Against Schema Source: https://github.com/open-api-spex/open_api_spex/blob/master/README.md Test API responses against their defined schemas using OpenApiSpex.TestAssertions.assert_schema/3. This example asserts that a GET request to the user index produces a valid UsersResponse. ```elixir use MyAppWeb.ConnCase import OpenApiSpex.TestAssertions test "UserController produces a UsersResponse", %{conn: conn} do json = conn |> get(user_path(conn, :index)) |> json_response(200) api_spec = MyAppWeb.ApiSpec.spec() assert_schema(json, "UsersResponse", api_spec) end ``` -------------------------------- ### Schema as Struct and Definition Example Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/11-quick-start-architecture.md Illustrates the dual nature of OpenAPI Spex schemas, which can define OpenAPI types and also be used to generate Elixir structs. ```elixir # Both are true: 1. MyAppWeb.Schemas.User is a module with schema() function 2. MyAppWeb.Schemas.User can be used as a struct: %MyAppWeb.Schemas.User{id: 1, name: "John"} ``` -------------------------------- ### Testing API Responses with JSON Content Type Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/07-test-assertions.md This example demonstrates how to test an API endpoint that is expected to return a user object in JSON format. It uses `json_response` and `assert_schema` to validate the response. ```elixir test "returns user in JSON", %{conn: conn, api_spec: spec} do user = insert(:user) response = conn |> get(~p"/users/#{user.id}", accept: :json) |> json_response(200) assert_schema(response, "User", spec) end ``` -------------------------------- ### Build Project Documentation Source: https://github.com/open-api-spex/open_api_spex/blob/master/CONTRIBUTING.md Generate and review project documentation to ensure formatting and content are accurate. ```bash mix docs; open doc/index.html ``` -------------------------------- ### Testing API Responses with XML Content Type Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/07-test-assertions.md This example shows how to test an API endpoint that returns a user object in XML format. Since `OpenApiSpex` does not have built-in XML parsing, this test manually checks for the presence of an XML tag. ```elixir test "returns user in XML", %{conn: conn, api_spec: spec} do user = insert(:user) response = conn |> get(~p"/users/#{user.id}", accept: :xml) |> response(200) # For XML, you would parse and validate separately assert response =~ "" end ``` -------------------------------- ### Define MediaType Struct Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/10-utilities-and-helpers.md Defines the structure for a MediaType object, including schema, example, examples, and encoding. ```elixir @type t :: %__MODULE__{ schema: Schema.t() | Reference.t() | nil, example: any, examples: %{String.t() => Example.t() | Reference.t()} | nil, encoding: %{String.t() => Encoding.t()} | nil, extensions: %{String.t() => any()} | nil } ``` -------------------------------- ### Define Header Struct Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/10-utilities-and-helpers.md Defines the structure for a Header object, including description, schema, and examples. ```elixir @type t :: %__MODULE__{ description: String.t() | nil, required: boolean | nil, deprecated: boolean | nil, allowEmptyValue: boolean | nil, style: Parameter.style() | nil, explode: boolean | nil, allowReserved: boolean | nil, schema: Schema.t() | Reference.t() | nil, example: any, examples: %{String.t() => Example.t() | Reference.t()} | nil, content: %{String.t() => MediaType.t()} | nil, extensions: %{String.t() => any()} | nil } ``` -------------------------------- ### Serve Swagger UI and API Spec Source: https://github.com/open-api-spex/open_api_spex/blob/master/README.md Configure routes to serve the Swagger UI interface and the OpenAPI specification. Ensure the `path:` option is set for the SwaggerUI plug. ```elixir scope "/" do pipe_through :browser # Use the default browser stack get "/", MyAppWeb.PageController, :index get "/swaggerui", OpenApiSpex.Plug.SwaggerUI, path: "/api/openapi" end scope "/api" do pipe_through :api resources "/users", MyAppWeb.UserController, only: [:create, :index, :show] get "/openapi", OpenApiSpex.Plug.RenderSpec, [] end ``` -------------------------------- ### Serve OpenAPI Documentation and Swagger UI Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/README.md Configure routes to serve your OpenAPI specification and the Swagger UI. The `RenderSpec` plug serves the spec, while `SwaggerUI` provides an interactive interface, linking to the spec via the `path` option. ```elixir get "/openapi", OpenApiSpex.Plug.RenderSpec, [] get "/swaggerui", OpenApiSpex.Plug.SwaggerUI, path: "/openapi" ``` -------------------------------- ### Define a Server with URL Variables Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/04-types-reference.md Configures a Server struct with templated URL variables, specifying allowed values and a default for the 'environment' variable. ```elixir %Server{ url: "https://{environment}.example.com", variables: %{ "environment" => %ServerVariable{ default: "production", enum: ["production", "staging"] } } } ``` -------------------------------- ### View OpenAPI Spec Task Help Source: https://github.com/open-api-spex/open_api_spex/blob/master/README.md Displays help information for the openapi.spec.json Mix task. This command provides details on available options and usage. ```shell mix help openapi.spec.json ``` -------------------------------- ### Tag Struct Example Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/04-types-reference.md Defines a tag for grouping operations in an OpenAPI specification. Use this to categorize your API endpoints. ```elixir %Tag{ name: "users", description: "Operations for managing users" } ``` -------------------------------- ### Define Optional Query Parameters Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/11-quick-start-architecture.md Declare optional parameters for an operation, specifying their location, type, and example values. ```elixir operation :index, parameters: [ page: [in: :query, type: :integer, required: false, example: 1], limit: [in: :query, type: :integer, required: false, example: 10], sort: [in: :query, type: :string, required: false, example: "name"] ] ``` -------------------------------- ### Initialize PutApiSpec Plug Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/05-plugs-integration.md The init/1 function for OpenApiSpex.Plug.PutApiSpec validates that a spec module implementing OpenApiSpex.OpenApi behavior is provided. ```elixir @impl Plug def init(opts) ``` -------------------------------- ### Define an Enum Schema Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/02-schema-definitions.md Defines a string schema with a restricted set of allowed values (enum) and a default example. ```elixir OpenApiSpex.schema(%{ type: :string, enum: ["pending", "active", "inactive"], example: "active" }) ``` -------------------------------- ### Get Schema Property Names Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/10-utilities-and-helpers.md Retrieves a list of atom names for properties defined in a schema. Useful for introspection. ```elixir @spec properties(Schema.t()) :: [atom] def properties(%Schema{} = schema) ``` ```elixir schema = %Schema{ properties: %{id: %Schema{}, name: %Schema{}, email: %Schema{}} } Schema.properties(schema) # => [:id, :name, :email] ``` -------------------------------- ### Troubleshooting: Module Not Found Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/QUICK-REFERENCE.md Call `resolve_schema_modules/1` on your spec if you encounter a 'Module not found' error. ```text Module not found | Call `resolve_schema_modules/1` on spec ``` -------------------------------- ### Serving Swagger UI with OpenApiSpex.Plug.SwaggerUI Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/05-plugs-integration.md Integrates Swagger UI to provide an interactive interface for exploring and testing the API. ```APIDOC ## Module: OpenApiSpex.Plug.SwaggerUI Serves Swagger UI for interactive API exploration. ### Usage ```elixir scope "/" do pipe_through :browser get "/swaggerui", OpenApiSpex.Plug.SwaggerUI, path: "/api/openapi" end scope "/api" do pipe_through :api get "/openapi", OpenApiSpex.Plug.RenderSpec, [] end ``` Then visit `GET /swaggerui` in a browser to explore the API interactively. ### OAuth2 Redirect For OAuth2 flows, Swagger UI requires a redirect URI endpoint: ```elixir scope "/" do get "/swaggerui/oauth2-redirect", OpenApiSpex.Plug.SwaggerUIOAuth2Redirect, [] end ``` ``` -------------------------------- ### Security Requirement with OAuth2 Scopes Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/04-types-reference.md Example of an OAuth2 security requirement specifying required scopes. This indicates the permissions needed for an operation. ```elixir # OAuth with scopes %{"oauth2" => ["read:users", "write:users"]} ``` -------------------------------- ### View OpenAPI Spec Task Help (YAML) Source: https://github.com/open-api-spex/open_api_spex/blob/master/README.md Displays help information for the openapi.spec.yaml Mix task. This command provides details on available options and usage. ```shell mix help openapi.spec.yaml ``` -------------------------------- ### OpenApiSpex Request Pipeline Plugs Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/11-quick-start-architecture.md Illustrates the sequence of Plugs used in a request pipeline for handling API requests, including loading the spec, validating and casting the request, and rendering the response or serving documentation. ```text Request Pipeline │ ├─→ PutApiSpec (load spec) ├─→ CastAndValidate (validate request) └─→ Controller Action └─→ RenderSpec / SwaggerUI (response) ``` -------------------------------- ### Run Project Tests Source: https://github.com/open-api-spex/open_api_spex/blob/master/CONTRIBUTING.md Execute project tests to ensure all components are functioning correctly before making changes or releasing. ```bash mix clean; mix test ``` -------------------------------- ### Troubleshooting: Struct Not Created Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/QUICK-REFERENCE.md Ensure `"x-struct": __MODULE__` is included in the schema if a struct is not being created. ```text Struct not created | Ensure `"x-struct": __MODULE__` in schema ``` -------------------------------- ### Configure SwaggerUI Plug Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/08-configuration.md Serve the Swagger UI interface at a specified route using the SwaggerUI plug. The `path` option is required to point to the OpenAPI JSON spec. ```elixir get "/swaggerui", OpenApiSpex.Plug.SwaggerUI, path: "/api/openapi" ``` -------------------------------- ### Skip or Focus Tests with Tags Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/07-test-assertions.md Demonstrates how to use ExUnit tags to skip tests (e.g., when a schema is unavailable) or focus on critical tests during development. ```elixir # Skip test if schema not available @tag :skip test "optional test for future schema", %{api_spec: spec} do assert_schema(value, "FutureSchema", spec) end # Focus on specific test @tag :focus test "critical endpoint matches spec", %{api_spec: spec} do assert_schema(response, "Critical", spec) end ``` -------------------------------- ### Helper for Constructing Parameter Structs Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/03-operations-and-controller-specs.md Creates a Parameter struct with specified name, location, type, description, and additional options. Useful for defining path, query, header, or cookie parameters. ```elixir @spec parameter( name :: atom, location :: Parameter.location(), type :: Reference.t() | Schema.t() | Parameter.type() | atom(), description :: String.t(), opts :: keyword ) :: Parameter.t() def parameter(name, location, type, description, opts \ []) ``` -------------------------------- ### Create an OpenAPI Specification Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/04-types-reference.md Constructs a basic OpenAPI specification object. Requires an Info struct and a Paths struct derived from a router. ```elixir %OpenApi{ info: %Info{title: "My API", version: "1.0"}, paths: Paths.from_router(MyAppWeb.Router) } ``` -------------------------------- ### Define Custom Parser Module Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/08-configuration.md Example implementation of a custom parser module that defines a `parse/2` function to handle specific content types. ```elixir defmodule MyApp.CustomParser do def parse(value, schema) do # Parse value according to schema {:ok, parsed_value} end end ``` -------------------------------- ### Operation.from_plug/2 Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/03-operations-and-controller-specs.md Constructs an `Operation.t()` struct from a plug module and options. It calls the `open_api_operation/1` callback on the plug module. ```APIDOC ## Function: Operation.from_plug/2 ### Description Constructs an `Operation.t()` struct from a plug module and options. It calls the `open_api_operation/1` callback on the plug module. ### Method `Operation.from_plug/2` ### Parameters #### Path Parameters - **plug** (module) - Required - Controller or plug module - **opts** (any) - Required - Action name or plug opts ### Returns `Operation.t() | nil` ``` -------------------------------- ### Serve Swagger UI with SwaggerUI Plug Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/05-plugs-integration.md Integrate the SwaggerUI plug to provide an interactive interface for exploring your API. Configure the path to your OpenAPI JSON spec. ```elixir scope "/" do pipe_through :browser get "/swaggerui", OpenApiSpex.Plug.SwaggerUI, path: "/api/openapi" end scope "/api" do pipe_through :api get "/openapi", OpenApiSpex.Plug.RenderSpec, [] end ``` -------------------------------- ### Get Spec and Operation Lookup Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/05-plugs-integration.md The get_spec_and_operation_lookup/1 function is an internal helper for other plugs to retrieve the cached API spec and an operation lookup map. ```elixir @spec get_spec_and_operation_lookup(Plug.Conn.t()) :: {spec :: OpenApi.t(), operation_lookup :: %{any => Operation.t()}} def get_spec_and_operation_lookup(conn) ``` -------------------------------- ### Troubleshooting: Validation Not Running Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/QUICK-REFERENCE.md Ensure the `CastAndValidate` plug is correctly placed in your Plug pipeline if validation is not running. ```text Validation not running | Ensure CastAndValidate plug is in pipeline ``` -------------------------------- ### Get HTTP Status Code from Atom Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/10-utilities-and-helpers.md Retrieves the integer HTTP status code corresponding to an atom. Useful for setting connection status. ```elixir Plug.Conn.Status.code(:ok) # => 200 Plug.Conn.Status.code(:unprocessable_entity) # => 422 ``` -------------------------------- ### Use Application Spec for Info Source: https://github.com/open-api-spex/open_api_spex/blob/master/README.md Alternatively, use your application's spec values for the title and version in the Info struct. ```elixir info: title: to_string(Application.spec(:my_app, :description)), version: to_string(Application.spec(:my_app, :vsn)) ``` -------------------------------- ### Discriminator with Mapping Example Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/04-types-reference.md Defines a discriminator for polymorphic types, specifying the property name to use for selection and an explicit mapping from values to schema references. ```elixir %Discriminator{ propertyName: "petType", mapping: %{ "cat" => "#/components/schemas/Cat", "dog" => "#/components/schemas/Dog" } } ``` -------------------------------- ### MediaType Definition Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/04-types-reference.md Defines the structure for request or response content, including schema, example, and encoding properties. Used to specify the media type of data. ```elixir %MediaType{ schema: UserSchema, example: %{"id" => 1, "name" => "John"}, encoding: %{ "picture" => %Encoding{contentType: "image/png"} } } ``` -------------------------------- ### Construct Response Struct Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/03-operations-and-controller-specs.md Shorthand function for creating Response structs, specifying description, media type, and schema. ```elixir @spec response( description :: String.t(), media_type :: String.t(), schema :: Schema.t() | Reference.t() | module ) :: Response.t() def response(description, media_type, schema) ``` ```elixir Operation.response("User data", "application/json", UserSchema) # => %Response{ # description: "User data", # content: %{"application/json" => %MediaType{schema: UserSchema}} # } ``` -------------------------------- ### Troubleshooting: Swagger UI Broken Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/QUICK-REFERENCE.md Check that the path to the spec matches the route if the Swagger UI is broken. ```text Swagger UI broken | Check path to spec matches route ``` -------------------------------- ### Security Requirement with API Key Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/04-types-reference.md Example of an API key security requirement with no specific scopes. This is used when API key authentication does not involve granular permissions. ```elixir # API Key (no scopes) %{"api_key" => []} ``` -------------------------------- ### HTTP Bearer Security Scheme Example Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/04-types-reference.md Defines an HTTP security scheme using the 'bearer' type with JWT format. This is commonly used for token-based authentication. ```elixir %SecurityScheme{ type: "http", scheme: "bearer", bearerFormat: "JWT" } ``` -------------------------------- ### Define a Static Server URL Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/04-types-reference.md Creates a Server struct representing a static base URL for an API. ```elixir %Server{url: "https://api.example.com"} ``` -------------------------------- ### Define Operation with Error Response Schema Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/09-error-handling.md Example of defining an operation in OpenAPI Spex to include a pre-configured error response schema for unprocessable entity errors. ```elixir operation :create, request_body: {"User", "application/json", User}, responses: [ ok: {"Created", "application/json", User}, unprocessable_entity: OpenApiSpex.JsonErrorResponse.response() ] ``` -------------------------------- ### Define Security Schemes in OpenAPI Specification Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/08-configuration.md Example of defining various security schemes (Bearer, API Key, OAuth2) within the components section of an OpenAPI specification. ```elixir components: %Components{ securitySchemes: %{ "bearer_auth" => %SecurityScheme{ type: "http", scheme: "bearer", bearerFormat: "JWT", description: "JWT token" }, "api_key" => %SecurityScheme{ type: "apiKey", name: "api_key", in: "header", description: "API key header" }, "oauth2" => %SecurityScheme{ type: "oauth2", flows: %{ authorizationCode: %{ authorizationUrl: "https://example.com/oauth/authorize", tokenUrl: "https://example.com/oauth/token", refreshUrl: "https://example.com/oauth/refresh", scopes: %{ "read:users" => "Read user data", "write:users" => "Create and update users" } } } } } } ``` -------------------------------- ### Publish Package to Hex.pm Source: https://github.com/open-api-spex/open_api_spex/blob/master/CONTRIBUTING.md Publish the project's latest version to the Hex.pm package manager. ```bash mix hex.publish ``` -------------------------------- ### Troubleshooting: Type Casting Fails Source: https://github.com/open-api-spex/open_api_spex/blob/master/_autodocs/QUICK-REFERENCE.md Verify that the schema format matches the expected type (e.g., using `:date` for date strings) if type casting fails. ```text Type casting fails | Check schema format matches type (e.g., `:date` for date strings) ```