mirror of
https://github.com/swift-server/swift-aws-lambda-runtime.git
synced 2026-05-03 07:22:27 +00:00
Do not use a String for Lambdacontext.ClientContext, use a struct instead. Fix for https://github.com/swift-server/swift-aws-lambda-runtime/issues/169 Note: this PR introduces an API change that will break function using `LambdaContext`, we should integrate this change during the beta otherwise it will require a major version bump. ### Motivation: Let the compiler detect type errors for us ### Modifications: - Create a struct for ClientContext and it's embedded ClientApplication - add three unit test to validate the struct ### Result: No more String? --------- Co-authored-by: Konrad `ktoso` Malawski <konrad.malawski@project13.pl>
This commit is contained in:
committed by
GitHub
parent
76c3a441de
commit
bae9f27ccb
@@ -16,6 +16,70 @@ import Dispatch
|
||||
import Logging
|
||||
import NIOCore
|
||||
|
||||
// MARK: - Client Context
|
||||
|
||||
/// AWS Mobile SDK client fields.
|
||||
public struct ClientApplication: Codable, Sendable {
|
||||
/// The mobile app installation id
|
||||
public let installationID: String?
|
||||
/// The app title for the mobile app as registered with AWS' mobile services.
|
||||
public let appTitle: String?
|
||||
/// The version name of the application as registered with AWS' mobile services.
|
||||
public let appVersionName: String?
|
||||
/// The app version code.
|
||||
public let appVersionCode: String?
|
||||
/// The package name for the mobile application invoking the function
|
||||
public let appPackageName: String?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case installationID = "installation_id"
|
||||
case appTitle = "app_title"
|
||||
case appVersionName = "app_version_name"
|
||||
case appVersionCode = "app_version_code"
|
||||
case appPackageName = "app_package_name"
|
||||
}
|
||||
|
||||
public init(
|
||||
installationID: String? = nil,
|
||||
appTitle: String? = nil,
|
||||
appVersionName: String? = nil,
|
||||
appVersionCode: String? = nil,
|
||||
appPackageName: String? = nil
|
||||
) {
|
||||
self.installationID = installationID
|
||||
self.appTitle = appTitle
|
||||
self.appVersionName = appVersionName
|
||||
self.appVersionCode = appVersionCode
|
||||
self.appPackageName = appPackageName
|
||||
}
|
||||
}
|
||||
|
||||
/// For invocations from the AWS Mobile SDK, data about the client application and device.
|
||||
public struct ClientContext: Codable, Sendable {
|
||||
/// Information about the mobile application invoking the function.
|
||||
public let client: ClientApplication?
|
||||
/// Custom properties attached to the mobile event context.
|
||||
public let custom: [String: String]?
|
||||
/// Environment settings from the mobile client.
|
||||
public let environment: [String: String]?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case client
|
||||
case custom
|
||||
case environment = "env"
|
||||
}
|
||||
|
||||
public init(
|
||||
client: ClientApplication? = nil,
|
||||
custom: [String: String]? = nil,
|
||||
environment: [String: String]? = nil
|
||||
) {
|
||||
self.client = client
|
||||
self.custom = custom
|
||||
self.environment = environment
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Context
|
||||
|
||||
/// Lambda runtime context.
|
||||
@@ -27,7 +91,7 @@ public struct LambdaContext: CustomDebugStringConvertible, Sendable {
|
||||
let invokedFunctionARN: String
|
||||
let deadline: DispatchWallTime
|
||||
let cognitoIdentity: String?
|
||||
let clientContext: String?
|
||||
let clientContext: ClientContext?
|
||||
let logger: Logger
|
||||
|
||||
init(
|
||||
@@ -36,7 +100,7 @@ public struct LambdaContext: CustomDebugStringConvertible, Sendable {
|
||||
invokedFunctionARN: String,
|
||||
deadline: DispatchWallTime,
|
||||
cognitoIdentity: String?,
|
||||
clientContext: String?,
|
||||
clientContext: ClientContext?,
|
||||
logger: Logger
|
||||
) {
|
||||
self.requestID = requestID
|
||||
@@ -77,7 +141,7 @@ public struct LambdaContext: CustomDebugStringConvertible, Sendable {
|
||||
}
|
||||
|
||||
/// For invocations from the AWS Mobile SDK, data about the client application and device.
|
||||
public var clientContext: String? {
|
||||
public var clientContext: ClientContext? {
|
||||
self.storage.clientContext
|
||||
}
|
||||
|
||||
@@ -94,7 +158,7 @@ public struct LambdaContext: CustomDebugStringConvertible, Sendable {
|
||||
invokedFunctionARN: String,
|
||||
deadline: DispatchWallTime,
|
||||
cognitoIdentity: String? = nil,
|
||||
clientContext: String? = nil,
|
||||
clientContext: ClientContext? = nil,
|
||||
logger: Logger
|
||||
) {
|
||||
self.storage = _Storage(
|
||||
@@ -117,7 +181,7 @@ public struct LambdaContext: CustomDebugStringConvertible, Sendable {
|
||||
}
|
||||
|
||||
public var debugDescription: String {
|
||||
"\(Self.self)(requestID: \(self.requestID), traceID: \(self.traceID), invokedFunctionARN: \(self.invokedFunctionARN), cognitoIdentity: \(self.cognitoIdentity ?? "nil"), clientContext: \(self.clientContext ?? "nil"), deadline: \(self.deadline))"
|
||||
"\(Self.self)(requestID: \(self.requestID), traceID: \(self.traceID), invokedFunctionARN: \(self.invokedFunctionARN), cognitoIdentity: \(self.cognitoIdentity ?? "nil"), clientContext: \(String(describing: self.clientContext)), deadline: \(self.deadline))"
|
||||
}
|
||||
|
||||
/// This interface is not part of the public API and must not be used by adopters. This API is not part of semver versioning.
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the SwiftAWSLambdaRuntime open source project
|
||||
//
|
||||
// Copyright (c) 2017-2022 Apple Inc. and the SwiftAWSLambdaRuntime project authors
|
||||
// Licensed under Apache License v2.0
|
||||
//
|
||||
// See LICENSE.txt for license information
|
||||
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Foundation
|
||||
import Testing
|
||||
|
||||
@testable import AWSLambdaRuntime
|
||||
|
||||
@Suite("LambdaContext ClientContext Tests")
|
||||
struct LambdaContextTests {
|
||||
|
||||
@Test("ClientContext with full data resolves correctly")
|
||||
func clientContextWithFullDataResolves() throws {
|
||||
let custom = ["key": "value"]
|
||||
let environment = ["key": "value"]
|
||||
let clientContext = ClientContext(
|
||||
client: ClientApplication(
|
||||
installationID: "test-id",
|
||||
appTitle: "test-app",
|
||||
appVersionName: "1.0",
|
||||
appVersionCode: "100",
|
||||
appPackageName: "com.test.app"
|
||||
),
|
||||
custom: custom,
|
||||
environment: environment
|
||||
)
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
let clientContextData = try encoder.encode(clientContext)
|
||||
|
||||
// Verify JSON encoding/decoding works correctly
|
||||
let decoder = JSONDecoder()
|
||||
let decodedClientContext = try decoder.decode(ClientContext.self, from: clientContextData)
|
||||
|
||||
let decodedClient = try #require(decodedClientContext.client)
|
||||
let originalClient = try #require(clientContext.client)
|
||||
|
||||
#expect(decodedClient.installationID == originalClient.installationID)
|
||||
#expect(decodedClient.appTitle == originalClient.appTitle)
|
||||
#expect(decodedClient.appVersionName == originalClient.appVersionName)
|
||||
#expect(decodedClient.appVersionCode == originalClient.appVersionCode)
|
||||
#expect(decodedClient.appPackageName == originalClient.appPackageName)
|
||||
#expect(decodedClientContext.custom == clientContext.custom)
|
||||
#expect(decodedClientContext.environment == clientContext.environment)
|
||||
}
|
||||
|
||||
@Test("ClientContext with empty data resolves correctly")
|
||||
func clientContextWithEmptyDataResolves() throws {
|
||||
let emptyClientContextJSON = "{}"
|
||||
let emptyClientContextData = emptyClientContextJSON.data(using: .utf8)!
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
let decodedClientContext = try decoder.decode(ClientContext.self, from: emptyClientContextData)
|
||||
|
||||
// With empty JSON, we expect nil values for optional fields
|
||||
#expect(decodedClientContext.client == nil)
|
||||
#expect(decodedClientContext.custom == nil)
|
||||
#expect(decodedClientContext.environment == nil)
|
||||
}
|
||||
|
||||
@Test("ClientContext with AWS Lambda JSON payload decodes correctly")
|
||||
func clientContextWithAWSLambdaJSONPayload() throws {
|
||||
let jsonPayload = """
|
||||
{
|
||||
"client": {
|
||||
"installation_id": "example-id",
|
||||
"app_title": "Example App",
|
||||
"app_version_name": "1.0",
|
||||
"app_version_code": "1",
|
||||
"app_package_name": "com.example.app"
|
||||
},
|
||||
"custom": {
|
||||
"customKey": "customValue"
|
||||
},
|
||||
"env": {
|
||||
"platform": "Android",
|
||||
"platform_version": "10"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
let jsonData = jsonPayload.data(using: .utf8)!
|
||||
let decoder = JSONDecoder()
|
||||
let decodedClientContext = try decoder.decode(ClientContext.self, from: jsonData)
|
||||
|
||||
// Verify client application data
|
||||
let client = try #require(decodedClientContext.client)
|
||||
#expect(client.installationID == "example-id")
|
||||
#expect(client.appTitle == "Example App")
|
||||
#expect(client.appVersionName == "1.0")
|
||||
#expect(client.appVersionCode == "1")
|
||||
#expect(client.appPackageName == "com.example.app")
|
||||
|
||||
// Verify custom properties
|
||||
let custom = try #require(decodedClientContext.custom)
|
||||
#expect(custom["customKey"] == "customValue")
|
||||
|
||||
// Verify environment settings
|
||||
let environment = try #require(decodedClientContext.environment)
|
||||
#expect(environment["platform"] == "Android")
|
||||
#expect(environment["platform_version"] == "10")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user