Skip to content

Commit

Permalink
Enable openapi schema versioning (#1488)
Browse files Browse the repository at this point in the history
* Convert the open api plug into a versinable macro

* Move V1 schemas to its own folder

* Update controllers to used versioned openapi

* Update router to pipethrough specific open api version

* Disable open api cache on dev

* Update controller tests to use new openapi versions

* Temporarily set openapi V1 version in CI docs generation

* Test paths generation in open api spec
  • Loading branch information
arbulu89 authored and rtorrero committed Jun 16, 2023
1 parent aaf49df commit bb11663
Show file tree
Hide file tree
Showing 43 changed files with 216 additions and 105 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ jobs:
- name: Build docs
uses: lee-dohm/generate-elixir-docs@v1
- name: Generate openapi.json
run: mix openapi.spec.json --start-app=false --spec TrentoWeb.OpenApi.ApiSpec
run: mix openapi.spec.json --start-app=false --spec TrentoWeb.OpenApi.V1.ApiSpec
- name: Generate Swagger UI
uses: Legion2/swagger-ui-action@v1
with:
Expand Down
2 changes: 2 additions & 0 deletions config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ config :trento, :checks_service, base_url: "http://localhost:4001"

config :unplug, :init_mode, :runtime

config :open_api_spex, :cache_adapter, OpenApiSpex.Plug.NoneCache

# Override with local dev.local.exs file
if File.exists?("#{__DIR__}/dev.local.exs") do
import_config "dev.local.exs"
Expand Down
2 changes: 1 addition & 1 deletion lib/trento_web/controllers/health_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule TrentoWeb.HealthController do

alias Ecto.Adapters.SQL

alias TrentoWeb.OpenApi.Schema.{
alias TrentoWeb.OpenApi.V1.Schema.{
Health,
Ready
}
Expand Down
2 changes: 1 addition & 1 deletion lib/trento_web/controllers/v1/about_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule TrentoWeb.V1.AboutController do

alias Trento.Hosts

alias TrentoWeb.OpenApi.Schema
alias TrentoWeb.OpenApi.V1.Schema

@version Mix.Project.config()[:version]

Expand Down
2 changes: 1 addition & 1 deletion lib/trento_web/controllers/v1/cluster_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule TrentoWeb.V1.ClusterController do

alias Trento.Clusters

alias TrentoWeb.OpenApi.Schema
alias TrentoWeb.OpenApi.V1.Schema

plug OpenApiSpex.Plug.CastAndValidate, json_render_error_v2: true
action_fallback TrentoWeb.FallbackController
Expand Down
2 changes: 1 addition & 1 deletion lib/trento_web/controllers/v1/discovery_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule TrentoWeb.V1.DiscoveryController do

alias Trento.Integration.Discovery

alias TrentoWeb.OpenApi.Schema
alias TrentoWeb.OpenApi.V1.Schema

plug OpenApiSpex.Plug.CastAndValidate, json_render_error_v2: true
action_fallback TrentoWeb.FallbackController
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule TrentoWeb.V1.HealthOverviewController do

alias Trento.SapSystems.HealthSummaryService

alias TrentoWeb.OpenApi.Schema
alias TrentoWeb.OpenApi.V1.Schema

operation(:overview,
summary: "Health overview of the discovered SAP Systems",
Expand Down
9 changes: 5 additions & 4 deletions lib/trento_web/controllers/v1/host_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ defmodule TrentoWeb.V1.HostController do
Hosts
}

alias TrentoWeb.OpenApi.V1.Schema

plug OpenApiSpex.Plug.CastAndValidate, json_render_error_v2: true
action_fallback TrentoWeb.FallbackController

Expand All @@ -17,8 +19,7 @@ defmodule TrentoWeb.V1.HostController do
description: "List all the discovered hosts on the target infrastructure",
responses: [
ok:
{"A collection of the discovered hosts", "application/json",
TrentoWeb.OpenApi.Schema.Host.HostsCollection}
{"A collection of the discovered hosts", "application/json", Schema.Host.HostsCollection}
]

@spec list(Plug.Conn.t(), map) :: Plug.Conn.t()
Expand All @@ -41,8 +42,8 @@ defmodule TrentoWeb.V1.HostController do
],
responses: [
no_content: "The heartbeat has been updated",
not_found: TrentoWeb.OpenApi.Schema.NotFound.response(),
bad_request: TrentoWeb.OpenApi.Schema.BadRequest.response(),
not_found: Schema.NotFound.response(),
bad_request: Schema.BadRequest.response(),
unprocessable_entity: OpenApiSpex.JsonErrorResponse.response()
]

Expand Down
2 changes: 1 addition & 1 deletion lib/trento_web/controllers/v1/prometheus_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule TrentoWeb.V1.PrometheusController do

alias Trento.Integration.Prometheus

alias TrentoWeb.OpenApi.Schema
alias TrentoWeb.OpenApi.V1.Schema

require Logger

Expand Down
6 changes: 4 additions & 2 deletions lib/trento_web/controllers/v1/sap_system_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule TrentoWeb.V1.SapSystemController do

alias Trento.SapSystems

alias TrentoWeb.OpenApi.V1.Schema

use OpenApiSpex.ControllerSpecs

tags ["Target Infrastructure"]
Expand All @@ -13,7 +15,7 @@ defmodule TrentoWeb.V1.SapSystemController do
responses: [
ok:
{"A collection of the discovered SAP Systems", "application/json",
TrentoWeb.OpenApi.Schema.SAPSystem.SAPSystemsCollection}
Schema.SAPSystem.SAPSystemsCollection}
]

