From 5b08f03582358636caa9789e2125c65fc0760607 Mon Sep 17 00:00:00 2001 From: Onno Vos Date: Thu, 15 Feb 2024 21:29:52 +0100 Subject: [PATCH] Issue #103: Migrate from aws-sdk-go to aws-sdk-go-v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Goal here has been to minimize the diff in aws-elixir and aws-erlang to a bare minimum. Some minor diffs were unavoidable and deemed acceptable * Fix SuccessStatusCode to always be 200 if not provided by AWS in the API * Before SuccessStatusCode was set to undefined/nil. This was unhandled and would've caused errors. * Fix aws-erlang/issues/148 to allow send_body_as_binary & receive_body_as_binary to be sent in as options * The default remains the same, but certain APIs may require a change of the default depending on the in- or output * Drop abbreviation from metadata. It is no longer available in aws-sdk-go-v2 and unused in aws-elixir * Slight formatting changes on aws-erlang docs as the new docs contain a lot more newlines and hence cause more noise * Uri Paths may have changed. This should not impact the clients. It was likely broken in the aws-sdk-go * QueryMap may have changed but should not impact the client. It was likely broken in the aws-sdk-go * Docs have changed (massively) since almost all documentation has been updated Co-authored-by: Amin Arria Co-authored-by: Gustavo Mora González Co-authored-by: Philip Sampaio --- .github/workflows/gen_elixir.yml | 10 +- .github/workflows/gen_erlang.yml | 61 +- lib/aws_codegen.ex | 11 +- lib/aws_codegen/docstring.ex | 8 +- lib/aws_codegen/name.ex | 1 + lib/aws_codegen/post_service.ex | 77 +- lib/aws_codegen/rest_service.ex | 132 ++- lib/aws_codegen/shapes.ex | 20 +- lib/aws_codegen/spec.ex | 78 +- lib/aws_codegen/util.ex | 33 + mix.exs | 6 +- mix.lock | 16 +- priv/post.ex.eex | 1 - priv/rest.erl.eex | 29 +- priv/rest.ex.eex | 1 - test/aws_codegen/docstring_test.exs | 4 +- test/aws_codegen/rest_service_test.exs | 98 +- test/aws_codegen/shapes_test.exs | 32 +- test/aws_codegen/spec_test.exs | 151 +-- test/aws_codegen_test.exs | 301 ++--- test/fixtures/apis_specs/cloudtrail-data.json | 1005 +++++++++++++++++ .../mobileanalytics-2014-06-05-api-2.json | 119 -- .../mobileanalytics-2014-06-05-docs-2.json | 98 -- 23 files changed, 1575 insertions(+), 717 deletions(-) create mode 100644 lib/aws_codegen/util.ex create mode 100644 test/fixtures/apis_specs/cloudtrail-data.json delete mode 100644 test/fixtures/apis_specs/mobileanalytics-2014-06-05-api-2.json delete mode 100644 test/fixtures/apis_specs/mobileanalytics-2014-06-05-docs-2.json diff --git a/.github/workflows/gen_elixir.yml b/.github/workflows/gen_elixir.yml index fc2724c..ebb392f 100644 --- a/.github/workflows/gen_elixir.yml +++ b/.github/workflows/gen_elixir.yml @@ -3,7 +3,7 @@ name: Generate aws-elixir on: push: branches: - - master + - master pull_request: workflow_dispatch: @@ -29,11 +29,11 @@ jobs: sudo apt-get update DEBIAN_FRONTEND=noninteractive sudo apt-get -y --no-install-recommends install build-essential cmake mix deps.get - - name: Checkout aws/aws-sdk-go + - name: Checkout aws/aws-sdk-go-v2 uses: actions/checkout@v2 with: - repository: aws/aws-sdk-go - path: aws/aws-sdk-go/ + repository: aws/aws-sdk-go-v2 + path: aws/aws-sdk-go-v2/ - name: Checkout aws-elixir uses: actions/checkout@v2 with: @@ -41,7 +41,7 @@ jobs: path: aws-beam/aws-elixir - name: Generate aws-elixir env: - SPEC_PATH: aws/aws-sdk-go/models/apis + SPEC_PATH: aws/aws-sdk-go-v2/codegen/sdk-codegen/aws-models TEMPLATE_PATH: priv ELIXIR_OUTPUT_PATH: aws-beam/aws-elixir/lib/aws/generated run: | diff --git a/.github/workflows/gen_erlang.yml b/.github/workflows/gen_erlang.yml index 53971ef..4b438d4 100644 --- a/.github/workflows/gen_erlang.yml +++ b/.github/workflows/gen_erlang.yml @@ -9,45 +9,60 @@ on: jobs: build: - runs-on: ubuntu-latest - name: Elixir ${{ matrix.elixir }} / OTP ${{ matrix.erlang }} - strategy: - fail-fast: false matrix: + platform: [ubuntu-latest] elixir: ["1.13.4"] erlang: ["25.0.4"] rebar3: ['3.20.0'] - + runs-on: ${{ matrix.platform }} + services: + ddb: + image: amazon/dynamodb-local:1.21.0 + ports: + - 8000:8000 + s3mock: + image: adobe/s3mock:2.11.0 + ports: + - 9090:9090 steps: - - uses: actions/checkout@v2 - - uses: erlef/setup-beam@v1 + - uses: actions/checkout@v4 + - name: Set up Erlang / Elixir / Rebar3 + uses: erlef/setup-beam@v1 with: - otp-version: "${{ matrix.erlang }}" - elixir-version: "${{ matrix.elixir }}" - rebar3-version: "${{ matrix.rebar3 }}" - - name: Install Dependencies - run: | - sudo apt-get update - DEBIAN_FRONTEND=noninteractive sudo apt-get -y --no-install-recommends install build-essential cmake - mix deps.get - - name: Checkout aws/aws-sdk-go - uses: actions/checkout@v2 + otp-version: ${{matrix.erlang}} + elixir-version: ${{matrix.elixir}} + rebar3-version: ${{matrix.rebar3}} + - name: Checkout aws/aws-sdk-go-v2 + uses: actions/checkout@v4 with: - repository: aws/aws-sdk-go - path: aws/aws-sdk-go/ + repository: aws/aws-sdk-go-v2 + path: aws/aws-sdk-go-v2/ - name: Checkout aws-erlang - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: repository: aws-beam/aws-erlang path: aws-beam/aws-erlang + - name: Get mix dependencies + run: mix deps.get - name: Generate aws-erlang env: - SPEC_PATH: aws/aws-sdk-go/models/apis + SPEC_PATH: aws/aws-sdk-go-v2/codegen/sdk-codegen/aws-models TEMPLATE_PATH: priv ERLANG_OUTPUT_PATH: aws-beam/aws-erlang/src run: mix run generate.exs erlang $SPEC_PATH $TEMPLATE_PATH $ERLANG_OUTPUT_PATH - - name: Run Erlang tests + - name: Compile + run: rebar3 compile + working-directory: aws-beam/aws-erlang + - name: Run EUnit Tests run: rebar3 eunit working-directory: aws-beam/aws-erlang - + - name: Run Common Tests + run: rebar3 ct + working-directory: aws-beam/aws-erlang + env: + DYNAMODB_HOST: localhost + S3MOCK_HOST: localhost + - name: Produce Documentation + run: rebar3 ex_doc + working-directory: aws-beam/aws-erlang diff --git a/lib/aws_codegen.ex b/lib/aws_codegen.ex index cedf8db..a614383 100644 --- a/lib/aws_codegen.ex +++ b/lib/aws_codegen.ex @@ -18,8 +18,7 @@ defmodule AWS.CodeGen do defmodule Service do @moduledoc false - defstruct abbreviation: nil, - actions: [], + defstruct actions: [], api_version: nil, credential_scope: nil, content_type: nil, @@ -141,16 +140,16 @@ defmodule AWS.CodeGen do @spec api_specs(binary(), :elixir | :erlang) :: [Spec.t()] defp api_specs(base_path, language) do - search_path = Path.join(base_path, "*/*") + search_path = Path.join(base_path, "*") IO.puts("Parsing specs in #{search_path}") - for path <- Path.wildcard(search_path) do - Spec.parse(path, language) + for file <- Path.wildcard(search_path) do + Spec.parse(file, language) end end defp get_endpoints_spec(base_path) do - Path.join([base_path, "..", "endpoints", "endpoints.json"]) + Path.join([base_path, "../../", "smithy-aws-go-codegen/src/main/resources/software/amazon/smithy/aws/go/codegen", "endpoints.json"]) |> Spec.parse_json() |> get_in(["partitions"]) |> Enum.filter(fn x -> x["partition"] == "aws" end) diff --git a/lib/aws_codegen/docstring.ex b/lib/aws_codegen/docstring.ex index 6486237..f2f37b8 100644 --- a/lib/aws_codegen/docstring.ex +++ b/lib/aws_codegen/docstring.ex @@ -272,7 +272,7 @@ defmodule AWS.CodeGen.Docstring do text = Floki.text(children) if String.contains?(text, "\n") do - "\n```\n#{String.trim_leading(text, "\n")}'''#{@two_break_lines}" + "\n```\n#{String.replace(text, "\n", "")}'''" else ## ex_doc blows up on these sorts of things as it sees them as a reference to a function. ## Just ignore them as they refer to aws-sdk-go based implementations and we don't really care about that @@ -297,9 +297,9 @@ defmodule AWS.CodeGen.Docstring do "`#{Floki.text(children)}'" end - other -> - other - end) + {_, _attrs, children} -> + "#{Floki.text(children)}" + end) |> Floki.raw_html(encode: true) end diff --git a/lib/aws_codegen/name.ex b/lib/aws_codegen/name.ex index e3e0c9b..18328ab 100644 --- a/lib/aws_codegen/name.ex +++ b/lib/aws_codegen/name.ex @@ -12,6 +12,7 @@ defmodule AWS.CodeGen.Name do """ def to_snake_case(text) do text + |> String.replace(~r/com\.amazonaws\.[^#]+#/, "") |> String.replace(@names_to_capitalize, &String.capitalize(&1)) |> String.to_charlist() |> Enum.map_join(&char_to_snake_case/1) diff --git a/lib/aws_codegen/post_service.ex b/lib/aws_codegen/post_service.ex index 076f094..c32e4bd 100644 --- a/lib/aws_codegen/post_service.ex +++ b/lib/aws_codegen/post_service.ex @@ -53,36 +53,34 @@ defmodule AWS.CodeGen.PostService do `:elixir` or `:erlang`. """ def load_context(language, %AWS.CodeGen.Spec{} = spec, endpoints_spec) do - metadata = spec.api["metadata"] - actions = collect_actions(language, spec.api, spec.doc) - - endpoint_prefix = metadata["endpointPrefix"] + %Service{actions: []} + service = spec.api["shapes"][spec.shape_name] + traits = service["traits"] + actions = collect_actions(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) - credential_scope = if is_global do endpoint_info["endpoints"]["aws-global"]["credentialScope"]["region"] end - - json_version = metadata["jsonVersion"] - protocol = metadata["protocol"] + json_version = AWS.CodeGen.Util.get_json_version(service) + protocol = spec.protocol |> to_string() content_type = @configuration[protocol][:content_type] content_type = content_type <> if protocol == "json", do: json_version, else: "" - signing_name = - case metadata["signingName"] do - nil -> endpoint_prefix - sn -> sn + if String.starts_with?(endpoint_prefix, "api.") do + String.replace(endpoint_prefix, "api.", "") + else + endpoint_prefix end %Service{ - abbreviation: metadata["serviceAbbreviation"], actions: actions, - api_version: metadata["apiVersion"], + api_version: service["version"], credential_scope: credential_scope, content_type: content_type, - docstring: Docstring.format(language, spec.doc["service"]), + docstring: Docstring.format(language, AWS.CodeGen.Util.service_docs(service)), decode: Map.fetch!(@configuration[protocol][language], :decode), encode: Map.fetch!(@configuration[protocol][language], :encode), endpoint_prefix: endpoint_prefix, @@ -90,28 +88,59 @@ defmodule AWS.CodeGen.PostService do json_version: json_version, language: language, module_name: spec.module_name, - protocol: protocol, + protocol: protocol |> to_string() |> String.replace("_", "-"), signing_name: signing_name, - signature_version: metadata["signatureVersion"], - service_id: metadata["serviceId"], - target_prefix: metadata["targetPrefix"] + signature_version: AWS.CodeGen.Util.get_signature_version(service), + service_id: AWS.CodeGen.Util.get_service_id(service), + target_prefix: target_prefix(spec.api) } end - defp collect_actions(language, api_spec, doc_spec) do - Enum.map(api_spec["operations"], fn {operation, metadata} -> + defp target_prefix(api) do + api["shapes"] + |> Enum.find(fn {_key, value} -> match?(%{"type" => "service"}, value) end) + |> case do + {key, _} -> + String.replace(key, ~r/.*#/, "") + nil -> nil + end + end + + defp collect_actions(language, api_spec) do + shapes = api_spec["shapes"] + + operations = + Enum.reduce(shapes, [], fn {_, shape}, acc -> + case shape["type"] do + "service" -> + + [acc | List.wrap(shape["operations"])] + "resource" -> + [shape["operations"], shape["collectionOperations"], shape["create"], shape["put"], shape["read"], shape["update"], shape["delete"], shape["list"]] + |> Enum.reject(&is_nil/1) + |> Kernel.++(acc) + _ -> + acc + end + end) + |> List.flatten() + |> Enum.map(fn %{"target" => target} -> target end) + + Enum.map(operations, fn operation -> + operation_spec = shapes[operation] %Action{ arity: 3, docstring: Docstring.format( language, - doc_spec["operations"][operation] + operation_spec["traits"]["smithy.api#documentation"] ), function_name: AWS.CodeGen.Name.to_snake_case(operation), - host_prefix: get_in(metadata, ["endpoint", "hostPrefix"]), - name: operation + host_prefix: operation_spec["traits"]["smithy.api#endpoint"]["hostPrefix"], + name: String.replace(operation, ~r/com\.amazonaws\.[^#]+#/, "") } end) |> Enum.sort(fn a, b -> a.function_name < b.function_name end) + |> Enum.uniq() end end diff --git a/lib/aws_codegen/rest_service.ex b/lib/aws_codegen/rest_service.ex index 9edb87b..07cb7aa 100644 --- a/lib/aws_codegen/rest_service.ex +++ b/lib/aws_codegen/rest_service.ex @@ -82,7 +82,7 @@ defmodule AWS.CodeGen.RestService do end @configuration %{ - "rest-xml" => %{ + :rest_xml => %{ content_type: "text/xml", elixir: %{ decode: "xml", @@ -93,7 +93,7 @@ defmodule AWS.CodeGen.RestService do encode: "aws_util:encode_xml(Input)" } }, - "rest-json" => %{ + :rest_json => %{ content_type: "application/x-amz-json-1.1", elixir: %{ decode: "json", @@ -113,10 +113,11 @@ defmodule AWS.CodeGen.RestService do `:elixir` or `:erlang`. """ def load_context(language, %AWS.CodeGen.Spec{} = spec, endpoints_spec) do - metadata = spec.api["metadata"] - actions = collect_actions(language, spec.api, spec.doc) - protocol = metadata["protocol"] - endpoint_prefix = metadata["endpointPrefix"] + service = spec.api["shapes"][spec.shape_name] + traits = service["traits"] + actions = collect_actions(language, spec.api) + protocol = spec.protocol + endpoint_prefix = traits["aws.api#service"]["endpointPrefix"] || traits["aws.api#service"]["arnNamespace"] ##TODO: for some reason this field is not always present and docs are not clear on what to do endpoint_info = endpoints_spec["services"][endpoint_prefix] is_global = not is_nil(endpoint_info) and not Map.get(endpoint_info, "isRegionalized", true) @@ -125,26 +126,26 @@ defmodule AWS.CodeGen.RestService do endpoint_info["endpoints"]["aws-global"]["credentialScope"]["region"] end - signing_name = metadata["signingName"] || endpoint_prefix + signing_name = traits["aws.auth#sigv4"]["name"] || endpoint_prefix %Service{ actions: actions, - api_version: metadata["apiVersion"], - docstring: Docstring.format(language, spec.doc["service"]), + api_version: service["version"], + docstring: Docstring.format(language, AWS.CodeGen.Util.service_docs(service)), credential_scope: credential_scope, content_type: @configuration[protocol][:content_type], decode: Map.fetch!(@configuration[protocol][language], :decode), encode: Map.fetch!(@configuration[protocol][language], :encode), endpoint_prefix: endpoint_prefix, is_global: is_global, - json_version: metadata["jsonVersion"], + json_version: AWS.CodeGen.Util.get_json_version(service), language: language, module_name: spec.module_name, - protocol: metadata["protocol"], + protocol: protocol |> to_string() |> String.replace("_", "-"), signing_name: signing_name, - signature_version: metadata["signatureVersion"], - service_id: metadata["serviceId"], - target_prefix: metadata["targetPrefix"] + signature_version: AWS.CodeGen.Util.get_signature_version(service), + service_id: AWS.CodeGen.Util.get_service_id(service), + target_prefix: nil, ##TODO: metadata["targetPrefix"] } end @@ -162,7 +163,6 @@ defmodule AWS.CodeGen.RestService do """ def function_parameters(action, required_only \\ false) do language = action.language - Enum.join([ join_parameters(action.url_parameters, language) | case action.method do @@ -203,31 +203,45 @@ defmodule AWS.CodeGen.RestService do ) end - defp collect_actions(language, api_spec, doc_spec) do + defp collect_actions(language, api_spec) do shapes = api_spec["shapes"] - Enum.map(api_spec["operations"], fn {operation, _metadata} -> - operation_spec = api_spec["operations"][operation] + operations = + Enum.reduce(shapes, [], fn {_, shape}, acc -> + case shape["type"] do + "service" -> + List.wrap(shape["operations"]) ++ acc + "resource" -> + [shape["operations"], shape["collectionOperations"], shape["create"], shape["put"], shape["read"], shape["update"], shape["delete"], shape["list"]] + |> Enum.reject(&is_nil/1) + |> Kernel.++(acc) + _ -> + acc + end + end) + |> List.flatten() + |> Enum.map(fn %{"target" => target} -> target end) + + Enum.map(operations, fn operation -> + operation_spec = shapes[operation] + request_uri = operation_spec["traits"]["smithy.api#http"]["uri"] url_parameters = collect_url_parameters(language, api_spec, operation) query_parameters = collect_query_parameters(language, api_spec, operation) + function_name = AWS.CodeGen.Name.to_snake_case(operation) request_header_parameters = collect_request_header_parameters(language, api_spec, operation) - request_headers_parameters = - collect_request_headers_parameters(language, api_spec, operation) - is_required = fn param -> param.required end required_query_parameters = Enum.filter(query_parameters, is_required) required_request_header_parameters = Enum.filter(request_header_parameters, is_required) - method = operation_spec["http"]["method"] + method = operation_spec["traits"]["smithy.api#http"]["method"] len_for_method = case method do "GET" -> case language do :elixir -> - 2 + length(request_header_parameters) + length(request_headers_parameters) + - length(query_parameters) + 2 + length(request_header_parameters) + length(query_parameters) :erlang -> 4 + length(required_request_header_parameters) + length(required_query_parameters) @@ -239,72 +253,76 @@ defmodule AWS.CodeGen.RestService do input_shape = Shapes.get_input_shape(operation_spec) output_shape = Shapes.get_output_shape(operation_spec) - %Action{ arity: length(url_parameters) + len_for_method, docstring: Docstring.format( language, - doc_spec["operations"][operation] + operation_spec["traits"]["smithy.api#documentation"] ), method: method, - request_uri: operation_spec["http"]["requestUri"], - success_status_code: operation_spec["http"]["responseCode"], - function_name: AWS.CodeGen.Name.to_snake_case(operation), + request_uri: request_uri, + success_status_code: success_status_code(operation_spec), + function_name: function_name, name: operation, url_parameters: url_parameters, query_parameters: query_parameters, required_query_parameters: required_query_parameters, request_header_parameters: request_header_parameters, - request_headers_parameters: request_headers_parameters, required_request_header_parameters: required_request_header_parameters, response_header_parameters: collect_response_header_parameters(language, api_spec, operation), send_body_as_binary?: Shapes.body_as_binary?(shapes, input_shape), receive_body_as_binary?: Shapes.body_as_binary?(shapes, output_shape), - host_prefix: get_in(operation_spec, ["endpoint", "hostPrefix"]), + host_prefix: operation_spec["traits"]["smithy.api#endpoint"]["hostPrefix"], language: language } end) |> Enum.sort(fn a, b -> a.function_name < b.function_name end) + |> Enum.uniq() + end + + defp success_status_code(operation_spec) do + if Map.has_key?(operation_spec["traits"]["smithy.api#http"], "code") do + operation_spec["traits"]["smithy.api#http"]["code"] + else + 200 + end end defp collect_url_parameters(language, api_spec, operation) do - collect_parameters(language, api_spec, operation, "input", "uri") + url_params = collect_parameters(language, api_spec, operation, "input", "smithy.api#httpLabel") + url_params end defp collect_query_parameters(language, api_spec, operation) do - collect_parameters(language, api_spec, operation, "input", "querystring") + query_params = collect_parameters(language, api_spec, operation, "input", "smithy.api#httpQueryParams") + params = collect_parameters(language, api_spec, operation, "input", "smithy.api#httpQuery") + query_params ++ params end defp collect_request_header_parameters(language, api_spec, operation) do - collect_parameters(language, api_spec, operation, "input", "header") - end - - defp collect_request_headers_parameters(language, api_spec, operation) do - collect_parameters(language, api_spec, operation, "input", "headers") + collect_parameters(language, api_spec, operation, "input", "smithy.api#httpHeader") end defp collect_response_header_parameters(language, api_spec, operation) do - collect_parameters(language, api_spec, operation, "output", "header") + collect_parameters(language, api_spec, operation, "output", "smithy.api#httpHeader") end defp collect_parameters(language, api_spec, operation, data_type, param_type) do - shape_name = api_spec["operations"][operation][data_type]["shape"] - + shape_name = api_spec["shapes"][operation][data_type]["target"] if shape_name do case api_spec["shapes"][shape_name] do nil -> [] - shape -> - required_members = Access.get(shape, "required", []) - + required_members = + for {name, %{"traits" => traits}} <- shape["members"], Map.has_key?(traits, "smithy.api#required"), do: name shape["members"] |> Enum.filter(filter_fn(param_type)) - |> Enum.map(fn {name, _} = x -> + |> Enum.map(fn {name, x} -> required = Enum.member?(required_members, name) - build_parameter(language, x, required) + build_parameter(language, {name, x["traits"][param_type]}, required) end) end else @@ -314,13 +332,26 @@ defmodule AWS.CodeGen.RestService do defp filter_fn(location) do fn {_name, member_spec} -> - case member_spec["location"] do - ^location -> true - _ -> false + case member_spec["traits"][location] do + nil -> false + _ -> true end end end + defp build_parameter(language, {name, %{}}, required) do + %Parameter{ + code_name: + if language == :elixir do + AWS.CodeGen.Name.to_snake_case(name) + else + AWS.CodeGen.Name.upcase_first(name) + end, + name: name, + location_name: name, + required: required + } + end defp build_parameter(language, {name, data}, required) do %Parameter{ code_name: @@ -330,8 +361,9 @@ defmodule AWS.CodeGen.RestService do AWS.CodeGen.Name.upcase_first(name) end, name: name, - location_name: data["locationName"], + location_name: data, required: required } end + end diff --git a/lib/aws_codegen/shapes.ex b/lib/aws_codegen/shapes.ex index 00af090..0559719 100644 --- a/lib/aws_codegen/shapes.ex +++ b/lib/aws_codegen/shapes.ex @@ -2,22 +2,24 @@ defmodule AWS.CodeGen.Shapes do @moduledoc false def get_input_shape(operation_spec) do - get_in(operation_spec, ["input", "shape"]) + get_in(operation_spec, ["input", "target"]) end def get_output_shape(operation_spec) do - get_in(operation_spec, ["output", "shape"]) + get_in(operation_spec, ["output", "target"]) end def body_as_binary?(shapes, shape) do - with %{"shape" => body_shape} = inner_spec <- - get_in(shapes, [shape, "members", "Body"]), - true <- !Map.has_key?(inner_spec, "location"), - %{"type" => type} <- Map.get(shapes, body_shape) do - type in ~w(blob string) - else - _ -> + ## TODO: Should we validate or search for trait `smithy.api#httpPayload` rather than + ## trust that the member is always named `Body`? + inner_spec = get_in(shapes, [shape, "members", "Body"]) + case inner_spec != nil && Map.has_key?(inner_spec, "target") do + false -> false + true -> + ## TODO: we should extract the type from the actual shape `type` rather than infer it from the naming + String.contains?(String.downcase(inner_spec["target"]), "blob") || String.contains?(String.downcase(inner_spec["target"]), "string") || false end end + end diff --git a/lib/aws_codegen/spec.ex b/lib/aws_codegen/spec.ex index 33d271c..9c1590b 100644 --- a/lib/aws_codegen/spec.ex +++ b/lib/aws_codegen/spec.ex @@ -6,51 +6,88 @@ defmodule AWS.CodeGen.Spec do @module_acronyms ~w(SMS WAF API SES HSM FSx IoT MTurk QLDB DB RDS A2I XRay EC2 SSO IVS MQ SDB OIDC ACMPCA SSM) @type t :: %__MODULE__{ - protocol: atom(), + protocol: binary(), module_name: binary(), filename: binary(), api: map(), - doc: map() | nil + language: binary(), + shape_name: binary() } defstruct protocol: nil, module_name: nil, filename: nil, api: nil, - doc: nil - - def parse(path, language, opts \\ []) do - api_filename = Keyword.get(opts, :api_filename, "api-2.json") - doc_filename = Keyword.get(opts, :doc_filename, "docs-2.json") - - api = path |> Path.join(api_filename) |> parse_json() - - protocol = - api["metadata"]["protocol"] - |> String.replace("-", "_") - |> String.to_atom() - - module_name = module_name(api, language) + language: nil, + shape_name: nil + + def parse(api_filename, language) do + api_name = api_filename + |> Path.basename() + |> String.replace(".json", "") + |> String.replace("-", "") + api = parse_json(api_filename) + service_name = + api + |> find_service() + |> String.replace("com.amazonaws.#{api_name}#", "") + traits = api["shapes"]["com.amazonaws." <> api_name <> "#" <> service_name]["traits"] + + protocol = Enum.find_value(traits, fn {k, _v} -> + case String.split(k, "#") do + ["aws.protocols", protocol] -> protocol + _ -> nil + end + end) + |> String.replace("restJson1", "rest_json") + |> String.replace("awsJson1_0", "json") + |> String.replace("awsJson1_1", "json") + |> String.replace("awsQuery", "query") + |> String.replace("restXml", "rest_xml") + |> String.replace("ec2Query", "ec2") + |> then(fn value-> + if value in ~w(rest_json json query rest_xml ec2) do + value + else + raise "the protocol #{value} is not valid" + end + end) + |> String.to_atom() + module_name = module_name(traits, language) filename = filename(module_name, language) - %AWS.CodeGen.Spec{ protocol: protocol, module_name: module_name, filename: filename, api: api, - doc: path |> Path.join(doc_filename) |> parse_json + language: language, + shape_name: "com.amazonaws." <> api_name <> "#" <> service_name } end + def find_service(api) do + {service, _} = Enum.find(api["shapes"], fn {service, value} -> + if match?(%{"type" => "service"}, value), do: service + end) + service + end + + def lowercase_keys(map) when is_map(map) do + Map.new(map, fn + {k, v} when is_map(v) -> {String.downcase(k), lowercase_keys(v)} + {k, v} -> {String.downcase(k), v} + end) + end + def parse_json(path) do if File.exists?(path) do path |> File.read!() |> Poison.Parser.parse!(%{}) end end - defp module_name(api, language) do + defp module_name(traits, language) do service_name = - (api["metadata"]["serviceId"] || api["metadata"]["endpointPrefix"]) + traits["aws.api#service"]["sdkId"] |> String.replace("-sync", "Sync") |> String.replace("Route 53", "Route53") |> String.replace(~r/ Service$/, "") @@ -75,6 +112,7 @@ defmodule AWS.CodeGen.Spec do service_name |> String.replace(" ", "_") |> String.replace(".", "_") + |> String.replace("-", "_") |> String.downcase() |> AWS.CodeGen.Name.to_snake_case() diff --git a/lib/aws_codegen/util.ex b/lib/aws_codegen/util.ex new file mode 100644 index 0000000..9c416fa --- /dev/null +++ b/lib/aws_codegen/util.ex @@ -0,0 +1,33 @@ +defmodule AWS.CodeGen.Util do + + def service_docs(service) do + with "

