Skip to content

Commit 9b8f7ae

Browse files
committed
Added new tests, updated readme and incorporated review comments
1 parent 4356b85 commit 9b8f7ae

7 files changed

+231
-64
lines changed

Libraries/src/Amazon.Lambda.AppSyncEvents/Amazon.Lambda.AppSyncEvents.csproj

+5-18
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,9 @@
1212
<PackageTags>AWS;Amazon;Lambda</PackageTags>
1313
</PropertyGroup>
1414

15-
<!-- .NET Standard 2.0 specific settings -->
16-
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
17-
<DefineConstants>NETSTANDARD_2_0</DefineConstants>
18-
<WarningsAsErrors>IL2026,IL2067,IL2075</WarningsAsErrors>
19-
<IsTrimmable>true</IsTrimmable>
15+
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0'">
16+
<WarningsAsErrors>IL2026,IL2067,IL2075</WarningsAsErrors>
17+
<IsTrimmable>true</IsTrimmable>
2018
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
21-
</PropertyGroup>
22-
23-
<!-- Newtonsoft.Json only for .NET Standard 2.0 -->
24-
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
25-
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
26-
</ItemGroup>
27-
28-
<!-- System.Text.Json for .NET Core 3.1 and .NET 8.0 -->
29-
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1' OR '$(TargetFramework)' == 'net8.0'">
30-
<PackageReference Include="System.Text.Json" Version="8.0.5" />
31-
</ItemGroup>
32-
33-
</Project>
19+
</PropertyGroup>
20+
</Project>
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,67 @@
11
using System.Collections.Generic;
2-
32
namespace Amazon.Lambda.AppSyncEvents
43
{
5-
// Represents an AppSync authorization event
4+
/// <summary>
5+
/// Represents an AWS AppSync authorization event that is sent to a Lambda authorizer
6+
/// for evaluating access permissions to the GraphQL API.
7+
/// </summary>
68
public class AppSyncAuthorizerEvent
79
{
8-
// The authorization token from the request
10+
/// <summary>
11+
/// Gets or sets the authorization token received from the client request.
12+
/// This token is used to make authorization decisions.
13+
/// </summary>
914
public string AuthorizationToken { get; set; }
1015

11-
// Headers from the request
16+
/// <summary>
17+
/// Gets or sets the headers from the client request.
18+
/// Contains key-value pairs of HTTP header names and their values.
19+
/// </summary>
1220
public Dictionary<string, string> RequestHeaders { get; set; }
1321

14-
// Context information about the request
22+
/// <summary>
23+
/// Gets or sets the context information about the AppSync request.
24+
/// Contains metadata about the API and the GraphQL operation being executed.
25+
/// </summary>
1526
public RequestContext RequestContext { get; set; }
1627
}
1728

18-
// Request context for AppSync authorization
19-
29+
/// <summary>
30+
/// Contains contextual information about the AppSync request being authorized.
31+
/// This class provides details about the API, account, and GraphQL operation.
32+
/// </summary>
2033
public class RequestContext
2134
{
22-
// The ID of the AppSync API
35+
/// <summary>
36+
/// Gets or sets the unique identifier of the AppSync API.
37+
/// </summary>
2338
public string ApiId { get; set; }
2439

25-
// The AWS account ID
40+
/// <summary>
41+
/// Gets or sets the AWS account ID where the AppSync API is deployed.
42+
/// </summary>
2643
public string AccountId { get; set; }
2744

28-
// Unique identifier for the request
45+
/// <summary>
46+
/// Gets or sets the unique identifier for this specific request.
47+
/// </summary>
2948
public string RequestId { get; set; }
3049

31-
// The GraphQL query string
50+
/// <summary>
51+
/// Gets or sets the GraphQL query string containing the operation to be executed.
52+
/// </summary>
3253
public string QueryString { get; set; }
3354

34-
// Name of the GraphQL operation
55+
/// <summary>
56+
/// Gets or sets the name of the GraphQL operation to be executed.
57+
/// This corresponds to the operation name in the GraphQL query.
58+
/// </summary>
3559
public string OperationName { get; set; }
3660

37-
/// Variables passed to the GraphQL operation.
61+
/// <summary>
62+
/// Gets or sets the variables passed to the GraphQL operation.
63+
/// Contains key-value pairs of variable names and their values.
64+
/// </summary>
3865
public Dictionary<string, object> Variables { get; set; }
3966
}
4067
}
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,49 @@
11
using System.Collections.Generic;
2-
#if NETSTANDARD_2_0
3-
using Newtonsoft.Json;
4-
#else
5-
using System.Text.Json.Serialization;
6-
#endif
2+
using System.Runtime.Serialization;
3+
74

