Skip to content

Commit b527837

Browse files
committed
feat(event): add AppSyncResolverEvent structure and example payload
1 parent fd0354e commit b527837

File tree

2 files changed

+158
-0
lines changed

2 files changed

+158
-0
lines changed

lambda-events/src/event/appsync/mod.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ where
6161
pub source_ip: Vec<String>,
6262
#[serde(default)]
6363
pub default_auth_strategy: Option<String>,
64+
#[serde(default)]
65+
pub groups: Option<Vec<String>>,
6466
}
6567

6668
pub type AppSyncOperation = String;
@@ -117,6 +119,88 @@ where
117119
pub ttl_override: Option<i64>,
118120
}
119121

122+
/// `AppSyncResolverEvent` represents the default payload structure sent by AWS AppSync
123+
/// when using **Direct Lambda Resolvers** (i.e., when both request and response mapping
124+
/// templates are disabled).
125+
///
126+
/// This structure includes the full AppSync **Context object**, as described in the
127+
/// [AppSync Direct Lambda resolver reference](https://docs.aws.amazon.com/appsync/latest/devguide/direct-lambda-reference.html).
128+
///
129+
/// It is recommended when working without VTL templates and relying on the standard
130+
/// AppSync-to-Lambda event format.
131+
///
132+
/// See also:
133+
/// - [AppSync resolver mapping template context reference](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference.html)
134+
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
135+
pub struct AppSyncResolverEvent<TArguments = Option<Value>, TSource = Option<Value>> {
136+
pub arguments: TArguments,
137+
pub identity: Option<AppSyncIdentity>,
138+
pub source: TSource,
139+
pub request: AppSyncRequest,
140+
pub info: AppSyncInfo,
141+
#[serde(default)]
142+
pub prev: Option<AppSyncPrevResult>,
143+
pub stash: HashMap<String, Value>,
144+
}
145+
146+
/// `AppSyncRequest` contains request-related metadata for a resolver invocation,
147+
/// including client-sent headers and optional custom domain name.
148+
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
149+
#[serde(rename_all = "camelCase")]
150+
pub struct AppSyncRequest {
151+
#[serde(default)]
152+
pub headers: HashMap<String, Option<String>>,
153+
#[serde(default)]
154+
pub domain_name: Option<String>,
155+
}
156+
157+
/// `AppSyncInfo` contains metadata about the current GraphQL field being resolved.
158+
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
159+
#[serde(rename_all = "camelCase")]
160+
pub struct AppSyncInfo {
161+
#[serde(default)]
162+
pub selection_set_list: Vec<String>,
163+
#[serde(rename = "selectionSetGraphQL")]
164+
pub selection_set_graphql: String,
165+
pub parent_type_name: String,
166+
pub field_name: String,
167+
#[serde(default)]
168+
pub variables: HashMap<String, Value>,
169+
}
170+
171+
/// `AppSyncPrevResult` contains the result of the previous step in a pipeline resolver.
172+
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
173+
pub struct AppSyncPrevResult {
174+
#[serde(default)]
175+
pub result: HashMap<String, Value>,
176+
}
177+
178+
/// `AppSyncIdentity` represents the identity of the caller as determined by the
179+
/// configured AppSync authorization mechanism (IAM, Cognito, OIDC, or Lambda).
180+
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
181+
#[serde(untagged, rename_all = "camelCase")]
182+
pub enum AppSyncIdentity {
183+
IAM(AppSyncIamIdentity),
184+
Cognito(AppSyncCognitoIdentity),
185+
OIDC(AppSyncIdentityOIDC),
186+
Lambda(AppSyncIdentityLambda),
187+
}
188+
189+
/// `AppSyncIdentityOIDC` represents identity information when using OIDC-based authorization.
190+
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
191+
pub struct AppSyncIdentityOIDC {
192+
pub claims: Value,
193+
pub issuer: String,
194+
pub sub: String,
195+
}
196+
197+
/// `AppSyncIdentityLambda` represents identity information when using AWS Lambda
198+
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
199+
#[serde(rename_all = "camelCase")]
200+
pub struct AppSyncIdentityLambda {
201+
pub resolver_context: Value,
202+
}
203+
120204
#[cfg(test)]
121205
mod test {
122206
use super::*;
@@ -160,4 +244,14 @@ mod test {
160244
let reparsed: AppSyncLambdaAuthorizerResponse = serde_json::from_slice(output.as_bytes()).unwrap();
161245
assert_eq!(parsed, reparsed);
162246
}
247+
248+
#[test]
249+
#[cfg(feature = "appsync")]
250+
fn example_appsync_vtl_resolver() {
251+
let data = include_bytes!("../../fixtures/example-appsync-vtl-resolver.json");
252+
let parsed: AppSyncResolverEvent = serde_json::from_slice(data).unwrap();
253+
let output: String = serde_json::to_string(&parsed).unwrap();
254+
let reparsed: AppSyncResolverEvent = serde_json::from_slice(output.as_bytes()).unwrap();
255+
assert_eq!(parsed, reparsed);
256+
}
163257
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"arguments": {
3+
"input": "foo"
4+
},
5+
"identity": {
6+
"sourceIp": [
7+
"x.x.x.x"
8+
],
9+
"userArn": "arn:aws:iam::123456789012:user/appsync",
10+
"accountId": "666666666666",
11+
"user": "AIDAAAAAAAAAAAAAAAAAA"
12+
},
13+
"info": {
14+
"fieldName": "greet",
15+
"parentTypeName": "Query",
16+
"selectionSetGraphQL": "",
17+
"selectionSetList": [],
18+
"variables": {
19+
"inputVar": "foo"
20+
}
21+
},
22+
"prev": null,
23+
"request": {
24+
"domainName": null,
25+
"headers": {
26+
"accept": "application/json, text/plain, */*",
27+
"accept-encoding": "gzip, deflate, br, zstd",
28+
"accept-language": "en-US,en;q=0.9,ja;q=0.8,en-GB;q=0.7",
29+
"cloudfront-forwarded-proto": "https",
30+
"cloudfront-is-desktop-viewer": "true",
31+
"cloudfront-is-mobile-viewer": "false",
32+
"cloudfront-is-smarttv-viewer": "false",
33+
"cloudfront-is-tablet-viewer": "false",
34+
"cloudfront-viewer-asn": "17676",
35+
"cloudfront-viewer-country": "JP",
36+
"content-length": "40",
37+
"content-type": "application/json",
38+
"host": "2ojpkjk2ejb57l7stgad5o4qiq.appsync-api.ap-northeast-1.amazonaws.com",
39+
"origin": "https://ap-northeast-1.console.aws.amazon.com",
40+
"priority": "u=1, i",
41+
"referer": "https://ap-northeast-1.console.aws.amazon.com/",
42+
"sec-ch-ua": "\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Microsoft Edge\";v=\"138\"",
43+
"sec-ch-ua-mobile": "?0",
44+
"sec-ch-ua-platform": "\"Windows\"",
45+
"sec-fetch-dest": "empty",
46+
"sec-fetch-mode": "cors",
47+
"sec-fetch-site": "cross-site",
48+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0",
49+
"via": "2.0 ee337d4db5c7ebfdc8ec0798a1ede776.cloudfront.net (CloudFront)",
50+
"x-amz-cf-id": "O3ZflUCq6_TzxjouyYB3zg7-kl7Ze-gXbniM2jJ3hAOfDFpPMGRu3Q==",
51+
"x-amz-user-agent": "AWS-Console-AppSync/",
52+
"x-amzn-appsync-is-vpce-request": "false",
53+
"x-amzn-remote-ip": "126.94.70.90",
54+
"x-amzn-requestid": "7ada8740-bbf4-49e8-bf45-f10b3d67159b",
55+
"x-amzn-trace-id": "Root=1-68713e21-7a03739120ad60703e794b22",
56+
"x-api-key": "***",
57+
"x-forwarded-for": "***",
58+
"x-forwarded-port": "443",
59+
"x-forwarded-proto": "https"
60+
}
61+
},
62+
"source": null,
63+
"stash": {}
64+
}

0 commit comments

Comments
 (0)