" <- service["traits"]["smithy.api#documentation"], do: "" + end + + def get_json_version(service) do + traits = service["traits"] + ["aws.protocols#" <> protocol | _] = Enum.filter(Map.keys(traits), &String.starts_with?(&1, "aws.protocols#")) + case protocol do + "restJson1" -> "1.1" ## TODO: according to the docs this should result in application/json but our current code will make it application/x-amz-json-1.1 + "awsJson1_0" -> "1.0" + "awsJson1_1" -> "1.1" + "awsQuery" -> nil + "restXml" -> nil + "ec2Query" -> nil + end + end + + def get_signature_version(service) do + traits = service["traits"] + signature = Enum.filter(Map.keys(traits), &String.starts_with?(&1, "aws.auth#")) + case signature do + ["aws.auth#sig" <> version] -> version + [] -> nil + end + end + + def get_service_id(service) do + service["traits"]["aws.api#service"]["sdkId"] + end + +end diff --git a/mix.exs b/mix.exs index af620ac..60d3721 100644 --- a/mix.exs +++ b/mix.exs @@ -24,9 +24,9 @@ defmodule AWS.CodeGen.Mixfile do defp deps do [ {:earmark, "~> 1.4", only: :dev}, - {:ex_doc, "~> 0.23.0", only: :dev}, - {:floki, "~> 0.29"}, - {:fast_html, "~> 2.0"}, + {:ex_doc, "~> 0.31.1", only: :dev}, + {:floki, "~> 0.35"}, + {:fast_html, "~> 2.3"}, {:poison, "~> 4.0 or ~> 5.0"} ] end diff --git a/mix.lock b/mix.lock index 2510112..75a2f5d 100644 --- a/mix.lock +++ b/mix.lock @@ -1,15 +1,15 @@ %{ "earmark": {:hex, :earmark, "1.4.27", "b413b0379043df51475a9b22ce344e8a58a117516c735b8871e6cdd5ed0f0153", [:mix], [{:earmark_parser, "~> 1.4.26", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "579ebe2eaf4c7e040815a73a268036bcd96e6aab8ad2b1fcd979aaeb1ea47e15"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.26", "f4291134583f373c7d8755566122908eb9662df4c4b63caa66a0eabe06569b0a", [:mix], [], "hexpm", "48d460899f8a0c52c5470676611c01f64f3337bad0b26ddab43648428d94aabc"}, - "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"}, - "ex_doc": {:hex, :ex_doc, "0.23.0", "a069bc9b0bf8efe323ecde8c0d62afc13d308b1fa3d228b65bca5cf8703a529d", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "f5e2c4702468b2fd11b10d39416ddadd2fcdd173ba2a0285ebd92c39827a5a16"}, - "fast_html": {:hex, :fast_html, "2.0.5", "c61760340606c1077ff1f196f17834056cb1dd3d5cb92a9f2cabf28bc6221c3c", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "605f4f4829443c14127694ebabb681778712ceecb4470ec32aa31012330e6506"}, - "floki": {:hex, :floki, "0.33.1", "f20f1eb471e726342b45ccb68edb9486729e7df94da403936ea94a794f072781", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "461035fd125f13fdf30f243c85a0b1e50afbec876cbf1ceefe6fddd2e6d712c6"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, + "elixir_make": {:hex, :elixir_make, "0.7.8", "505026f266552ee5aabca0b9f9c229cbb496c689537c9f922f3eb5431157efc7", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "7a71945b913d37ea89b06966e1342c85cfe549b15e6d6d081e8081c493062c07"}, + "ex_doc": {:hex, :ex_doc, "0.31.1", "8a2355ac42b1cc7b2379da9e40243f2670143721dd50748bf6c3b1184dae2089", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3178c3a407c557d8343479e1ff117a96fd31bafe52a039079593fb0524ef61b0"}, + "fast_html": {:hex, :fast_html, "2.3.0", "08c1d8ead840dd3060ba02c761bed9f37f456a1ddfe30bcdcfee8f651cec06a6", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "f18e3c7668f82d3ae0b15f48d48feeb257e28aa5ab1b0dbf781c7312e5da029d"}, + "floki": {:hex, :floki, "0.35.3", "0c8c6234aa71cb2b069cf801e8f8f30f8d096eb452c3dae2ccc409510ec32720", [:mix], [], "hexpm", "6d9f07f3fc76599f3b66c39f4a81ac62c8f4d9631140268db92aacad5d0e56d4"}, "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, - "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, + "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"}, "poison": {:hex, :poison, "5.0.0", "d2b54589ab4157bbb82ec2050757779bfed724463a544b6e20d79855a9e43b24", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "11dc6117c501b80c62a7594f941d043982a1bd05a1184280c0d9166eb4d8d3fc"}, } diff --git a/priv/post.ex.eex b/priv/post.ex.eex index baad7c7..fc8bd43 100644 --- a/priv/post.ex.eex +++ b/priv/post.ex.eex @@ -13,7 +13,6 @@ defmodule <%= context.module_name %> do def metadata do %{ - abbreviation: <%= inspect(context.abbreviation) %>, api_version: <%= inspect(context.api_version) %>, content_type: <%= inspect(context.content_type) %>, credential_scope: <%= inspect(context.credential_scope) %>, diff --git a/priv/rest.erl.eex b/priv/rest.erl.eex index b4de859..cfa3a4d 100644 --- a/priv/rest.erl.eex +++ b/priv/rest.erl.eex @@ -24,11 +24,13 @@ <%= action.function_name %>(Client<%= AWS.CodeGen.RestService.required_function_parameters(action) %>, QueryMap, HeadersMap, Options0) when is_map(Client), is_map(QueryMap), is_map(HeadersMap), is_list(Options0) -> Path = ["<%= AWS.CodeGen.RestService.Action.url_path(action) %>"],<%= if AWS.CodeGen.RestService.Context.s3_context?(context) do %> -<%= if "Bucket" in Enum.map(action.url_parameters, & &1.code_name) do %><% else %> Bucket = undefined,<% end %><% end %> - SuccessStatusCode = <%= if is_nil(action.success_status_code), do: "undefined", else: inspect(action.success_status_code) %>, - Options = [{send_body_as_binary, <%= action.send_body_as_binary? %>}, - {receive_body_as_binary, <%= action.receive_body_as_binary? %>} - | Options0], +<%= if !String.contains?("Bucket", AWS.CodeGen.RestService.required_function_parameters(action)) do %><% else %> Bucket = undefined,<% end %><% end %> + SuccessStatusCode = <%= inspect(action.success_status_code) %>, + {SendBodyAsBinary, Options1} = proplists_take(send_body_as_binary, Options0, <%= action.send_body_as_binary? %>), + {ReceiveBodyAsBinary, Options2} = proplists_take(receive_body_as_binary, Options1, <%= action.receive_body_as_binary? %>), + Options = [{send_body_as_binary, SendBodyAsBinary}, + {receive_body_as_binary, ReceiveBodyAsBinary} + | Options2], <%= if length(action.request_header_parameters) > 0 do %> Headers0 = [<%= for parameter <- Enum.drop(action.request_header_parameters, -1) do %> @@ -73,12 +75,14 @@ <%= action.function_name %>(Client<%= AWS.CodeGen.RestService.function_parameters(action) %>, Input0, Options0) -> Method = <%= AWS.CodeGen.RestService.Action.method(action) %>, Path = ["<%= AWS.CodeGen.RestService.Action.url_path(action) %>"],<%= if AWS.CodeGen.RestService.Context.s3_context?(context) do %> -<%= if "Bucket" in Enum.map(action.url_parameters, & &1.code_name) do %><% else %> Bucket = undefined,<% end %><% end %> - SuccessStatusCode = <%= if is_nil(action.success_status_code), do: "undefined", else: inspect(action.success_status_code) %>, - Options = [{send_body_as_binary, <%= action.send_body_as_binary? %>}, - {receive_body_as_binary, <%= action.receive_body_as_binary? %>}, +<%= if !String.contains?("Bucket", AWS.CodeGen.RestService.required_function_parameters(action)) do %><% else %> Bucket = undefined,<% end %><% end %> + SuccessStatusCode = <%= inspect(action.success_status_code) %>, + {SendBodyAsBinary, Options1} = proplists_take(send_body_as_binary, Options0, <%= action.send_body_as_binary? %>), + {ReceiveBodyAsBinary, Options2} = proplists_take(receive_body_as_binary, Options1, <%= action.receive_body_as_binary? %>), + Options = [{send_body_as_binary, SendBodyAsBinary}, + {receive_body_as_binary, ReceiveBodyAsBinary}, {append_sha256_content_hash, <%= Enum.member?(["put_bucket_cors", "put_bucket_lifecycle", "put_bucket_tagging", "delete_objects"], action.function_name) %>} - | Options0], + | Options2], <%= if length(action.request_header_parameters) > 0 do %> HeadersMapping = [<%= for parameter <- Enum.drop(action.request_header_parameters, -1) do %> {<<"<%= parameter.location_name %>">>, <<"<%= parameter.name %>">>},<% end %><%= for parameter <- Enum.slice action.request_header_parameters, -1..-1 do %> @@ -131,6 +135,11 @@ %% Internal functions %%==================================================================== +-spec proplists_take(any(), proplists:proplists(), any()) -> {any(), proplists:proplists()}. +proplists_take(Key, Proplist, Default) -> + Value = proplists:get_value(Key, Proplist, Default), + {Value, proplists:delete(Key, Proplist)}. + -spec request(aws_client:aws_client(), atom(), iolist(), list(), list(), map() | undefined, list(), pos_integer() | undefined<%= if AWS.CodeGen.RestService.Context.s3_context?(context) do %>, binary() | undefined<% end %>) -> {ok, {integer(), list()}} | diff --git a/priv/rest.ex.eex b/priv/rest.ex.eex index 3a28a3c..003ad39 100644 --- a/priv/rest.ex.eex +++ b/priv/rest.ex.eex @@ -13,7 +13,6 @@ defmodule <%= context.module_name %> do def metadata do %{ - abbreviation: <%= inspect(context.abbreviation) %>, api_version: <%= inspect(context.api_version) %>, content_type: <%= inspect(context.content_type) %>, credential_scope: <%= inspect(context.credential_scope) %>, diff --git a/test/aws_codegen/docstring_test.exs b/test/aws_codegen/docstring_test.exs index 3c09b8e..3cba636 100644 --- a/test/aws_codegen/docstring_test.exs +++ b/test/aws_codegen/docstring_test.exs @@ -215,9 +215,7 @@ defmodule AWS.CodeGen.DocstringTest do %% This is a `code' and a multiline is here: %% %% ``` - %% puts "hello" - %% ''' - %% + %% puts "hello"''' %% == Section title == %% %% Here is a link with the same text: [https://foo] diff --git a/test/aws_codegen/rest_service_test.exs b/test/aws_codegen/rest_service_test.exs index 50d4458..ed8bd69 100644 --- a/test/aws_codegen/rest_service_test.exs +++ b/test/aws_codegen/rest_service_test.exs @@ -3,10 +3,7 @@ defmodule AWS.CodeGen.RestServiceTest do alias AWS.CodeGen.RestService - # This is the smallest spec for a service. - # The original is at: https://github.com/aws/aws-sdk-go/blob/e2d6cb448883e4f4fcc5246650f89bde349041ec/models/apis/mobileanalytics/2014-06-05/api-2.json - @service_spec_file "test/fixtures/apis_specs/mobileanalytics-2014-06-05-api-2.json" - @service_docs_file "test/fixtures/apis_specs/mobileanalytics-2014-06-05-docs-2.json" + @service_spec_file "test/fixtures/apis_specs/cloudtrail-data.json" @endpoints_spec %{ "services" => %{ "mobileanalytics" => %{ @@ -23,85 +20,78 @@ defmodule AWS.CodeGen.RestServiceTest do |> File.read!() |> Poison.decode!() - docs = - @service_docs_file - |> File.read!() - |> Poison.decode!() - - [specs: service_specs, docs: docs] + [specs: service_specs] end describe "load_context/5" do - test "prepares context for Elixir", %{specs: specs, docs: docs} do + test "prepares context for Elixir", %{specs: specs} do spec = %AWS.CodeGen.Spec{ api: specs, - doc: docs, - module_name: "AWS.MobileAnalytics", - filename: "mobile_analytics.ex", - protocol: :rest_json + module_name: "AWS.CloudTrailData", + filename: "cloud_trail_data.ex", + protocol: :rest_json, + language: :elixir, + shape_name: "com.amazonaws.cloudtraildata#CloudTrailDataService" } assert %AWS.CodeGen.Service{ - abbreviation: nil, actions: [ action ], - api_version: "2014-06-05", + api_version: "2021-08-11", content_type: "application/x-amz-json-1.1", credential_scope: nil, decode: "json", - docstring: - " Amazon Mobile Analytics is a service for collecting, visualizing, and\n understanding app usage data at scale.", + docstring: " The CloudTrail Data Service lets you ingest events into CloudTrail from any\n source in your\n hybrid environments, such as in-house or SaaS applications hosted on-premises or\n in the cloud,\n virtual machines, or containers.\n\n You can store, access, analyze, troubleshoot and take action on\n this data without maintaining multiple log aggregators and reporting tools.\n After you run\n `PutAuditEvents` to ingest your application activity into CloudTrail, you can\n use CloudTrail Lake to search, query, and analyze the data that is logged\n from your applications.", encode: "json", - endpoint_prefix: "mobileanalytics", + endpoint_prefix: "cloudtrail-data", is_global: false, - json_version: nil, - module_name: "AWS.MobileAnalytics", + json_version: "1.1", + module_name: "AWS.CloudTrailData", protocol: "rest-json", - service_id: nil, + service_id: "CloudTrail Data", signature_version: "v4", - signing_name: "mobileanalytics", + signing_name: "cloudtrail-data", target_prefix: nil } = RestService.load_context(:elixir, spec, @endpoints_spec) assert action == %RestService.Action{ arity: 3, - docstring: - " The PutEvents operation records one or more events.\n\n You can have up to 1,500 unique custom events per app, any combination of up to\n 40 attributes and metrics per custom event, and any number of attribute or\n metric values.", - function_name: "put_events", + docstring: " Ingests your application events into CloudTrail Lake.\n\n A required parameter,\n `auditEvents`, accepts the JSON records (also called\n *payload*) of events that you want CloudTrail to ingest. You\n can add up to 100 of these events (or up to 1 MB) per `PutAuditEvents`\n request.", + function_name: "put_audit_events", language: :elixir, method: "POST", - name: "PutEvents", - query_parameters: [], + name: "com.amazonaws.cloudtraildata#PutAuditEvents", + query_parameters: [ + %RestService.Parameter{ + code_name: "channel_arn", + location_name: "channelArn", + name: "channelArn", + required: true + }, + %RestService.Parameter{ + code_name: "external_id", + location_name: "externalId", + name: "externalId", + required: false + } + ], receive_body_as_binary?: false, - request_header_parameters: [ - %RestService.Parameter{ - code_name: "client_context", - location_name: "x-amz-Client-Context", - name: "clientContext", - required: true - }, - %RestService.Parameter{ - code_name: "client_context_encoding", - location_name: "x-amz-Client-Context-Encoding", - name: "clientContextEncoding", - required: false - } - ], - request_uri: "/2014-06-05/events", - required_query_parameters: [], - required_request_header_parameters: [ - %RestService.Parameter{ - code_name: "client_context", - location_name: "x-amz-Client-Context", - name: "clientContext", - required: true - } - ], + request_header_parameters: [], + request_uri: "/PutAuditEvents", + required_query_parameters: [ + %RestService.Parameter{ + code_name: "channel_arn", + location_name: "channelArn", + name: "channelArn", + required: true + } + ], + required_request_header_parameters: [], response_header_parameters: [], send_body_as_binary?: false, - success_status_code: 202, + success_status_code: 200, url_parameters: [] } end diff --git a/test/aws_codegen/shapes_test.exs b/test/aws_codegen/shapes_test.exs index b0fa06d..8e4b7ce 100644 --- a/test/aws_codegen/shapes_test.exs +++ b/test/aws_codegen/shapes_test.exs @@ -4,31 +4,39 @@ defmodule AWS.CodeGen.ShapesTest do test "body_as_binary?/2" do shapes = %{ - "Body" => %{"type" => "blob"}, - "PutObjectRequest" => %{ + "com.amazonaws.s3#PutObjectRequest" => %{ "type" => "structure", "members" => %{ + "ACL" => %{ + "target" => "com.amazonaws.s3#ObjectCannedACL", + }, "Body" => %{ - "shape" => "Body", - "streaming" => true + "target" => "com.amazonaws.s3#StreamingBlob", } } }, - "PutObjectAclRequest" => %{ + "com.amazonaws.s3#StreamingBlob" => %{ + "type" => "blob", + "traits" => %{ + "smithy.api#streaming" => %{} + } + }, + "com.amazonaws.s3#PutObjectAclRequest" => %{ "type" => "structure", "members" => %{ "ACL" => %{ - "shape" => "ObjectCannedACL", - "location" => "header", - "locationName" => "x-amz-acl" + "target" => "com.amazonaws.s3#ObjectCannedACL", + }, + "AccessControlPolicy" => %{ + "target" => "com.amazonaws.s3#AccessControlPolicy", } } - } + }, } - assert Shapes.body_as_binary?(shapes, "PutObjectRequest") + assert Shapes.body_as_binary?(shapes, "com.amazonaws.s3#PutObjectRequest") - refute Shapes.body_as_binary?(shapes, "PutObjectAclRequest") - refute Shapes.body_as_binary?(shapes, "AnotherShape") + refute Shapes.body_as_binary?(shapes, "com.amazonaws.s3#PutObjectAclRequest") + refute Shapes.body_as_binary?(shapes, "com.amazonaws.s3#AnotherShape") end end diff --git a/test/aws_codegen/spec_test.exs b/test/aws_codegen/spec_test.exs index c5e1f8d..11098ad 100644 --- a/test/aws_codegen/spec_test.exs +++ b/test/aws_codegen/spec_test.exs @@ -5,128 +5,45 @@ defmodule AWS.CodeGen.SpecTest do test "parse/3" do assert %Spec{ - module_name: "AWS.MobileAnalytics", - filename: "mobile_analytics.ex", api: api, - doc: docs, + filename: "cloud_trail_data.ex", + module_name: "AWS.CloudTrailData", protocol: :rest_json - } = - Spec.parse("test/fixtures/apis_specs", :elixir, - api_filename: "mobileanalytics-2014-06-05-api-2.json", - doc_filename: "mobileanalytics-2014-06-05-docs-2.json" - ) + } = + Spec.parse("test/fixtures/apis_specs/cloudtrail-data.json", :elixir) assert %Spec{ - module_name: "aws_mobileanalytics", - filename: "aws_mobileanalytics.erl" + filename: "aws_cloudtrail_data.erl", + module_name: "aws_cloudtrail_data" } = - Spec.parse("test/fixtures/apis_specs", :erlang, - api_filename: "mobileanalytics-2014-06-05-api-2.json", - doc_filename: "mobileanalytics-2014-06-05-docs-2.json" - ) - - assert api == %{ - "metadata" => %{ - "apiVersion" => "2014-06-05", - "endpointPrefix" => "mobileanalytics", - "protocol" => "rest-json", - "serviceFullName" => "Amazon Mobile Analytics", - "signatureVersion" => "v4" - }, - "operations" => %{ - "PutEvents" => %{ - "errors" => [ - %{ - "error" => %{"httpStatusCode" => 400}, - "exception" => true, - "shape" => "BadRequestException" - } - ], - "http" => %{ - "method" => "POST", - "requestUri" => "/2014-06-05/events", - "responseCode" => 202 - }, - "input" => %{"shape" => "PutEventsInput"}, - "name" => "PutEvents" - } - }, - "shapes" => %{ - "BadRequestException" => %{ - "error" => %{"httpStatusCode" => 400}, - "exception" => true, - "members" => %{"message" => %{"shape" => "String"}}, - "type" => "structure" - }, - "Double" => %{"type" => "double"}, - "Event" => %{ - "members" => %{ - "attributes" => %{"shape" => "MapOfStringToString"}, - "eventType" => %{"shape" => "String50Chars"}, - "metrics" => %{"shape" => "MapOfStringToNumber"}, - "session" => %{"shape" => "Session"}, - "timestamp" => %{"shape" => "ISO8601Timestamp"}, - "version" => %{"shape" => "String10Chars"} - }, - "required" => ["eventType", "timestamp"], - "type" => "structure" - }, - "EventListDefinition" => %{"member" => %{"shape" => "Event"}, "type" => "list"}, - "ISO8601Timestamp" => %{"type" => "string"}, - "Long" => %{"type" => "long"}, - "MapOfStringToNumber" => %{ - "key" => %{"shape" => "String50Chars"}, - "max" => 50, - "min" => 0, - "type" => "map", - "value" => %{"shape" => "Double"} - }, - "MapOfStringToString" => %{ - "key" => %{"shape" => "String50Chars"}, - "max" => 50, - "min" => 0, - "type" => "map", - "value" => %{"shape" => "String0to1000Chars"} - }, - "PutEventsInput" => %{ - "members" => %{ - "clientContext" => %{ - "location" => "header", - "locationName" => "x-amz-Client-Context", - "shape" => "String" - }, - "clientContextEncoding" => %{ - "location" => "header", - "locationName" => "x-amz-Client-Context-Encoding", - "shape" => "String" - }, - "events" => %{"shape" => "EventListDefinition"} - }, - "required" => ["events", "clientContext"], - "type" => "structure" - }, - "Session" => %{ - "members" => %{ - "duration" => %{"shape" => "Long"}, - "id" => %{"shape" => "String50Chars"}, - "startTimestamp" => %{"shape" => "ISO8601Timestamp"}, - "stopTimestamp" => %{"shape" => "ISO8601Timestamp"} - }, - "type" => "structure" - }, - "String" => %{"type" => "string"}, - "String0to1000Chars" => %{"max" => 1000, "min" => 0, "type" => "string"}, - "String10Chars" => %{"max" => 10, "min" => 1, "type" => "string"}, - "String50Chars" => %{"max" => 50, "min" => 1, "type" => "string"} - }, - "version" => "2.0" - } + Spec.parse("test/fixtures/apis_specs/cloudtrail-data.json", :erlang) - assert %{ - "operations" => _operations, - "service" => _service_description, - "shapes" => _shapes, - "version" => "2.0" - } = docs + assert api == + %{ + "shapes" => %{ + "com.amazonaws.cloudtraildata#AuditEvent" => %{"members" => %{"eventData" => %{"target" => "smithy.api#String", "traits" => %{"smithy.api#documentation" => "

