Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add example keys for 'input' to docs for post_service generated files #106

Closed
wants to merge 3 commits into from
Closed
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
1 change: 1 addition & 0 deletions lib/aws_codegen.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ defmodule AWS.CodeGen do
protocol: nil,
signature_version: nil,
service_id: nil,
shapes: %{},
signing_name: nil,
target_prefix: nil
end
Expand Down
55 changes: 54 additions & 1 deletion lib/aws_codegen/post_service.ex
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
defmodule AWS.CodeGen.PostService do
alias AWS.CodeGen.Docstring
alias AWS.CodeGen.Service
alias AWS.CodeGen.Shapes

Check warning on line 4 in lib/aws_codegen/post_service.ex

View workflow job for this annotation

GitHub Actions / Elixir 1.13.4 / OTP 25.0.4

unused alias Shapes

Check warning on line 4 in lib/aws_codegen/post_service.ex

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 1.13.4, 25.0.4, 3.20.0)

unused alias Shapes

Check warning on line 4 in lib/aws_codegen/post_service.ex

View workflow job for this annotation

GitHub Actions / Elixir 1.13.4 / OTP 25.0.4

unused alias Shapes
alias AWS.CodeGen.Name

Check warning on line 5 in lib/aws_codegen/post_service.ex

View workflow job for this annotation

GitHub Actions / Elixir 1.13.4 / OTP 25.0.4

unused alias Name

Check warning on line 5 in lib/aws_codegen/post_service.ex

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 1.13.4, 25.0.4, 3.20.0)

unused alias Name

Check warning on line 5 in lib/aws_codegen/post_service.ex

View workflow job for this annotation

GitHub Actions / Elixir 1.13.4 / OTP 25.0.4

unused alias Name

defmodule Action do
defstruct arity: nil,
docstring: nil,
function_name: nil,
input: nil,
output: nil,
errors: %{},
host_prefix: nil,
name: nil
end

defmodule Shape do
defstruct name: nil,
type: nil,
members: [],
member: [],
enum: [],
min: nil,
required: [],
is_input: nil
end