def list(conn, _) do
Expand All @@ -28,7 +30,7 @@ defmodule TrentoWeb.V1.SapSystemController do
responses: [
ok:
{"A collection of the discovered HANA Databases", "application/json",
TrentoWeb.OpenApi.Schema.Database.DatabasesCollection}
Schema.Database.DatabasesCollection}
]

def list_databases(conn, _) do
Expand Down
2 changes: 1 addition & 1 deletion lib/trento_web/controllers/v1/settings_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule TrentoWeb.V1.SettingsController do

alias Trento.Installation

alias TrentoWeb.OpenApi.Schema
alias TrentoWeb.OpenApi.V1.Schema

use OpenApiSpex.ControllerSpecs

Expand Down
4 changes: 2 additions & 2 deletions lib/trento_web/controllers/v1/tags_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule TrentoWeb.V1.TagsController do

alias Trento.Tags

alias TrentoWeb.OpenApi.Schema
alias TrentoWeb.OpenApi.V1.Schema

action_fallback TrentoWeb.FallbackController
plug OpenApiSpex.Plug.CastAndValidate, json_render_error_v2: true
Expand Down Expand Up @@ -69,7 +69,7 @@ defmodule TrentoWeb.V1.TagsController do
],
responses: [
no_content: "The tag has been removed from the resource",
bad_request: TrentoWeb.OpenApi.Schema.BadRequest.response(),
bad_request: Schema.BadRequest.response(),
unprocessable_entity: OpenApiSpex.JsonErrorResponse.response(),
not_found: OpenApiSpex.JsonErrorResponse.response()
]
Expand Down
135 changes: 82 additions & 53 deletions lib/trento_web/openapi/api_spec.ex
Original file line number Diff line number Diff line change
@@ -1,64 +1,93 @@
defmodule TrentoWeb.OpenApi.ApiSpec do
@moduledoc """
OpenApi specification entry point
`api_version` must be provided to specify the version of this openapi specification
Example:
use TrentoWeb.OpenApi.ApiSpec,
api_version: "v1"
"""

alias OpenApiSpex.{
Components,
Info,
OpenApi,
Paths,
SecurityScheme,
Server,
Tag
}
defmacro __using__(opts) do
api_version =
Keyword.get(opts, :api_version) || raise ArgumentError, "expected :api_version option"

alias TrentoWeb.{Endpoint, Router}
@behaviour OpenApi
quote do
alias OpenApiSpex.{
Components,
Info,
OpenApi,
Paths,
SecurityScheme,
Server,
Tag
}

@impl OpenApi
def spec do
OpenApiSpex.resolve_schema_modules(%OpenApi{
servers: [
endpoint()
],
info: %Info{
title: "Trento",
description: to_string(Application.spec(:trento, :description)),
version: to_string(Application.spec(:trento, :vsn))
},
components: %Components{
securitySchemes: %{"authorization" => %SecurityScheme{type: "http", scheme: "bearer"}}
},
security: [%{"authorization" => []}],
# Populate the paths from a phoenix router
paths: Paths.from_router(Router),
tags: [
%Tag{
name: "Target Infrastructure",
description: "Providing access to the discovered target infrastructure"
},
%Tag{
name: "Checks",
description: "Providing Checks related feature"
},
%Tag{
name: "Platform",
description: "Providing access to Trento Platform features"
}
]
})
end
alias TrentoWeb.{Endpoint, Router}
@behaviour OpenApi

@impl OpenApi
def spec(router \\ Router) do
OpenApiSpex.resolve_schema_modules(%OpenApi{
servers: [
endpoint()
],
info: %Info{
title: "Trento",
description: to_string(Application.spec(:trento, :description)),
version: to_string(Application.spec(:trento, :vsn))
},
components: %Components{
securitySchemes: %{"authorization" => %SecurityScheme{type: "http", scheme: "bearer"}}
},
security: [%{"authorization" => []}],
paths: build_paths_for_version(unquote(api_version), router),
tags: [
%Tag{
name: "Target Infrastructure",
description: "Providing access to the discovered target infrastructure"
},
%Tag{
name: "Checks",
description: "Providing Checks related feature"
},
%Tag{
name: "Platform",
description: "Providing access to Trento Platform features"
}
]
})
end