The content of an audit event that comes from the event, such as userIdentity, \n userAgent, and eventSource.

", "smithy.api#required" => %{}}}, "eventDataChecksum" => %{"target" => "smithy.api#String", "traits" => %{"smithy.api#documentation" => "

A checksum is a base64-SHA256 algorithm that helps you verify that CloudTrail receives the event that matches \n with the checksum. Calculate the checksum by running a command like the following:

\n

\n printf %s $eventdata | openssl dgst -binary -sha256 | base64\n

"}}, "id" => %{"target" => "com.amazonaws.cloudtraildata#Uuid", "traits" => %{"smithy.api#documentation" => "

The original event ID from the source event.

", "smithy.api#required" => %{}}}}, "traits" => %{"smithy.api#documentation" => "

An event from a source outside of Amazon Web Services that you want CloudTrail \n to log.

"}, "type" => "structure"}, + "com.amazonaws.cloudtraildata#AuditEventResultEntries" => %{"member" => %{"target" => "com.amazonaws.cloudtraildata#AuditEventResultEntry"}, "traits" => %{"smithy.api#length" => %{"max" => 100, "min" => 0}}, "type" => "list"}, + "com.amazonaws.cloudtraildata#AuditEventResultEntry" => %{"members" => %{"eventID" => %{"target" => "com.amazonaws.cloudtraildata#Uuid", "traits" => %{"smithy.api#documentation" => "

