Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: bindings for EndpointsRuleEngine #79

Merged
merged 33 commits into from
Oct 27, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@ jobs:
- uses: actions/checkout@v1
- name: GitHub Action for SwiftLint
uses: norio-nomura/action-swiftlint@3.1.0
- name: GitHub Action for SwiftLint with --strict
uses: norio-nomura/action-swiftlint@3.1.0
with:
args: --strict
Comment on lines 20 to -26
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strict is ignoring the .swiftlint config file and redundant to the line 20 after removing strict.

- name: GitHub Action for SwiftLint (Only files changed in the PR)
uses: norio-nomura/action-swiftlint@3.1.0
env:
Expand Down
2 changes: 2 additions & 0 deletions Source/AwsCommonRuntimeKit/crt/CRTError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ public enum CRTError: Error {
case memoryAllocationFailure
case stringConversionError(UnsafePointer<aws_string>?)
case crtError(AWSError)
// TODO: cleanup error handling https://github.com/awslabs/aws-crt-swift/issues/77
case awsError(AWSCommonRuntimeError)
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0.

import AwsCSdkUtils

/// Request context used for resolving endpoint
public class CRTAWSEndpointsRequestContext {
let rawValue: OpaquePointer

/// Initialize a new request context
/// - Parameter allocator: Allocator to use for request context creation
public init(allocator: Allocator = defaultAllocator) throws {
guard let rawValue = aws_endpoints_request_context_new(allocator.rawValue) else {
throw CRTError.awsError(AWSCommonRuntimeError())
}

self.rawValue = rawValue
}
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved

/// Add a string endpoint parameter to the request context
/// - Parameters:
/// - name: The name of the parameter
/// - value: The value of the parameter
/// - allocator: The allocator to use for the parameter
public func add(name: String, value: String?, allocator: Allocator = defaultAllocator) throws {
guard let value = value else {
return
}
waahm7 marked this conversation as resolved.
Show resolved Hide resolved
let namePtr: UnsafeMutablePointer<aws_byte_cursor> = fromPointer(ptr: name.awsByteCursor)
let valuePtr: UnsafeMutablePointer<aws_byte_cursor> = fromPointer(ptr: value.awsByteCursor)
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved
let success = aws_endpoints_request_context_add_string(allocator.rawValue,
rawValue,
namePtr.pointee,
valuePtr.pointee)
if success != 0 {
throw CRTError.awsError(AWSCommonRuntimeError())
}
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved
}

/// Add a bool endpoint parameter to the request context
/// - Parameters:
/// - name: The name of the parameter
/// - value: The value of the parameter
/// - allocator: The allocator to use for the parameter
public func add(name: String, value: Bool?, allocator: Allocator = defaultAllocator) throws {
guard let value = value else {
return
}
let namePtr: UnsafeMutablePointer<aws_byte_cursor> = fromPointer(ptr: name.awsByteCursor)
let success = aws_endpoints_request_context_add_boolean(allocator.rawValue, rawValue, namePtr.pointee, value)
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved
if success != 0 {
throw CRTError.awsError(AWSCommonRuntimeError())
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0.

import Foundation
import AwsCSdkUtils

/// Resolved endpoint
public class CRTAWSEndpointResolvedEndpoint {
let rawValue: OpaquePointer

/// Initialize a new resolved endpoint
/// - Parameter rawValue: The raw value of the resolved endpoint
public init(rawValue: OpaquePointer) {
self.rawValue = rawValue

aws_endpoints_resolved_endpoint_acquire(rawValue)
}
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved

/// Get the type of the resolved endpoint
/// - Returns: The type of the resolved endpoint
public func getType() -> CRTAWSEndpointsResolvedEndpointType {
let type = aws_endpoints_resolved_endpoint_get_type(rawValue)
return CRTAWSEndpointsResolvedEndpointType(rawValue: type)
}

/// Get the URL of the resolved endpoint
/// - Returns: The URL of the resolved endpoint
public func getURL() throws -> String? {
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved
let urlOut = UnsafeMutablePointer<aws_byte_cursor>.allocate(capacity: 1)
let success = aws_endpoints_resolved_endpoint_get_url(rawValue, urlOut)
if success != 0 {
throw CRTError.awsError(AWSCommonRuntimeError())
}
return urlOut.pointee.toString()
}

/// Get the properties of the resolved endpoint
/// - Returns: The properties of the resolved endpoint
public func getProperties() throws -> [String: AnyHashable]? {
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved
let propsOut = UnsafeMutablePointer<aws_byte_cursor>.allocate(capacity: 1)
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved
let success = aws_endpoints_resolved_endpoint_get_properties(rawValue, propsOut)
if success != 0 {
throw CRTError.awsError(AWSCommonRuntimeError())
}
guard let data = propsOut.pointee.toString()?.data(using: .utf8) else {
return nil
}
return try JSONSerialization.jsonObject(with: data) as? [String: AnyHashable]
}

/// Get the error of the resolved endpoint
/// - Parameter allocator: The allocator to use for the error
public func getError() throws -> String? {
let errorOut = UnsafeMutablePointer<aws_byte_cursor>.allocate(capacity: 1)
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved
let success = aws_endpoints_resolved_endpoint_get_error(rawValue, errorOut)
if success != 0 {
throw CRTError.awsError(AWSCommonRuntimeError())
}
return errorOut.pointee.toString()
}

/// Get headers of the resolved endpoint
/// - Returns: The headers of the resolved endpoint
public func getHeaders() throws -> [String: [String]]? {
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved
let headersOut: UnsafeMutablePointer<UnsafePointer<aws_hash_table>?>
= UnsafeMutablePointer<UnsafePointer<aws_hash_table>?>.allocate(capacity: 1)
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved
let success = aws_endpoints_resolved_endpoint_get_headers(rawValue, headersOut)
if success != 0 {
throw CRTError.awsError(AWSCommonRuntimeError())
}

var headers: [String: [String]] = [:]
var iter = aws_hash_iter_begin(headersOut.pointee)

while !aws_hash_iter_done(&iter) {
let key = iter.element.key.bindMemory(to: String.self, capacity: 1).pointee
let value = iter.element.value.bindMemory(to: aws_array_list.self, capacity: 1).pointee.toStringArray()
headers[key] = value
aws_hash_iter_next(&iter)
}

return headers
}

deinit {
aws_endpoints_resolved_endpoint_release(rawValue)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0.

import AwsCSdkUtils

/// Resolved endpoint type
public enum CRTAWSEndpointsResolvedEndpointType {
/// Used for endpoints that are resolved successfully
case endpoint
/// Used for endpoints that resolve to an error
case error
}

extension CRTAWSEndpointsResolvedEndpointType: RawRepresentable, CaseIterable {
public init(rawValue: aws_endpoints_resolved_endpoint_type) {
let value = Self.allCases.first(where: {$0.rawValue == rawValue})
self = value ?? .endpoint
}

public var rawValue: aws_endpoints_resolved_endpoint_type {
switch self {
case .endpoint:
return AWS_ENDPOINTS_RESOLVED_ENDPOINT
case .error:
return AWS_ENDPOINTS_RESOLVED_ERROR
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0.

import AwsCSdkUtils

/// Rule engine for matching endpoint rules
public class CRTAWSEndpointsRuleEngine {
let rawValue: OpaquePointer

/// Initialize a new rule engine
/// - Parameters:
/// - ruleSetString: The rule set string to use for the rule engine
/// - allocator: The allocator to use for creating rule engine
public init(ruleSetString: String, allocator: Allocator = defaultAllocator) throws {
guard let ruleSet = aws_endpoints_ruleset_new_from_string(allocator.rawValue, ruleSetString.newByteCursor().rawValue),
let rawValue = aws_endpoints_rule_engine_new(allocator.rawValue, ruleSet) else {
throw CRTError.awsError(AWSCommonRuntimeError())
}

self.rawValue = rawValue
}

/// Resolve an endpoint from the rule engine using the provided request context
/// - Parameter context: The request context to use for endpoint resolution
/// - Returns: The resolved endpoint
public func resolve(context: CRTAWSEndpointsRequestContext) throws -> CRTAWSEndpointResolvedEndpoint? {
let resolvedEndpoint: UnsafeMutablePointer<OpaquePointer?>? = UnsafeMutablePointer.allocate(capacity: 1)
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved
let success = aws_endpoints_rule_engine_resolve(rawValue, context.rawValue, resolvedEndpoint)
if success != 0 {
throw CRTError.awsError(AWSCommonRuntimeError())
}

guard let pointee = resolvedEndpoint?.pointee else {
return nil
}

return CRTAWSEndpointResolvedEndpoint(rawValue: pointee)
}

deinit {
aws_endpoints_rule_engine_release(rawValue)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0.

import XCTest
import Foundation
@testable import AwsCommonRuntimeKit

class CRTAWSEndpointsRuleEngineTests: CrtXCBaseTestCase {
ganeshnj marked this conversation as resolved.
Show resolved Hide resolved
let ruleSetString =
"""
{
"version": "1.0",
"serviceId": "example",
"parameters": {
"Region": {
"type": "string",
"builtIn": "AWS::Region",
"documentation": "The region to dispatch the request to"
}
},
"rules": [
{
"documentation": "rules for when region isSet",
"type": "tree",
"conditions": [
{
"fn": "isSet",
"argv": [
{
"ref": "Region"
}
]
}
],
"rules": [
{
"type": "endpoint",
"conditions": [
{
"fn": "aws.partition",
"argv": [
{
"ref": "Region"
}
],
"assign": "partitionResult"
}
],
"endpoint": {
"url": "https://example.{Region}.{partitionResult#dnsSuffix}"
}
},
{
"type": "error",
"documentation": "invalid region value",
"conditions": [],
"error": "unable to determine endpoint for region: {Region}"
}
]
},
{
"type": "endpoint",
"documentation": "the single service global endpoint",
"conditions": [],
"endpoint": {
"url": "https://example.amazonaws.com"
}
}
]
}
"""

func testResolve() throws {
let engine = try CRTAWSEndpointsRuleEngine(ruleSetString: ruleSetString)
let context = try CRTAWSEndpointsRequestContext()
try context.add(name: "Region", value: "us-west-2")
let endpoint = try engine.resolve(context: context)
let url = try endpoint?.getURL()
XCTAssertNotNil(url)
XCTAssertEqual("https://example.us-west-2.amazonaws.com", url!)
}

func testRuleSetParsingPerformance() {
measure {
_ = try! CRTAWSEndpointsRuleEngine(ruleSetString: ruleSetString)
}
}

func testRuleSetEvaluationPerformance() {
let engine = try! CRTAWSEndpointsRuleEngine(ruleSetString: ruleSetString)
let context = try! CRTAWSEndpointsRequestContext()
try! context.add(name: "Region", value: "us-west-2")
measure {
let _ = try! engine.resolve(context: context)
}
}

func testResolvePerformance() {
measure {
let engine = try! CRTAWSEndpointsRuleEngine(ruleSetString: ruleSetString)
let context = try! CRTAWSEndpointsRequestContext()
try! context.add(name: "Region", value: "us-west-2")
let _ = try! engine.resolve(context: context)
}
}
}
2 changes: 1 addition & 1 deletion aws-common-runtime/aws-c-sdkutils
Submodule aws-c-sdkutils updated 47 files
+4 −1 CMakeLists.txt
+1 −1 README.md
+300 −0 include/aws/sdkutils/endpoints_rule_engine.h
+50 −0 include/aws/sdkutils/private/endpoints_eval_util.h
+182 −0 include/aws/sdkutils/private/endpoints_ruleset_types_impl.h
+9 −0 include/aws/sdkutils/sdkutils.h
+292 −0 source/endpoints_eval_util.c
+2,128 −0 source/endpoints_rule_engine.c
+1,045 −0 source/endpoints_ruleset.c
+179 −0 source/endpoints_ruleset_types_impl.c
+1 −1 source/resource_name.c
+10 −0 source/sdkutils.c
+27 −0 tests/CMakeLists.txt
+140 −0 tests/endpoints_eval_util_tests.c
+443 −0 tests/endpoints_rule_engine_tests.c
+0 −7 tests/resource_name_tests.c
+41 −0 tests/resources/partitions.json
+41 −0 tests/resources/sample_ruleset.json
+32 −0 tests/resources/test-cases/aws-region.json
+45 −0 tests/resources/test-cases/default-values.json
+48 −0 tests/resources/test-cases/eventbridge.json
+50 −0 tests/resources/test-cases/fns.json
+25 −0 tests/resources/test-cases/headers.json
+148 −0 tests/resources/test-cases/is-virtual-hostable-s3-bucket.json
+27 −0 tests/resources/test-cases/local-region-override.json
+152 −0 tests/resources/test-cases/parse-arn.json
+153 −0 tests/resources/test-cases/parse-url.json
+125 −0 tests/resources/test-cases/partition-fn.json
+185 −0 tests/resources/test-cases/substring.json
+75 −0 tests/resources/test-cases/uri-encode.json
+56 −0 tests/resources/test-cases/valid-hostlabel.json
+44 −0 tests/resources/valid-rules/aws-region.json
+44 −0 tests/resources/valid-rules/default-values.json
+41 −0 tests/resources/valid-rules/deprecated-param.json
+323 −0 tests/resources/valid-rules/eventbridge.json
+128 −0 tests/resources/valid-rules/fns.json
+46 −0 tests/resources/valid-rules/get-attr-type-inference.json
+44 −0 tests/resources/valid-rules/headers.json
+48 −0 tests/resources/valid-rules/is-virtual-hostable-s3-bucket.json
+29 −0 tests/resources/valid-rules/minimal-ruleset.json
+251 −0 tests/resources/valid-rules/parse-arn.json
+102 −0 tests/resources/valid-rules/parse-url.json
+101 −0 tests/resources/valid-rules/partition-fn.json
+36 −0 tests/resources/valid-rules/region-override.json
+95 −0 tests/resources/valid-rules/substring.json
+44 −0 tests/resources/valid-rules/uri-encode.json
+55 −0 tests/resources/valid-rules/valid-hostlabel.json