From ae082994736dec44cab51997987577ddc1b00021 Mon Sep 17 00:00:00 2001 From: Maxime MENAGER Date: Thu, 12 Jan 2023 10:32:13 +0100 Subject: [PATCH 1/2] feat: add time machine --- lib/chargebeex/builder.ex | 5 +- lib/chargebeex/time_machine/time_machine.ex | 48 ++++ test/chargebeex/builder/time_machine_test.exs | 34 +++ test/chargebeex/time_machine_test.exs | 211 ++++++++++++++++++ test/support/fixtures/time_machine.ex | 21 ++ 5 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 lib/chargebeex/time_machine/time_machine.ex create mode 100644 test/chargebeex/builder/time_machine_test.exs create mode 100644 test/chargebeex/time_machine_test.exs create mode 100644 test/support/fixtures/time_machine.ex diff --git a/lib/chargebeex/builder.ex b/lib/chargebeex/builder.ex index 1258f39..81c314d 100644 --- a/lib/chargebeex/builder.ex +++ b/lib/chargebeex/builder.ex @@ -14,7 +14,8 @@ defmodule Chargebeex.Builder do Item, ItemPrice, Quote, - QuotedSubscription + QuotedSubscription, + TimeMachine } def build(%{"list" => resources, "next_offset" => next_offset}) do @@ -45,6 +46,7 @@ defmodule Chargebeex.Builder do def build_resource(%{"item_price" => params}), do: ItemPrice.build(params) def build_resource(%{"quote" => params}), do: Quote.build(params) def build_resource(%{"quoted_subscription" => params}), do: QuotedSubscription.build(params) + def build_resource(%{"time_machine" => params}), do: TimeMachine.build(params) def build_resource("subscription", params), do: Subscription.build(params) def build_resource("customer", params), do: Customer.build(params) @@ -60,6 +62,7 @@ defmodule Chargebeex.Builder do def build_resource("item_price", params), do: ItemPrice.build(params) def build_resource("quote", params), do: Quote.build(params) def build_resource("quoted_subscription", params), do: QuotedSubscription.build(params) + def build_resource("time_machine", params), do: TimeMachine.build(params) def build_resource(_resource, params), do: params end diff --git a/lib/chargebeex/time_machine/time_machine.ex b/lib/chargebeex/time_machine/time_machine.ex new file mode 100644 index 0000000..2be99d4 --- /dev/null +++ b/lib/chargebeex/time_machine/time_machine.ex @@ -0,0 +1,48 @@ +defmodule Chargebeex.TimeMachine do + @resource "time_machine" + + use TypedStruct + use Chargebeex.Resource, resource: @resource, only: [:retrieve] + + typedstruct do + field :name, String.t() + field :time_travel_status, String.t() + field :genesis_time, integer() + field :destination_time, integer() + field :failure_code, String.t() + field :failure_reason, String.t() + field :error_json, String.t() + field :object, String.t() + field :resources, map(), defualt: %{} + end + + @moduledoc """ + Struct that represent a Chargebee's API TimeMachine. + """ + def build(raw_data) do + attrs = %{ + name: raw_data["name"], + time_travel_status: raw_data["time_travel_status"], + genesis_time: raw_data["genesis_time"], + destination_time: raw_data["destination_time"], + failure_code: raw_data["failure_code"], + failure_reason: raw_data["failure_reason"], + error_json: raw_data["error_json"], + object: raw_data["object"] + } + + struct(__MODULE__, attrs) + end + + @doc """ + Restart the time machine. + """ + def start_afresh(id), do: generic_action(:post, @resource, "start_afresh", id) + + @doc """ + Travel forward in time. + """ + def travel_forward(id, params) do + generic_action(:post, @resource, "travel_forward", id, params) + end +end diff --git a/test/chargebeex/builder/time_machine_test.exs b/test/chargebeex/builder/time_machine_test.exs new file mode 100644 index 0000000..95947c6 --- /dev/null +++ b/test/chargebeex/builder/time_machine_test.exs @@ -0,0 +1,34 @@ +defmodule Chargebeex.Builder.TimeMachineTest do + use ExUnit.Case, async: true + + alias Chargebeex.Builder + alias Chargebeex.Fixtures.TimeMachine, as: TimeMachineFixture + alias Chargebeex.TimeMachine + + describe "build/1" do + test "should build an time machine" do + builded = + TimeMachineFixture.retrieve() + |> Jason.decode!() + |> Builder.build() + + assert %{"time_machine" => %TimeMachine{}} = builded + end + + test "should have time machine params" do + time_machine = + TimeMachineFixture.retrieve() + |> Jason.decode!() + |> Builder.build() + |> Map.get("time_machine") + + params = TimeMachineFixture.time_machine_params() |> Jason.decode!() + + assert time_machine.destination_time == Map.get(params, "destination_time") + assert time_machine.genesis_time == Map.get(params, "genesis_time") + assert time_machine.name == Map.get(params, "name") + assert time_machine.object == Map.get(params, "object") + assert time_machine.time_travel_status == Map.get(params, "time_travel_status") + end + end +end diff --git a/test/chargebeex/time_machine_test.exs b/test/chargebeex/time_machine_test.exs new file mode 100644 index 0000000..6f614ec --- /dev/null +++ b/test/chargebeex/time_machine_test.exs @@ -0,0 +1,211 @@ +defmodule Chargebeex.TimeMachineTest do + use ExUnit.Case, async: true + + import Hammox + + alias Chargebeex.Fixtures.{Common, TimeMachine} + alias Chargebeex.TimeMachine + + setup :verify_on_exit! + + describe "retrieve" do + test "with bad authentication should fail" do + unauthorized = Common.unauthorized() + + expect( + Chargebeex.HTTPClientMock, + :get, + fn url, body, headers -> + assert url == "https://test-namespace.chargebee.com/api/v2/time_machines/delorean" + assert headers == [{"Authorization", "Basic dGVzdF9jaGFyZ2VlYmVlX2FwaV9rZXk6"}] + assert body == "" + + {:ok, 401, [], Jason.encode!(unauthorized)} + end + ) + + assert {:error, 401, [], ^unauthorized} = TimeMachine.retrieve("delorean") + end + + test "with resource not found should fail" do + not_found = Common.not_found() + + expect( + Chargebeex.HTTPClientMock, + :get, + fn url, body, headers -> + assert url == "https://test-namespace.chargebee.com/api/v2/time_machines/delorean" + assert headers == [{"Authorization", "Basic dGVzdF9jaGFyZ2VlYmVlX2FwaV9rZXk6"}] + assert body == "" + + {:ok, 404, [], Jason.encode!(not_found)} + end + ) + + assert {:error, 404, [], ^not_found} = TimeMachine.retrieve("delorean") + end + + test "with resource found should succeed" do + expect( + Chargebeex.HTTPClientMock, + :get, + fn url, body, headers -> + assert url == "https://test-namespace.chargebee.com/api/v2/time_machines/delorean" + assert headers == [{"Authorization", "Basic dGVzdF9jaGFyZ2VlYmVlX2FwaV9rZXk6"}] + assert body == "" + + {:ok, 200, [], Jason.encode!(%{time_machine: %{}})} + end + ) + + assert {:ok, %TimeMachine{}} = TimeMachine.retrieve("delorean") + end + end + + describe "start_afresh" do + test "with bad authentication should fail" do + unauthorized = Common.unauthorized() + + expect( + Chargebeex.HTTPClientMock, + :post, + fn url, body, headers -> + assert url == + "https://test-namespace.chargebee.com/api/v2/time_machines/delorean/start_afresh" + + assert headers == [ + {"Authorization", "Basic dGVzdF9jaGFyZ2VlYmVlX2FwaV9rZXk6"}, + {"Content-Type", "application/x-www-form-urlencoded"} + ] + + assert body == "" + + {:ok, 401, [], Jason.encode!(unauthorized)} + end + ) + + assert {:error, 401, [], ^unauthorized} = TimeMachine.start_afresh("delorean") + end + + test "with resource not found should fail" do + not_found = Common.not_found() + + expect( + Chargebeex.HTTPClientMock, + :post, + fn url, body, headers -> + assert url == + "https://test-namespace.chargebee.com/api/v2/time_machines/delorean/start_afresh" + + assert headers == [ + {"Authorization", "Basic dGVzdF9jaGFyZ2VlYmVlX2FwaV9rZXk6"}, + {"Content-Type", "application/x-www-form-urlencoded"} + ] + + assert body == "" + + {:ok, 404, [], Jason.encode!(not_found)} + end + ) + + assert {:error, 404, [], ^not_found} = TimeMachine.start_afresh("delorean") + end + + test "with resource found should succeed" do + expect( + Chargebeex.HTTPClientMock, + :post, + fn url, body, headers -> + assert url == + "https://test-namespace.chargebee.com/api/v2/time_machines/delorean/start_afresh" + + assert headers == [ + {"Authorization", "Basic dGVzdF9jaGFyZ2VlYmVlX2FwaV9rZXk6"}, + {"Content-Type", "application/x-www-form-urlencoded"} + ] + + assert body == "" + + {:ok, 200, [], Jason.encode!(%{time_machine: %{}})} + end + ) + + assert {:ok, %TimeMachine{}} = TimeMachine.start_afresh("delorean") + end + end + + describe "travel_forward" do + test "with bad authentication should fail" do + unauthorized = Common.unauthorized() + + expect( + Chargebeex.HTTPClientMock, + :post, + fn url, body, headers -> + assert url == + "https://test-namespace.chargebee.com/api/v2/time_machines/delorean/travel_forward" + + assert headers == [ + {"Authorization", "Basic dGVzdF9jaGFyZ2VlYmVlX2FwaV9rZXk6"}, + {"Content-Type", "application/x-www-form-urlencoded"} + ] + + assert body == "destination_time=1586274640" + + {:ok, 401, [], Jason.encode!(unauthorized)} + end + ) + + assert {:error, 401, [], ^unauthorized} = + TimeMachine.travel_forward("delorean", %{destination_time: 1_586_274_640}) + end + + test "with resource not found should fail" do + not_found = Common.not_found() + + expect( + Chargebeex.HTTPClientMock, + :post, + fn url, body, headers -> + assert url == + "https://test-namespace.chargebee.com/api/v2/time_machines/delorean/travel_forward" + + assert headers == [ + {"Authorization", "Basic dGVzdF9jaGFyZ2VlYmVlX2FwaV9rZXk6"}, + {"Content-Type", "application/x-www-form-urlencoded"} + ] + + assert body == "destination_time=1586274640" + + {:ok, 404, [], Jason.encode!(not_found)} + end + ) + + assert {:error, 404, [], ^not_found} = + TimeMachine.travel_forward("delorean", %{destination_time: 1_586_274_640}) + end + + test "with resource found should succeed" do + expect( + Chargebeex.HTTPClientMock, + :post, + fn url, body, headers -> + assert url == + "https://test-namespace.chargebee.com/api/v2/time_machines/delorean/travel_forward" + + assert headers == [ + {"Authorization", "Basic dGVzdF9jaGFyZ2VlYmVlX2FwaV9rZXk6"}, + {"Content-Type", "application/x-www-form-urlencoded"} + ] + + assert body == "destination_time=1586274640" + + {:ok, 200, [], Jason.encode!(%{time_machine: %{}})} + end + ) + + assert {:ok, %TimeMachine{}} = + TimeMachine.travel_forward("delorean", %{destination_time: 1_586_274_640}) + end + end +end diff --git a/test/support/fixtures/time_machine.ex b/test/support/fixtures/time_machine.ex new file mode 100644 index 0000000..f10efc7 --- /dev/null +++ b/test/support/fixtures/time_machine.ex @@ -0,0 +1,21 @@ +defmodule Chargebeex.Fixtures.TimeMachine do + def time_machine_params() do + """ + { + "destination_time": 1585065021, + "genesis_time": 1585065021, + "name": "delorean", + "object": "time_machine", + "time_travel_status": "succeeded" + } + """ + end + + def retrieve() do + """ + { + "time_machine": #{time_machine_params()} + } + """ + end +end From fc072e3573df540001fc1a77988aefe62a4896c4 Mon Sep 17 00:00:00 2001 From: Maxime MENAGER Date: Thu, 12 Jan 2023 17:03:13 +0100 Subject: [PATCH 2/2] chore: use ExConstructor --- lib/chargebeex/time_machine/time_machine.ex | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/lib/chargebeex/time_machine/time_machine.ex b/lib/chargebeex/time_machine/time_machine.ex index 2be99d4..a48f617 100644 --- a/lib/chargebeex/time_machine/time_machine.ex +++ b/lib/chargebeex/time_machine/time_machine.ex @@ -16,23 +16,7 @@ defmodule Chargebeex.TimeMachine do field :resources, map(), defualt: %{} end - @moduledoc """ - Struct that represent a Chargebee's API TimeMachine. - """ - def build(raw_data) do - attrs = %{ - name: raw_data["name"], - time_travel_status: raw_data["time_travel_status"], - genesis_time: raw_data["genesis_time"], - destination_time: raw_data["destination_time"], - failure_code: raw_data["failure_code"], - failure_reason: raw_data["failure_reason"], - error_json: raw_data["error_json"], - object: raw_data["object"] - } - - struct(__MODULE__, attrs) - end + use ExConstructor, :build @doc """ Restart the time machine.