defp endpoint do
if Process.whereis(Endpoint) do
# Populate the Server info from a phoenix endpoint
Server.from_endpoint(Endpoint)
else
# If the endpoint is not running, use a placeholder
# this happens when generarting openapi.json with --start-app=false
# e.g. mix openapi.spec.json --start-app=false --spec WandaWeb.ApiSpec
%OpenApiSpex.Server{url: "https://demo.trento-project.io"}
end
end

defp build_paths_for_version(version, router) do
excluded_versions = List.delete(router.available_api_versions(), version)

router
|> Paths.from_router()
|> Enum.reject(fn {path, _info} ->
current_version =
path
|> String.trim("/")
|> String.split("/")
|> Enum.at(1)

defp endpoint do
if Process.whereis(Endpoint) do
# Populate the Server info from a phoenix endpoint
Server.from_endpoint(Endpoint)
else
# If the endpoint is not running, use a placeholder
# this happens when generarting openapi.json with --start-app=false
# e.g. mix openapi.spec.json --start-app=false --spec WandaWeb.ApiSpec
%OpenApiSpex.Server{url: "https://demo.trento-project.io"}
Enum.member?(excluded_versions, current_version)
end)
|> Map.new()
end
end
end
end
8 changes: 8 additions & 0 deletions lib/trento_web/openapi/v1/api_spec.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
defmodule TrentoWeb.OpenApi.V1.ApiSpec do
@moduledoc """
OpenApi specification entry point for V1 version
"""

use TrentoWeb.OpenApi.ApiSpec,
api_version: "v1"
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule TrentoWeb.OpenApi.Schema.BadRequest do
defmodule TrentoWeb.OpenApi.V1.Schema.BadRequest do
@moduledoc """
Bad Request
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule TrentoWeb.OpenApi.Schema.Checks.ChecksSelectionRequest do
defmodule TrentoWeb.OpenApi.V1.Schema.Checks.ChecksSelectionRequest do
@moduledoc false
require OpenApiSpex
alias OpenApiSpex.Schema
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
defmodule TrentoWeb.OpenApi.Schema.ChecksCatalog do
defmodule TrentoWeb.OpenApi.V1.Schema.ChecksCatalog do
@moduledoc false

require OpenApiSpex

alias OpenApiSpex.Schema
alias TrentoWeb.OpenApi.Schema.Provider
alias TrentoWeb.OpenApi.V1.Schema.Provider

defmodule Check do
@moduledoc false
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
defmodule TrentoWeb.OpenApi.Schema.Cluster do
defmodule TrentoWeb.OpenApi.V1.Schema.Cluster do
@moduledoc false

require OpenApiSpex
require Trento.Domain.Enums.ClusterType, as: ClusterType

alias OpenApiSpex.Schema

alias TrentoWeb.OpenApi.Schema.{Provider, ResourceHealth, Tags}
alias TrentoWeb.OpenApi.V1.Schema.{Provider, ResourceHealth, Tags}

defmodule ClusterResource do
@moduledoc false
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
defmodule TrentoWeb.OpenApi.Schema.Database do
defmodule TrentoWeb.OpenApi.V1.Schema.Database do
@moduledoc false

require OpenApiSpex
alias OpenApiSpex.Schema

alias TrentoWeb.OpenApi.Schema.{ResourceHealth, Tags}
alias TrentoWeb.OpenApi.V1.Schema.{ResourceHealth, Tags}

defmodule DatabaseInstance do
@moduledoc false
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule TrentoWeb.OpenApi.Schema.DiscoveryEvent do
defmodule TrentoWeb.OpenApi.V1.Schema.DiscoveryEvent do
@moduledoc false

alias OpenApiSpex.Schema
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule TrentoWeb.OpenApi.Schema.Health do
defmodule TrentoWeb.OpenApi.V1.Schema.Health do
@moduledoc """
Healthcheck
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
defmodule TrentoWeb.OpenApi.Schema.Host do
defmodule TrentoWeb.OpenApi.V1.Schema.Host do
@moduledoc false

require OpenApiSpex
alias OpenApiSpex.Schema

alias TrentoWeb.OpenApi.Schema.{Provider, SlesSubscription, Tags}
alias TrentoWeb.OpenApi.V1.Schema.{Provider, SlesSubscription, Tags}

defmodule IPv4 do
@moduledoc false
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule TrentoWeb.OpenApi.Schema.HttpStd do
defmodule TrentoWeb.OpenApi.V1.Schema.HttpStd do
@moduledoc false

require OpenApiSpex
Expand Down
Loading

0 comments on commit bb11663

Please sign in to comment.