# Functional overview

## OpenAPI specification overview

This library generates OpenAPI 2.0 documents. The authoritative specification for this document’s structure will always be the official documentation over at swagger.io and the OpenAPI 2.0 specification page.

Because the above specifications are a bit heavy and convoluted, here is a general overview of how the specification is structured, starting from the root `Swagger` object.

- `Swagger` object

    - `info`, `schemes`, `securityDefinitions` and other informative attributes

    - `paths`: `Paths` object

        A list of all the paths in the API in the form of a mapping

        - `{path}`: `PathItem` - each `PathItem` has multiple operations keyed by method
            - `{http_method}`: `Operation`

                Each operation is thus uniquely identified by its `(path, http_method)` combination, e.g. `GET /articles/`, `POST /articles/`, etc.

            - `parameters`: [`Parameter`] - and a list of path parameters

    - `definitions`: named Models

        A list of all the named models in the API in the form of a mapping

        - `{ModelName}`: `Schema`

- `Operation` contains the following information about each operation:
    - `parameters`: [`Parameter`]

        A list of all the query, header and form parameters accepted by the operation.

        - there can also be at most one body parameter whose structure is represented by a `Schema` or a reference to one (`SchemaRef`)

    - `responses`: `Responses`

        A list of all the possible responses the operation is expected to return. Each response can optionally have a `Schema` which describes the structure of its body.

        - `{status_code}`: `Response` - mapping of status code to response definition

    - `operationId` - should be unique across all operations

    - `tags` - used to group operations in the listing

It is interesting to note the main differences between `Parameter` and `Schema` objects:

`Schema` | `Parameter`
--- | ---
Can nest other Schemas | Cannot nest other Parameters Can only nest a Schema if the parameter is `in: body`
Cannot describe file uploads - `file` is not permitted as a value for `type` | Can describe file uploads via `type` = `file`, but only as part of a form `Operation` [1]
Can be used in `Response`s | Cannot be used in `Response`s
Cannot be used in form `Operation`s [1] | Can be used in form `Operation`s [1]
Can only describe request or response bodies | Can describe `query`, `form`, `header` or `path` parameters

[1] (1,2,3)

a form Operation is an `Operation` that consumes `multipart/form-data` or `application/x-www-form-urlencoded` content

> - a form Operation cannot have `body` parameters

> - a non-form operation cannot have `form` parameters

## Default behavior

This section describes where information is sourced from when using the default generation process.

- `Paths` are generated by exploring the patterns registered in your default `urlconf`, or the `patterns` and `urlconf` you specified when constructing `OpenAPISchemaGenerator`; only views inheriting from Django Rest Framework’s `APIView` are looked at, all other views are ignored

- `path` `Parameter`s are generated by looking in the URL pattern for any template parameters; attempts are made to guess their type from the views `queryset` and `lookup_field`, if applicable. You can override path parameters via `manual_parameters` in @swagger_auto_schema.

- `query` `Parameter`s - i.e. parameters specified in the URL as `/path/?query1=value&query2=value` - are generated from your view’s `filter_backends` and `paginator`, if any are declared. Additional parameters can be specified via the `query_serializer` and `manual_parameters` arguments of @swagger_auto_schema

- The request body is only generated for the HTTP `POST`, `PUT` and `PATCH` methods, and is sourced from the view’s `serializer_class`. You can also override the request body using the `request_body` argument of @swagger_auto_schema.

  > - if the view represents a form request (that is, all its parsers are of the `multipart/form-data` or `application/x-www-form-urlencoded` media types), the request body will be output as `form` `Parameter`s

  > - if it is not a form request, the request body will be output as a single `body` `Parameter` wrapped around a `Schema`

- `header` `Parameter`s are supported by the OpenAPI specification but are never generated by this library; you can still add them using `manual_parameters`.

- `Responses` are generated as follows:

  > - if `responses` is provided to @swagger_auto_schema and contains at least one success status code (i.e. any 2xx status code), no automatic response is generated and the given response is used as described in the `@swagger_auto_schema documentation`

  > - otherwise, an attempt is made to generate a default response:

  >   > - the success status code is assumed to be `204` for `DELETE` requests, `201` for `POST` requests, and `200` for all other request methods

  >   > - if the view has a request body, the same `Serializer` or `Schema` as in the request body is used in generating the `Response` schema; this is inline with the default `GenericAPIView` and `GenericViewSet` behavior

  >   > - if the view has no request body, its `serializer_class` is used to generate the `Response` schema

  >   > - if the view is a list view (as defined by `is_list_view()`), the response schema is wrapped in an array

  >   > - if the view is also paginated, the response schema is then wrapped in the appropriate paging response structure

  >   > - the description of the response is left blank

- `Response` headers are supported by the OpenAPI specification but not currently supported by this library; you can still add them manually by providing an appropriately structured dictionary to the `headers` property of a `Response` object

- descriptions for `Operation`s, `Parameter`s and `Schema`s are picked up from docstrings and `help_text` attributes in the same manner as the default DRF SchemaGenerator

- The base URL for the API consists of three values - the `host`, `schemes` and `basePath` attributes

- The host name and scheme are determined, in descending order of priority:

  > - from the `url` argument passed to `get_schema_view()` (more specifically, to the underlying `OpenAPISchemaGenerator`)

  > - from the DEFAULT_API_URL setting

  > - inferred from the request made to the schema endpoint

  > For example, an url of `https://www.example.com:8080/some/path` will populate the `host` and `schemes` attributes with `www.example.com:8080` and `['https']`, respectively. The path component will be ignored.

- The base path is determined as the concatenation of two variables:

  > 1. the SCRIPT_NAME wsgi environment variable; this is set, for example, when serving the site from a sub-path using web server url rewriting

  >    Tip

  >    The Django FORCE_SCRIPT_NAME setting can be used to override the SCRIPT_NAME or set it when it’s missing from the environment.

  > 2. the longest common path prefix of all the urls in your API - see `determine_path_prefix()`

- When using API versioning with `NamespaceVersioning` or `URLPathVersioning`, versioned endpoints that do not match the version used to access the `SchemaView` will be excluded from the endpoint list - for example, `/api/v1.0/endpoint` will be shown when viewing `/api/v1.0/swagger/`, while `/api/v2.0/endpoint` will not

  Other versioning schemes are not presently supported.

## A note on limitations

When schema generation is requested, available endpoints are inspected by enumeration all the routes registered in Django’s urlconf. Each registered view is then artificially instantiated for introspection, and it is this step that brings some limitations to what can be done:

- the `request` the view sees will always be the request made against the schema view endpoint - e.g. `GET /swagger.yaml`

- path parameters will not be filled

This means that you could get surprizing results if your `get_serializer` or `get_serializer_class` methods depend on the incoming request, call `get_object` or in general depend on any stateful logic. You can prevent this in a few ways:

- provide a fixed serializer for request and response body introspection using @swagger_auto_schema, to prevent `get_serializer` from being called on the view

- exclude your endpoint from introspection

- use the `swagger_fake_view` marker to detect requests generated by `drf-yasg`:

  >     def get_serializer_class(self):
  >        if getattr(self, 'swagger_fake_view', False):
  >           return TodoTreeSerializer

  >        raise NotImplementedError("must not call this")