The event ID assigned by CloudTrail.

", "smithy.api#required" => %{}}}, "id" => %{"target" => "com.amazonaws.cloudtraildata#Uuid", "traits" => %{"smithy.api#documentation" => "

The original event ID from the source event.

", "smithy.api#required" => %{}}}}, "traits" => %{"smithy.api#documentation" => "

A response that includes successful and failed event results.

"}, "type" => "structure"}, + "com.amazonaws.cloudtraildata#AuditEvents" => %{"member" => %{"target" => "com.amazonaws.cloudtraildata#AuditEvent"}, "traits" => %{"smithy.api#length" => %{"max" => 100, "min" => 1}}, "type" => "list"}, + "com.amazonaws.cloudtraildata#ChannelArn" => %{"traits" => %{"aws.api#arnReference" => %{}, "smithy.api#pattern" => "^arn:.*$"}, "type" => "string"}, + "com.amazonaws.cloudtraildata#ChannelInsufficientPermission" => %{"members" => %{"message" => %{"target" => "smithy.api#String"}}, "traits" => %{"smithy.api#documentation" => "

The caller's account ID must be the same as the channel owner's account ID.

", "smithy.api#error" => "client"}, "type" => "structure"}, + "com.amazonaws.cloudtraildata#ChannelNotFound" => %{"members" => %{"message" => %{"target" => "smithy.api#String"}}, "traits" => %{"smithy.api#documentation" => "

