Skip to content

Commit a29d207

Browse files
authored
Implement wookiee encoding from original SWAPI (#45)
* Implement wookiee encoding from original SWAPI * Add tests for wookiee * Fix function name
1 parent 12651db commit a29d207

10 files changed

Lines changed: 277 additions & 6 deletions

lib/swapi_web/controllers/film_controller.ex

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ defmodule SWAPIWeb.FilmController do
4545
minimum: 1,
4646
default: 10
4747
}
48+
],
49+
format: [
50+
in: :query,
51+
description: "Specifies the encoding to be used for the response",
52+
schema: %Schema{
53+
type: :string,
54+
default: "json",
55+
enum: ["json", "wookiee"]
56+
}
4857
]
4958
],
5059
responses: [
@@ -69,7 +78,16 @@ defmodule SWAPIWeb.FilmController do
6978
operation(:show,
7079
summary: "Get a specific film resource",
7180
parameters: [
72-
id: [in: :path, description: "Film ID", type: :integer]
81+
id: [in: :path, description: "Film ID", type: :integer],
82+
format: [
83+
in: :query,
84+
description: "Specifies the encoding to be used for the response",
85+
schema: %Schema{
86+
type: :string,
87+
default: "json",
88+
enum: ["json", "wookiee"]
89+
}
90+
]
7391
],
7492
responses: [
7593
ok: {"A film", "application/json", SWAPIWeb.Schemas.Film}

lib/swapi_web/controllers/person_controller.ex

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ defmodule SWAPIWeb.PersonController do
4545
minimum: 1,
4646
default: 10
4747
}
48+
],
49+
format: [
50+
in: :query,
51+
description: "Specifies the encoding to be used for the response",
52+
schema: %Schema{
53+
type: :string,
54+
default: "json",
55+
enum: ["json", "wookiee"]
56+
}
4857
]
4958
],
5059
responses: [
@@ -69,7 +78,16 @@ defmodule SWAPIWeb.PersonController do
6978
operation(:show,
7079
summary: "Get a specific people resource",
7180
parameters: [
72-
id: [in: :path, description: "Person ID", type: :integer]
81+
id: [in: :path, description: "Person ID", type: :integer],
82+
format: [
83+
in: :query,
84+
description: "Specifies the encoding to be used for the response",
85+
schema: %Schema{
86+
type: :string,
87+
default: "json",
88+
enum: ["json", "wookiee"]
89+
}
90+
]
7391
],
7492
responses: [
7593
ok: {"A person", "application/json", SWAPIWeb.Schemas.Person}

lib/swapi_web/controllers/planet_controller.ex

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ defmodule SWAPIWeb.PlanetController do
4545
minimum: 1,
4646
default: 10
4747
}
48+
],
49+
format: [
50+
in: :query,
51+
description: "Specifies the encoding to be used for the response",
52+
schema: %Schema{
53+
type: :string,
54+
default: "json",
55+
enum: ["json", "wookiee"]
56+
}
4857
]
4958
],
5059
responses: [
@@ -69,7 +78,16 @@ defmodule SWAPIWeb.PlanetController do
6978
operation(:show,
7079
summary: "Get a specific planet resource",
7180
parameters: [
72-
id: [in: :path, description: "Planet ID", type: :integer]
81+
id: [in: :path, description: "Planet ID", type: :integer],
82+
format: [
83+
in: :query,
84+
description: "Specifies the encoding to be used for the response",
85+
schema: %Schema{
86+
type: :string,
87+
default: "json",
88+
enum: ["json", "wookiee"]
89+
}
90+
]
7391
],
7492
responses: [
7593
ok: {"A planet", "application/json", SWAPIWeb.Schemas.Planet}

lib/swapi_web/controllers/root_controller.ex

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,25 @@ defmodule SWAPIWeb.RootController do
22
use SWAPIWeb, :controller
33
use OpenApiSpex.ControllerSpecs
44

5+
alias OpenApiSpex.Schema
6+
57
action_fallback SWAPIWeb.FallbackController
68

79
tags(["root"])
810

911
operation(:index,
1012
summary: "Get URL roots for all available resources",
13+
parameters: [
14+
format: [
15+
in: :query,
16+
description: "Specifies the encoding to be used for the response",
17+
schema: %Schema{
18+
type: :string,
19+
default: "json",
20+
enum: ["json", "wookiee"]
21+
}
22+
]
23+
],
1124
responses: [
1225
ok: {"List of endpoints", "application/json", SWAPIWeb.Schemas.Root}
1326
]

lib/swapi_web/controllers/species_controller.ex

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ defmodule SWAPIWeb.SpeciesController do
4545
minimum: 1,
4646
default: 10
4747
}
48+
],
49+
format: [
50+
in: :query,
51+
description: "Specifies the encoding to be used for the response",
52+
schema: %Schema{
53+
type: :string,
54+
default: "json",
55+
enum: ["json", "wookiee"]
56+
}
4857
]
4958
],
5059
responses: [
@@ -69,7 +78,16 @@ defmodule SWAPIWeb.SpeciesController do
6978
operation(:show,
7079
summary: "Get a specific species resource",
7180
parameters: [
72-
id: [in: :path, description: "Species ID", type: :integer]
81+
id: [in: :path, description: "Species ID", type: :integer],
82+
format: [
83+
in: :query,
84+
description: "Specifies the encoding to be used for the response",
85+
schema: %Schema{
86+
type: :string,
87+
default: "json",
88+
enum: ["json", "wookiee"]
89+
}
90+
]
7391
],
7492
responses: [
7593
ok: {"A species", "application/json", SWAPIWeb.Schemas.Species}

lib/swapi_web/controllers/starship_controller.ex

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ defmodule SWAPIWeb.StarshipController do
4545
minimum: 1,
4646
default: 10
4747
}
48+
],
49+
format: [
50+
in: :query,
51+
description: "Specifies the encoding to be used for the response",
52+
schema: %Schema{
53+
type: :string,
54+
default: "json",
55+
enum: ["json", "wookiee"]
56+
}
4857
]
4958
],
5059
responses: [
@@ -69,7 +78,16 @@ defmodule SWAPIWeb.StarshipController do
6978
operation(:show,
7079
summary: "Get a specific starship resource",
7180
parameters: [
72-
id: [in: :path, description: "Starship ID", type: :integer]
81+
id: [in: :path, description: "Starship ID", type: :integer],
82+
format: [
83+
in: :query,
84+
description: "Specifies the encoding to be used for the response",
85+
schema: %Schema{
86+
type: :string,
87+
default: "json",
88+
enum: ["json", "wookiee"]
89+
}
90+
]
7391
],
7492
responses: [
7593
ok: {"A starship", "application/json", SWAPIWeb.Schemas.Starship}

lib/swapi_web/controllers/vehicle_controller.ex

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ defmodule SWAPIWeb.VehicleController do
4545
minimum: 1,
4646
default: 10
4747
}
48+
],
49+
format: [
50+
in: :query,
51+
description: "Specifies the encoding to be used for the response",
52+
schema: %Schema{
53+
type: :string,
54+
default: "json",
55+
enum: ["json", "wookiee"]
56+
}
4857
]
4958
],
5059
responses: [
@@ -69,7 +78,16 @@ defmodule SWAPIWeb.VehicleController do
6978
operation(:show,
7079
summary: "Get a specific vehicle resource",
7180
parameters: [
72-
id: [in: :path, description: "Vehicle ID", type: :integer]
81+
id: [in: :path, description: "Vehicle ID", type: :integer],
82+
format: [
83+
in: :query,
84+
description: "Specifies the encoding to be used for the response",
85+
schema: %Schema{
86+
type: :string,
87+
default: "json",
88+
enum: ["json", "wookiee"]
89+
}
90+
]
7391
],
7492
responses: [
7593
ok: {"A vehicle", "application/json", SWAPIWeb.Schemas.Vehicle}

lib/swapi_web/router.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ defmodule SWAPIWeb.Router do
1717
"public, max-age=86400, s-max-age=172800, stale-while-revalidate=2678400"
1818

1919
plug OpenApiSpex.Plug.PutApiSpec, module: SWAPIWeb.ApiSpec
20+
plug SWAPIWeb.WookieeEncoder
2021
end
2122

2223
pipeline :browser do

lib/swapi_web/wookiee_encoder.ex

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
defmodule SWAPIWeb.WookieeEncoder do
2+
@moduledoc """
3+
This module handles wookie encoding for the REST API.
4+
"""
5+
6+
import Plug.Conn
7+
8+
@behaviour Plug
9+
10+
@map %{
11+
?a => ~c"ra",
12+
?b => ~c"rh",
13+
?c => ~c"oa",
14+
?d => ~c"wa",
15+
?e => ~c"wo",
16+
?f => ~c"ww",
17+
?g => ~c"rr",
18+
?h => ~c"ac",
19+
?i => ~c"ah",
20+
?j => ~c"sh",
21+
?k => ~c"or",
22+
?l => ~c"an",
23+
?m => ~c"sc",
24+
?n => ~c"wh",
25+
?o => ~c"oo",
26+
?p => ~c"ak",
27+
?q => ~c"rq",
28+
?r => ~c"rc",
29+
?s => ~c"c",
30+
?t => ~c"ao",
31+
?u => ~c"hu",
32+
?v => ~c"ho",
33+
?w => ~c"oh",
34+
?x => ~c"k",
35+
?y => ~c"ro",
36+
?z => ~c"uf"
37+
}
38+
39+
@spec translate_to_wookiee(String.t()) :: String.t()
40+
def translate_to_wookiee(text) do
41+
text
42+
|> String.to_charlist()
43+
|> Enum.reverse()
44+
|> Enum.reduce(~c"", fn char, acc ->
45+
case @map[char] do
46+
nil -> [char | acc]
47+
value -> value ++ acc
48+
end
49+
end)
50+
|> to_string()
51+
end
52+
53+
@spec init(Plug.opts()) :: Plug.opts()
54+
def init(opts), do: opts
55+
56+
@spec call(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t()
57+
def call(%{query_params: %{"format" => "wookiee"}} = conn, _opts) do
58+
register_before_send(conn, &before_send_callback/1)
59+
end
60+
61+
def call(conn, _opts), do: conn
62+
63+
@spec before_send_callback(Plug.Conn.t()) :: Plug.Conn.t()
64+
defp before_send_callback(%{status: 200} = conn) do
65+
wookie_body =
66+
conn.resp_body
67+
|> flatten_body()
68+
|> translate_to_wookiee()
69+
70+
resp(conn, conn.status, wookie_body)
71+
end
72+
73+
defp before_send_callback(conn), do: conn
74+
75+
defp flatten_list_improper(list) do
76+
case list do
77+
[] -> []
78+
[h | t] when is_list(h) -> flatten_list_improper(h) ++ flatten_list_improper(t)
79+
[h | t] -> [h | flatten_list_improper(t)]
80+
scalar -> [scalar]
81+
end
82+
end
83+
84+
defp flatten_body(list) do
85+
list
86+
|> flatten_list_improper()
87+
|> Enum.map_join("", fn
88+
x when is_integer(x) -> <<x::utf8>>
89+
x -> x
90+
end)
91+
end
92+
end
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
defmodule SWAPIWeb.WookieeEncoderTest do
2+
use SWAPIWeb.ConnCase, async: true
3+
4+
import SWAPI.PlanetsFixtures
5+
6+
alias SWAPIWeb.WookieeEncoder
7+
8+
describe "translate_to_wookie/1" do
9+
test "converts lowercase letters" do
10+
assert "aoacwo rqhuahoaor rhrcooohwh wwook shhuscakc oohoworc aoacwo anraufro waoorr" =
11+
WookieeEncoder.translate_to_wookiee("the quick brown fox jumps over the lazy dog")
12+
end
13+
14+
test "leaves JSON special characters alone" do
15+
test_string = "\\{}\"\'[]:"
16+
assert ^test_string = WookieeEncoder.translate_to_wookiee(test_string)
17+
end
18+
19+
test "leaves other non-lowercase characters alone" do
20+
assert "Twocao. 12345" = WookieeEncoder.translate_to_wookiee("Test. 12345")
21+
end
22+
end
23+
24+
describe "call/2" do
25+
setup do
26+
%{id: planet_id} =
27+
planet_fixture(%{
28+
name: "Tatooine"
29+
})
30+
31+
{:ok, %{planet_id: planet_id}}
32+
end
33+
34+
test "converts JSON to wookiee if the format is wookiee", %{conn: conn, planet_id: planet_id} do
35+
conn = get(conn, ~p"/api/planets/#{planet_id}", format: "wookiee")
36+
assert %{"whrascwo" => "Traaoooooahwhwo"} = json_response(conn, 200)
37+
end
38+
39+
test "does not modify the response if the format is not specified", %{
40+
conn: conn,
41+
planet_id: planet_id
42+
} do
43+
conn = get(conn, ~p"/api/planets/#{planet_id}")
44+
assert %{"name" => "Tatooine"} = json_response(conn, 200)
45+
end
46+
47+
test "does not modify the response if the format is json", %{conn: conn, planet_id: planet_id} do
48+
conn = get(conn, ~p"/api/planets/#{planet_id}", format: "json")
49+
assert %{"name" => "Tatooine"} = json_response(conn, 200)
50+
end
51+
52+
test "does not modify the response if the request fails", %{conn: conn} do
53+
conn = get(conn, ~p"/api/planets/123456789", format: "wookiee")
54+
assert %{"detail" => "Not Found"} = json_response(conn, 404)
55+
end
56+
end
57+
end

0 commit comments

Comments
 (0)