Contents

amzn/smoke-aws-generate

Code generator to generate the SmokeAWS library from service models.

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.

Package Metadata

Repository: amzn/smoke-aws-generate

Stars: 19

Forks: 8

Open issues: 4

Default branch: main

Primary language: swift

License: Apache-2.0

README: README.md