From 912ab3c83c4160ac62d2ff6f99e738e3c820ea10 Mon Sep 17 00:00:00 2001 From: Brian Underwood <public@brian-underwood.codes> Date: Tue, 3 May 2022 11:23:47 +0200 Subject: [PATCH] Add the ability to have safe params This will pass a {:safe, _} tuple down into XmlBuilder where we can avoid escaping strings that don't need to be escaped --- README.md | 8 ++++ lib/soap/request/params.ex | 39 +++++++++---------- mix.exs | 2 +- .../xml/send_service/MarkedAsSafeRequest.xml | 1 + test/soap/request/params_test.exs | 14 +++++++ 5 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 test/fixtures/xml/send_service/MarkedAsSafeRequest.xml diff --git a/README.md b/README.md index bc436ea..3005779 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,14 @@ iex> {:ok, response} = Soap.call(wsdl, action, params) }} ``` +Safe strings: + +You might have strings that you know are safe where you'd like to skip the escaping step in generating the XML (for example if you have a lot of Base64 encoded data). For this situation you can mark data as "safe": + +```elixir +params = %{data: {:__safe, data}} +``` + Parse response: ```elixir diff --git a/lib/soap/request/params.ex b/lib/soap/request/params.ex index 7f10aa8..941dccd 100644 --- a/lib/soap/request/params.ex +++ b/lib/soap/request/params.ex @@ -90,6 +90,7 @@ defmodule Soap.Request.Params do validate_type(k, v, type) end + defp validate_type(k, {:safe, v}, type), do: validate_type(k, v, type) defp validate_type(_k, v, "string") when is_binary(v), do: nil defp validate_type(k, _v, type = "string"), do: type_error_message(k, type) @@ -159,44 +160,40 @@ defmodule Soap.Request.Params do params |> Enum.map(&construct_xml_request_body/1) end - @spec construct_xml_request_body(params :: tuple()) :: tuple() - defp construct_xml_request_body({tag, attrs, nested}) do - [{to_string(tag), attrs, construct_xml_request_body(nested)}] + defp construct_xml_request_body({:__safe, value}), do: {:safe, value} + + defp construct_xml_request_body({tag, value}) do + {to_string(tag), nil, construct_xml_request_body(value)} end - defp construct_xml_request_body(params) when is_tuple(params) do - params - |> Tuple.to_list() - |> Enum.map(&construct_xml_request_body/1) - |> insert_tag_parameters - |> List.to_tuple() + @spec construct_xml_request_body({term(), term()}) :: {term(), term()} + defp construct_xml_request_body({tag, attrs, nested}) do + [{to_string(tag), attrs, construct_xml_request_body(nested)}] end @spec construct_xml_request_body(params :: String.t() | atom() | number()) :: String.t() defp construct_xml_request_body(params) when is_atom(params), do: params |> to_string() defp construct_xml_request_body(params) when is_binary(params) or is_number(params), do: params + # defp construct_xml_request_header({:__safe, value}), do: {:safe, value} + + @spec construct_xml_request_header({term(), term()}) :: {term(), term()} + defp construct_xml_request_header({tag, value}) do + {to_string(tag), nil, construct_xml_request_header(value)} + end + + @spec insert_tag_parameters(params :: list()) :: list() + defp insert_tag_parameters(params) when is_list(params), do: params |> List.insert_at(1, nil) + @spec construct_xml_request_header(params :: map() | list()) :: list() defp construct_xml_request_header(params) when is_map(params) or is_list(params) do params |> Enum.map(&construct_xml_request_header/1) end - @spec construct_xml_request_header(params :: tuple()) :: tuple() - defp construct_xml_request_header(params) when is_tuple(params) do - params - |> Tuple.to_list() - |> Enum.map(&construct_xml_request_header/1) - |> insert_tag_parameters - |> List.to_tuple() - end - @spec construct_xml_request_header(params :: String.t() | atom() | number()) :: String.t() defp construct_xml_request_header(params) when is_atom(params) or is_number(params), do: params |> to_string defp construct_xml_request_header(params) when is_binary(params), do: params - @spec insert_tag_parameters(params :: list()) :: list() - defp insert_tag_parameters(params) when is_list(params), do: params |> List.insert_at(1, nil) - @spec add_action_tag_wrapper(list(), map(), String.t()) :: list() defp add_action_tag_wrapper(body, wsdl, operation) do action_tag_attributes = handle_element_form_default(wsdl[:schema_attributes]) diff --git a/mix.exs b/mix.exs index 58ef0a4..06ee65d 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule Soap.MixProject do use Mix.Project @source_url "https://github.com/elixir-soap/soap" - @version "1.1.0" + @version "1.1.1" def project do [ diff --git a/test/fixtures/xml/send_service/MarkedAsSafeRequest.xml b/test/fixtures/xml/send_service/MarkedAsSafeRequest.xml new file mode 100644 index 0000000..06ee718 --- /dev/null +++ b/test/fixtures/xml/send_service/MarkedAsSafeRequest.xml @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="com.esendex.ems.soapinterface" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><env:Header/><env:Body><tns:sendMessage xmlns="com.esendex.ems.soapinterface"><type>TY&PE</type></tns:sendMessage></env:Body></env:Envelope> diff --git a/test/soap/request/params_test.exs b/test/soap/request/params_test.exs index 636330c..0e8a22c 100644 --- a/test/soap/request/params_test.exs +++ b/test/soap/request/params_test.exs @@ -56,6 +56,20 @@ defmodule Soap.Request.ParamsTest do assert function_result == xml_body end + test "values can be marked as safe" do + xml_body = + Fixtures.load_xml("send_service/MarkedAsSafeRequest.xml") + |> String.replace("WSPB", "123") + + parameters = %{type: {:__safe, "TY&PE"}} + {_, wsdl} = Wsdl.parse_from_file(@wsdl_path) + # This will be an invalid XML file, but we need to test that XmlBuilder + # sends the value straight through + function_result = Params.build_body(wsdl, @operation, parameters, nil) + + assert function_result == xml_body + end + test "#build_body returns wrong date format errors" do parameters = %{"date" => "09:00:00"} {_, wsdl} = Wsdl.parse_from_file(@wsdl_path)