Skip to content

Commit

Permalink
feat: move endpoints classes to smithy-swift
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewFossAWS committed May 9, 2024
1 parent 8d38691 commit c4cee9e
Show file tree
Hide file tree
Showing 20 changed files with 968 additions and 0 deletions.
58 changes: 58 additions & 0 deletions Sources/ClientRuntime/Endpoints/AWSEndpoint.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import ClientRuntime

/**
A structure used by the service client to determine the endpoint.
The SDK will automatically resolve endpoints per API client using an internal resolver.
*/
public struct AWSEndpoint: Equatable {
/**
The endpoint object contains the host name (e.g. "{service-id}.{region}.amazonaws.com"),
the transport protocol (e.g. "HTTPS") and the port to connect to when making requests to this endpoint.
*/
public let endpoint: Endpoint

/**
Flag indicating that the hostname can be modified by the SDK client.
If the hostname is mutable the SDK clients may modify any part of the hostname based
on the requirements of the API (e.g. adding or removing content in the hostname). If the hostname
is expected to be mutable and the client cannot modify the endpoint correctly, the operation
will likely fail.
*/
public let isHostnameImmutable: Bool
/**
The service name that should be used for signing requests to this endpoint.
This overrides the default signing name used by an SDK client.
*/
public let signingName: String?
/**
The region that should be used for signing requests to this endpoint.
This overrides the default signing region used by an SDK client.
*/
public let signingRegion: String?

public init(endpoint: Endpoint,
isHostnameImmutable: Bool = false,
signingName: String? = nil,
signingRegion: String? = nil) {
self.endpoint = endpoint
self.isHostnameImmutable = isHostnameImmutable
self.signingName = signingName
self.signingRegion = signingRegion
}

public static func resolveEndpoint(partitions: [Partition], region: String) throws -> AWSEndpoint {
guard !partitions.isEmpty else {
throw EndpointError.partitionsEmpty(
"The partitions array cannot be empty in order to properly resolve an AWS endpoint")
}

let candidate = partitions.first { $0.canResolveEndpoint(region: region)} ?? partitions[0]
return try candidate.resolveEndpoint(region: region)
}
}
16 changes: 16 additions & 0 deletions Sources/ClientRuntime/Endpoints/CredentialScope.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

public struct CredentialScope {
let region: String?
let serviceId: String?

public init(region: String? = nil, serviceId: String? = nil) {
self.region = region
self.serviceId = serviceId
}
}
13 changes: 13 additions & 0 deletions Sources/ClientRuntime/Endpoints/EndpointError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

public enum EndpointError: Error {
case hostnameIsNil(String)
case partitionsEmpty(String)
case unresolved(String?)
case authScheme(String?)
}
123 changes: 123 additions & 0 deletions Sources/ClientRuntime/Endpoints/EndpointsAuthSchemeResolver.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

/// Supported authentication schemes
public enum EndpointsAuthScheme: Equatable {
case sigV4(SigV4Parameters)
case sigV4A(SigV4AParameters)
case none

/// The name of the auth scheme
public var name: String {
switch self {
case .sigV4: return "sigv4"
case .sigV4A: return "sigv4a"
case .none: return "none"
}
}
}

extension EndpointsAuthScheme {
/// Initialize an AuthScheme from a dictionary
/// - Parameter dictionary: Dictionary containing the auth scheme
public init(from dictionary: [String: Any]) throws {
guard let name = dictionary["name"] as? String else {
throw EndpointError.authScheme("Invalid auth scheme")
}
switch name {
case "sigv4":
self = .sigV4(try SigV4Parameters(from: dictionary))
case "sigv4a":
self = .sigV4A(try SigV4AParameters(from: dictionary))
case "none":
self = .none
default:
throw EndpointError.authScheme("Unknown auth scheme \(name)")
}
}
}

extension EndpointsAuthScheme {
/// SigV4 auth scheme
public struct SigV4Parameters: Equatable {

/// Service name to use for signing
public let signingName: String?

/// Region to use for signing
public let signingRegion: String?

/// When true, do not double-escape path during signing
public let disableDoubleEncoding: Bool?
}
}

extension EndpointsAuthScheme.SigV4Parameters {
/// Initialize a SigV4AuthScheme from a dictionary
/// - Parameter dictionary: Dictionary containing the auth scheme
init(from dictionary: [String: Any]) throws {
self.signingName = dictionary["signingName"] as? String
self.signingRegion = dictionary["signingRegion"] as? String
self.disableDoubleEncoding = dictionary["disableDoubleEncoding"] as? Bool
}
}

extension EndpointsAuthScheme {
/// SigV4a auth scheme
public struct SigV4AParameters: Equatable {

/// Service name to use for signing
public let signingName: String?

/// The set of signing regions to use for this endpoint. Currently,
/// this will always be ["*"].
public let signingRegionSet: [String]?

/// When true, do not double-escape path during signing
public let disableDoubleEncoding: Bool?
}
}

