Skip to content

Commit

Permalink
Initial Swagger integration (#512)
Browse files Browse the repository at this point in the history
* add open api to the mix

* Adds base OpenApi spec

* Wire api doc and openapi spec route to the router

* Adds host list operation to openapi spec

* Rename default api tag to Landscape

* Make dialyzer happy

* Polish Host OpenApi schema
  • Loading branch information
nelsonkopliku authored May 11, 2022
1 parent 894e09f commit 44fc08e
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .formatter.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[
import_deps: [:ecto, :phoenix, :commanded],
import_deps: [:ecto, :phoenix, :commanded, :open_api_spex],
locals_without_parens: [
# mock
assert_called: :*,
Expand Down
16 changes: 15 additions & 1 deletion lib/trento_web/controllers/host_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,29 @@ defmodule TrentoWeb.HostController do

alias Trento.Support.StructHelper

@spec list(Plug.Conn.t(), map) :: Plug.Conn.t()
use OpenApiSpex.ControllerSpecs

tags ["Landscape"]

operation :list,
summary: "List hosts",
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}
]

@spec list(Plug.Conn.t(), map) :: Plug.Conn.t()
def list(conn, _) do
# TODO: replace to_map with DTO approach
hosts = Hosts.get_all_hosts() |> StructHelper.to_map()

json(conn, hosts)
end

operation :heartbeat, false

def heartbeat(conn, %{"id" => id}) do
case Heartbeats.heartbeat(id) do
{:ok, _} ->
Expand Down
34 changes: 34 additions & 0 deletions lib/trento_web/openapi/api_spec.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
defmodule TrentoWeb.OpenApi.ApiSpec do
@moduledoc """
OpenApi specification entry point
"""

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

@impl OpenApi
def spec do
%OpenApi{
servers: [
# Populate the Server info from a phoenix endpoint
Server.from_endpoint(Endpoint)
],
info: %Info{
title: "Trento",
description: to_string(Application.spec(:trento, :description)),
version: to_string(Application.spec(:trento, :vsn))
},
# Populate the paths from a phoenix router
paths: Paths.from_router(Router),
tags: [
%Tag{
name: "Landscape",
description: "Providing access to the discovered target infrastructure"
}
]
}
# Discover request/response schemas from path specs
|> OpenApiSpex.resolve_schema_modules()
end
end
95 changes: 95 additions & 0 deletions lib/trento_web/openapi/schema/host.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
defmodule TrentoWeb.OpenApi.Schema.Host do
@moduledoc false

require OpenApiSpex
alias OpenApiSpex.Schema

defmodule IPv4 do
@moduledoc false

OpenApiSpex.schema(%{
title: "IPv4",
type: :string,
format: :ipv4
})
end

defmodule IPv6 do
@moduledoc false

OpenApiSpex.schema(%{
title: "IPv6",
type: :string,
format: :ipv6
})
end

defmodule HostItem do
@moduledoc false

OpenApiSpex.schema(%{
title: "Host",
description: "A discovered host on the target infrastructure",
type: :object,
properties: %{
id: %Schema{type: :integer, description: "Host ID"},
hostname: %Schema{type: :string, description: "Host name"},
ip_addresses: %Schema{
type: :array,
description: "IP addresses",
items: %Schema{
title: "IP address",
oneOf: [
IPv4,
IPv6
]
}
},
ssh_address: TrentoWeb.OpenApi.Schema.Host.IPv4,
agent_version: %Schema{
type: :string,
description: "Version of the agent installed on the host"
},
cluster_id: %Schema{
type: :string,
description: "Identifier of the cluster this host is part of",
format: :uuid
},
heartbeat: %Schema{
type: :string,
description: "Host's last heartbeat status",
enum: [:critical, :passing, :unknown]
},
provider: %Schema{
type: :string,
description: "Detected Provider on which the host is running",
enum: [:azure, :aws, :gcp, :unknown]
},
provider_data: TrentoWeb.OpenApi.Schema.Provider.ProviderData,
tags: %Schema{
title: "Tags",
description: "A list of tags attached to a resource",
type: :array,
items: TrentoWeb.OpenApi.Schema.Tag
},
sles_subscriptions: %Schema{
title: "SlesSubscriptions",
description: "A list of the available SLES Subscriptions on a host",
type: :array,
items: TrentoWeb.OpenApi.Schema.SlesSubscription
}
}
})
end

defmodule HostsCollection do
@moduledoc false

OpenApiSpex.schema(%{
title: "HostsCollection",
description: "A list of the discovered hosts",
type: :array,
items: HostItem
})
end
end
37 changes: 37 additions & 0 deletions lib/trento_web/openapi/schema/provider.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
defmodule TrentoWeb.OpenApi.Schema.Provider do
@moduledoc false

require OpenApiSpex
alias OpenApiSpex.Schema

defmodule ProviderData do
@moduledoc false

OpenApiSpex.schema(%{
title: "ProviderMetadata",
description: "Detected metadata for any provider",
oneOf: [
TrentoWeb.OpenApi.Schema.Provider.AzureProviderData
]
})
end

defmodule AzureProviderData do
@moduledoc false

OpenApiSpex.schema(%{
title: "AzureProviderData",
description: "Azure detected metadata",
type: :object,
properties: %{
resource_group: %Schema{type: :string},
location: %Schema{type: :string},
vm_size: %Schema{type: :string},
data_disk_number: %Schema{type: :integer},
offer: %Schema{type: :string},
sku: %Schema{type: :string},
admin_username: %Schema{type: :string}
}
})
end
end
23 changes: 23 additions & 0 deletions lib/trento_web/openapi/schema/sles_subscription.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
defmodule TrentoWeb.OpenApi.Schema.SlesSubscription do
@moduledoc false

require OpenApiSpex
alias OpenApiSpex.Schema

OpenApiSpex.schema(%{
title: "SlesSubscription",
description: "A discovered SLES Subscription on a host",
type: :object,
properties: %{
host_id: %Schema{type: :string, format: :uuid},
identifier: %Schema{type: :string},
version: %Schema{type: :string},
arch: %Schema{type: :string},
status: %Schema{type: :string},
subscription_status: %Schema{type: :string},
type: %Schema{type: :string},
starts_at: %Schema{type: :string},
expires_at: %Schema{type: :string}
}
})
end
18 changes: 18 additions & 0 deletions lib/trento_web/openapi/schema/tag.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule TrentoWeb.OpenApi.Schema.Tag do
@moduledoc false

require OpenApiSpex
alias OpenApiSpex.Schema

OpenApiSpex.schema(%{
title: "Tag",
description: "A tag attached to a resource",
type: :object,
properties: %{
id: %Schema{type: :integer},
resource_id: %Schema{type: :string, format: :uuid},
resource_type: %Schema{type: :string, enum: [:host, :cluster, :sap_system, :database]},
value: %Schema{type: :string}
}
})
end
8 changes: 8 additions & 0 deletions lib/trento_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ defmodule TrentoWeb.Router do

pipeline :api do
plug :accepts, ["json"]
plug OpenApiSpex.Plug.PutApiSpec, module: TrentoWeb.OpenApi.ApiSpec
end

pipeline :protected do
Expand All @@ -37,6 +38,8 @@ defmodule TrentoWeb.Router do
scope "/" do
pipe_through :browser

get "/api/doc", OpenApiSpex.Plug.SwaggerUI, path: "/api/openapi"

pow_session_routes()
end

Expand Down Expand Up @@ -116,6 +119,11 @@ defmodule TrentoWeb.Router do
get "/prometheus/targets", PrometheusController, :targets
end

scope "/api" do
pipe_through :api
get "/openapi", OpenApiSpex.Plug.RenderSpec, []
end

# Other scopes may use custom stacks.
# scope "/api", TrentoWeb do
# pipe_through :api
Expand Down
2 changes: 2 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule Trento.MixProject do
def project do
[
app: :trento,
description: "Easing your life in administering SAP applications",
version: "1.0.0",
elixir: "~> 1.13",
elixirc_paths: elixirc_paths(Mix.env()),
Expand Down Expand Up @@ -63,6 +64,7 @@ defmodule Trento.MixProject do
{:jason, "~> 1.2"},
{:mock, "~> 0.3.0", only: :test},
{:mox, "~> 1.0", only: :test},
{:open_api_spex, "~> 3.11"},
{:phoenix, "~> 1.6.2"},
{:phoenix_ecto, "~> 4.4"},
{:phoenix_html, "~> 3.0"},
Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"},
"mox": {:hex, :mox, "1.0.1", "b651bf0113265cda0ba3a827fcb691f848b683c373b77e7d7439910a8d754d6e", [:mix], [], "hexpm", "35bc0dea5499d18db4ef7fe4360067a59b06c74376eb6ab3bd67e6295b133469"},
"open_api_spex": {:hex, :open_api_spex, "3.11.0", "e4ea9c00e2891c3195c2df511c49e280e7210272ba0077d8d9975258c70e43c0", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1 or ~> 4.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "640bd09f6bdd96cc0264213bfa229b0f8b3868df3384311de57e0f3fa22fc3af"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"phoenix": {:hex, :phoenix, "1.6.8", "9a34e5f4dd3ba959176c199fd5b2277b02e64005462428b71cf6ce9cb5e09cb4", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9f4d616aeb9c5e019bddfc1f9078b8c06f852ffa838e67f925559cc0993e9f71"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
Expand Down

0 comments on commit 44fc08e

Please sign in to comment.