The channel could not be found.

", "smithy.api#error" => "client"}, "type" => "structure"}, + "com.amazonaws.cloudtraildata#ChannelUnsupportedSchema" => %{"members" => %{"message" => %{"target" => "smithy.api#String"}}, "traits" => %{"smithy.api#documentation" => "

The schema type of the event is not supported.

", "smithy.api#error" => "client"}, "type" => "structure"}, + "com.amazonaws.cloudtraildata#CloudTrailDataService" => %{"operations" => [%{"target" => "com.amazonaws.cloudtraildata#PutAuditEvents"}], "traits" => %{"aws.api#service" => %{"endpointPrefix" => "cloudtrail-data", "sdkId" => "CloudTrail Data"}, "aws.auth#sigv4" => %{"name" => "cloudtrail-data"}, "aws.protocols#restJson1" => %{}, "smithy.api#cors" => %{}, "smithy.api#documentation" => "

The CloudTrail Data Service lets you ingest events into CloudTrail from any source in your\nhybrid environments, such as in-house or SaaS applications hosted on-premises or in the cloud,\nvirtual machines, or containers. You can store, access, analyze, troubleshoot and take action on\nthis data without maintaining multiple log aggregators and reporting tools. After you run \nPutAuditEvents to ingest your application activity into CloudTrail, you can use CloudTrail Lake to search, query, and analyze the data that is logged\nfrom your applications.

", "smithy.api#title" => "AWS CloudTrail Data Service", "smithy.rules#endpointRuleSet" => %{"parameters" => %{"Endpoint" => %{"builtIn" => "SDK::Endpoint", "documentation" => "Override the endpoint used to send this request", "required" => false, "type" => "String"}, "Region" => %{"builtIn" => "AWS::Region", "documentation" => "The AWS region used to dispatch the request.", "required" => false, "type" => "String"}, "UseDualStack" => %{"builtIn" => "AWS::UseDualStack", "default" => false, "documentation" => "When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.", "required" => true, "type" => "Boolean"}, "UseFIPS" => %{"builtIn" => "AWS::UseFIPS", "default" => false, "documentation" => "When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.", "required" => true, "type" => "Boolean"}}, "rules" => [%{"conditions" => [%{"argv" => [%{"ref" => "Endpoint"}], "fn" => "isSet"}], "rules" => [%{"conditions" => [%{"argv" => [%{"ref" => "UseFIPS"}, true], "fn" => "booleanEquals"}], "error" => "Invalid Configuration: FIPS and custom endpoint are not supported", "type" => "error"}, %{"conditions" => [], "rules" => [%{"conditions" => [%{"argv" => [%{"ref" => "UseDualStack"}, true], "fn" => "booleanEquals"}], "error" => "Invalid Configuration: Dualstack and custom endpoint are not supported", "type" => "error"}, %{"conditions" => [], "endpoint" => %{"headers" => %{}, "properties" => %{}, "url" => %{"ref" => "Endpoint"}}, "type" => "endpoint"}], "type" => "tree"}], "type" => "tree"}, %{"conditions" => [], "rules" => [%{"conditions" => [%{"argv" => [%{"ref" => "Region"}], "fn" => "isSet"}], "rules" => [%{"conditions" => [%{"argv" => [%{"ref" => "Region"}], "assign" => "PartitionResult", "fn" => "aws.partition"}], "rules" => [%{"conditions" => [%{"argv" => [%{"ref" => "UseFIPS"}, true], "fn" => "booleanEquals"}, %{"argv" => [%{"ref" => "UseDualStack"}, true], "fn" => "booleanEquals"}], "rules" => [%{"conditions" => [%{"argv" => [true, %{"argv" => [%{"ref" => "PartitionResult"}, "supportsFIPS"], "fn" => "getAttr"}], "fn" => "booleanEquals"}, %{"argv" => [true, %{"argv" => [%{"ref" => "PartitionResult"}, "supportsDualStack"], "fn" => "getAttr"}], "fn" => "booleanEquals"}], "rules" => [%{"conditions" => [], "rules" => [%{"conditions" => [], "endpoint" => %{"headers" => %{}, "properties" => %{}, "url" => "https://cloudtrail-data-fips.{Region}.{PartitionResult#dualStackDnsSuffix}"}, "type" => "endpoint"}], "type" => "tree"}], "type" => "tree"}, %{"conditions" => [], "error" => "FIPS and DualStack are enabled, but this partition does not support one or both", "type" => "error"}], "type" => "tree"}, %{"conditions" => [%{"argv" => [%{"ref" => "UseFIPS"}, true], "fn" => "booleanEquals"}], "rules" => [%{"conditions" => [%{"argv" => [true, %{"argv" => [%{"ref" => "PartitionResult"}, "supportsFIPS"], "fn" => "getAttr"}], "fn" => "booleanEquals"}], "rules" => [%{"conditions" => [], "rules" => [%{"conditions" => [], "endpoint" => %{"headers" => %{}, "properties" => %{}, "url" => "https://cloudtrail-data-fips.{Region}.{PartitionResult#dnsSuffix}"}, "type" => "endpoint"}], "type" => "tree"}], "type" => "tree"}, %{"conditions" => [], "error" => "FIPS is enabled but this partition does not support FIPS", "type" => "error"}], "type" => "tree"}, %{"conditions" => [%{"argv" => [%{"ref" => "UseDualStack"}, true], "fn" => "booleanEquals"}], "rules" => [%{"conditions" => [%{"argv" => [true, %{"argv" => [%{"ref" => "PartitionResult"}, "supportsDualStack"], "fn" => "getAttr"}], "fn" => "booleanEquals"}], "rules" => [%{"conditions" => [], "rules" => [%{"conditions" => [], "endpoint" => %{"headers" => %{}, "properties" => %{}, "url" => "https://cloudtrail-data.{Region}.{PartitionResult#dualStackDnsSuffix}"}, "type" => "endpoint"}], "type" => "tree"}], "type" => "tree"}, %{"conditions" => [], "error" => "DualStack is enabled but this partition does not support DualStack", "type" => "error"}], "type" => "tree"}, %{"conditions" => [], "rules" => [%{"conditions" => [], "endpoint" => %{"headers" => %{}, "properties" => %{}, "url" => "https://cloudtrail-data.{Region}.{PartitionResult#dnsSuffix}"}, "type" => "endpoint"}], "type" => "tree"}], "type" => "tree"}], "type" => "tree"}, %{"conditions" => [], "error" => "Invalid Configuration: Missing Region", "type" => "error"}], "type" => "tree"}], "version" => "1.0"}, "smithy.rules#endpointTests" => %{"testCases" => [%{"documentation" => "For region us-east-1 with FIPS enabled and DualStack enabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data-fips.us-east-1.api.aws"}}, "params" => %{"Region" => "us-east-1", "UseDualStack" => true, "UseFIPS" => true}}, %{"documentation" => "For region us-east-1 with FIPS enabled and DualStack disabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data-fips.us-east-1.amazonaws.com"}}, "params" => %{"Region" => "us-east-1", "UseDualStack" => false, "UseFIPS" => true}}, %{"documentation" => "For region us-east-1 with FIPS disabled and DualStack enabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data.us-east-1.api.aws"}}, "params" => %{"Region" => "us-east-1", "UseDualStack" => true, "UseFIPS" => false}}, %{"documentation" => "For region us-east-1 with FIPS disabled and DualStack disabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data.us-east-1.amazonaws.com"}}, "params" => %{"Region" => "us-east-1", "UseDualStack" => false, "UseFIPS" => false}}, %{"documentation" => "For region cn-north-1 with FIPS enabled and DualStack enabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data-fips.cn-north-1.api.amazonwebservices.com.cn"}}, "params" => %{"Region" => "cn-north-1", "UseDualStack" => true, "UseFIPS" => true}}, %{"documentation" => "For region cn-north-1 with FIPS enabled and DualStack disabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data-fips.cn-north-1.amazonaws.com.cn"}}, "params" => %{"Region" => "cn-north-1", "UseDualStack" => false, "UseFIPS" => true}}, %{"documentation" => "For region cn-north-1 with FIPS disabled and DualStack enabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data.cn-north-1.api.amazonwebservices.com.cn"}}, "params" => %{"Region" => "cn-north-1", "UseDualStack" => true, "UseFIPS" => false}}, %{"documentation" => "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data.cn-north-1.amazonaws.com.cn"}}, "params" => %{"Region" => "cn-north-1", "UseDualStack" => false, "UseFIPS" => false}}, %{"documentation" => "For region us-gov-east-1 with FIPS enabled and DualStack enabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data-fips.us-gov-east-1.api.aws"}}, "params" => %{"Region" => "us-gov-east-1", "UseDualStack" => true, "UseFIPS" => true}}, %{"documentation" => "For region us-gov-east-1 with FIPS enabled and DualStack disabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data-fips.us-gov-east-1.amazonaws.com"}}, "params" => %{"Region" => "us-gov-east-1", "UseDualStack" => false, "UseFIPS" => true}}, %{"documentation" => "For region us-gov-east-1 with FIPS disabled and DualStack enabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data.us-gov-east-1.api.aws"}}, "params" => %{"Region" => "us-gov-east-1", "UseDualStack" => true, "UseFIPS" => false}}, %{"documentation" => "For region us-gov-east-1 with FIPS disabled and DualStack disabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data.us-gov-east-1.amazonaws.com"}}, "params" => %{"Region" => "us-gov-east-1", "UseDualStack" => false, "UseFIPS" => false}}, %{"documentation" => "For region us-iso-east-1 with FIPS enabled and DualStack enabled", "expect" => %{"error" => "FIPS and DualStack are enabled, but this partition does not support one or both"}, "params" => %{"Region" => "us-iso-east-1", "UseDualStack" => true, "UseFIPS" => true}}, %{"documentation" => "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data-fips.us-iso-east-1.c2s.ic.gov"}}, "params" => %{"Region" => "us-iso-east-1", "UseDualStack" => false, "UseFIPS" => true}}, %{"documentation" => "For region us-iso-east-1 with FIPS disabled and DualStack enabled", "expect" => %{"error" => "DualStack is enabled but this partition does not support DualStack"}, "params" => %{"Region" => "us-iso-east-1", "UseDualStack" => true, "UseFIPS" => false}}, %{"documentation" => "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data.us-iso-east-1.c2s.ic.gov"}}, "params" => %{"Region" => "us-iso-east-1", "UseDualStack" => false, "UseFIPS" => false}}, %{"documentation" => "For region us-isob-east-1 with FIPS enabled and DualStack enabled", "expect" => %{"error" => "FIPS and DualStack are enabled, but this partition does not support one or both"}, "params" => %{"Region" => "us-isob-east-1", "UseDualStack" => true, "UseFIPS" => true}}, %{"documentation" => "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data-fips.us-isob-east-1.sc2s.sgov.gov"}}, "params" => %{"Region" => "us-isob-east-1", "UseDualStack" => false, "UseFIPS" => true}}, %{"documentation" => "For region us-isob-east-1 with FIPS disabled and DualStack enabled", "expect" => %{"error" => "DualStack is enabled but this partition does not support DualStack"}, "params" => %{"Region" => "us-isob-east-1", "UseDualStack" => true, "UseFIPS" => false}}, %{"documentation" => "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect" => %{"endpoint" => %{"url" => "https://cloudtrail-data.us-isob-east-1.sc2s.sgov.gov"}}, "params" => %{"Region" => "us-isob-east-1", "UseDualStack" => false, "UseFIPS" => false}}, %{"documentation" => "For custom endpoint with region set and fips disabled and dualstack disabled", "expect" => %{"endpoint" => %{"url" => "https://example.com"}}, "params" => %{"Endpoint" => "https://example.com", "Region" => "us-east-1", "UseDualStack" => false, "UseFIPS" => false}}, %{"documentation" => "For custom endpoint with region not set and fips disabled and dualstack disabled", "expect" => %{"endpoint" => %{"url" => "https://example.com"}}, "params" => %{"Endpoint" => "https://example.com", "UseDualStack" => false, "UseFIPS" => false}}, %{"documentation" => "For custom endpoint with fips enabled and dualstack disabled", "expect" => %{"error" => "Invalid Configuration: FIPS and custom endpoint are not supported"}, "params" => %{"Endpoint" => "https://example.com", "Region" => "us-east-1", "UseDualStack" => false, "UseFIPS" => true}}, %{"documentation" => "For custom endpoint with fips disabled and dualstack enabled", "expect" => %{"error" => "Invalid Configuration: Dualstack and custom endpoint are not supported"}, "params" => %{"Endpoint" => "https://example.com", "Region" => "us-east-1", "UseDualStack" => true, "UseFIPS" => false}}, %{"documentation" => "Missing region", "expect" => %{"error" => "Invalid Configuration: Missing Region"}}], "version" => "1.0"}}, "type" => "service", "version" => "2021-08-11"}, + "com.amazonaws.cloudtraildata#DuplicatedAuditEventId" => %{"members" => %{"message" => %{"target" => "smithy.api#String"}}, "traits" => %{"smithy.api#documentation" => "

