brendanhay / amazonka

A comprehensive Amazon Web Services SDK for Haskell.
https://amazonka.brendanhay.nz
Other
605 stars 226 forks source link

`instance AWSRequest (Request a) where` #1000

Open parsonsmatt opened 4 months ago

parsonsmatt commented 4 months ago

We were doing some header modification in our work app, and I was unable to presign a Request GetObject. It seems like a potentially missing instance:

instance AWSRequest a => AWSRequest (Request a) where
    type AWSResponse (Request a) = AWSResponse a

    request serviceModifier req =
        req { service = serviceModifier (service req) }

    response k s p r =
        response k s (Proxy :: Proxy a) r

This builds fine on my machine and seems to fit the spirit of the class. Should I open a PR?

endgame commented 3 months ago

I've been thinking about this for a couple of days, and I'm trying to decide between "this seems reasonable, as it's sort of the 'least specific' request that makes sense" and "how did this become necessary?".

You said this was from your work, so I don't expect you to be able to show full code, but are you able to sketch out what's going on? Are you talking to a non-AWS store? Have you tried to use the hooks system to manipulate requests (and was it insufficient?)?

parsonsmatt commented 3 months ago

Actually, I didnt' end up needing it - we were modifying the request headers for presigning just like how defaultHeaders already does, so I was able to remove it. We needed the AWSRequest (Request a) instance so we could call presign on the Request a after modifying headers.

I did not try to use the hooks system. I don't think that would have worked for this case, because we needed to presign the request and send that signature to our front-end, rather than sending the request ourselves at that point.

presignWithHeaders would have worked to take care of this use case - but if there's an instance AWSRequest (Request a), then you could just as easily do presign (request newGetObject & request_headers %~ defaultHeaders & request_service %~ modifyService).

That said, it does seem like directly modifying the Request a is a bit of a smell? Possibly this instance should be TypeErrored with guidance on accepted alternatives? Or we can wait until the per-instance-warning pragmas are available to avoid poisoning the well.

endgame commented 3 months ago

I'm not sure that we want exactly this, but we might want something like it? It would be really valuable for Amazonka to be able to presign arbitrary HTTP requests - signed non-API requests turn up in Lambda Function URLs, API Gateway authorisers and probably other places.

I'm not sure if that means we want a custom "raw request" in amazonka-core or something (which is what I've done in the past, along with a simple representation of an arbitrary HTTP response), but I wonder if there's a better method that involves a carefully-chosen instance on Request a? The fact that your proposed instance doesn't give you access to the "raw" HTTP response feels a bit odd: I'd expect a symmetric interface where you can inspect additional details on the request as well as the response.