Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F112363
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
18 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/.gitignore b/.gitignore
index 12179ea..c2d0238 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,20 +1,24 @@
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where 3rd-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
+
+# IDE / plugin files
+.elixir_ls/
+.vscode/
diff --git a/examples/phoenix_app/README.md b/examples/phoenix_app/README.md
index b351da7..7e182ce 100644
--- a/examples/phoenix_app/README.md
+++ b/examples/phoenix_app/README.md
@@ -1,7 +1,19 @@
# PhoenixApp
This simple Phoenix API Application demonstrates the usage of `open_api_spex`.
- [api_spec.ex](lib/phoenix_app_web/api_spec.ex) contains the outline of the api spec
- [schemas.ex](lib/phoenix_app_web/schemas.ex) conains the request/response schema modules
- - [router.ex][lib/phoenix_app_web/router.ex]
\ No newline at end of file
+ - [router.ex](lib/phoenix_app_web/router.ex)
+
+
+To run this application:
+
+ ```
+ mix deps.get
+ mix ecto.create
+ mix ecto.migrate
+ mix phx.server
+ ```
+
+ Navigate to [http://localhost:4000/swaggerui](http://localhost:4000/swaggerui)
\ No newline at end of file
diff --git a/.gitignore b/examples/plug_app/.gitignore
similarity index 100%
copy from .gitignore
copy to examples/plug_app/.gitignore
diff --git a/examples/plug_app/README.md b/examples/plug_app/README.md
new file mode 100644
index 0000000..964a41d
--- /dev/null
+++ b/examples/plug_app/README.md
@@ -0,0 +1,18 @@
+# PlugApp
+
+This simple Plug API Application demonstrates the usage of `open_api_spex`.
+
+ - [api_spec.ex](lib/plug_app/api_spec.ex) contains the outline of the api spec
+ - [schemas.ex](lib/plug_app/schemas.ex) conains the request/response schema modules
+ - [router.ex](lib/plug_app/router.ex) contains the plug router
+
+ To run this application:
+
+ ```
+ mix deps.get
+ mix ecto.create
+ mix ecto.migrate
+ mix run --no-halt
+ ```
+
+ Navigate to [http://localhost:4000/swaggerui](http://localhost:4000/swaggerui)
\ No newline at end of file
diff --git a/examples/plug_app/config/config.exs b/examples/plug_app/config/config.exs
new file mode 100644
index 0000000..a1eb420
--- /dev/null
+++ b/examples/plug_app/config/config.exs
@@ -0,0 +1,12 @@
+# This file is responsible for configuring your application
+# and its dependencies with the aid of the Mix.Config module.
+use Mix.Config
+
+config :plug_app, ecto_repos: [PlugApp.Repo]
+
+config :plug_app, PlugApp.Repo,
+ adapter: Sqlite.Ecto2,
+ database: "priv/repo/plug_app.db"
+
+config :logger, level: :debug
+
diff --git a/examples/plug_app/lib/plug_app/accounts/accounts.ex b/examples/plug_app/lib/plug_app/accounts/accounts.ex
new file mode 100644
index 0000000..7aedeba
--- /dev/null
+++ b/examples/plug_app/lib/plug_app/accounts/accounts.ex
@@ -0,0 +1,17 @@
+defmodule PlugApp.Accounts do
+ alias PlugApp.Accounts.User
+ alias PlugApp.Repo
+
+ def list_users() do
+ Repo.all(User)
+ end
+
+ def get_user!(id) do
+ Repo.get(User, id)
+ end
+
+ def create_user(%{name: name, email: email}) do
+ user = Repo.insert!(%User{name: name, email: email})
+ {:ok, user}
+ end
+end
diff --git a/examples/plug_app/lib/plug_app/accounts/user.ex b/examples/plug_app/lib/plug_app/accounts/user.ex
new file mode 100644
index 0000000..ca4becf
--- /dev/null
+++ b/examples/plug_app/lib/plug_app/accounts/user.ex
@@ -0,0 +1,9 @@
+defmodule PlugApp.Accounts.User do
+ use Ecto.Schema
+
+ schema "users" do
+ field :name, :string
+ field :email, :string
+ timestamps()
+ end
+end
diff --git a/examples/plug_app/lib/plug_app/api_spec.ex b/examples/plug_app/lib/plug_app/api_spec.ex
new file mode 100644
index 0000000..e0ca006
--- /dev/null
+++ b/examples/plug_app/lib/plug_app/api_spec.ex
@@ -0,0 +1,22 @@
+defmodule PlugApp.ApiSpec do
+ alias OpenApiSpex.{Info, OpenApi, Paths}
+
+ def spec do
+ %OpenApi{
+ info: %Info{
+ title: "Plug App",
+ version: "1.0"
+ },
+ paths: %{
+ "/api/users/{id}" => OpenApiSpex.PathItem.from_routes([
+ %{verb: :get, plug: PlugApp.UserHandler.Show, opts: []}
+ ]),
+ "/api/users" => OpenApiSpex.PathItem.from_routes([
+ %{verb: :get, plug: PlugApp.UserHandler.Index, opts: []},
+ %{verb: :post, plug: PlugApp.UserHandler.Create, opts: []}
+ ])
+ }
+ }
+ |> OpenApiSpex.resolve_schema_modules()
+ end
+end
\ No newline at end of file
diff --git a/examples/plug_app/lib/plug_app/application.ex b/examples/plug_app/lib/plug_app/application.ex
new file mode 100644
index 0000000..33a7bdd
--- /dev/null
+++ b/examples/plug_app/lib/plug_app/application.ex
@@ -0,0 +1,20 @@
+defmodule PlugApp.Application do
+ # See https://hexdocs.pm/elixir/Application.html
+ # for more information on OTP Applications
+ @moduledoc false
+
+ use Application
+
+ def start(_type, _args) do
+ # List all child processes to be supervised
+ children = [
+ PlugApp.Repo,
+ Plug.Adapters.Cowboy.child_spec(:http, PlugApp.Router, [], [port: 4000])
+ ]
+
+ # See https://hexdocs.pm/elixir/Supervisor.html
+ # for other strategies and supported options
+ opts = [strategy: :one_for_one, name: PlugApp.Supervisor]
+ Supervisor.start_link(children, opts)
+ end
+end
diff --git a/examples/plug_app/lib/plug_app/repo.ex b/examples/plug_app/lib/plug_app/repo.ex
new file mode 100644
index 0000000..2a7eac6
--- /dev/null
+++ b/examples/plug_app/lib/plug_app/repo.ex
@@ -0,0 +1,3 @@
+defmodule PlugApp.Repo do
+ use Ecto.Repo, otp_app: :plug_app, adapter: Sqlite.Ecto2
+end
\ No newline at end of file
diff --git a/examples/plug_app/lib/plug_app/router.ex b/examples/plug_app/lib/plug_app/router.ex
new file mode 100644
index 0000000..d765f71
--- /dev/null
+++ b/examples/plug_app/lib/plug_app/router.ex
@@ -0,0 +1,35 @@
+defmodule PlugApp.Router.Html do
+ use Plug.Router
+
+ plug :match
+ plug :dispatch
+
+ get "/swaggerui", to: OpenApiSpex.Plug.SwaggerUI, init_opts: [path: "/api/openapi"]
+end
+
+defmodule PlugApp.Router.Api do
+ use Plug.Router
+
+ plug OpenApiSpex.Plug.PutApiSpec, module: PlugApp.ApiSpec
+ plug :match
+ plug :dispatch
+
+ get "/api/users", to: PlugApp.UserHandler.Index
+ post "/api/users", to: PlugApp.UserHandler.Create
+ get "/api/users/:id", to: PlugApp.UserHandler.Show
+ get "/api/openapi", to: OpenApiSpex.Plug.RenderSpec
+end
+
+defmodule PlugApp.Router do
+ use Plug.Router
+
+ plug Plug.RequestId
+ plug Plug.Logger
+ plug Plug.Parsers, parsers: [:json], pass: ["*/*"], json_decoder: Poison
+ plug :match
+ plug :dispatch
+
+ match "/api/*_", to: PlugApp.Router.Api
+ match "/*_", to: PlugApp.Router.Html
+end
+
diff --git a/examples/plug_app/lib/plug_app/schemas.ex b/examples/plug_app/lib/plug_app/schemas.ex
new file mode 100644
index 0000000..e803d07
--- /dev/null
+++ b/examples/plug_app/lib/plug_app/schemas.ex
@@ -0,0 +1,91 @@
+defmodule PlugApp.Schemas do
+ require OpenApiSpex
+ alias OpenApiSpex.Schema
+
+ defmodule User do
+ OpenApiSpex.schema %{
+ title: "User",
+ description: "A user of the app",
+ type: :object,
+ properties: %{
+ id: %Schema{type: :integer, description: "User ID"},
+ name: %Schema{type: :string, description: "User name", pattern: ~r/[a-zA-Z][a-zA-Z0-9_]+/},
+ email: %Schema{type: :string, description: "Email address", format: :email},
+ inserted_at: %Schema{type: :string, description: "Creation timestamp", format: :'date-time'},
+ updated_at: %Schema{type: :string, description: "Update timestamp", format: :'date-time'}
+ },
+ required: [:name, :email],
+ example: %{
+ "id" => 123,
+ "name" => "Joe User",
+ "email" => "joe@gmail.com",
+ "inserted_at" => "2017-09-12T12:34:55Z",
+ "updated_at" => "2017-09-13T10:11:12Z"
+ }
+ }
+ end
+
+ defmodule UserRequest do
+ OpenApiSpex.schema %{
+ title: "UserRequest",
+ description: "POST body for creating a user",
+ type: :object,
+ properties: %{
+ user: User
+ },
+ required: [:user],
+ example: %{
+ "user" => %{
+ "name" => "Joe User",
+ "email" => "joe@gmail.com"
+ }
+ }
+ }
+ end
+
+ defmodule UserResponse do
+ OpenApiSpex.schema %{
+ title: "UserResponse",
+ description: "Response schema for single user",
+ type: :object,
+ properties: %{
+ data: User
+ },
+ example: %{
+ "data" => %{
+ "id" => 123,
+ "name" => "Joe User",
+ "email" => "joe@gmail.com",
+ "inserted_at" => "2017-09-12T12:34:55Z",
+ "updated_at" => "2017-09-13T10:11:12Z"
+ }
+ },
+ "x-struct": __MODULE__
+ }
+ end
+
+ defmodule UsersResponse do
+ OpenApiSpex.schema %{
+ title: "UsersResponse",
+ description: "Response schema for multiple users",
+ type: :object,
+ properties: %{
+ data: %Schema{description: "The users details", type: :array, items: User}
+ },
+ example: %{
+ "data" => [
+ %{
+ "id" => 123,
+ "name" => "Joe User",
+ "email" => "joe@gmail.com"
+ },
+ %{
+ "id" => 456,
+ "name" => "Jay Consumer",
+ "email" => "jay@yahoo.com"
+ }
+ ]
+ }
+ }
+ end
+end
\ No newline at end of file
diff --git a/examples/plug_app/lib/plug_app/user_handler.ex b/examples/plug_app/lib/plug_app/user_handler.ex
new file mode 100644
index 0000000..6a2761f
--- /dev/null
+++ b/examples/plug_app/lib/plug_app/user_handler.ex
@@ -0,0 +1,117 @@
+defmodule PlugApp.UserHandler do
+ alias OpenApiSpex.Operation
+ alias PlugApp.{Accounts, Accounts.User}
+ alias PlugApp.Schemas
+ import OpenApiSpex.Operation, only: [parameter: 5, request_body: 4, response: 3]
+
+ defmodule Index do
+ use Plug.Builder
+
+ plug OpenApiSpex.Plug.Cast, operation_id: "UserHandler.Index"
+ plug OpenApiSpex.Plug.Validate
+ plug :index
+
+ def open_api_operation(_) do
+ %Operation{
+ tags: ["users"],
+ summary: "List users",
+ description: "List all useres",
+ operationId: "UserHandler.Index",
+ responses: %{
+ 200 => response("User List Response", "application/json", Schemas.UsersResponse)
+ }
+ }
+ end
+
+ def index(conn, _opts) do
+ users = Accounts.list_users()
+ conn
+ |> Plug.Conn.put_resp_header("Content-Type", "application/json")
+ |> Plug.Conn.send_resp(200, render(users))
+ end
+
+ def render(users) do
+ %{
+ data: Enum.map(users, &Map.take(&1, [:id, :name, :email]))
+ }
+ |> Poison.encode!(pretty: true)
+ end
+ end
+
+ defmodule Show do
+ use Plug.Builder
+
+ plug OpenApiSpex.Plug.Cast, operation_id: "UserHandler.Show"
+ plug OpenApiSpex.Plug.Validate
+ plug :load
+ plug :show
+
+ def open_api_operation(_) do
+ %Operation{
+ tags: ["users"],
+ summary: "Show user",
+ description: "Show a user by ID",
+ operationId: "UserHandler.Show",
+ parameters: [
+ parameter(:id, :path, :integer, "User ID", example: 123, minimum: 1)
+ ],
+ responses: %{
+ 200 => response("User", "application/json", Schemas.UserResponse)
+ }
+ }
+ end
+
+ def load(conn = %Plug.Conn{params: %{id: id}}, _opts) do
+ case Accounts.get_user!(id) do
+ nil ->
+ conn
+ |> put_resp_header("Content-Type", "application/json")
+ |> send_resp(404, ~s({"error": "User not found"}))
+ |> halt()
+
+ user -> assign(conn, :user, user)
+ end
+ end
+
+ def show(conn = %Plug.Conn{assigns: %{user: user}}, _opts) do
+ conn
+ |> put_resp_header("Content-Type", "application/json")
+ |> send_resp(200, render(user))
+ end
+
+ def render(user) do
+ %{
+ data: Map.take(user, [:id, :name, :email])
+ }
+ |> Poison.encode!(pretty: true)
+ end
+ end
+
+ defmodule Create do
+ use Plug.Builder
+
+ plug OpenApiSpex.Plug.Cast, operation_id: "UserHandler.Create"
+ plug OpenApiSpex.Plug.Validate
+ plug :create
+
+ def open_api_operation(_) do
+ %Operation{
+ tags: ["users"],
+ summary: "Create user",
+ description: "Create a user",
+ operationId: "UserHandler.Create",
+ requestBody: request_body("The user attributes", "application/json", Schemas.UserRequest, required: true),
+ responses: %{
+ 201 => response("User", "application/json", Schemas.UserResponse)
+ }
+ }
+ end
+
+ def create(conn = %Plug.Conn{params: %Schemas.UserRequest{user: user_params}}, _opts) do
+ with {:ok, %User{} = user} <- Accounts.create_user(user_params) do
+ conn
+ |> Plug.Conn.send_resp(201, Show.render(user))
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/examples/plug_app/mix.exs b/examples/plug_app/mix.exs
new file mode 100644
index 0000000..8ceaab1
--- /dev/null
+++ b/examples/plug_app/mix.exs
@@ -0,0 +1,34 @@
+defmodule PlugApp.Mixfile do
+ use Mix.Project
+
+ def project do
+ [
+ app: :plug_app,
+ version: "0.1.0",
+ elixir: "~> 1.5",
+ start_permanent: Mix.env == :prod,
+ deps: deps()
+ ]
+ end
+
+ # Run "mix help compile.app" to learn about applications.
+ def application do
+ [
+ extra_applications: [:logger],
+ mod: {PlugApp.Application, []}
+ ]
+ end
+
+ # Run "mix help deps" to learn about dependencies.
+ defp deps do
+ [
+ {:open_api_spex, path: "../../"},
+ {:cowboy, "~> 1.0"},
+ {:plug, "~> 1.0"},
+ {:ecto, "~> 2.2"},
+ {:sqlite_ecto2, "~> 2.2"}
+ # {:dep_from_hexpm, "~> 0.3.0"},
+ # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
+ ]
+ end
+end
diff --git a/examples/plug_app/mix.lock b/examples/plug_app/mix.lock
new file mode 100644
index 0000000..b5d3270
--- /dev/null
+++ b/examples/plug_app/mix.lock
@@ -0,0 +1,15 @@
+%{"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [], [], "hexpm"},
+ "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
+ "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], [], "hexpm"},
+ "db_connection": {:hex, :db_connection, "1.1.2", "2865c2a4bae0714e2213a0ce60a1b12d76a6efba0c51fbda59c9ab8d1accc7a8", [], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
+ "decimal": {:hex, :decimal, "1.4.1", "ad9e501edf7322f122f7fc151cce7c2a0c9ada96f2b0155b8a09a795c2029770", [], [], "hexpm"},
+ "ecto": {:hex, :ecto, "2.2.6", "3fd1067661d6d64851a0d4db9acd9e884c00d2d1aa41cc09da687226cf894661", [], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
+ "esqlite": {:hex, :esqlite, "0.2.3", "1a8b60877fdd3d50a8a84b342db04032c0231cc27ecff4ddd0d934485d4c0cd5", [], [], "hexpm"},
+ "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [], [], "hexpm"},
+ "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
+ "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [], [], "hexpm"},
+ "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [], [], "hexpm"},
+ "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [], [], "hexpm"},
+ "sbroker": {:hex, :sbroker, "1.0.0", "28ff1b5e58887c5098539f236307b36fe1d3edaa2acff9d6a3d17c2dcafebbd0", [], [], "hexpm"},
+ "sqlite_ecto2": {:hex, :sqlite_ecto2, "2.2.2", "7a3e5c0521e1cb6e30a4907ba4d952b97db9b2ab5d1a4806ceeb66a10b23ba65", [], [{:connection, "~> 1.0.3", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 2.2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.2.3", [hex: :esqlite, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: false]}, {:sqlitex, "~> 1.3.2 or ~> 1.4", [hex: :sqlitex, repo: "hexpm", optional: false]}], "hexpm"},
+ "sqlitex": {:hex, :sqlitex, "1.3.3", "3aac5fd702be346f71d9de6e01702c9954484cd0971aa443490bb3bde045d919", [], [{:decimal, "~> 1.1", [hex: :decimal, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.2.3", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm"}}
diff --git a/examples/plug_app/priv/repo/migrations/20171015110840_add_users.exs b/examples/plug_app/priv/repo/migrations/20171015110840_add_users.exs
new file mode 100644
index 0000000..8af3749
--- /dev/null
+++ b/examples/plug_app/priv/repo/migrations/20171015110840_add_users.exs
@@ -0,0 +1,11 @@
+defmodule PlugApp.Repo.Migrations.AddUsers do
+ use Ecto.Migration
+
+ def change do
+ create table(:users) do
+ add :name, :string, null: false
+ add :email, :string, null: false
+ timestamps()
+ end
+ end
+end
diff --git a/examples/plug_app/test/test_helper.exs b/examples/plug_app/test/test_helper.exs
new file mode 100644
index 0000000..869559e
--- /dev/null
+++ b/examples/plug_app/test/test_helper.exs
@@ -0,0 +1 @@
+ExUnit.start()
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 23, 10:57 AM (21 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38947
Default Alt Text
(18 KB)
Attached To
Mode
R22 open_api_spex
Attached
Detach File
Event Timeline
Log In to Comment