Two or more entries in the request have the same event ID.

", "smithy.api#error" => "client"}, "type" => "structure"}, + "com.amazonaws.cloudtraildata#ErrorCode" => %{"traits" => %{"smithy.api#length" => %{"max" => 128, "min" => 1}}, "type" => "string"}, + "com.amazonaws.cloudtraildata#ErrorMessage" => %{"traits" => %{"smithy.api#length" => %{"max" => 1024, "min" => 1}}, "type" => "string"}, + "com.amazonaws.cloudtraildata#ExternalId" => %{"traits" => %{"smithy.api#length" => %{"max" => 1224, "min" => 2}, "smithy.api#pattern" => "^[\\w+=,.@:\\/-]*$"}, "type" => "string"}, + "com.amazonaws.cloudtraildata#InvalidChannelARN" => %{"members" => %{"message" => %{"target" => "smithy.api#String"}}, "traits" => %{"smithy.api#documentation" => "

The specified channel ARN is not a valid \n channel ARN.

", "smithy.api#error" => "client"}, "type" => "structure"}, + "com.amazonaws.cloudtraildata#PutAuditEvents" => %{"errors" => [%{"target" => "com.amazonaws.cloudtraildata#ChannelInsufficientPermission"}, %{"target" => "com.amazonaws.cloudtraildata#ChannelNotFound"}, %{"target" => "com.amazonaws.cloudtraildata#ChannelUnsupportedSchema"}, %{"target" => "com.amazonaws.cloudtraildata#DuplicatedAuditEventId"}, %{"target" => "com.amazonaws.cloudtraildata#InvalidChannelARN"}, %{"target" => "com.amazonaws.cloudtraildata#UnsupportedOperationException"}], "input" => %{"target" => "com.amazonaws.cloudtraildata#PutAuditEventsRequest"}, "output" => %{"target" => "com.amazonaws.cloudtraildata#PutAuditEventsResponse"}, "traits" => %{"smithy.api#documentation" => "

Ingests your application events into CloudTrail Lake. A required parameter,\n auditEvents, accepts the JSON records (also called\n payload) of events that you want CloudTrail to ingest. You\n can add up to 100 of these events (or up to 1 MB) per PutAuditEvents\n request.

", "smithy.api#http" => %{"method" => "POST", "uri" => "/PutAuditEvents"}}, "type" => "operation"}, + "com.amazonaws.cloudtraildata#PutAuditEventsRequest" => %{"members" => %{"auditEvents" => %{"target" => "com.amazonaws.cloudtraildata#AuditEvents", "traits" => %{"smithy.api#documentation" => "

The JSON payload of events that you want to ingest. You can also point to the JSON event\n payload in a file.

", "smithy.api#required" => %{}}}, "channelArn" => %{"target" => "com.amazonaws.cloudtraildata#ChannelArn", "traits" => %{"smithy.api#documentation" => "

The ARN or ID (the ARN suffix) of a channel.

", "smithy.api#httpQuery" => "channelArn", "smithy.api#required" => %{}}}, "externalId" => %{"target" => "com.amazonaws.cloudtraildata#ExternalId", "traits" => %{"smithy.api#documentation" => "

A unique identifier that is conditionally required when the channel's resource policy includes an external \n ID. This value can be any string, \n such as a passphrase or account number.

", "smithy.api#httpQuery" => "externalId"}}}, "type" => "structure"}, + "com.amazonaws.cloudtraildata#PutAuditEventsResponse" => %{"members" => %{"failed" => %{"target" => "com.amazonaws.cloudtraildata#ResultErrorEntries", "traits" => %{"smithy.api#documentation" => "

Lists events in the provided event payload that could not be \n ingested into CloudTrail, and includes the error code and error message \n returned for events that could not be ingested.

", "smithy.api#required" => %{}}}, "successful" => %{"target" => "com.amazonaws.cloudtraildata#AuditEventResultEntries", "traits" => %{"smithy.api#documentation" => "

Lists events in the provided event payload that were successfully ingested \n into CloudTrail.

", "smithy.api#required" => %{}}}}, "type" => "structure"}, + "com.amazonaws.cloudtraildata#ResultErrorEntries" => %{"member" => %{"target" => "com.amazonaws.cloudtraildata#ResultErrorEntry"}, "traits" => %{"smithy.api#length" => %{"max" => 100, "min" => 0}}, "type" => "list"}, + "com.amazonaws.cloudtraildata#ResultErrorEntry" => %{"members" => %{"errorCode" => %{"target" => "com.amazonaws.cloudtraildata#ErrorCode", "traits" => %{"smithy.api#documentation" => "

The error code for events that could not be ingested by CloudTrail. Possible error codes include: FieldTooLong, FieldNotFound, \n InvalidChecksum, InvalidData, InvalidRecipient, InvalidEventSource, AccountNotSubscribed, \n Throttling, and InternalFailure.

", "smithy.api#required" => %{}}}, "errorMessage" => %{"target" => "com.amazonaws.cloudtraildata#ErrorMessage", "traits" => %{"smithy.api#documentation" => "

The message that describes the error for events that could not be ingested by CloudTrail.

", "smithy.api#required" => %{}}}, "id" => %{"target" => "com.amazonaws.cloudtraildata#Uuid", "traits" => %{"smithy.api#documentation" => "

The original event ID from the source event that could not be ingested by CloudTrail.

", "smithy.api#required" => %{}}}}, "traits" => %{"smithy.api#documentation" => "

Includes the error code and error message for events that could not be ingested by CloudTrail.

"}, "type" => "structure"}, + "com.amazonaws.cloudtraildata#UnsupportedOperationException" => %{"members" => %{"message" => %{"target" => "smithy.api#String"}}, "traits" => %{"smithy.api#documentation" => "

The operation requested is not supported in this region or account.

