Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion lib/swapi_web/controllers/film_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ defmodule SWAPIWeb.FilmController do
minimum: 1,
default: 10
}
],
format: [
in: :query,
description: "Specifies the encoding to be used for the response",
schema: %Schema{
type: :string,
default: "json",
enum: ["json", "wookiee"]
}
]
],
responses: [
Expand All @@ -69,7 +78,16 @@ defmodule SWAPIWeb.FilmController do
operation(:show,
summary: "Get a specific film resource",
parameters: [
id: [in: :path, description: "Film ID", type: :integer]
id: [in: :path, description: "Film ID", type: :integer],
format: [
in: :query,
description: "Specifies the encoding to be used for the response",
schema: %Schema{
type: :string,
default: "json",
enum: ["json", "wookiee"]
}
]
],
responses: [
ok: {"A film", "application/json", SWAPIWeb.Schemas.Film}
Expand Down
20 changes: 19 additions & 1 deletion lib/swapi_web/controllers/person_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ defmodule SWAPIWeb.PersonController do
minimum: 1,
default: 10
}
],
format: [
in: :query,
description: "Specifies the encoding to be used for the response",
schema: %Schema{
type: :string,
default: "json",
enum: ["json", "wookiee"]
}
]
],
responses: [
Expand All @@ -69,7 +78,16 @@ defmodule SWAPIWeb.PersonController do
operation(:show,
summary: "Get a specific people resource",
parameters: [
id: [in: :path, description: "Person ID", type: :integer]
id: [in: :path, description: "Person ID", type: :integer],
format: [
in: :query,
description: "Specifies the encoding to be used for the response",
schema: %Schema{
type: :string,
default: "json",
enum: ["json", "wookiee"]
}
]
],
responses: [
ok: {"A person", "application/json", SWAPIWeb.Schemas.Person}
Expand Down
20 changes: 19 additions & 1 deletion lib/swapi_web/controllers/planet_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ defmodule SWAPIWeb.PlanetController do
minimum: 1,
default: 10
}
],
format: [
in: :query,
description: "Specifies the encoding to be used for the response",
schema: %Schema{
type: :string,
default: "json",
enum: ["json", "wookiee"]
}
]
],
responses: [
Expand All @@ -69,7 +78,16 @@ defmodule SWAPIWeb.PlanetController do
operation(:show,
summary: "Get a specific planet resource",
parameters: [
id: [in: :path, description: "Planet ID", type: :integer]
id: [in: :path, description: "Planet ID", type: :integer],
format: [
in: :query,
description: "Specifies the encoding to be used for the response",
schema: %Schema{
type: :string,
default: "json",
enum: ["json", "wookiee"]
}
]
],
responses: [
ok: {"A planet", "application/json", SWAPIWeb.Schemas.Planet}
Expand Down
13 changes: 13 additions & 0 deletions lib/swapi_web/controllers/root_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,25 @@ defmodule SWAPIWeb.RootController do
use SWAPIWeb, :controller
use OpenApiSpex.ControllerSpecs

alias OpenApiSpex.Schema

action_fallback SWAPIWeb.FallbackController

tags(["root"])

operation(:index,
summary: "Get URL roots for all available resources",
parameters: [
format: [
in: :query,
description: "Specifies the encoding to be used for the response",
schema: %Schema{
type: :string,
default: "json",
enum: ["json", "wookiee"]
}
]
],
responses: [
ok: {"List of endpoints", "application/json", SWAPIWeb.Schemas.Root}
]
Expand Down
20 changes: 19 additions & 1 deletion lib/swapi_web/controllers/species_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ defmodule SWAPIWeb.SpeciesController do
minimum: 1,
default: 10
}
],
format: [
in: :query,
description: "Specifies the encoding to be used for the response",
schema: %Schema{
type: :string,
default: "json",
enum: ["json", "wookiee"]
}
]
],
responses: [
Expand All @@ -69,7 +78,16 @@ defmodule SWAPIWeb.SpeciesController do
operation(:show,
summary: "Get a specific species resource",
parameters: [
id: [in: :path, description: "Species ID", type: :integer]
id: [in: :path, description: "Species ID", type: :integer],
format: [
in: :query,
description: "Specifies the encoding to be used for the response",
schema: %Schema{
type: :string,
default: "json",
enum: ["json", "wookiee"]
}
]
],
responses: [
ok: {"A species", "application/json", SWAPIWeb.Schemas.Species}
Expand Down
20 changes: 19 additions & 1 deletion lib/swapi_web/controllers/starship_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ defmodule SWAPIWeb.StarshipController do
minimum: 1,
default: 10
}
],
format: [
in: :query,
description: "Specifies the encoding to be used for the response",
schema: %Schema{
type: :string,
default: "json",
enum: ["json", "wookiee"]
}
]
],
responses: [
Expand All @@ -69,7 +78,16 @@ defmodule SWAPIWeb.StarshipController do
operation(:show,
summary: "Get a specific starship resource",
parameters: [
id: [in: :path, description: "Starship ID", type: :integer]
id: [in: :path, description: "Starship ID", type: :integer],
format: [
in: :query,
description: "Specifies the encoding to be used for the response",
schema: %Schema{
type: :string,
default: "json",
enum: ["json", "wookiee"]
}
]
],
responses: [
ok: {"A starship", "application/json", SWAPIWeb.Schemas.Starship}
Expand Down
20 changes: 19 additions & 1 deletion lib/swapi_web/controllers/vehicle_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ defmodule SWAPIWeb.VehicleController do
minimum: 1,
default: 10
}
],
format: [
in: :query,
description: "Specifies the encoding to be used for the response",
schema: %Schema{
type: :string,
default: "json",
enum: ["json", "wookiee"]
}
]
],
responses: [
Expand All @@ -69,7 +78,16 @@ defmodule SWAPIWeb.VehicleController do
operation(:show,
summary: "Get a specific vehicle resource",
parameters: [
id: [in: :path, description: "Vehicle ID", type: :integer]
id: [in: :path, description: "Vehicle ID", type: :integer],
format: [
in: :query,
description: "Specifies the encoding to be used for the response",
schema: %Schema{
type: :string,
default: "json",
enum: ["json", "wookiee"]
}
]
],
responses: [
ok: {"A vehicle", "application/json", SWAPIWeb.Schemas.Vehicle}
Expand Down
1 change: 1 addition & 0 deletions lib/swapi_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ defmodule SWAPIWeb.Router do
"public, max-age=86400, s-max-age=172800, stale-while-revalidate=2678400"

plug OpenApiSpex.Plug.PutApiSpec, module: SWAPIWeb.ApiSpec
plug SWAPIWeb.WookieeEncoder
end

pipeline :browser do
Expand Down
92 changes: 92 additions & 0 deletions lib/swapi_web/wookiee_encoder.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
defmodule SWAPIWeb.WookieeEncoder do
@moduledoc """
This module handles wookie encoding for the REST API.
"""

import Plug.Conn

@behaviour Plug

@map %{
?a => ~c"ra",
?b => ~c"rh",
?c => ~c"oa",
?d => ~c"wa",
?e => ~c"wo",
?f => ~c"ww",
?g => ~c"rr",
?h => ~c"ac",
?i => ~c"ah",
?j => ~c"sh",
?k => ~c"or",
?l => ~c"an",
?m => ~c"sc",
?n => ~c"wh",
?o => ~c"oo",
?p => ~c"ak",
?q => ~c"rq",
?r => ~c"rc",
?s => ~c"c",
?t => ~c"ao",
?u => ~c"hu",
?v => ~c"ho",
?w => ~c"oh",
?x => ~c"k",
?y => ~c"ro",
?z => ~c"uf"
}

@spec translate_to_wookiee(String.t()) :: String.t()
def translate_to_wookiee(text) do
text
|> String.to_charlist()
|> Enum.reverse()
|> Enum.reduce(~c"", fn char, acc ->
case @map[char] do
nil -> [char | acc]
value -> value ++ acc
end
end)
|> to_string()
end

@spec init(Plug.opts()) :: Plug.opts()
def init(opts), do: opts

@spec call(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t()
def call(%{query_params: %{"format" => "wookiee"}} = conn, _opts) do
register_before_send(conn, &before_send_callback/1)
end

def call(conn, _opts), do: conn

@spec before_send_callback(Plug.Conn.t()) :: Plug.Conn.t()
defp before_send_callback(%{status: 200} = conn) do
wookie_body =
conn.resp_body
|> flatten_body()
|> translate_to_wookiee()

resp(conn, conn.status, wookie_body)
end

defp before_send_callback(conn), do: conn

defp flatten_list_improper(list) do
case list do
[] -> []
[h | t] when is_list(h) -> flatten_list_improper(h) ++ flatten_list_improper(t)
[h | t] -> [h | flatten_list_improper(t)]
scalar -> [scalar]
end
end

defp flatten_body(list) do
list
|> flatten_list_improper()
|> Enum.map_join("", fn
x when is_integer(x) -> <<x::utf8>>
x -> x
end)
end
end
57 changes: 57 additions & 0 deletions test/swapi_web/wookiee_encoder_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
defmodule SWAPIWeb.WookieeEncoderTest do
use SWAPIWeb.ConnCase, async: true

import SWAPI.PlanetsFixtures

alias SWAPIWeb.WookieeEncoder

describe "translate_to_wookie/1" do
test "converts lowercase letters" do
assert "aoacwo rqhuahoaor rhrcooohwh wwook shhuscakc oohoworc aoacwo anraufro waoorr" =
WookieeEncoder.translate_to_wookiee("the quick brown fox jumps over the lazy dog")
end

test "leaves JSON special characters alone" do
test_string = "\\{}\"\'[]:"
assert ^test_string = WookieeEncoder.translate_to_wookiee(test_string)
end

test "leaves other non-lowercase characters alone" do
assert "Twocao. 12345" = WookieeEncoder.translate_to_wookiee("Test. 12345")
end
end

describe "call/2" do
setup do
%{id: planet_id} =
planet_fixture(%{
name: "Tatooine"
})

{:ok, %{planet_id: planet_id}}
end

test "converts JSON to wookiee if the format is wookiee", %{conn: conn, planet_id: planet_id} do
conn = get(conn, ~p"/api/planets/#{planet_id}", format: "wookiee")
assert %{"whrascwo" => "Traaoooooahwhwo"} = json_response(conn, 200)
end

test "does not modify the response if the format is not specified", %{
conn: conn,
planet_id: planet_id
} do
conn = get(conn, ~p"/api/planets/#{planet_id}")
assert %{"name" => "Tatooine"} = json_response(conn, 200)
end

test "does not modify the response if the format is json", %{conn: conn, planet_id: planet_id} do
conn = get(conn, ~p"/api/planets/#{planet_id}", format: "json")
assert %{"name" => "Tatooine"} = json_response(conn, 200)
end

test "does not modify the response if the request fails", %{conn: conn} do
conn = get(conn, ~p"/api/planets/123456789", format: "wookiee")
assert %{"detail" => "Not Found"} = json_response(conn, 404)
end
end
end
Loading