Skip to content

Latest commit

 

History

History
306 lines (230 loc) · 12.8 KB

README.md

File metadata and controls

306 lines (230 loc) · 12.8 KB

Build - main Branch Swift 5.5, 5.6 and 5.7 Tested Join the Smoke Server Side community on gitter Apache 2

SmokeAWSGenerate

SmokeAWSGenerate primarily provides a code generator that will generate a Swift client package using an Open API/Swagger model for endpoints hosted by AWS API Gateway.

By default, the generator will create two targets in the Swift client package, a Model target and a Client target.

  • The model target will create Swift types and enumerations for the objects specified in the model
  • The client target will create
    1. a Swift protocol based on the model operations.
    2. an API Gateway client implementation that conforms to the protocol.
    3. a Mock implementation that conforms to the protocol with optional overrides for each API. By default returns a default instance of each APIs return type.
    4. a Throwing Mock implementation that conforms to the protocol with optional overrides for each API. By default throws a specified error.
    5. a Configuration type that can used to share client configuration between clients.
    6. an Operations client that can be used to share the underlying http client between clients.

Step 1: Prepare the location for the new Swift Client

This might be a Github repository or some other repository. Check out this location so you can add files to it.

Step 2: Prepare your OpenAPI 3.0 or Swagger model

Depending on your use case, this model can either be hosted with the same Swift package as the Swift client or in a separate package.

Step 1A: Model in the same Swift package

For models in the same Swift package, just go ahead and create the model according to the Open API spec or Swagger spec. Typically this model will be in the root directory of the Client package.

Step 1B: Model in the separate Swift Package

If the model is hosted in a separate Swift Package, the model file will need to be specified as a resource of that package. The following shows the minimal Swift Package manifest that is required for a model package.

// swift-tools-version: 5.6
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "ServiceModel",
    products: [
        .library(
            name: "ServiceModel",
            targets: ["ServiceModel"]),
    ],
    targets: [
        .target(
            name: "ServiceModel",
            dependencies: [],
            path: "api",
            resources: [.copy("OpenAPI30.yaml")]),
    ]
)

This model package can have other products and targets if required. If your model package has only your model file (for example if you want to share your model across your service and client packages independently of anything else), you will need to add an empty Swift file in the base directory of the target (in this case /api) due to a current limitation of SwiftPM.

Then go ahead and create the model according to the Open API spec or Swagger spec.

Step 2: Generate the Client package

Clone this repository (smoke-aws-generate) and from its base directory, run the following command, replacing values as appropriate.

This command will generate the package manifest and other scaffolding required to build the client package.

Step 2A: Model in the same Swift package

swift run APIGatewayClientInitialize -c release --base-file-path <path-to-the-client-package> \
--base-name "PersistenceExample" \
--model-format "OPENAPI3_0" \
--model-path "OpenAPI30.yaml"

Step 2B: Model in the separate Swift Package

swift run APIGatewayClientInitialize -c release --base-file-path <path-to-the-client-package> \
--base-name "PersistenceExample" \
--model-format "OPENAPI3_0" \
--model-path "OpenAPI30.yaml" \
--model-product-dependency "ServiceModel" \
--package-location "https://github.com/example/service-model.git" \
--version-requirement-type "from"
--version-requirement "1.0.0"

Note: SWAGGER must be used for the --model-format parameter when using Swagger 2.0 model files.

Note: You can optionally specify a --model-target-dependency parameter if the target where the model file is hosted is not the same as the product name.

You can also optionally specify --model-target-name and --client-target-name parameters to specify custom target names. Otherwise \(base-name)Model and \(base-name)Client will be used.

Note: You can also manually generate a Swift package manifest and structure along with the configuration file (see next step). The APIGatewayClientInitialize executable is simply a convenience and not required to build the client package.

Step 3: Update the codegen configuration

As part of the previous step, a configuration file called api-gateway-client-swift-codegen.json will have been generated in the base directory of the client package. This file stores configuration options for the build-time code generation.

{
  "baseName" : "PersistenceExample",
  "modelFormat" : "OPENAPI3_0",
  "modelLocations" : {
    "default" : {
      "modelFilePath" : "OpenAPI30.yaml",
      "modelProductDependency" : "ServiceModel"
    }
  }
}