", "smithy.api#error" => "client"}, "type" => "structure"}, + "com.amazonaws.cloudtraildata#Uuid" => %{"traits" => %{"smithy.api#length" => %{"max" => 128, "min" => 1}, "smithy.api#pattern" => "^[-_A-Za-z0-9]+$"}, "type" => "string"} + }, + "smithy" => "2.0" + } end end diff --git a/test/aws_codegen_test.exs b/test/aws_codegen_test.exs index c01083e..de43c64 100644 --- a/test/aws_codegen_test.exs +++ b/test/aws_codegen_test.exs @@ -3,10 +3,7 @@ defmodule AWS.CodeGenTest do alias AWS.CodeGen.RestService - # This is the smallest spec for a service. - # The original is at: https://github.com/aws/aws-sdk-go/blob/e2d6cb448883e4f4fcc5246650f89bde349041ec/models/apis/mobileanalytics/2014-06-05/api-2.json - @service_spec_file "test/fixtures/apis_specs/mobileanalytics-2014-06-05-api-2.json" - @service_docs_file "test/fixtures/apis_specs/mobileanalytics-2014-06-05-docs-2.json" + @service_spec_file "test/fixtures/apis_specs/cloudtrail-data.json" @endpoints_spec %{ "services" => %{ "mobileanalytics" => %{ @@ -23,28 +20,24 @@ defmodule AWS.CodeGenTest do |> File.read!() |> Poison.decode!() - docs = - @service_docs_file - |> File.read!() - |> Poison.decode!() - - [specs: service_specs, docs: docs] + [specs: service_specs] end describe "render/2" do defp setup_context(language, specs, docs \\ nil) do spec = %AWS.CodeGen.Spec{ api: specs, - doc: docs, - module_name: "AWS.MobileAnalytics", - filename: "mobile_analytics.ex", - protocol: :rest_json + module_name: "AWS.CloudTrailData", + filename: "cloud_trail_data.ex", + protocol: :rest_json, + language: :elixir, + shape_name: "com.amazonaws.cloudtraildata#CloudTrailDataService" } RestService.load_context(language, spec, @endpoints_spec) end - test "renders the Elixir module without docs", %{specs: specs} do + test "renders the Elixir module with docs", %{specs: specs} do context = setup_context(:elixir, specs) result = @@ -57,73 +50,20 @@ defmodule AWS.CodeGenTest do # WARNING: DO NOT EDIT, AUTO-GENERATED CODE! # See https://github.com/aws-beam/aws-codegen for more details. - defmodule AWS.MobileAnalytics do - alias AWS.Client - alias AWS.Request - - def metadata do - %{ - abbreviation: nil, - api_version: "2014-06-05", - content_type: "application/x-amz-json-1.1", - credential_scope: nil, - endpoint_prefix: "mobileanalytics", - global?: false, - protocol: "rest-json", - service_id: nil, - signature_version: "v4", - signing_name: "mobileanalytics", - target_prefix: nil - } - end - - def put_events(%Client{} = client, input, options \\\\ []) do - url_path = "/2014-06-05/events" - - {headers, input} = - [ - {"clientContext", "x-amz-Client-Context"}, - {"clientContextEncoding", "x-amz-Client-Context-Encoding"} - ] - |> Request.build_params(input) - - query_params = [] - - meta = metadata() - - Request.request_rest( - client, - meta, - :post, - url_path, - query_params, - headers, - input, - options, - 202 - ) - end - end - """) - end - - test "renders the Elixir module with docs", %{specs: specs, docs: docs} do - context = setup_context(:elixir, specs, docs) - - result = - context - |> AWS.CodeGen.render("priv/rest.ex.eex") - |> IO.iodata_to_binary() - - assert result == - String.trim_leading(""" - # WARNING: DO NOT EDIT, AUTO-GENERATED CODE! - # See https://github.com/aws-beam/aws-codegen for more details. - - defmodule AWS.MobileAnalytics do + defmodule AWS.CloudTrailData do @moduledoc \"\"\" - Amazon Mobile Analytics is a service for collecting, visualizing, and - understanding app usage data at scale. + The CloudTrail Data Service lets you ingest events into CloudTrail from any + source in your + hybrid environments, such as in-house or SaaS applications hosted on-premises or + in the cloud, + virtual machines, or containers. + + You can store, access, analyze, troubleshoot and take action on + this data without maintaining multiple log aggregators and reporting tools. + After you run + `PutAuditEvents` to ingest your application activity into CloudTrail, you can + use CloudTrail Lake to search, query, and analyze the data that is logged + from your applications. \"\"\" alias AWS.Client @@ -131,39 +71,39 @@ defmodule AWS.CodeGenTest do def metadata do %{ - abbreviation: nil, - api_version: "2014-06-05", + api_version: "2021-08-11", content_type: "application/x-amz-json-1.1", credential_scope: nil, - endpoint_prefix: "mobileanalytics", + endpoint_prefix: "cloudtrail-data", global?: false, protocol: "rest-json", - service_id: nil, + service_id: "CloudTrail Data", signature_version: "v4", - signing_name: "mobileanalytics", + signing_name: "cloudtrail-data", target_prefix: nil } end @doc \"\"\" - The PutEvents operation records one or more events. + Ingests your application events into CloudTrail Lake. - You can have up to 1,500 unique custom events per app, any combination of up to - 40 attributes and metrics per custom event, and any number of attribute or - metric values. + A required parameter, + `auditEvents`, accepts the JSON records (also called + *payload*) of events that you want CloudTrail to ingest. You + can add up to 100 of these events (or up to 1 MB) per `PutAuditEvents` + request. \"\"\" - def put_events(%Client{} = client, input, options \\\\ []) do - url_path = "/2014-06-05/events" + def put_audit_events(%Client{} = client, input, options \\\\ []) do + url_path = "/PutAuditEvents" + headers = [] - {headers, input} = + {query_params, input} = [ - {"clientContext", "x-amz-Client-Context"}, - {"clientContextEncoding", "x-amz-Client-Context-Encoding"} + {"channelArn", "channelArn"}, + {"externalId", "externalId"} ] |> Request.build_params(input) - query_params = [] - meta = metadata() Request.request_rest( @@ -175,7 +115,7 @@ defmodule AWS.CodeGenTest do headers, input, options, - 202 + 200 ) end end @@ -198,38 +138,60 @@ defmodule AWS.CodeGenTest do # WARNING: DO NOT EDIT, AUTO-GENERATED CODE! # See https://github.com/aws-beam/aws-codegen for more details. - defmodule AWS.MobileAnalytics do + defmodule AWS.CloudTrailData do + @moduledoc \"\"\" + The CloudTrail Data Service lets you ingest events into CloudTrail from any + source in your + hybrid environments, such as in-house or SaaS applications hosted on-premises or + in the cloud, + virtual machines, or containers. + + You can store, access, analyze, troubleshoot and take action on + this data without maintaining multiple log aggregators and reporting tools. + After you run + `PutAuditEvents` to ingest your application activity into CloudTrail, you can + use CloudTrail Lake to search, query, and analyze the data that is logged + from your applications. + \"\"\" + alias AWS.Client alias AWS.Request def metadata do %{ - abbreviation: nil, - api_version: "2014-06-05", + api_version: "2021-08-11", content_type: "application/x-amz-json-1.1", credential_scope: nil, - endpoint_prefix: "mobileanalytics", + endpoint_prefix: "cloudtrail-data", global?: false, protocol: "rest-json", - service_id: nil, + service_id: "CloudTrail Data", signature_version: "v4", - signing_name: "mobileanalytics", + signing_name: "cloudtrail-data", target_prefix: nil } end - def put_events(%Client{} = client, input, options \\\\ []) do - url_path = "/2014-06-05/events" + @doc \"\"\" + Ingests your application events into CloudTrail Lake. + + A required parameter, + `auditEvents`, accepts the JSON records (also called + *payload*) of events that you want CloudTrail to ingest. You + can add up to 100 of these events (or up to 1 MB) per `PutAuditEvents` + request. + \"\"\" + def put_audit_events(%Client{} = client, input, options \\\\ []) do + url_path = "/PutAuditEvents" + headers = [] - {headers, input} = + {query_params, input} = [ - {"clientContext", "x-amz-Client-Context"}, - {"clientContextEncoding", "x-amz-Client-Context-Encoding"} + {"channelArn", "channelArn"}, + {"externalId", "externalId"} ] |> Request.build_params(input) - query_params = [] - options = Keyword.put( options, @@ -255,7 +217,7 @@ defmodule AWS.CodeGenTest do headers, input, options, - 202 + 200 ) end end @@ -284,51 +246,68 @@ defmodule AWS.CodeGenTest do # WARNING: DO NOT EDIT, AUTO-GENERATED CODE! # See https://github.com/aws-beam/aws-codegen for more details. - defmodule AWS.MobileAnalytics do + defmodule AWS.CloudTrailData do + @moduledoc \"\"\" + The CloudTrail Data Service lets you ingest events into CloudTrail from any + source in your + hybrid environments, such as in-house or SaaS applications hosted on-premises or + in the cloud, + virtual machines, or containers. + + You can store, access, analyze, troubleshoot and take action on + this data without maintaining multiple log aggregators and reporting tools. + After you run + `PutAuditEvents` to ingest your application activity into CloudTrail, you can + use CloudTrail Lake to search, query, and analyze the data that is logged + from your applications. + \"\"\" + alias AWS.Client alias AWS.Request def metadata do %{ - abbreviation: nil, - api_version: "2014-06-05", + api_version: "2021-08-11", content_type: "application/x-amz-json-1.1", credential_scope: nil, - endpoint_prefix: "mobileanalytics", + endpoint_prefix: "cloudtrail-data", global?: false, protocol: "rest-json", - service_id: nil, + service_id: "CloudTrail Data", signature_version: "v4", - signing_name: "mobileanalytics", + signing_name: "cloudtrail-data", target_prefix: nil } end - def put_events( - %Client{} = client, - client_context, - client_context_encoding \\\\ nil, - options \\\\ [] - ) do - url_path = "/2014-06-05/events" + @doc \"\"\" + Ingests your application events into CloudTrail Lake. + + A required parameter, + `auditEvents`, accepts the JSON records (also called + *payload*) of events that you want CloudTrail to ingest. You + can add up to 100 of these events (or up to 1 MB) per `PutAuditEvents` + request. + \"\"\" + def put_audit_events(%Client{} = client, channel_arn, external_id \\\\ nil, options \\\\ []) do + url_path = "/PutAuditEvents" headers = [] + query_params = [] - headers = - if !is_nil(client_context) do - [{"x-amz-Client-Context", client_context} | headers] + query_params = + if !is_nil(external_id) do + [{\"externalId\", external_id} | query_params] else - headers + query_params end - headers = - if !is_nil(client_context_encoding) do - [{"x-amz-Client-Context-Encoding", client_context_encoding} | headers] + query_params = + if !is_nil(channel_arn) do + [{\"channelArn\", channel_arn} | query_params] else - headers + query_params end - query_params = [] - options = Keyword.put( options, @@ -345,7 +324,7 @@ defmodule AWS.CodeGenTest do meta = metadata() - Request.request_rest(client, meta, :get, url_path, query_params, headers, nil, options, 202) + Request.request_rest(client, meta, :get, url_path, query_params, headers, nil, options, 200) end end """) @@ -368,38 +347,60 @@ defmodule AWS.CodeGenTest do # WARNING: DO NOT EDIT, AUTO-GENERATED CODE! # See https://github.com/aws-beam/aws-codegen for more details. - defmodule AWS.MobileAnalytics do + defmodule AWS.CloudTrailData do + @moduledoc \"\"\" + The CloudTrail Data Service lets you ingest events into CloudTrail from any + source in your + hybrid environments, such as in-house or SaaS applications hosted on-premises or + in the cloud, + virtual machines, or containers. + + You can store, access, analyze, troubleshoot and take action on + this data without maintaining multiple log aggregators and reporting tools. + After you run + `PutAuditEvents` to ingest your application activity into CloudTrail, you can + use CloudTrail Lake to search, query, and analyze the data that is logged + from your applications. + \"\"\" + alias AWS.Client alias AWS.Request def metadata do %{ - abbreviation: nil, - api_version: "2014-06-05", + api_version: "2021-08-11", content_type: "application/x-amz-json-1.1", credential_scope: nil, - endpoint_prefix: "mobileanalytics", + endpoint_prefix: "cloudtrail-data", global?: false, protocol: "rest-json", - service_id: nil, + service_id: "CloudTrail Data", signature_version: "v4", - signing_name: "mobileanalytics", + signing_name: "cloudtrail-data", target_prefix: nil } end - def put_events(%Client{} = client, input, options \\\\ []) do - url_path = "/2014-06-05/events" + @doc \"\"\" + Ingests your application events into CloudTrail Lake. + + A required parameter, + `auditEvents`, accepts the JSON records (also called + *payload*) of events that you want CloudTrail to ingest. You + can add up to 100 of these events (or up to 1 MB) per `PutAuditEvents` + request. + \"\"\" + def put_audit_events(%Client{} = client, input, options \\\\ []) do + url_path = "/PutAuditEvents" + headers = [] - {headers, input} = + {query_params, input} = [ - {"clientContext", "x-amz-Client-Context"}, - {"clientContextEncoding", "x-amz-Client-Context-Encoding"} + {"channelArn", "channelArn"}, + {"externalId", "externalId"} ] |> Request.build_params(input) - query_params = [] - meta = metadata() |> Map.put_new(:host_prefix, "my-host-prefix.") Request.request_rest( @@ -411,7 +412,7 @@ defmodule AWS.CodeGenTest do headers, input, options, - 202 + 200 ) end end diff --git a/test/fixtures/apis_specs/cloudtrail-data.json b/test/fixtures/apis_specs/cloudtrail-data.json new file mode 100644 index 0000000..bde1305 --- /dev/null +++ b/test/fixtures/apis_specs/cloudtrail-data.json @@ -0,0 +1,1005 @@ +{ + "smithy": "2.0", + "shapes": { + "com.amazonaws.cloudtraildata#AuditEvent": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.cloudtraildata#Uuid", + "traits": { + "smithy.api#documentation": "

The original event ID from the source event.

", + "smithy.api#required": {} + } + }, + "eventData": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The content of an audit event that comes from the event, such as userIdentity, \n userAgent, and eventSource.

", + "smithy.api#required": {} + } + }, + "eventDataChecksum": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

A checksum is a base64-SHA256 algorithm that helps you verify that CloudTrail receives the event that matches \n with the checksum. Calculate the checksum by running a command like the following:

\n

\n printf %s $eventdata | openssl dgst -binary -sha256 | base64\n

" + } + } + }, + "traits": { + "smithy.api#documentation": "

An event from a source outside of Amazon Web Services that you want CloudTrail \n to log.

" + } + }, + "com.amazonaws.cloudtraildata#AuditEventResultEntries": { + "type": "list", + "member": { + "target": "com.amazonaws.cloudtraildata#AuditEventResultEntry" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 100 + } + } + }, + "com.amazonaws.cloudtraildata#AuditEventResultEntry": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.cloudtraildata#Uuid", + "traits": { + "smithy.api#documentation": "

The original event ID from the source event.

", + "smithy.api#required": {} + } + }, + "eventID": { + "target": "com.amazonaws.cloudtraildata#Uuid", + "traits": { + "smithy.api#documentation": "

The event ID assigned by CloudTrail.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A response that includes successful and failed event results.

" + } + }, + "com.amazonaws.cloudtraildata#AuditEvents": { + "type": "list", + "member": { + "target": "com.amazonaws.cloudtraildata#AuditEvent" + }, + "traits": { + "smithy.api#length": { + "min": 1, + "max": 100 + } + } + }, + "com.amazonaws.cloudtraildata#ChannelArn": { + "type": "string", + "traits": { + "aws.api#arnReference": {}, + "smithy.api#pattern": "^arn:.*$" + } + }, + "com.amazonaws.cloudtraildata#ChannelInsufficientPermission": { + "type": "structure", + "members": { + "message": { + "target": "smithy.api#String" + } + }, + "traits": { + "smithy.api#documentation": "

The caller's account ID must be the same as the channel owner's account ID.

", + "smithy.api#error": "client" + } + }, + "com.amazonaws.cloudtraildata#ChannelNotFound": { + "type": "structure", + "members": { + "message": { + "target": "smithy.api#String" + } + }, + "traits": { + "smithy.api#documentation": "

The channel could not be found.

", + "smithy.api#error": "client" + } + }, + "com.amazonaws.cloudtraildata#ChannelUnsupportedSchema": { + "type": "structure", + "members": { + "message": { + "target": "smithy.api#String" + } + }, + "traits": { + "smithy.api#documentation": "

The schema type of the event is not supported.

", + "smithy.api#error": "client" + } + }, + "com.amazonaws.cloudtraildata#CloudTrailDataService": { + "type": "service", + "version": "2021-08-11", + "operations": [ + { + "target": "com.amazonaws.cloudtraildata#PutAuditEvents" + } + ], + "traits": { + "aws.api#service": { + "sdkId": "CloudTrail Data", + "endpointPrefix": "cloudtrail-data" + }, + "aws.auth#sigv4": { + "name": "cloudtrail-data" + }, + "aws.protocols#restJson1": {}, + "smithy.api#cors": {}, + "smithy.api#documentation": "

The CloudTrail Data Service lets you ingest events into CloudTrail from any source in your\nhybrid environments, such as in-house or SaaS applications hosted on-premises or in the cloud,\nvirtual machines, or containers. You can store, access, analyze, troubleshoot and take action on\nthis data without maintaining multiple log aggregators and reporting tools. After you run \nPutAuditEvents to ingest your application activity into CloudTrail, you can use CloudTrail Lake to search, query, and analyze the data that is logged\nfrom your applications.

