XML endpoints with the-sett/elm-aws-core

Hi,

I’m using @rupert the-sett/elm-aws-core to get information from the AWS API, which unfortunately is very inconsistent. Most of the endpoints return JSON and that works fine with that lib (I’m using auto scaling and ecs without issues), but the EC2 endpoint returns XML.

The lib doesn’t have any options not to decode JSON as far as I can tell, which does not work at all :

let ec2 region = Service.defineRegional "ec2" "2016-11-15" Service.QUERY Service.SignV4 (Service.setXmlNamespace "https://ec2.amazonaws.com/doc/2016-11-15/") region in
let params = [("Action", "DescribeImages"), ("Version", "2016-11-15"), ("Owner.1", "self")] in
Http.request "DescribeImages" GET "/" Http.emptyBody JSONDECODERHERE |> Http.addQuery params |> Http.send (ec2 region) creds |> Task.attempt msg
Failed : Problem with the given value:

"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<DescribeImagesResponse .......

As you can see in there you need to pass a JSON Decoder to Http.request, but that, of course, fails when receiving XML. I asked the question on SO where it was suggested to use elm’s HTTP package directly, which can return the raw string instead of trying to parse it, but that doesn’t look simple to integrate with the package, or at least I don’t see how without re-implementing a good chunk of it.

Any suggestions on how to query the EC2 API ?

Thank you

Hi,

AWS services cover 5 possible Protocols, which are described by the Protocol enum, which I’ve annotated with some comments to describe what each is like:

type Protocol
    = EC2 -- XML based, requests always POST, endpoint name in header.
    | JSON -- JSON based, requests always POST, endpoint name in header.
    | QUERY -- Query param based.
    | REST_JSON -- JSON REST API
    | REST_XML -- XML REST API

Unfortunately, I have only got as far as implementing and testing support for the JSON protocol.

It looks like you are wanting to use EC2, which is the odd one out; as far as I can tell, only the EC2 service uses this protocol.

The intention is to add support for all AWS services, but its a big task and I am not committed to a timeline for completing it. However, good to know that there is some demand, and I will record this in an issue on the GitHub repository at least in order to guide prioritisation.

Things you could do to get it working.

  1. Perhaps you could use the JSON protocol, but just use Decode.string to get the whole response body as a String? and then do the XML parsing yourself on that String. Not 100% sure this will work, its a hack but might get you up and running if it does work.

  2. You can do the HTTP request using the elm/http package easily enough. The tricky part is the V4 signing of the request. You can find the code for that below, either try to use it as-is, or copy paste into your own project and adapt it as needed there:

  1. Fork elm-aws-core and add support for EC2 yourself. Will be happy to support getting a good patch out of that and incorporating it into the published package too.

Hi @Ulrar!

If you are going to take the pass #1 @rupert suggested, and only interested in “decodeing” XML responses (not “sending” XML document), please take a look at elm-xml-decode which I maintain.
It is much like Json.Decode, with some twists for XML characteristics. If you are already familiar with Json.Decode, it should not be a big gap.

The workflow shoule be like this: you accept XML responses from APIs as raw strings using Json.Decode.string, then plug the result into Xml.Decode.run with your own Xml.Decode.Decoder YourData.
Contact me wherever (GitHub issue could be nicer), if you find any difficulties. :chears:

Hi,

I did try #1 yesterday already actually, hoping to use elm-xml-decode (I already wrote the decoder with it, looks very easy indeed !) and unfortunately that’s what generates the error above. I assume Json.Decode.string still tries to decode the whole thing as json, it’s probably expecting the whole thing to be wrapped in curly brackets.

I’ll try to get that working then, and if I do get it working I’ll make sure to make a PR with my code.
Thanks !

Just having a think about it… on the request you build (which is confusingly called Unsigned), a Decoder needs to be included:

type alias Unsigned a =
    { name : String
    , method : String
    , path : String
    , body : Body
    , decoder : Decoder a
    , headers : List ( String, String )
    , query : List ( String, String )
    , responseParser : Maybe (Http.Response String -> Result Http.Error a)
    }

If I were to change the type of that decoder field to String -> a instead of Decoder it will keep things more open, and you would be able to pass in your own String -> a function that decodes the String into XML into whatever Elm records you like.

Actually I think the decoder needs its type to be String -> Result String a, to be able to handle the case where decoding fails (with an error message).

It will be possible to do this as decoder is only used in the context of Decode.decodeString for decoding string responses as JSON.

Also, I forgot about this feature that is already there:

https://package.elm-lang.org/packages/the-sett/elm-aws-core/latest/AWS-Core-Http#setResponseParser

This lets you override the Decoder and instead provide your own handler for the entire HTTP response. You should be able to get the response body from an Http.GoodStatus_ response and decode to XML that way?

Ok, I published a new version. The old Http.request function is renamed to Http.requestWithJsonDecoder, and has been replaced by one that takes the decoder as a String -> Result String a.

https://package.elm-lang.org/packages/the-sett/elm-aws-core/4.0.0/AWS-Core-Http#request

Hopefully, this will get you over the line?

1 Like

That works perfectly ! I was trying to get that to work with setResponseParser when you published that, but with the new Http.request I’m able to use elm-xml-decode and parse the XML.
Now I’ll try to get the Batch endpoint to work, which works yet another way of course.

Thanks a lot for the help and quick new version !

Great agility @rupert!

I misrepresented where things are at with this explanation.

In addition to elm-aws-core I am working on Elm AWS Codegen. This aims to generate service stubs in Elm for all AWS services. It will effectively give you one function for each endpoint, and a data model descibing all the requests and responses so you never have to get down to building these all yourself. It is the code generator that only supports the JSON protocol so far.

elm-aws-core should probably be able to support them all, but I have not yet tested it on all.

I’ve added an issue on the code generator for tracking services there is a demand for:

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.