diff --git a/lib/gnat/jetstream/api/object.ex b/lib/gnat/jetstream/api/object.ex index 8f0435d..713cfc1 100644 --- a/lib/gnat/jetstream/api/object.ex +++ b/lib/gnat/jetstream/api/object.ex @@ -145,6 +145,38 @@ defmodule Gnat.Jetstream.API.Object do end end + @doc """ + Returns true if the provided stream is an Object bucket, false otherwise + ## Parameters + * `stream_name` - the stream name to test + """ + @spec is_object_bucket_stream?(stream_name :: binary()) :: boolean() + def is_object_bucket_stream?(stream_name) do + String.starts_with?(stream_name, "OBJ_") + end + + @doc """ + Returns a list of all Object buckets + """ + @spec list_buckets(conn :: Gnat.t()) :: {:error, term()} | {:ok, list(String.t())} + def list_buckets(conn) do + with {:ok, %{streams: streams}} <- Stream.list(conn) do + stream_names = + streams + |> Enum.flat_map(fn bucket -> + if is_object_bucket_stream?(bucket) do + [bucket |> String.trim_leading(@stream_prefix)] + else + [] + end + end) + {:ok, stream_names} + else + {:error, reason} -> + {:error, reason} + end + end + defp stream_name(bucket_name) do "#{@stream_prefix}#{bucket_name}" end diff --git a/test/jetstream/api/object_test.exs b/test/jetstream/api/object_test.exs index 416131f..b567c09 100644 --- a/test/jetstream/api/object_test.exs +++ b/test/jetstream/api/object_test.exs @@ -150,6 +150,8 @@ defmodule Gnat.Jetstream.API.ObjectTest do assert size_after_changelog < size_after_readme assert {:ok, [meta]} = Object.list(:gnat, bucket) assert meta.name == "WAT" + + assert :ok = Object.delete_bucket(:gnat, bucket) end test "return an error if the object store doesn't exist" do @@ -185,6 +187,28 @@ defmodule Gnat.Jetstream.API.ObjectTest do :ok = Object.delete_bucket(:gnat, bucket) end + describe "list_buckets/2" do + test "list buckets when none exists" do + assert {:ok, []} = Object.list_buckets(:gnat) + end + + test "list buckets properly" do + assert {:ok, %{config: _config}} = Object.create_bucket(:gnat, "TEST_BUCKET_1") + assert {:ok, %{config: _config}} = Object.create_bucket(:gnat, "TEST_BUCKET_2") + assert {:ok, ["TEST_BUCKET_1", "TEST_BUCKET_2"]} = Object.list_buckets(:gnat) + :ok = Object.delete_bucket(:gnat, "TEST_BUCKET_1") + :ok = Object.delete_bucket(:gnat, "TEST_BUCKET_2") + end + + test "ignore streams that are not buckets" do + assert {:ok, %{config: _config}} = Object.create_bucket(:gnat, "TEST_BUCKET_1") + stream = %Stream{name: "TEST_STREAM_1", subjects: ["TEST_STREAM_1.subject1", "TEST_STREAM_1.subject2"]} + assert {:ok, _response} = Stream.create(:gnat, stream) + assert {:ok, ["TEST_BUCKET_1"]} = Object.list_buckets(:gnat) + :ok = Object.delete_bucket(:gnat, "TEST_BUCKET_1") + end + end + # create a random 1MB binary file # re-use it on subsequent test runs if it already exists defp generate_big_file(tmp_dir) do