You can add the following additional options to this configuration file-

  • modelFormat: The expected format of the model file. Optional; defaulting to OPENAPI3_0. SWAGGER can also be specified.
  • modelOverride: A set of overrides to apply to the model. Optional.
  • httpClientConfiguration: Configuration for the generated http service clients. Optional.
  • shapeProtocols: ENABLED will conform model types to shape protocols that allow for easy conversion between different models. Optional; defaulting to DISABLED.
  • eventLoopFutureClientAPIs: ENABLED will generate EventLoopFuture-returning client APIs. Mock and Throwing Mock will require an EventLoop passed to their initializer. Optional; defaulting to DISABLED.
  • minimumCompilerSupport: UNKNOWN will generate a client that supports Swift 5.5 and 5.4. Optional; defaulting to 5.6.
  • clientConfigurationType: GENERATOR will generate a legacy client generator type instead of the configuration and operations clients types. Optional; defaulting to CONFIGURATION_OBJECT.
  • modelTargets: Provides the ability to customise the model target used by a client target. Optional, if not specified ``(baseName)Model` will be used.

The schemas for the modelOverride and httpClientConfiguration fields can be found here - https://github.com/amzn/service-model-swift-code-generate/blob/main/Sources/ServiceModelEntities/ModelOverride.swift.

An example configuration - including modelOverride configuration - can be found here - https://github.com/amzn/smoke-framework-examples/blob/612fd9dca5d8417d2293a203aca4b02672889d12/PersistenceExampleService/smoke-framework-codegen.json.

Shape protocols allow you to convert between similar types in different models

extension Model1.Location: Model2.LocationShape {}

let model2Location = model1.asModel2Location()

The modelTargets option can be specified as shown below.

{
  "baseName" : "PersistenceExample",
  "modelFormat" : "OPENAPI3_0",
  "modelLocations" : {
    "default" : {
      "modelFilePath" : "OpenAPI30.yaml",
      "modelProductDependency" : "ServiceModel"
    }
  },
  "modelTargets": {
      "MyClientTarget": {
          "modelTargetName": "MyModelTarget"
      }
  }
}

Step 4: Depend on the client package

You can now use the client package from other Swift packages by depending on the Client target.

Basic Usage

The easiest way to use the client is to initialize it directly and then at some later point shut it down.

let client = APIGatewayPersistenceExampleClient(credentialsProvider: credentialsProvider, 
                                                awsRegion: awsRegion,
                                                endpointHostName: endpointHostName)

...
// Use the client
...

try await client.shutdown()

Credential Providers need to conform to the CredentialsProvider protocol from SmokeAWSCore. Smoke AWS Credentials provides implementations for obtaining or assuming short-lived rotating AWS IAM credentials.

The client initializer can also optionally accept logger, timeoutConfiguration, connectionPoolConfiguration, retryConfiguration, eventLoopProvider and reportingConfiguration.

Reusing Configuration or the underlying HTTP client

For use cases where you want to reuse the underlying HTTP client between instances, you can use the operations client type (or similarly the configuration object type to share client configuration but not the underlying HTTP client).

// Start of application
let operationsClient = APIGatewayPersistenceExampleOperationsClient(credentialsProvider: credentialsProvider, 
                                                                    awsRegion: awsRegion,
                                                                    endpointHostName: endpointHostName)
                                                                    
// Per-request
let client = APIGatewayPersistenceExampleClient(operationsClient: operationsClient,
                                                logger: logger)
// Use the client within the request
// This client doesn't need to be explicitly shutdown 
// as it doesn't own the underlying http client
// client.shutdown() would be a no-op

// End of application
try await operationsClient.shutdown()

Using Mock client implementations for testing

You can use the Mock and Throwing Mock client implementations for unit testing. These implementations conform to the generated client protocol. Using this protocol within application code will allow you to test using a mock client and use the API Gateway client for actual usage.

Each client API can be overridden with any logic required for a unit test.

func testCodeThatUsesGetCustomerDetails() {
    func getCustomerDetails(input: PersistenceExampleModel.GetCustomerDetailsRequest) async throws 
    -> PersistenceExampleModel.CustomerAttributes {
       // mock behaviour of the API
    }
    
    let mockClient = MockPersistenceExampleClient(getCustomerDetails: getCustomerDetails)

    // run a test using the mock client

Convenience initializers for web and service frameworks

Each client also provides a set of convenience initializers using the HTTPClientInvocationAttributes protocol to pass the Logger, EventLoop, InternalRequestId and a metrics aggregator associated with the current request/invocation.

For example, when using the smoke-framework, you can directly pass the provided SmokeServerInvocationReporting instance into the initializer of the client.

    public func getInvocationContext(
            invocationReporting: SmokeServerInvocationReporting<SmokeInvocationTraceContext>) -> TheServiceContext {
        let theClient = APIGatewayAnotherServiceClient(operationsClient: self.theOperationsClient, invocationAttributes: invocationReporting)
        
        ...
        
        return TheServiceContext(...
                                 theClient: theClient,
                                 ...)
    }

If you want the client to ignore the EventLoop provided by the HTTPClientInvocationAttributes instance, you can set ignoreInvocationEventLoop on the configuration object or operations client to true. Otherwise, the client will attempt to execute the client http requests on the same event loop as the provided invocation.

Generate the SmokeAWS library

The SmokeAWSGenerate executable is a code generator for the SmokeAWS library.

Step 1: Check out the SmokeAWS repository

Clone the SmokeAWS repository to your local machine.

Step 2: Check out this repository

Clone this repository to your local machine.

Step 3: Run the code generator

From within your checked out copy of this repository, run this command-

swift run -c release SmokeAWSGenerate \
  --base-file-path <path_to_the_smoke_aws_repository>

License

This library is licensed under the Apache 2.0 License.