85
namespace Amazon.Lambda.AppSyncEvents
96
{
10-
public class AppSyncAuthorizerResult
11-
{
12-
// Indicates if the request is authorized
13-
#if NETSTANDARD_2_0
14-
[JsonProperty("isAuthorized")]
15-
#else
16-
[JsonPropertyName("isAuthorized")]
7+
/// <summary>
8+
/// Represents the result returned by an AWS AppSync Lambda authorizer.
9+
/// </summary>
10+
[DataContract]
11+
public class AppSyncAuthorizerResult
12+
{
13+
/// <summary>
14+
/// Indicates if the request is authorized
15+
/// </summary>
16+
[DataMember(Name = "isAuthorized")]
17+
#if NETCOREAPP3_1_OR_GREATER
18+
[System.Text.Json.Serialization.JsonPropertyName("isAuthorized")]
1719
#endif
18-
public bool IsAuthorized { get; set; }
20+
public bool IsAuthorized { get; set; }
1921

20-
// Custom context to pass to resolvers, only supports key-value pairs.
21-
#if NETSTANDARD_2_0
22-
[JsonProperty("resolverContext")]
23-
#else
24-
[JsonPropertyName("resolverContext")]
22+
/// <summary>
23+
/// Custom context to pass to resolvers, only supports key-value pairs.
24+
/// </summary>
25+
[DataMember(Name = "resolverContext")]
26+
#if NETCOREAPP3_1_OR_GREATER
27+
[System.Text.Json.Serialization.JsonPropertyName("resolverContext")]
2528
#endif
26-
public Dictionary<string, string> ResolverContext { get; set; }
29+
public Dictionary<string, string> ResolverContext { get; set; }
2730

28-
// List of fields that are denied access
29-
#if NETSTANDARD_2_0
30-
[JsonProperty("deniedFields")]
31-
#else
32-
[JsonPropertyName("deniedFields")]
31+
/// <summary>
32+
/// List of fields that are denied access
33+
/// </summary>
34+
[DataMember(Name = "deniedFields")]
35+
#if NETCOREAPP3_1_OR_GREATER
36+
[System.Text.Json.Serialization.JsonPropertyName("deniedFields")]
3337
#endif
34-
public IEnumerable<string> DeniedFields { get; set; }
38+
public IEnumerable<string> DeniedFields { get; set; }
3539

36-
// The number of seconds that the response should be cached for
37-
#if NETSTANDARD_2_0
38-
[JsonProperty("ttlOverride")]
39-
#else
40-
[JsonPropertyName("ttlOverride")]
40+
/// <summary>
41+
/// The number of seconds that the response should be cached for
42+
/// </summary>
43+
[DataMember(Name = "ttlOverride")]
44+
#if NETCOREAPP3_1_OR_GREATER
45+
[System.Text.Json.Serialization.JsonPropertyName("ttlOverride")]
4146
#endif
42-
public int? TtlOverride { get; set; }
43-
}
47+
public int? TtlOverride { get; set; }
48+
}
4449
}

Libraries/src/Amazon.Lambda.AppSyncEvents/README.md

+34-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This package contains classes that can be used as input types for Lambda functions that process AppSync events.
44

5-
# Sample Function
5+
## Sample Function
66

