Skip to content

Commit 2444e20

Browse files
committed
add: basic vhost endpoints
1 parent 328a27e commit 2444e20

File tree

17 files changed

+463
-147
lines changed

17 files changed

+463
-147
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ erl_crash.dump
2020
*.ez
2121

2222
# Ignore package tarball (built via "mix hex.build").
23-
ex_rabbitmqadmin-*.tar
23+
ex_rabbitmq_admin-*.tar
2424

2525
# Temporary files, for example, from tests.
2626
/tmp/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Intility AS
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
# ExRabbitMQAdmin
22

3-
**TODO: Add description**
3+
Simple client library for the RabbitMQ [HTTP API](https://www.rabbitmq.com/management.html#http-api),
4+
built on [Tesla](https://github.com/elixir-tesla/tesla).
45

56
## Installation
67

78
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
8-
by adding `ex_rabbitmqadmin` to your list of dependencies in `mix.exs`:
9+
by adding `ex_rabbitmq_admin` to your list of dependencies in `mix.exs`:
910

1011
```elixir
1112
def deps do
1213
[
13-
{:ex_rabbitmqadmin, "~> 0.1.0"}
14+
{:ex_rabbitmq_admin, "~> 0.1.0"}
1415
]
1516
end
1617
```
1718

1819
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
1920
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
20-
be found at <https://hexdocs.pm/ex_rabbitmqadmin>.
21+
be found at <https://hexdocs.pm/ex_rabbitmq_admin>.
2122

23+
## Contribution
24+
25+
The RabbitMQ HTTP API is available [here](https://rawcdn.githack.com/rabbitmq/rabbitmq-server/v3.11.2/deps/rabbitmq_management/priv/www/api/index.html).
26+
27+
### Running the test suite
28+
29+
```shell
30+
$ MIX_ENV=test mix coveralls
31+
```

config/config.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ case config_env() do
66

77
config :tesla, adapter: {Tesla.Adapter.Hackney, [recv_timeout: 30_000]}
88

9-
config :ex_rabbitmqadmin, ExRabbitMQAdmin.Client,
9+
config :ex_rabbitmq_admin, ExRabbitMQAdmin.Client,
1010
base_url: System.get_env("RABBITMQ_HTTP_BASE_URL", "http://localhost:5672"),
1111
username: "guest",
1212
password: "guest"

lib/ex_rabbitmq_admin.ex

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
defmodule ExRabbitMQAdmin do
2+
@moduledoc """
3+
Default module for the RabbitMQ admin client.
4+
5+
### Configuration
6+
7+
Some options, such as e.g. `base_url` are read from the config.
8+
9+
# config.ex
10+
config :ex_rabbitmq_admin, ExRabbitMQAdmin,
11+
base_url: "https://rabbitmq.example.com:15672",
12+
username: "guest",
13+
password: "guest"
14+
15+
16+
### Examples
17+
18+
* Create a client, add basic auth by reading default values from config and
19+
list all virtual hosts running in the RabbitMQ cluster.
20+
21+
iex> ExRabbitMQAdmin.client()
22+
|> ExRabbitMQAdmin.add_basic_auth_middleware()
23+
|> ExRabbitMQAdmin.VHost.list_vhosts()
24+
{:ok, %Tesla.Env{status: 200, body: [...]}}
25+
"""
26+
27+
use ExRabbitMQAdmin.Client,
28+
otp_app: :ex_rabbitmq_admin
29+
end

lib/ex_rabbitmq_admin/client.ex

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
defmodule ExRabbitMQAdmin.Client do
2+
@moduledoc """
3+
Macro for creating RabbitMQ HTTP API client.
4+
5+
### Configuration
6+
7+
Some options, such as e.g. `base_url` are read from the config. For each module that
8+
uses this macro, add an entry to your `config.exs` as:
9+
10+
# config.ex
11+
config :my_app, MyApp.RabbitClient,
12+
base_url: "https://rabbitmq.example.com:15672",
13+
username: "guest",
14+
password: "guest"
15+
16+
#### Parameters
17+
18+
* `otp_app` - Namespace used to look up configuration values such as `base_url` and others.
19+
20+
21+
### Example
22+
23+
defmodule MyApp.RabbitClient do
24+
use ExRabbitMQAdmin.Client, otp_app: :my_app
25+
end
26+
27+
"""
28+
defmacro __using__(opts) do
29+
{:ok, otp_app} = Keyword.fetch(opts, :otp_app)
30+
31+
quote location: :keep do
32+
@spec client(opts :: Keyword.t()) :: Tesla.Client.t()
33+
def client, do: Application.get_env(unquote(otp_app), __MODULE__) |> client()
34+
35+
def client(opts) do
36+
middleware = [
37+
{Tesla.Middleware.BaseUrl, client_option(opts, :base_url)},
38+
{Tesla.Middleware.Logger, filter_headers: ["authorization"]},
39+
{Tesla.Middleware.JSON, engine: Jason}
40+
]
41+
42+
Tesla.client(middleware)
43+
end
44+
45+
@spec bearer_auth_middleware(client :: Tesla.Client.t(), opts :: Keyword.t()) :: [
46+
{Tesla.Middleware.BearerAuth, Keyword.t()}
47+
]
48+
def bearer_auth_middleware(client, opts) when is_list(opts) do
49+
[
50+
{Tesla.Middleware.BearerAuth, token: client_option(opts, :token)}
51+
| Tesla.Client.middleware(client)
52+
]
53+
end
54+
55+
def add_bearer_auth_middleware(client, opts),
56+
do: bearer_auth_middleware(client, opts) |> Tesla.client()
57+
58+
@spec basic_auth_middleware(client :: Tesla.Client.t(), opts :: Keyword.t()) ::
59+
[{Tesla.Middleware.BasicAuth, Keyword.t()}]
60+
def basic_auth_middleware(client, opts) when is_list(opts) do
61+
[
62+
{Tesla.Middleware.BasicAuth,
63+
username: client_option(opts, :username), password: client_option(opts, :password)}
64+
| Tesla.Client.middleware(client)
65+
]
66+
end
67+
68+
def basic_auth_middleware(client) do
69+
[
70+
{Tesla.Middleware.BasicAuth,
71+
username: client_option(:username), password: client_option(:password)}
72+
| Tesla.Client.middleware(client)
73+
]
74+
end
75+
76+
@spec add_basic_auth_middleware(client :: Tesla.Client.t(), opts :: Keyword.t()) ::
77+
Tesla.Client.t()
78+
def add_basic_auth_middleware(client, opts) when is_list(opts),
79+
do: basic_auth_middleware(client, opts) |> Tesla.client()
80+
81+
def add_basic_auth_middleware(client), do: basic_auth_middleware(client) |> Tesla.client()
82+
83+
@spec client_option(opts :: Keyword.t(), atom()) :: any()
84+
defp client_option(key) when is_atom(key),
85+
do: Application.get_env(unquote(otp_app), __MODULE__) |> client_option(key)
86+
87+
defp client_option(opts, :base_url), do: Keyword.get(opts, :base_url, "")
88+
defp client_option(opts, :username), do: Keyword.get(opts, :username, "")
89+
defp client_option(opts, :password), do: Keyword.get(opts, :password, "")
90+
defp client_option(opts, :token), do: Keyword.get(opts, :token, "")
91+
end
92+
end
93+
end

lib/ex_rabbitmq_admin/vhost.ex

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
defmodule ExRabbitMQAdmin.VHost do
2+
@moduledoc """
3+
This module contains functions for interacting with RabbitMQ VHosts.
4+
"""
5+
6+
@doc """
7+
List all virtual hosts running on the RabbitMQ cluster.
8+
"""
9+
@spec list_vhosts(client :: Tesla.Client.t()) :: {:ok, Tesla.Env.t()}
10+
def list_vhosts(client), do: client |> Tesla.get("/api/vhosts")
11+
12+
@doc """
13+
List all open open connections in a specific virtual host.
14+
Optionally pass pagination parameters to filter connections.
15+
16+
### Params
17+
18+
* `name` - type: `binary`, required: `true`
19+
* `params` - type: `map`, reqiured: `false`
20+
21+
* `page` - type: `non_neg_integer`, required: `false`
22+
* `page_size` - type: `non_neg_integer`, required: `false`
23+
* `use_regex` - type: `boolean`, required: `false`
24+
25+
```
26+
%{
27+
"page" => 1,
28+
"page_size" => 100,
29+
"use_regex" => true
30+
}
31+
32+
```
33+
"""
34+
@spec list_vhost_connections(client :: Tesla.Client.t(), name :: String.t(), params :: map()) ::
35+
{:ok, Tesla.Env.t()}
36+
def list_vhost_connections(client, name, params \\ %{}) do
37+
client |> Tesla.get("/api/vhosts/#{name}/connections", params)
38+
end
39+
40+
@doc """
41+
List all open channels for a specific virtual host.
42+
Optionally pass pagination parameters to filter channels.
43+
44+
### Params
45+
46+
* `name` - type: `binary`, required: `true`
47+
* `params` - type: `map`, reqiured: `false`
48+
49+
* `page` - type: `non_neg_integer`, required: `false`
50+
* `page_size` - type: `non_neg_integer`, required: `false`
51+
* `use_regex` - type: `boolean`, required: `false`
52+
53+
```
54+
%{
55+
"page" => 1,
56+
"page_size" => 100,
57+
"use_regex" => true
58+
}
59+
60+
```
61+
"""
62+
@spec list_vhost_channels(client :: Tesla.Client.t(), name :: String.t(), params :: map()) ::
63+
{:ok, Tesla.Env.t()}
64+
def list_vhost_channels(client, name, params \\ %{}) do
65+
client |> Tesla.get("/api/vhosts/#{name}/channels", params)
66+
end
67+
68+
@doc """
69+
List all permissions for a specific virtual host.
70+
"""
71+
@spec list_vhost_permissions(client :: Tesla.Client.t(), name :: String.t()) ::
72+
{:ok, Tesla.Env.t()}
73+
def list_vhost_permissions(client, name) do
74+
client |> Tesla.get("/api/vhosts/#{name}/permissions")
75+
end
76+
77+
@doc """
78+
List all topic permissions for a specific virtual host.
79+
"""
80+
@spec list_vhost_topic_permissions(client :: Tesla.Client.t(), name :: String.t()) ::
81+
{:ok, Tesla.Env.t()}
82+
def list_vhost_topic_permissions(client, name) do
83+
client |> Tesla.get("/api/vhosts/#{name}/topic-permissions")
84+
end
85+
86+
@doc """
87+
Get an individual virtual host by name.
88+
"""
89+
@spec get_vhost(client :: Telsa.Client.t(), name :: String.t()) :: {:ok, Tesla.Env.t()}
90+
def get_vhost(client, name) when is_binary(name), do: client |> Tesla.get("/api/vhosts/#{name}")
91+
92+
@doc """
93+
Create a new virtual host with given name.
94+
95+
### Params
96+
97+
* `params` - type: `map`
98+
99+
* `name` - type: `binary`, required: `true`
100+
* `description` - type: `binary`, required: `false`
101+
* `tags` - type: `binary`, required: `false`. Comma-separated list of tags to be set on the virtual host
102+
103+
```
104+
%{
105+
"name" => "accounting",
106+
"description" => "this vhost is for accounting messages",
107+
"tags" => "finance,production"
108+
}
109+
```
110+
"""
111+
@spec put_vhost(client :: Telsa.Client.t(), params :: map) :: {:ok, Tesla.Env.t()}
112+
def put_vhost(client, %{"name" => name} = params),
113+
do: client |> Tesla.put("/api/vhosts/#{name}", params)
114+
115+
@doc """
116+
Delete a specific virtual host by name.
117+
"""
118+
@spec delete_vhost(client :: Tesla.Client.t(), name :: String.t()) :: {:ok, Tesla.Env.t()}
119+
def delete_vhost(client, name), do: client |> Tesla.delete("/api/vhosts/#{name}")
120+
121+
@doc """
122+
Start a specific virtual host on given node.
123+
"""
124+
@spec start_vhost(client :: Tesla.Client.t(), name :: String.t(), node :: String.t()) ::
125+
{:ok, Tesla.Env.t()}
126+
def start_vhost(client, name, node) do
127+
client |> Tesla.post("/api/vhosts/#{name}/start/#{node}")
128+
end
129+
end

lib/ex_rabbitmqadmin.ex

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)