Skip to content

Commit

Permalink
[WIP] POC for using ETag to verify screens config version (#234)
Browse files Browse the repository at this point in the history
* Fix default config bug

* Start returning etag when getting config and add put operation

* Add and delete logic

* credo

* Update lib/screenplay/config/config.ex

Co-authored-by: Christian Maddox <[email protected]>

* Remove edit action

* Return more specific error messages

---------

Co-authored-by: Christian Maddox <[email protected]>
  • Loading branch information
PaulJKim and cmaddox5 committed Dec 13, 2023
1 parent 5924a06 commit 5621525
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 27 deletions.
2 changes: 1 addition & 1 deletion config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ sftp_client_module =
_ -> Screenplay.Outfront.FakeSFTPClient
end

env = System.get_env("ENVIRONMENT_NAME")
env = System.get_env("ENVIRONMENT_NAME", "dev")

config :screenplay,
alerts_s3_path: "screenplay/" <> System.get_env("ALERTS_S3_FILENAME", ""),
Expand Down
39 changes: 36 additions & 3 deletions lib/screenplay/config/config.ex
Original file line number Diff line number Diff line change
@@ -1,12 +1,45 @@
defmodule Screenplay.Config.PermanentConfig do
@moduledoc false
alias Screenplay.Config.S3Fetch

def add_new_screen do
@spec add_new_screen(binary(), map(), binary()) ::
{:error, :etag_mismatch | :config_not_fetched | :config_not_written} | :ok
def add_new_screen(screen_id, screen, etag) do
case get_current_config(etag) do
{:ok, config} ->
config
|> Map.put(screen_id, Jason.decode!(screen))
|> S3Fetch.put_screens_config()

error ->
error
end
end

def edit_screen do
@spec delete_screen(binary(), binary()) ::
{:error, :etag_mismatch | :config_not_fetched | :config_not_written} | :ok
def delete_screen(screen_id, etag) do
case get_current_config(etag) do
{:ok, config} ->
config
|> Map.delete(screen_id)
|> S3Fetch.put_screens_config()

error ->
error
end
end

def delete_screen do
defp get_current_config(etag) do
case S3Fetch.get_screens_config() do
{:ok, config, ^etag} ->
{:ok, config}

:error ->
{:error, :config_not_fetched}

_ ->
{:error, :etag_mismatch}
end
end
end
55 changes: 46 additions & 9 deletions lib/screenplay/config/s3_fetch.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ defmodule Screenplay.Config.S3Fetch do
@behaviour Screenplay.Config.Fetch

def get_config do
with {:ok, config_contents} <- do_get(:config),
{:ok, location_contents} <- do_get(:screen_locations),
{:ok, place_description_contents} <- do_get(:place_descriptions),
with {:ok, config_contents, _} <- do_get(:config),
{:ok, location_contents, _} <- do_get(:screen_locations),
{:ok, place_description_contents, _} <- do_get(:place_descriptions),
{:ok, config_json} <- Jason.decode(config_contents),
{:ok, location_json} <- Jason.decode(location_contents),
{:ok, place_description_json} <- Jason.decode(place_description_contents) do
Expand All @@ -18,15 +18,29 @@ defmodule Screenplay.Config.S3Fetch do
end
end

def get_screens_config do
with {:ok, screens_contents, etag} <- do_get(:screens),
{:ok, screens_json} <- Jason.decode(screens_contents) do
{:ok, screens_json, etag}
else
_ -> :error
end
end

defp do_get(file_spec) do
bucket = Application.get_env(:screenplay, :config_s3_bucket)
path = config_path_for_environment(file_spec)

get_operation = ExAws.S3.get_object(bucket, path)

case ExAws.request(get_operation) do
{:ok, %{body: body, status_code: 200}} ->
{:ok, body}
{:ok, %{body: body, headers: headers, status_code: 200}} ->
etag =
headers
|> Enum.into(%{})
|> Map.get("ETag")

{:ok, body, etag}

{:error, err} ->
Logger.error(err)
Expand All @@ -35,12 +49,35 @@ defmodule Screenplay.Config.S3Fetch do
end

defp config_path_for_environment(file_spec) do
base_path = "screenplay/#{Application.get_env(:screenplay, :environment_name, "dev")}"
base_path = "screenplay/#{Application.get_env(:screenplay, :environment_name)}"

case file_spec do
:config -> "#{base_path}/places_and_screens.json"
:screen_locations -> "#{base_path}/screen_locations.json"
:place_descriptions -> "#{base_path}/place_descriptions.json"
:config ->
"#{base_path}/places_and_screens.json"

:screen_locations ->
"#{base_path}/screen_locations.json"

:place_descriptions ->
"#{base_path}/place_descriptions.json"

:screens ->
"screens/screens-#{Application.get_env(:screenplay, :environment_name)}.json"
end
end

def put_screens_config(config) do
bucket = Application.get_env(:screenplay, :config_s3_bucket)
path = config_path_for_environment(:screens)

put_operation = ExAws.S3.put_object(bucket, path, Jason.encode!(config, pretty: true))

case ExAws.request(put_operation) do
{:ok, %{status_code: 200}} ->
:ok

_ ->
{:error, :config_not_written}
end
end
end
39 changes: 26 additions & 13 deletions lib/screenplay_web/controllers/config_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,35 @@ defmodule ScreenplayWeb.ConfigController do

alias Screenplay.Config.PermanentConfig

def index(conn, _params) do
render(conn, "index.html")
end
def add(conn, %{"screen_id" => screen_id, "screen" => screen, "etag" => etag}) do
case PermanentConfig.add_new_screen(screen_id, screen, etag) do
:ok ->
send_resp(conn, 200, "OK")

def add(conn, _params) do
PermanentConfig.add_new_screen()
conn
end
{:error, :etag_mismatch} ->
send_resp(conn, 400, "Config version mismatch")

{:error, :config_not_fetched} ->
send_resp(conn, 400, "S3 Operation Failed: Get")

def edit(conn, _params) do
PermanentConfig.edit_screen()
conn
{:error, :config_not_written} ->
send_resp(conn, 400, "S3 Operation Failed: Put")
end
end

def delete(conn, _params) do
PermanentConfig.delete_screen()
conn
def delete(conn, %{"screen_id" => screen_id, "etag" => etag}) do
case PermanentConfig.delete_screen(screen_id, etag) do
:ok ->
send_resp(conn, 200, "OK")

{:error, :etag_mismatch} ->
send_resp(conn, 400, "Config version mismatch")

{:error, :config_not_fetched} ->
send_resp(conn, 400, "S3 Operation Failed: Get")

{:error, :config_not_written} ->
send_resp(conn, 400, "S3 Operation Failed: Put")
end
end
end
1 change: 0 additions & 1 deletion lib/screenplay_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ defmodule ScreenplayWeb.Router do
])

post("/add", ConfigController, :add)
post("/edit", ConfigController, :edit)
post("/delete", ConfigController, :delete)
end

Expand Down

0 comments on commit 5621525

Please sign in to comment.