Skip to content

Commit 02a54df

Browse files
authored
Merge branch 'main' into sebsto/DispatchWallTime
2 parents 5fb71d8 + bae9f27 commit 02a54df

File tree

2 files changed

+183
-5
lines changed

2 files changed

+183
-5
lines changed

Sources/AWSLambdaRuntime/LambdaContext.swift

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,70 @@
1515
import Logging
1616
import NIOCore
1717

18+
// MARK: - Client Context
19+
20+
/// AWS Mobile SDK client fields.
21+
public struct ClientApplication: Codable, Sendable {
22+
/// The mobile app installation id
23+
public let installationID: String?
24+
/// The app title for the mobile app as registered with AWS' mobile services.
25+
public let appTitle: String?
26+
/// The version name of the application as registered with AWS' mobile services.
27+
public let appVersionName: String?
28+
/// The app version code.
29+
public let appVersionCode: String?
30+
/// The package name for the mobile application invoking the function
31+
public let appPackageName: String?
32+
33+
private enum CodingKeys: String, CodingKey {
34+
case installationID = "installation_id"
35+
case appTitle = "app_title"
36+
case appVersionName = "app_version_name"
37+
case appVersionCode = "app_version_code"
38+
case appPackageName = "app_package_name"
39+
}
40+
41+
public init(
42+
installationID: String? = nil,
43+
appTitle: String? = nil,
44+
appVersionName: String? = nil,
45+
appVersionCode: String? = nil,
46+
appPackageName: String? = nil
47+
) {
48+
self.installationID = installationID
49+
self.appTitle = appTitle
50+
self.appVersionName = appVersionName
51+
self.appVersionCode = appVersionCode
52+
self.appPackageName = appPackageName
53+
}
54+
}
55+
56+
/// For invocations from the AWS Mobile SDK, data about the client application and device.
57+
public struct ClientContext: Codable, Sendable {
58+
/// Information about the mobile application invoking the function.
59+
public let client: ClientApplication?
60+
/// Custom properties attached to the mobile event context.
61+
public let custom: [String: String]?
62+
/// Environment settings from the mobile client.
63+
public let environment: [String: String]?
64+
65+
private enum CodingKeys: String, CodingKey {
66+
case client
67+
case custom
68+
case environment = "env"
69+
}
70+
71+
public init(
72+
client: ClientApplication? = nil,
73+
custom: [String: String]? = nil,
74+
environment: [String: String]? = nil
75+
) {
76+
self.client = client
77+
self.custom = custom
78+
self.environment = environment
79+
}
80+
}
81+
1882
// MARK: - Context
1983