77
The following is a sample class and Lambda function that receives AppSync resolver event record data as an `appSyncResolverEvent` and logs some of the incoming event data. (Note that by default anything written to Console will be logged as CloudWatch Logs events.)
88

@@ -36,3 +36,36 @@ public void Handler(AppSyncResolverEvent<Dictionary<string, object>> appSyncReso
3636
}
3737
}
3838
```
39+
40+
## Example of Custom Lambda Authorizer
41+
This example demonstrates how to implement a custom Lambda authorizer for AppSync using the AppSync Events package. The authorizer function receives an `AppSyncAuthorizerEvent` containing the authorization token and request context. It returns an `AppSyncAuthorizerResult` that determines whether the request is authorized and includes additional context.
42+
43+
The function also provides some data in the `resolverContext` object. This information is available in the AppSync resolver’s context `identity` object.
44+
45+
```
46+
public async Task<AppSyncAuthorizerResult> CustomLambdaAuthorizerHandler(AppSyncAuthorizerEvent appSyncAuthorizerEvent)
47+
{
48+
var authorizationToken = appSyncAuthorizerEvent.AuthorizationToken;
49+
var apiId = appSyncAuthorizerEvent.RequestContext.ApiId;
50+
var accountId = appSyncAuthorizerEvent.RequestContext.AccountId;
51+
52+
var response = new AppSyncAuthorizerResult
53+
{
54+
IsAuthorized = authorizationToken == "custom-authorized",
55+
ResolverContext = new Dictionary<string, string>
56+
{
57+
{ "userid", "test-user-id" },
58+
{ "info", "contextual information A" },
59+
{ "more_info", "contextual information B" }
60+
},
61+
DeniedFields = new List<string>
62+
{
63+
$"arn:aws:appsync:{Environment.GetEnvironmentVariable("AWS_REGION")}:{accountId}:apis/{apiId}/types/Event/fields/comments",
64+
"Mutation.createEvent"
65+
},
66+
TtlOverride = 10
67+
};
68+
69+
return response;
70+
}
71+
```

Libraries/test/EventsTests.Shared/EventTests.cs

+67
Original file line numberDiff line numberDiff line change
@@ -4113,6 +4113,73 @@ public void AppSyncTestOidcAuthorizer(Type serializerType)
41134113
}
41144114
}
41154115

4116+
[Theory]
4117+
[InlineData(typeof(JsonSerializer))]
4118+
#if NETCOREAPP3_1_OR_GREATER
4119+
[InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))]
4120+
[InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
4121+
#endif
4122+
public void AppSyncTestLambdaAuthorizerRequestEvent(Type serializerType)
4123+
{
4124+
var serializer = Activator.CreateInstance(serializerType) as ILambdaSerializer;
4125+
using (var fileStream = LoadJsonTestFile("appsync-event-lambda-authorizer-request.json"))
4126+
{
4127+
var request = serializer.Deserialize<AppSyncAuthorizerEvent>(fileStream);
4128+
4129+
// Assert Authorization Token
4130+
Assert.Equal("custom-token", request.AuthorizationToken);
4131+
4132+
// Assert Request Context
4133+
Assert.NotNull(request.RequestContext);
4134+
Assert.Equal("xxxxxxxx", request.RequestContext.ApiId);
4135+
Assert.Equal("112233445566", request.RequestContext.AccountId);
4136+
Assert.Equal("36307622-97fe-4dfa-bd71-b15b1d03ce97", request.RequestContext.RequestId);
4137+
Assert.Equal("MyQuery", request.RequestContext.OperationName);
4138+
Assert.NotNull(request.RequestContext.Variables);
4139+
Assert.Empty(request.RequestContext.Variables);
4140+
Assert.Contains("listTodos", request.RequestContext.QueryString);
4141+
4142+
// Assert Request Headers
4143+
Assert.NotNull(request.RequestHeaders);
4144+
Assert.Equal("This is test token", request.RequestHeaders["authorization"]);
4145+
Assert.Equal("application/json", request.RequestHeaders["content-type"]);
4146+
Assert.Equal("https://ap-south-1.console.aws.amazon.com", request.RequestHeaders["origin"]);
4147+
}
4148+
}
4149+
4150+
[Theory]
4151+
[InlineData(typeof(JsonSerializer))]
4152+
#if NETCOREAPP3_1_OR_GREATER
4153+
[InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))]
4154+
[InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
4155+
#endif
4156+
public void AppSyncTestLambdaAuthorizerResponseEvent(Type serializerType)
4157+
{
4158+
var response = new AppSyncAuthorizerResult
4159+
{
4160+
IsAuthorized = true,
4161+
ResolverContext = new Dictionary<string, string>
4162+
{
4163+
{ "userid", "test-user-id" },
4164+
{ "info", "contextual information A" },
4165+
{ "more_info", "contextual information B" }
4166+
},
4167+
DeniedFields = new List<string>
4168+
{
4169+
"arn:aws:appsync:us-east-1:1234567890:apis/xxxxxx/types/Event/fields/comments",
4170+
"Mutation.createEvent"
4171+
},
4172+
TtlOverride = 10
4173+
};
4174+
4175+
var serializer = Activator.CreateInstance(serializerType) as ILambdaSerializer;
4176+
var json = SerializeJson(serializer, response);
4177+
var actualObject = JObject.Parse(json);
4178+
var expectedJObject = JObject.Parse(File.ReadAllText("appsync-event-lambda-authorizer-response.json"));
4179+
4180+
Assert.True(JToken.DeepEquals(actualObject, expectedJObject));
4181+
}
4182+
41164183
class ClassUsingPascalCase
41174184
{
41184185
public int SomeValue { get; set; }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"authorizationToken": "custom-token",
3+
"requestContext": {
4+
"apiId": "xxxxxxxx",
5+
"accountId": "112233445566",
6+
"requestId": "36307622-97fe-4dfa-bd71-b15b1d03ce97",
7+
"queryString": "query MyQuery {\n listTodos {\n completed\n createdAt\n description\n id\n title\n updatedAt\n }\n}\n",
8+
"operationName": "MyQuery",
9+
"variables": {}
10+
},
11+
"requestHeaders": {
12+
"x-forwarded-for": "182.70.233.9, 15.158.25.215",
13+
"accept-encoding": "gzip, deflate, br, zstd",
14+
"sec-ch-ua-mobile": "?0",
15+
"referer": "https://ap-south-1.console.aws.amazon.com/",
16+
"via": "2.0 30c122bd8d8efabc1fd1b3b11bfb53ea.cloudfront.net (CloudFront)",
17+
"content-type": "application/json",
18+
"origin": "https://ap-south-1.console.aws.amazon.com",
19+
"sec-fetch-mode": "cors",
20+
"authorization": "This is test token",
21+
"sec-fetch-dest": "empty",
22+
"content-length": "159",
23+
"x-amz-user-agent": "AWS-Console-AppSync/",
24+
"sec-ch-ua-platform": "\"macOS\"",
25+
"x-forwarded-proto": "https",
26+
"accept-language": "en-US,en;q=0.9",
27+
"host": "cndjdbaxrfhzppcdhvfvx3a7zq.appsync-api.ap-south-1.amazonaws.com",
28+
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
29+
"sec-fetch-site": "cross-site",
30+
"accept": "application/json, text/plain, */*",
31+
"priority": "u=1, i",
32+
"sec-ch-ua": "\"Chromium\";v=\"134\", \"Not:A-Brand\";v=\"24\", \"Google Chrome\";v=\"134\"",
33+
"x-forwarded-port": "443"
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"isAuthorized": true,
3+
"resolverContext": {
4+
"userid": "test-user-id",
5+
"info": "contextual information A",
6+
"more_info": "contextual information B"
7+
},
8+
"deniedFields": [
9+
"arn:aws:appsync:us-east-1:1234567890:apis/xxxxxx/types/Event/fields/comments",
10+
"Mutation.createEvent"
11+
],
12+
"ttlOverride": 10
13+
}

0 commit comments

Comments
 (0)