smithy-lang/smithy

How to define an input that has several fields read from http body, and also holds the whole http body as a string?

x1a0 opened this issue · 6 comments

x1a0 commented

Hi,

I am implementing a service, that needs to verify incoming Slack request signature. The signature is calculated as:

sha256(request_timestamp, request_body, secret)

However if using @httpPayload, it seems I cannot define other members in the input structure that are extracted from http body.

@httpPayload is meant to encapsulate the entire body, which is why you can't apply the trait to multiple members of the input structure. I need a little more information about your use case to provide a recommendation, but if you are wanting the payload to consist of multiple fields, you should have a structure that contains everything you want included in the body, and apply the trait to a member targeting that structure.

x1a0 commented

Oh hmm... sorry I didn't make it clear. In my case, I only need @httpPayload trait on one member. However there are other members that I want to read from the body (json).
This is the input structure:

structure SlackWebhookInput {
    @required
    type: String

    challenge: String

    token: String

    @httpHeader("X-Slack-Signature")
    signature: String

    @httpHeader("X-Slack-Request-Timestamp")
    request_timestamp: String

    @httpPayload
    body: String

    event: Event
}

And I got this error:

18| structure SlackWebhookInput {
  | ^

A member of this structure is marked with the httpPayload trait, but the
following structure members are not explicitly bound to the HTTP message:
challenge, event, token, type

FAILURE: Validated 511 shapes (ERROR: 1)
x1a0 commented

@haydenbaker I just realized that my requirement might be a server-side only requirement. On client side, one would never need to create a request by provide both whole payload and individual members. So I am not sure if Smithy could support what I want...

WRT the error message, all other members must be bound. If you want the body to consist of multiple members, again, you need a separate structure.

@haydenbaker Sorry that I don't really follow on "you need a separate structure" :-|
I understand that all other members must be bound. However in my case, "other members" are also read from body - not header, not query string, not path. Is there a way that I could bind those members to body as well somehow?
Basically, only one member is annotated with @httpPayload, and some other members are read from the body JSON.

That's not how @httpPayload functions. The trait's definition is:

Binds a single structure member to the body of an HTTP message.

The one member is the only thing that will be populated from the body. Anything inside the body of the request should be bound to members in the structure targeting it or left to make up an implicit body.

Most generated service implementations will provide you access to the raw HTTP body if necessary, for use in your other piece. Going that route would mean removing the @httpPayload member, making the structure look like this:

structure SlackWebhookInput {
    @required
    type: String

    challenge: String

    token: String

    @httpHeader("X-Slack-Signature")
    signature: String

    @httpHeader("X-Slack-Request-Timestamp")
    request_timestamp: String

    event: Event
}

Meaning the on-wire representation would look like this:

{
    "challenge": "Some string content",
    "token": "Some string content",
    "event": <the Event shape's encoding>
}