2084
/// Lambda runtime context.
@@ -26,7 +90,7 @@ public struct LambdaContext: CustomDebugStringConvertible, Sendable {
2690
let invokedFunctionARN: String
2791
let deadline: LambdaClock.Instant
2892
let cognitoIdentity: String?
29-
let clientContext: String?
93+
let clientContext: ClientContext?
3094
let logger: Logger
3195

3296
init(
@@ -35,7 +99,7 @@ public struct LambdaContext: CustomDebugStringConvertible, Sendable {
3599
invokedFunctionARN: String,
36100
deadline: LambdaClock.Instant,
37101
cognitoIdentity: String?,
38-
clientContext: String?,
102+
clientContext: ClientContext?,
39103
logger: Logger
40104
) {
41105
self.requestID = requestID
@@ -76,7 +140,7 @@ public struct LambdaContext: CustomDebugStringConvertible, Sendable {
76140
}
77141

78142
/// For invocations from the AWS Mobile SDK, data about the client application and device.
79-
public var clientContext: String? {
143+
public var clientContext: ClientContext? {
80144
self.storage.clientContext
81145
}
82146

@@ -93,7 +157,7 @@ public struct LambdaContext: CustomDebugStringConvertible, Sendable {
93157
invokedFunctionARN: String,
94158
deadline: LambdaClock.Instant,
95159
cognitoIdentity: String? = nil,
96-
clientContext: String? = nil,
160+
clientContext: ClientContext? = nil,
97161
logger: Logger
98162
) {
99163
self.storage = _Storage(
@@ -113,7 +177,7 @@ public struct LambdaContext: CustomDebugStringConvertible, Sendable {
113177
}
114178

115179
public var debugDescription: String {
116-
"\(Self.self)(requestID: \(self.requestID), traceID: \(self.traceID), invokedFunctionARN: \(self.invokedFunctionARN), cognitoIdentity: \(self.cognitoIdentity ?? "nil"), clientContext: \(self.clientContext ?? "nil"), deadline: \(self.deadline))"
180+
"\(Self.self)(requestID: \(self.requestID), traceID: \(self.traceID), invokedFunctionARN: \(self.invokedFunctionARN), cognitoIdentity: \(self.cognitoIdentity ?? "nil"), clientContext: \(String(describing: self.clientContext)), deadline: \(self.deadline))"
117181
}
118182

119183
/// This interface is not part of the public API and must not be used by adopters. This API is not part of semver versioning.
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2017-2022 Apple Inc. and the SwiftAWSLambdaRuntime project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Foundation
16+
import Testing
17+
18+
@testable import AWSLambdaRuntime
19+
20+
@Suite("LambdaContext ClientContext Tests")
21+
struct LambdaContextTests {
22+
23+
@Test("ClientContext with full data resolves correctly")
24+
func clientContextWithFullDataResolves() throws {
25+
let custom = ["key": "value"]
26+
let environment = ["key": "value"]
27+
let clientContext = ClientContext(
28+
client: ClientApplication(
29+
installationID: "test-id",
30+
appTitle: "test-app",
31+
appVersionName: "1.0",
32+
appVersionCode: "100",
33+
appPackageName: "com.test.app"
34+
),
35+
custom: custom,
36+
environment: environment
37+
)
38+
39+
let encoder = JSONEncoder()
40+
let clientContextData = try encoder.encode(clientContext)
41+
42+
// Verify JSON encoding/decoding works correctly
43+
let decoder = JSONDecoder()
44+
let decodedClientContext = try decoder.decode(ClientContext.self, from: clientContextData)
45+
46+
let decodedClient = try #require(decodedClientContext.client)
47+
let originalClient = try #require(clientContext.client)
48+
49+
#expect(decodedClient.installationID == originalClient.installationID)
50+
#expect(decodedClient.appTitle == originalClient.appTitle)
51+
#expect(decodedClient.appVersionName == originalClient.appVersionName)
52+
#expect(decodedClient.appVersionCode == originalClient.appVersionCode)
53+
#expect(decodedClient.appPackageName == originalClient.appPackageName)
54+
#expect(decodedClientContext.custom == clientContext.custom)
55+
#expect(decodedClientContext.environment == clientContext.environment)
56+
}
57+
58+
@Test("ClientContext with empty data resolves correctly")
59+
func clientContextWithEmptyDataResolves() throws {
60+
let emptyClientContextJSON = "{}"
61+
let emptyClientContextData = emptyClientContextJSON.data(using: .utf8)!
62+
63+
let decoder = JSONDecoder()
64+
let decodedClientContext = try decoder.decode(ClientContext.self, from: emptyClientContextData)
65+
66+
// With empty JSON, we expect nil values for optional fields
67+
#expect(decodedClientContext.client == nil)
68+
#expect(decodedClientContext.custom == nil)
69+
#expect(decodedClientContext.environment == nil)
70+
}
71+
72+
@Test("ClientContext with AWS Lambda JSON payload decodes correctly")
73+
func clientContextWithAWSLambdaJSONPayload() throws {
74+
let jsonPayload = """
75+
{
76+
"client": {
77+
"installation_id": "example-id",
78+
"app_title": "Example App",
79+
"app_version_name": "1.0",
80+
"app_version_code": "1",
81+
"app_package_name": "com.example.app"
82+
},
83+
"custom": {
84+
"customKey": "customValue"
85+
},
86+
"env": {
87+
"platform": "Android",
88+
"platform_version": "10"
89+
}
90+
}
91+
"""
92+
93+
let jsonData = jsonPayload.data(using: .utf8)!
94+
let decoder = JSONDecoder()
95+
let decodedClientContext = try decoder.decode(ClientContext.self, from: jsonData)
96+
97+
// Verify client application data
98+
let client = try #require(decodedClientContext.client)
99+
#expect(client.installationID == "example-id")
100+
#expect(client.appTitle == "Example App")
101+
#expect(client.appVersionName == "1.0")
102+
#expect(client.appVersionCode == "1")
103+
#expect(client.appPackageName == "com.example.app")
104+
105+
// Verify custom properties
106+
let custom = try #require(decodedClientContext.custom)
107+
#expect(custom["customKey"] == "customValue")
108+
109+
// Verify environment settings
110+
let environment = try #require(decodedClientContext.environment)
111+
#expect(environment["platform"] == "Android")
112+
#expect(environment["platform_version"] == "10")
113+
}
114+
}

0 commit comments

Comments
 (0)