", + "smithy.api#title": "AWS CloudTrail Data Service", + "smithy.rules#endpointRuleSet": { + "version": "1.0", + "parameters": { + "Region": { + "builtIn": "AWS::Region", + "required": false, + "documentation": "The AWS region used to dispatch the request.", + "type": "String" + }, + "UseDualStack": { + "builtIn": "AWS::UseDualStack", + "required": true, + "default": false, + "documentation": "When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.", + "type": "Boolean" + }, + "UseFIPS": { + "builtIn": "AWS::UseFIPS", + "required": true, + "default": false, + "documentation": "When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.", + "type": "Boolean" + }, + "Endpoint": { + "builtIn": "SDK::Endpoint", + "required": false, + "documentation": "Override the endpoint used to send this request", + "type": "String" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "endpoint": { + "url": { + "ref": "Endpoint" + }, + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Region" + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "aws.partition", + "argv": [ + { + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + }, + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + }, + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://cloudtrail-data-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://cloudtrail-data-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://cloudtrail-data.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://cloudtrail-data.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + } + ] + }, + { + "conditions": [], + "error": "Invalid Configuration: Missing Region", + "type": "error" + } + ] + } + ] + }, + "smithy.rules#endpointTests": { + "testCases": [ + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data-fips.us-east-1.api.aws" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data-fips.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data.us-east-1.api.aws" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data-fips.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data-fips.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data-fips.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data-fips.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data-fips.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data-fips.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://cloudtrail-data.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with region not set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "UseFIPS": false, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips enabled and dualstack disabled", + "expect": { + "error": "Invalid Configuration: FIPS and custom endpoint are not supported" + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips disabled and dualstack enabled", + "expect": { + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } + } + ], + "version": "1.0" + } + } + }, + "com.amazonaws.cloudtraildata#DuplicatedAuditEventId": { + "type": "structure", + "members": { + "message": { + "target": "smithy.api#String" + } + }, + "traits": { + "smithy.api#documentation": "

Two or more entries in the request have the same event ID.

", + "smithy.api#error": "client" + } + }, + "com.amazonaws.cloudtraildata#ErrorCode": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 128 + } + } + }, + "com.amazonaws.cloudtraildata#ErrorMessage": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 1024 + } + } + }, + "com.amazonaws.cloudtraildata#ExternalId": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 2, + "max": 1224 + }, + "smithy.api#pattern": "^[\\w+=,.@:\\/-]*$" + } + }, + "com.amazonaws.cloudtraildata#InvalidChannelARN": { + "type": "structure", + "members": { + "message": { + "target": "smithy.api#String" + } + }, + "traits": { + "smithy.api#documentation": "

The specified channel ARN is not a valid \n channel ARN.

", + "smithy.api#error": "client" + } + }, + "com.amazonaws.cloudtraildata#PutAuditEvents": { + "type": "operation", + "input": { + "target": "com.amazonaws.cloudtraildata#PutAuditEventsRequest" + }, + "output": { + "target": "com.amazonaws.cloudtraildata#PutAuditEventsResponse" + }, + "errors": [ + { + "target": "com.amazonaws.cloudtraildata#ChannelInsufficientPermission" + }, + { + "target": "com.amazonaws.cloudtraildata#ChannelNotFound" + }, + { + "target": "com.amazonaws.cloudtraildata#ChannelUnsupportedSchema" + }, + { + "target": "com.amazonaws.cloudtraildata#DuplicatedAuditEventId" + }, + { + "target": "com.amazonaws.cloudtraildata#InvalidChannelARN" + }, + { + "target": "com.amazonaws.cloudtraildata#UnsupportedOperationException" + } + ], + "traits": { + "smithy.api#documentation": "

Ingests your application events into CloudTrail Lake. A required parameter,\n auditEvents, accepts the JSON records (also called\n payload) of events that you want CloudTrail to ingest. You\n can add up to 100 of these events (or up to 1 MB) per PutAuditEvents\n request.

", + "smithy.api#http": { + "uri": "/PutAuditEvents", + "method": "POST" + } + } + }, + "com.amazonaws.cloudtraildata#PutAuditEventsRequest": { + "type": "structure", + "members": { + "auditEvents": { + "target": "com.amazonaws.cloudtraildata#AuditEvents", + "traits": { + "smithy.api#documentation": "

The JSON payload of events that you want to ingest. You can also point to the JSON event\n payload in a file.

", + "smithy.api#required": {} + } + }, + "channelArn": { + "target": "com.amazonaws.cloudtraildata#ChannelArn", + "traits": { + "smithy.api#documentation": "

The ARN or ID (the ARN suffix) of a channel.

", + "smithy.api#httpQuery": "channelArn", + "smithy.api#required": {} + } + }, + "externalId": { + "target": "com.amazonaws.cloudtraildata#ExternalId", + "traits": { + "smithy.api#documentation": "

A unique identifier that is conditionally required when the channel's resource policy includes an external \n ID. This value can be any string, \n such as a passphrase or account number.

", + "smithy.api#httpQuery": "externalId" + } + } + } + }, + "com.amazonaws.cloudtraildata#PutAuditEventsResponse": { + "type": "structure", + "members": { + "successful": { + "target": "com.amazonaws.cloudtraildata#AuditEventResultEntries", + "traits": { + "smithy.api#documentation": "

Lists events in the provided event payload that were successfully ingested \n into CloudTrail.

", + "smithy.api#required": {} + } + }, + "failed": { + "target": "com.amazonaws.cloudtraildata#ResultErrorEntries", + "traits": { + "smithy.api#documentation": "

Lists events in the provided event payload that could not be \n ingested into CloudTrail, and includes the error code and error message \n returned for events that could not be ingested.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.cloudtraildata#ResultErrorEntries": { + "type": "list", + "member": { + "target": "com.amazonaws.cloudtraildata#ResultErrorEntry" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 100 + } + } + }, + "com.amazonaws.cloudtraildata#ResultErrorEntry": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.cloudtraildata#Uuid", + "traits": { + "smithy.api#documentation": "

The original event ID from the source event that could not be ingested by CloudTrail.

", + "smithy.api#required": {} + } + }, + "errorCode": { + "target": "com.amazonaws.cloudtraildata#ErrorCode", + "traits": { + "smithy.api#documentation": "

The error code for events that could not be ingested by CloudTrail. Possible error codes include: FieldTooLong, FieldNotFound, \n InvalidChecksum, InvalidData, InvalidRecipient, InvalidEventSource, AccountNotSubscribed, \n Throttling, and InternalFailure.

", + "smithy.api#required": {} + } + }, + "errorMessage": { + "target": "com.amazonaws.cloudtraildata#ErrorMessage", + "traits": { + "smithy.api#documentation": "

The message that describes the error for events that could not be ingested by CloudTrail.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Includes the error code and error message for events that could not be ingested by CloudTrail.

" + } + }, + "com.amazonaws.cloudtraildata#UnsupportedOperationException": { + "type": "structure", + "members": { + "message": { + "target": "smithy.api#String" + } + }, + "traits": { + "smithy.api#documentation": "

The operation requested is not supported in this region or account.

", + "smithy.api#error": "client" + } + }, + "com.amazonaws.cloudtraildata#Uuid": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 128 + }, + "smithy.api#pattern": "^[-_A-Za-z0-9]+$" + } + } + } +} \ No newline at end of file diff --git a/test/fixtures/apis_specs/mobileanalytics-2014-06-05-api-2.json b/test/fixtures/apis_specs/mobileanalytics-2014-06-05-api-2.json deleted file mode 100644 index c593da2..0000000 --- a/test/fixtures/apis_specs/mobileanalytics-2014-06-05-api-2.json +++ /dev/null @@ -1,119 +0,0 @@ -{ - "version":"2.0", - "metadata":{ - "apiVersion":"2014-06-05", - "endpointPrefix":"mobileanalytics", - "serviceFullName":"Amazon Mobile Analytics", - "signatureVersion":"v4", - "protocol":"rest-json" - }, - "operations":{ - "PutEvents":{ - "name":"PutEvents", - "http":{ - "method":"POST", - "requestUri":"/2014-06-05/events", - "responseCode":202 - }, - "input":{"shape":"PutEventsInput"}, - "errors":[ - { - "shape":"BadRequestException", - "error":{"httpStatusCode":400}, - "exception":true - } - ] - } - }, - "shapes":{ - "BadRequestException":{ - "type":"structure", - "members":{ - "message":{"shape":"String"} - }, - "error":{"httpStatusCode":400}, - "exception":true - }, - "Double":{"type":"double"}, - "Event":{ - "type":"structure", - "required":[ - "eventType", - "timestamp" - ], - "members":{ - "eventType":{"shape":"String50Chars"}, - "timestamp":{"shape":"ISO8601Timestamp"}, - "session":{"shape":"Session"}, - "version":{"shape":"String10Chars"}, - "attributes":{"shape":"MapOfStringToString"}, - "metrics":{"shape":"MapOfStringToNumber"} - } - }, - "EventListDefinition":{ - "type":"list", - "member":{"shape":"Event"} - }, - "ISO8601Timestamp":{"type":"string"}, - "Long":{"type":"long"}, - "MapOfStringToNumber":{ - "type":"map", - "key":{"shape":"String50Chars"}, - "value":{"shape":"Double"}, - "min":0, - "max":50 - }, - "MapOfStringToString":{ - "type":"map", - "key":{"shape":"String50Chars"}, - "value":{"shape":"String0to1000Chars"}, - "min":0, - "max":50 - }, - "PutEventsInput":{ - "type":"structure", - "required":[ - "events", - "clientContext" - ], - "members":{ - "events":{"shape":"EventListDefinition"}, - "clientContext":{ - "shape":"String", - "location":"header", - "locationName":"x-amz-Client-Context" - }, - "clientContextEncoding":{ - "shape":"String", - "location":"header", - "locationName":"x-amz-Client-Context-Encoding" - } - } - }, - "Session":{ - "type":"structure", - "members":{ - "id":{"shape":"String50Chars"}, - "duration":{"shape":"Long"}, - "startTimestamp":{"shape":"ISO8601Timestamp"}, - "stopTimestamp":{"shape":"ISO8601Timestamp"} - } - }, - "String":{"type":"string"}, - "String0to1000Chars":{ - "type":"string", - "min":0, - "max":1000 - }, - "String10Chars":{ - "type":"string", - "min":1, - "max":10 - }, - "String50Chars":{ - "type":"string", - "min":1, - "max":50 - } - } -} diff --git a/test/fixtures/apis_specs/mobileanalytics-2014-06-05-docs-2.json b/test/fixtures/apis_specs/mobileanalytics-2014-06-05-docs-2.json deleted file mode 100644 index 838e32f..0000000 --- a/test/fixtures/apis_specs/mobileanalytics-2014-06-05-docs-2.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "version": "2.0", - "operations": { - "PutEvents": "

The PutEvents operation records one or more events. You can have up to 1,500 unique custom events per app, any combination of up to 40 attributes and metrics per custom event, and any number of attribute or metric values.

" - }, - "service": "

Amazon Mobile Analytics is a service for collecting, visualizing, and understanding app usage data at scale.

", - "shapes": { - "BadRequestException": { - "base": "

An exception object returned when a request fails.

", - "refs": { - } - }, - "Double": { - "base": null, - "refs": { - "MapOfStringToNumber$value": null - } - }, - "Event": { - "base": "

A JSON object representing a batch of unique event occurrences in your app.

", - "refs": { - "EventListDefinition$member": null - } - }, - "EventListDefinition": { - "base": null, - "refs": { - "PutEventsInput$events": "

An array of Event JSON objects

" - } - }, - "ISO8601Timestamp": { - "base": null, - "refs": { - "Event$timestamp": "

The time the event occurred in ISO 8601 standard date time format. For example, 2014-06-30T19:07:47.885Z

", - "Session$startTimestamp": "

The time the event started in ISO 8601 standard date time format. For example, 2014-06-30T19:07:47.885Z

", - "Session$stopTimestamp": "

The time the event terminated in ISO 8601 standard date time format. For example, 2014-06-30T19:07:47.885Z

" - } - }, - "Long": { - "base": null, - "refs": { - "Session$duration": "

The duration of the session.

" - } - }, - "MapOfStringToNumber": { - "base": null, - "refs": { - "Event$metrics": "

A collection of key-value pairs that gives additional, measurable context to the event. The key-value pairs are specified by the developer.

This collection can be empty or the attribute object can be omitted.

" - } - }, - "MapOfStringToString": { - "base": null, - "refs": { - "Event$attributes": "

A collection of key-value pairs that give additional context to the event. The key-value pairs are specified by the developer.

This collection can be empty or the attribute object can be omitted.

" - } - }, - "PutEventsInput": { - "base": "

A container for the data needed for a PutEvent operation

", - "refs": { - } - }, - "Session": { - "base": "

Describes the session. Session information is required on ALL events.

", - "refs": { - "Event$session": "

The session the event occured within.

" - } - }, - "String": { - "base": null, - "refs": { - "BadRequestException$message": "

A text description associated with the BadRequestException object.

", - "PutEventsInput$clientContext": "

The client context including the client ID, app title, app version and package name.

", - "PutEventsInput$clientContextEncoding": "

The encoding used for the client context.

" - } - }, - "String0to1000Chars": { - "base": null, - "refs": { - "MapOfStringToString$value": null - } - }, - "String10Chars": { - "base": null, - "refs": { - "Event$version": "

The version of the event.

" - } - }, - "String50Chars": { - "base": null, - "refs": { - "Event$eventType": "

A name signifying an event that occurred in your app. This is used for grouping and aggregating like events together for reporting purposes.

", - "MapOfStringToNumber$key": null, - "MapOfStringToString$key": null, - "Session$id": "

A unique identifier for the session

" - } - } - } -}