extension EndpointsAuthScheme.SigV4AParameters {
/// Initialize a SigV4AAuthScheme from a dictionary
/// - Parameter dictionary: Dictionary containing the auth scheme
init(from dictionary: [String: Any]) throws {
self.signingName = dictionary["signingName"] as? String
self.signingRegionSet = dictionary["signingRegionSet"] as? [String]
self.disableDoubleEncoding = dictionary["disableDoubleEncoding"] as? Bool
}
}

/// Resolves the auth scheme to use for a given endpoint
public protocol EndpointsAuthSchemeResolver {

/// Resolves the auth scheme to use for a given endpoint
/// If no auth scheme is supported, returns nil and the SDK must throw an error
/// - Parameter authSchemes: auth schemes to resolve
/// - Returns: Auth scheme to use
func resolve(authSchemes: [EndpointsAuthScheme]) throws -> EndpointsAuthScheme
}

/// Default implementation of AuthSchemeResolver
public struct DefaultEndpointsAuthSchemeResolver: EndpointsAuthSchemeResolver {

/// Supported auth schemes by the SDK
let supportedAuthSchemes: Set<String>

public init(supportedAuthSchemes: Set<String> = ["sigv4", "sigv4a", "none"]) {
self.supportedAuthSchemes = supportedAuthSchemes
}

public func resolve(authSchemes: [EndpointsAuthScheme]) throws -> EndpointsAuthScheme {
guard let authScheme = authSchemes.first(where: { supportedAuthSchemes.contains($0.name) }) else {
throw EndpointError.authScheme("Failed to resolve auth scheme. Supported schemes: \(supportedAuthSchemes),"
+ "available schemes: \(authSchemes.map { $0.name })")
}

return authScheme
}
}
26 changes: 26 additions & 0 deletions Sources/ClientRuntime/Endpoints/EndpointsRequestContext.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0.

import AwsCommonRuntimeKit

/// Wrapper for CRTAWSEndpointsRequestContext
public class EndpointsRequestContext {

let crtContext: AwsCommonRuntimeKit.EndpointsRequestContext

public init() throws {
self.crtContext = try AwsCommonRuntimeKit.EndpointsRequestContext()
}

public func add(name: String, value: String?) throws {
try crtContext.add(name: name, value: value)
}

public func add(name: String, value: Bool?) throws {
try crtContext.add(name: name, value: value)
}

public func toCRT() -> AwsCommonRuntimeKit.EndpointsRequestContext {
crtContext
}
}
54 changes: 54 additions & 0 deletions Sources/ClientRuntime/Endpoints/EndpointsResolvedEndpoint.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0.

import AwsCommonRuntimeKit

/// Wrapper for CRTAWSEndpointResolvedEndpoint
public class EndpointsResolvedEndpoint {

let crtResolvedEndpoint: AwsCommonRuntimeKit.ResolvedEndpoint

init(crtResolvedEndpoint: AwsCommonRuntimeKit.ResolvedEndpoint) {
self.crtResolvedEndpoint = crtResolvedEndpoint
}

public func getType() -> EndpointsResolvedEndpointType {
EndpointsResolvedEndpointType(crtType: crtResolvedEndpoint)
}

public func getError() -> String? {
switch crtResolvedEndpoint {
case .endpoint:
return nil
case let .error(message):
return message
}
}

public func getURL() -> String? {
switch crtResolvedEndpoint {
case let .endpoint(url, _, _):
return url
case .error:
return nil
}
}

public func getProperties() -> [String: AnyHashable]? {
switch crtResolvedEndpoint {
case let .endpoint(_, _, properties):
return properties
case .error:
return nil
}
}

public func getHeaders() -> [String: [String]]? {
switch crtResolvedEndpoint {
case let .endpoint(_, headers, _):
return headers
case .error:
return nil
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.

import AwsCommonRuntimeKit

/// Wrapper for CRTAWSEndpointsResolvedEndpointType
public enum EndpointsResolvedEndpointType {
case error
case endpoint

init(crtType: AwsCommonRuntimeKit.ResolvedEndpoint) {
switch crtType {
case .error:
self = .error
case .endpoint:
self = .endpoint
}
}
}
20 changes: 20 additions & 0 deletions Sources/ClientRuntime/Endpoints/EndpointsRuleEngine.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0.

import AwsCommonRuntimeKit

/// Wrapper for CRTAWSEndpointsRuleEngine
public class EndpointsRuleEngine {

let crtEngine: AwsCommonRuntimeKit.EndpointsRuleEngine

public init(partitions: String = partitionJSON, ruleSet: String) throws {
CommonRuntimeKit.initialize() // ensures CRT is set up before calling the CRT endpoint rules engine
crtEngine = try AwsCommonRuntimeKit.EndpointsRuleEngine(partitions: partitions, ruleSet: ruleSet)
}

public func resolve(context: EndpointsRequestContext) throws -> EndpointsResolvedEndpoint? {
let crtResolvedEndpoint = try crtEngine.resolve(context: context.toCRT())
return EndpointsResolvedEndpoint(crtResolvedEndpoint: crtResolvedEndpoint)
}
}
Loading

0 comments on commit c4cee9e

Please sign in to comment.