@configuration %{
"ec2" => %{
content_type: "application/x-www-form-urlencoded",
Expand Down Expand Up @@ -57,6 +73,7 @@
service = spec.api["shapes"][spec.shape_name]
traits = service["traits"]
actions = collect_actions(language, spec.api)
shapes = collect_shapes(language, spec.api)
endpoint_prefix = traits["aws.api#service"]["endpointPrefix"] || traits["aws.api#service"]["arnNamespace"]
endpoint_info = endpoints_spec["services"][endpoint_prefix]
is_global = not is_nil(endpoint_info) and not Map.get(endpoint_info, "isRegionalized", true)
Expand Down Expand Up @@ -89,6 +106,7 @@
language: language,
module_name: spec.module_name,
protocol: protocol |> to_string() |> String.replace("_", "-"),
shapes: shapes,
signing_name: signing_name,
signature_version: AWS.CodeGen.Util.get_signature_version(service),
service_id: AWS.CodeGen.Util.get_service_id(service),
Expand Down Expand Up @@ -137,10 +155,45 @@
),
function_name: AWS.CodeGen.Name.to_snake_case(operation),
host_prefix: operation_spec["traits"]["smithy.api#endpoint"]["hostPrefix"],
name: String.replace(operation, ~r/com\.amazonaws\.[^#]+#/, "")
name: String.replace(operation, ~r/com\.amazonaws\.[^#]+#/, ""),
input: operation_spec["input"],
output: operation_spec["output"],
errors: operation_spec["errors"]
}
end)
|> Enum.sort(fn a, b -> a.function_name < b.function_name end)
|> Enum.uniq()
end

defp collect_shapes(_language, api_spec) do
api_spec["shapes"]
|> Enum.sort(fn {name_a, _}, {name_b, _} -> name_a < name_b end)
|> Enum.map(fn {name, shape} ->
{name,
%Shape{
name: name,
type: shape["type"],
member: shape["member"],
members: shape["members"],
min: shape["min"],
enum: shape["enum"],
is_input: is_input?(shape)
}}
end)
|> Enum.into(%{})
end

defp is_input?(shape) do
if Map.has_key?(shape, "traits") do
traits = shape["traits"]
if Map.has_key?(traits, "smithy.api#input") do
true
else
false
end
else
true
end
end

end
1 change: 1 addition & 0 deletions lib/aws_codegen/shapes.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
defmodule AWS.CodeGen.Shapes do
alias AWS.CodeGen.Name

Check warning on line 2 in lib/aws_codegen/shapes.ex

View workflow job for this annotation

GitHub Actions / Elixir 1.13.4 / OTP 25.0.4

unused alias Name

Check warning on line 2 in lib/aws_codegen/shapes.ex

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 1.13.4, 25.0.4, 3.20.0)

unused alias Name

Check warning on line 2 in lib/aws_codegen/shapes.ex

View workflow job for this annotation

GitHub Actions / Elixir 1.13.4 / OTP 25.0.4

unused alias Name
@moduledoc false

def get_input_shape(operation_spec) do
Expand Down
150 changes: 150 additions & 0 deletions lib/aws_codegen/types.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
defmodule AWS.CodeGen.Types do
alias AWS.CodeGen.PostService.Shape
alias AWS.CodeGen.Name

Check warning on line 3 in lib/aws_codegen/types.ex

View workflow job for this annotation

GitHub Actions / Elixir 1.13.4 / OTP 25.0.4

unused alias Name

Check warning on line 3 in lib/aws_codegen/types.ex

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 1.13.4, 25.0.4, 3.20.0)

unused alias Name

Check warning on line 3 in lib/aws_codegen/types.ex

View workflow job for this annotation

GitHub Actions / Elixir 1.13.4 / OTP 25.0.4

unused alias Name

# Unfortunately, gotta patch over auto-defining types that already exist in Elixir

def shape_to_type(:elixir, "String", _) do
"String.t()"
end
def shape_to_type(:erlang, "String", _) do
"string()"
end

def shape_to_type(:elixir, "string", _) do
"String.t()"
end
def shape_to_type(:erlang, "string", _) do
"string()"
end

def shape_to_type(:elixir, "Identifier", _) do
"String.t()"
end
def shape_to_type(:erlang, "Identifier", _) do
"string()"
end

def shape_to_type(:elixir, "identifier", _) do
"String.t()"
end
def shape_to_type(:erlang, "identifier", _) do
"string()"
end

def shape_to_type(:elixir, "XmlString" <> _rest, _) do
"String.t()"
end
def shape_to_type(:erlang, "XmlString" <> _rest, _) do
"string"
end

def shape_to_type(:elixir, "NullablePositiveInteger", _) do
"nil | non_neg_integer()"
end
def shape_to_type(:erlang, "NullablePositiveInteger", _) do
"undefined | non_neg_integer()"
end

def shape_to_type(_, %Shape{type: type}, _module_name) when type in ["float", "double", "long"] do
"float()"
end

def shape_to_type(_, %Shape{type: "timestamp"}, _module_name) do
"non_neg_integer()"
end

def shape_to_type(_, %Shape{type: "map"}, _module_name) do
"map()"
end

def shape_to_type(_, %Shape{type: "blob"}, _module_name) do
"binary()"
end

def shape_to_type(:elixir, %Shape{type: "string"}, _module_name) do
"String.t()"
end
def shape_to_type(:erlang, %Shape{type: "string"}, _module_name) do
"string()"
end

def shape_to_type(_, %Shape{type: "integer"}, _module_name) do
"integer()"
end

def shape_to_type(_, %Shape{type: "boolean"}, _module_name) do
"boolean()"
end

def shape_to_type(_, %Shape{type: "enum"}, _module_name) do
"list(any())"
end

def shape_to_type(_, %Shape{type: "union"}, _module_name) do
"list()"
end

def shape_to_type(_, %Shape{type: "document"}, _module_name) do
"any()"
end

def shape_to_type(context, shape_name, module_name, all_shapes) do
case shape_name do
"smithy.api#String" ->
"[#{shape_to_type(context.language, %Shape{type: "string"}, module_name)}]"

"smithy.api#Integer" ->
"[#{shape_to_type(context.language, %Shape{type: "integer"}, module_name)}]"

"smithy.api#Timestamp" ->
"[#{shape_to_type(context.language, %Shape{type: "timestamp"}, module_name)}]"

"smithy.api#PrimitiveLong" ->
"[#{shape_to_type(context.language, %Shape{type: "long"}, module_name)}]"

"smithy.api#Long" ->
"[#{shape_to_type(context.language, %Shape{type: "long"}, module_name)}]"

"smithy.api#Boolean" ->
"[#{shape_to_type(context.language, %Shape{type: "boolean"}, module_name)}]"

"smithy.api#PrimitiveBoolean" ->
"[#{shape_to_type(context.language, %Shape{type: "boolean"}, module_name)}]"

"smithy.api#Double" ->
"[#{shape_to_type(context.language, %Shape{type: "double"}, module_name)}]"

"smithy.api#Document" ->
"[#{shape_to_type(context.language, %Shape{type: "document"}, module_name)}]"

"smithy.api#Unit" ->
"[]"

_ ->
case all_shapes[shape_name] do
%Shape{type: "structure"} ->
type = "#{AWS.CodeGen.Name.to_snake_case(String.replace(shape_name, ~r/com\.amazonaws\.[^#]+#/, ""))}"
if AWS.CodeGen.Util.reserved_type(type) do
"#{String.downcase(String.replace(context.module_name, ["aws_", "AWS."], ""))}_#{type}()"
else
"#{type}()"
end

%Shape{type: "list", member: member} ->
type = "#{shape_to_type(context, member["target"], module_name, all_shapes)}"
if AWS.CodeGen.Util.reserved_type(type) do
"list(#{String.downcase(String.replace(context.module_name, ["aws_", "AWS."], ""))}_#{type}())"
else
"list(#{type}())"
end

nil ->
raise "Tried to reference an undefined shape for #{shape_name}"

shape ->
shape_to_type(context.language, shape, module_name)
end
end
end
end
132 changes: 132 additions & 0 deletions lib/aws_codegen/util.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,136 @@ defmodule AWS.CodeGen.Util do
service["traits"]["aws.api#service"]["sdkId"]
end

def input_keys(action, context) do
shapes = context.shapes
input_shape = action.input["target"]
maybe_shape = Enum.filter(shapes, fn {name, _shape} -> input_shape == name end)
case maybe_shape do
[] ->
[]
[{_name, shape}] ->
Enum.reduce(shape.members,
[],
fn {name, %{"traits" => traits}}, acc ->
if Map.has_key?(traits, "smithy.api#required") do
[name <> " Required: true" | acc]
else
[name <> " Required: false" | acc]
end
{name, _shape}, acc ->
[name <> " Required: false" | acc]
end)
|> Enum.reverse()
end
end

def types(context) do
Enum.reduce(context.shapes,
Map.new(),
fn {_name, shape}, acc ->
if shape.type == "structure" and not is_nil(shape.members) do
type = AWS.CodeGen.Name.to_snake_case(String.replace(shape.name, ~r/com\.amazonaws\.[^#]+#/, ""))
types = Enum.reduce(shape.members,
Map.new(),
fn {_name, shape_member}, a ->
target = shape_member["target"]
shape_member_type = AWS.CodeGen.Types.shape_to_type(context, target, context.module_name, context.shapes)
Map.put(a, is_required(context.language, shape.is_input, shape_member, target), shape_member_type)
end)
if reserved_type(type) do
Map.put(acc, "#{String.downcase(String.replace(context.module_name, "AWS.", ""))}_#{type}", types)
else
Map.put(acc, type, types)
end
else
acc
end
end)
end

defp is_required(:elixir, is_input, shape, target) do
trimmed_name = String.replace(target, ~r/com\.amazonaws\.[^#]+#/, "")
if is_input do
if Map.has_key?(shape, "traits") do
if Map.has_key?(shape["traits"], "smithy.api#required") do
"required(\"#{trimmed_name}\")"
else
"optional(\"#{trimmed_name}\")"
end
else
"optional(\"#{trimmed_name}\")"
end
else
"\"#{trimmed_name}\""
end
end
defp is_required(:erlang, _is_input, _shape, target) do
# There is no optional/1 | required/1 type of thing in Erlang.
# Instead we'd use := | => but let's deal with that problem later...
trimmed_name = String.replace(target, ~r/com\.amazonaws\.[^#]+#/, "")
"\"#{trimmed_name}\""
end

def function_argument_type(:elixir, action) do
case Map.get(action.input, "target") do
"smithy.api#Unit" -> "%{}"
type ->
"#{AWS.CodeGen.Name.to_snake_case(String.replace(type, ~r/com\.amazonaws\.[^#]+#/, ""))}()"
end
end
def function_argument_type(:erlang, action) do
case Map.get(action.input, "target") do
"smithy.api#Unit" -> "\#{}"
type ->
"#{AWS.CodeGen.Name.to_snake_case(String.replace(type, ~r/com\.amazonaws\.[^#]+#/, ""))}()"
end
end

def return_type(action) do
case Map.get(action.output, "target") do
"smithy.api#Unit" -> "[]"
type ->
normal = "{:ok, #{AWS.CodeGen.Name.to_snake_case(String.replace(type, ~r/com\.amazonaws\.[^#]+#/, ""))}(), any()}"
errors =
if is_list(action.errors) do
Enum.map(action.errors,
fn %{"target" => error_type} ->
"{:error, #{AWS.CodeGen.Name.to_snake_case(String.replace(error_type, ~r/com\.amazonaws\.[^#]+#/, ""))}()}"
_ ->
""
end )
else
[]
end
Enum.join([normal, "{:error, {:unexpected_response, any()}}" | errors], " | ")
end
end
def return_type(:erlang, action) do
case Map.get(action.output, "target") do
"smithy.api#Unit" -> "[]"
type ->
normal = "{ok, #{AWS.CodeGen.Name.to_snake_case(String.replace(type, ~r/com\.amazonaws\.[^#]+#/, ""))}(), tuple()}"
errors =
if is_list(action.errors) do
Enum.map(action.errors,
fn %{"target" => error_type} ->
"{error, #{AWS.CodeGen.Name.to_snake_case(String.replace(error_type, ~r/com\.amazonaws\.[^#]+#/, ""))}(), tuple()}"
_ ->
""
end )
else
[]
end
Enum.join([normal, "{error, any()}" | errors], " |\n ")
end
end

def reserved_type(type) do
if type == "node" || type == "term" do
true
else
false
end
end

end
Loading
Loading