diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8fb3004..031cd14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,9 +26,9 @@ jobs: strategy: matrix: tag: - - swift:5.8 - swift:5.9 - swift:5.10 + - swift:6.0 container: image: ${{ matrix.tag }} steps: diff --git a/Package.swift b/Package.swift index 45837ea..44ea194 100644 --- a/Package.swift +++ b/Package.swift @@ -9,7 +9,7 @@ let package = Package( .plugin(name: "SotoCodeGeneratorPlugin", targets: ["SotoCodeGeneratorPlugin"]), ], dependencies: [ - .package(url: "https://github.com/soto-project/soto-smithy.git", from: "0.4.1"), + .package(url: "https://github.com/soto-project/soto-smithy.git", from: "0.4.2"), .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"), .package(url: "https://github.com/hummingbird-project/swift-mustache.git", from: "2.0.0-beta"), .package(url: "https://github.com/apple/swift-log.git", from: "1.4.0"), diff --git a/Sources/SotoCodeGeneratorLib/AwsService+paginators.swift b/Sources/SotoCodeGeneratorLib/AwsService+paginators.swift index a703451..9b8d94c 100644 --- a/Sources/SotoCodeGeneratorLib/AwsService+paginators.swift +++ b/Sources/SotoCodeGeneratorLib/AwsService+paginators.swift @@ -17,7 +17,7 @@ import SotoSmithy extension AwsService { /// Generate paginator context - func generatePaginatorContext() throws -> [String: Any] { + func generatePaginatorContext(_ operationContexts: [ShapeId: OperationContext]) throws -> [String: Any] { let paginatorOperations = self.operations.filter { $0.value.hasTrait(type: PaginatedTrait.self) } guard paginatorOperations.count > 0 else { return [:] } var context: [String: Any] = ["name": serviceName] @@ -53,9 +53,14 @@ extension AwsService { inputKeyToken = nil } + guard var operation = operationContexts[operation.key] else { continue } + if let inputKeyToken { + // remove input key from list of parameters to paginator function + operation.initParameters = operation.initParameters.filter { $0.parameter != inputKeyToken.toSwiftVariableCase() } + } paginatorContexts.append( PaginatorContext( - operation: try self.generateOperationContext(operationShape, operationName: operation.key, streaming: false), + operation: operation, inputKey: inputKeyToken.map { self.toKeyPath(token: $0, structure: inputShape) }, outputKey: self.toKeyPath(token: outputToken, structure: outputShape), moreResultsKey: paginatedTruncatedTrait.map { self.toKeyPath(token: $0.isTruncated, structure: outputShape) } diff --git a/Sources/SotoCodeGeneratorLib/AwsService+shapes.swift b/Sources/SotoCodeGeneratorLib/AwsService+shapes.swift index b7afbb4..d8d63a6 100644 --- a/Sources/SotoCodeGeneratorLib/AwsService+shapes.swift +++ b/Sources/SotoCodeGeneratorLib/AwsService+shapes.swift @@ -18,10 +18,7 @@ import SotoSmithyAWS extension AwsService { /// Generate context for outputting Shapes - func generateShapesContext() throws -> [String: Any] { - var context: [String: Any] = [:] - context["name"] = serviceName - + func generateShapesContext() throws -> ShapesContext { // generate enums let traitEnums: [EnumContext] = try model .select(from: "[trait|enum]") @@ -30,13 +27,13 @@ extension AwsService { .select(type: EnumShape.self) .compactMap { self.generateEnumContext($0.value, shapeName: $0.key.shapeName) } let enums = (traitEnums + shapeEnums).sorted { $0.name < $1.name } - var shapeContexts: [[String: Any]] = enums.map { ["enum": $0] } + var shapeContexts: [ShapesContext.ShapeType] = enums.map { .enum($0) } // generate structures let structures = model.select(type: StructureShape.self).sorted { $0.key.shapeName < $1.key.shapeName } for structure in structures { guard let shapeContext = self.generateStructureContext(structure.value, shapeId: structure.key, typeIsUnion: false) else { continue } - shapeContexts.append(["struct": shapeContext]) + shapeContexts.append(.struct(shapeContext)) } // generate unions @@ -46,16 +43,20 @@ extension AwsService { let typeIsUnion = union.value.members?.count == 1 ? false : true guard let shapeContext = self.generateStructureContext(union.value, shapeId: union.key, typeIsUnion: typeIsUnion) else { continue } if typeIsUnion { - shapeContexts.append(["enumWithValues": shapeContext]) + shapeContexts.append(.enumWithValues(shapeContext)) } else { - shapeContexts.append(["struct": shapeContext]) + shapeContexts.append(.struct(shapeContext)) } } + let errorContext = try self.generateErrorContext() - if shapeContexts.count > 0 { - context["shapes"] = shapeContexts - } - return context + return .init( + name: self.serviceName, + shapes: shapeContexts, + errors: errorContext.count > 0 ? errorContext : nil, + scope: "public", + extraCode: nil /* self.generateExtraCode() */ + ) } /// Generate the context information for outputting an enum from strings with enum traits @@ -622,4 +623,42 @@ extension AwsService { } return nil } + + func generateExtraCode() -> String? { + switch self.serviceName { + case "DynamoDB": + """ + extension DynamoDB.AttributeValue: Equatable { + public static func == (lhs: Self, rhs: Self) -> Bool { + switch (lhs, rhs) { + case (.b(let lhs), .b(let rhs)): + return lhs == rhs + case (.bool(let lhs), .bool(let rhs)): + return lhs == rhs + case (.bs(let lhs), .bs(let rhs)): + return lhs == rhs + case (.l(let lhs), .l(let rhs)): + return lhs == rhs + case (.m(let lhs), .m(let rhs)): + return lhs == rhs + case (.n(let lhs), .n(let rhs)): + return lhs == rhs + case (.ns(let lhs), .ns(let rhs)): + return lhs == rhs + case (.null(let lhs), .null(let rhs)): + return lhs == rhs + case (.s(let lhs), .s(let rhs)): + return lhs == rhs + case (.ss(let lhs), .ss(let rhs)): + return lhs == rhs + default: + return false + } + } + } + """ + default: + nil + } + } } diff --git a/Sources/SotoCodeGeneratorLib/AwsService+waiters.swift b/Sources/SotoCodeGeneratorLib/AwsService+waiters.swift index f799e02..ce82756 100644 --- a/Sources/SotoCodeGeneratorLib/AwsService+waiters.swift +++ b/Sources/SotoCodeGeneratorLib/AwsService+waiters.swift @@ -17,17 +17,18 @@ import SotoSmithy extension AwsService { /// Generate list of waiter contexts - func generateWaiterContexts() throws -> [String: Any] { + func generateWaiterContexts(_ operationContexts: [ShapeId: OperationContext]) throws -> [String: Any] { var context: [String: Any] = [:] context["name"] = self.serviceName var waiters: [WaiterContext] = [] for operation in self.operations { guard let trait = operation.value.trait(type: WaitableTrait.self) else { continue } + guard let operationContext = operationContexts[operation.key] else { continue } for waiter in trait.value { let waiterContext = try generateWaiterContext( waiter.value, name: waiter.key, - operation: operation.value, + operationContext: operationContext, operationName: operation.key ) waiters.append(waiterContext) @@ -40,12 +41,11 @@ extension AwsService { } /// Generate waiter context from waiter - func generateWaiterContext(_ waiter: WaitableTrait.Waiter, name: String, operation: OperationShape, operationName: ShapeId) throws -> WaiterContext { + func generateWaiterContext(_ waiter: WaitableTrait.Waiter, name: String, operationContext: OperationContext, operationName: ShapeId) throws -> WaiterContext { var acceptorContexts: [AcceptorContext] = [] for acceptor in waiter.acceptors { acceptorContexts.append(self.generateAcceptorContext(acceptor)) } - let operationContext = try self.generateOperationContext(operation, operationName: operationName, streaming: false) return .init( waiterName: name, operation: operationContext, @@ -88,7 +88,7 @@ extension AwsService { /// Basically convert all fields into format used for variables - ie lowercase first character func generatePathArgument(argument: String) -> String { // a field is any series of letters that doesn't end with a `(` - var output: String = "" + var output = "" var index = argument.startIndex var fieldStartIndex: String.Index? while index != argument.endIndex { diff --git a/Sources/SotoCodeGeneratorLib/AwsService.swift b/Sources/SotoCodeGeneratorLib/AwsService.swift index ec80cf1..f446b19 100644 --- a/Sources/SotoCodeGeneratorLib/AwsService.swift +++ b/Sources/SotoCodeGeneratorLib/AwsService.swift @@ -50,6 +50,8 @@ struct AwsService { self.logger = logger self.markInputOutputShapes(model) + // this is a breaking change (maybe for v8) + // self.removeEmptyInputs(model) } /// Return service name from API @@ -91,7 +93,8 @@ struct AwsService { let serviceId = serviceEntry.key let service = serviceEntry.value let authSigV4 = service.trait(type: AwsAuthSigV4Trait.self) - let operations = try generateOperationContexts() + + let operationContexts = try self.generateOperationContexts() context["name"] = self.serviceName context["description"] = self.processDocs(from: service) @@ -132,9 +135,18 @@ struct AwsService { context["variantEndpoints"] = self.getVariantEndpoints() .map { (variant: $0.key, endpoints: $0.value) } .sorted { $0.variant < $1.variant } - context["operations"] = operations.operations - context["streamingOperations"] = operations.streamingOperations + context["operations"] = operationContexts.values.sorted { $0.funcName < $1.funcName } + let paginators = try self.generatePaginatorContext(operationContexts) + let waiters = try self.generateWaiterContexts(operationContexts) + if paginators["paginators"] != nil { + context["paginators"] = paginators + } + if waiters["waiters"] != nil { + context["waiters"] = waiters + } + context["logger"] = self.getSymbol(for: "Logger", from: "Logging", model: self.model, namespace: serviceId.namespace ?? "") + return context } @@ -173,34 +185,27 @@ struct AwsService { return context } - /// Generate list of operation and streaming operation contexts - func generateOperationContexts() throws -> (operations: [OperationContext], streamingOperations: [OperationContext]) { - var operationContexts: [OperationContext] = [] - var streamingOperationContexts: [OperationContext] = [] + /// Generate map of operation + func generateOperationContexts() throws -> [ShapeId: OperationContext] { + var operationContexts: [ShapeId: OperationContext] = [:] let operations = self.operations for operation in operations { - let operationContext = try generateOperationContext(operation.value, operationName: operation.key, streaming: false) - operationContexts.append(operationContext) - - if let output = operation.value.output, - let outputShape = model.shape(for: output.target) as? StructureShape, - let payloadMember = getPayloadMember(from: outputShape), - let payloadShape = model.shape(for: payloadMember.value.target), - payloadShape.trait(type: StreamingTrait.self) != nil, - payloadShape is BlobShape - { - let operationContext = try generateOperationContext(operation.value, operationName: operation.key, streaming: true) - streamingOperationContexts.append(operationContext) - } + let operationContext = try generateOperationContext( + operation.value, + operationName: operation.key, + streaming: false + ) + operationContexts[operation.key] = operationContext } - return ( - operations: operationContexts.sorted { $0.funcName < $1.funcName }, - streamingOperations: streamingOperationContexts.sorted { $0.funcName < $1.funcName } - ) + return operationContexts } /// Generate context for rendering a single operation. Used by both `generateServiceContext` and `generatePaginatorContext` - func generateOperationContext(_ operation: OperationShape, operationName: ShapeId, streaming: Bool) throws -> OperationContext { + func generateOperationContext( + _ operation: OperationShape, + operationName: ShapeId, + streaming: Bool + ) throws -> OperationContext { let httpTrait = operation.trait(type: HttpTrait.self) let deprecatedTrait = operation.trait(type: DeprecatedTrait.self) let endpointTrait = operation.trait(type: EndpointTrait.self) @@ -216,6 +221,11 @@ struct AwsService { if outputShapeTarget == "smithy.api#Unit" { outputShapeTarget = nil } + // get member contexts from shape + var initParamContext: [OperationInitParamContext] = [] + if let inputShapeTarget { + initParamContext = self.generateInitParameterContexts(inputShapeTarget) + } return OperationContext( comment: self.processDocs(from: operation), funcName: operationName.shapeName.toSwiftVariableCase(), @@ -228,10 +238,44 @@ struct AwsService { deprecated: deprecatedTrait?.message, streaming: streaming ? "ByteBuffer" : nil, documentationUrl: nil, // added to comment - endpointRequired: requireEndpointDiscovery.map { OperationContext.DiscoverableEndpoint(required: $0) } + endpointRequired: requireEndpointDiscovery.map { OperationContext.DiscoverableEndpoint(required: $0) }, + initParameters: initParamContext ) } + func generateInitParameterContexts(_ inputShapeId: ShapeId) -> [OperationInitParamContext] { + guard let shape = self.model.shape(for: inputShapeId) as? StructureShape else { return [] } + guard let members = shape.members else { return [] } + let sortedMembers = members.map { $0 }.sorted { $0.key.lowercased() < $1.key.lowercased() } + var contexts: [MemberContext] = [] + for member in sortedMembers { + guard let targetShape = self.model.shape(for: member.value.target) else { continue } + // member context + let memberContext = self.generateMemberContext( + member.value, + targetShape: targetShape, + name: member.key, + shapeName: inputShapeId.shapeName, + typeIsUnion: false, + isOutputShape: false + ) + contexts.append(memberContext) + } + return contexts.compactMap { + if !$0.deprecated { + OperationInitParamContext( + variable: $0.variable, + parameter: $0.parameter, + type: $0.type, + default: $0.default, + comment: $0.comment + ) + } else { + nil + } + } + } + static func getTrait(from shape: SotoSmithy.Shape, trait: T.Type, id: ShapeId) throws -> T { guard let trait = shape.trait(type: T.self) else { throw Error(reason: "\(id) does not have a \(T.staticName) trait") @@ -456,6 +500,24 @@ struct AwsService { } } + func removeEmptyInputs(_ model: Model) { + for operation in self.operations { + if let input = operation.value.input { + if let shape = model.shape(for: input.target) { + if let structureShape = shape as? StructureShape { + if let members = structureShape.members { + if members.count == 0 { + operation.value.input = nil + } + } else { + operation.value.input = nil + } + } + } + } + } + } + /// The JSON decoder requires an array to exist, even if it is empty so we have to make /// all arrays in output shapes optional func removeRequiredTraitFromOutputCollections(_ model: Model) { @@ -666,6 +728,29 @@ extension AwsService { let streaming: String? let documentationUrl: String? let endpointRequired: DiscoverableEndpoint? + var initParameters: [OperationInitParamContext] + } + + struct OperationInitParamContext { + let variable: String + let parameter: String + let type: String + let `default`: String? + let comment: [String.SubSequence] + } + + struct ShapesContext { + enum ShapeType { + case `enum`(EnumContext) + case `struct`(StructureContext) + case enumWithValues(StructureContext) + } + + let name: String + let shapes: [ShapeType] + let errors: [String: Any]? + var scope: String + let extraCode: String? } struct PaginatorContext { diff --git a/Sources/SotoCodeGeneratorLib/SotoCodeGen.swift b/Sources/SotoCodeGeneratorLib/SotoCodeGen.swift index d6714fd..3ad2153 100644 --- a/Sources/SotoCodeGeneratorLib/SotoCodeGen.swift +++ b/Sources/SotoCodeGeneratorLib/SotoCodeGen.swift @@ -206,16 +206,10 @@ public struct SotoCodeGen { } let scope = config.access == .internal ? "internal" : "public" + var shapesContext = try service.generateShapesContext() var apiContext = try service.generateServiceContext() - let paginators = try service.generatePaginatorContext() - let waiters = try service.generateWaiterContexts() + shapesContext.scope = scope apiContext["scope"] = scope - if paginators["paginators"] != nil { - apiContext["paginators"] = paginators - } - if waiters["waiters"] != nil { - apiContext["waiters"] = waiters - } let api = self.library.render(apiContext, withTemplate: "api")! if try api @@ -224,13 +218,6 @@ public struct SotoCodeGen { self.logger.info("Wrote \(prefix)_api.swift") } - var shapesContext = try service.generateShapesContext() - let errorContext = try service.generateErrorContext() - shapesContext["scope"] = scope - if errorContext["errors"] != nil { - shapesContext["errors"] = errorContext - } - let shapes = self.library.render(shapesContext, withTemplate: "shapes")! if self.command.output, try shapes.writeIfChanged( toFile: "\(basePath)/\(prefix)_shapes.swift" diff --git a/Sources/SotoCodeGeneratorLib/Templates/api.swift b/Sources/SotoCodeGeneratorLib/Templates/api.swift index 117d265..d292832 100644 --- a/Sources/SotoCodeGeneratorLib/Templates/api.swift +++ b/Sources/SotoCodeGeneratorLib/Templates/api.swift @@ -17,6 +17,12 @@ extension Templates { {{%CONTENT_TYPE:TEXT}} {{>header}} + #if os(Linux) && compiler(<5.10) + // swift-corelibs-foundation hasn't been updated with Sendable conformances + @preconcurrency import Foundation + #else + import Foundation + #endif @_exported import SotoCore {{#middlewareFramework}} @@ -39,6 +45,7 @@ extension Templates { {{scope}} let config: AWSServiceConfig {{#endpointDiscovery}} /// endpoint storage + @usableFromInline let endpointStorage: AWSEndpointStorage {{/endpointDiscovery}} @@ -242,8 +249,9 @@ extension Templates { @available(*, deprecated, message: "{{.}}") {{/deprecated}} @Sendable + @inlinable {{scope}} func {{funcName}}({{#inputShape}}_ input: {{.}}, {{/inputShape}}logger: {{logger}} = AWSClient.loggingDisabled) async throws{{#outputShape}} -> {{.}}{{/outputShape}} { - return try await self.client.execute( + try await self.client.execute( operation: "{{name}}", path: "{{path}}", httpMethod: .{{httpMethod}}, @@ -255,10 +263,45 @@ extension Templates { logger: logger ) } + {{#inputShape}} + {{#comment}} + {{>comment}} + {{/comment}} + {{#documentationUrl}} + /// {{.}} + {{/documentationUrl}} + /// + /// Parameters: + {{#initParameters}} + /// - {{parameter}}: {{first(comment)}} + {{/initParameters}} + /// - logger: Logger use during operation + {{#deprecated}} + @available(*, deprecated, message: "{{.}}") + {{/deprecated}} + @inlinable + {{scope}} func {{funcName}}( + {{#initParameters}} + {{parameter}}: {{type}}{{#default}} = {{.}}{{/default}}, + {{/initParameters}} + logger: {{logger}} = AWSClient.loggingDisabled + ) async throws{{#outputShape}} -> {{.}}{{/outputShape}} { + let input = {{inputShape}}( + {{^empty(initParameters)}} + {{#initParameters}} + {{parameter}}: {{variable}}{{^last()}}, {{/last()}} + {{/initParameters}} + {{/empty(initParameters)}} + ) + return try await self.{{funcName}}(input, logger: logger) + } + {{/inputShape}} {{/operations}} {{#endpointDiscovery}} - @Sendable func getEndpoint(logger: Logger) async throws -> AWSEndpoints { + @Sendable + @usableFromInline + func getEndpoint(logger: Logger) async throws -> AWSEndpoints { let response = try await self.describeEndpoints(.init(), logger: logger) return .init(endpoints: response.endpoints.map { .init(address: "https://\($0.address)", cachePeriodInMinutes: $0.cachePeriodInMinutes) diff --git a/Sources/SotoCodeGeneratorLib/Templates/paginators.swift b/Sources/SotoCodeGeneratorLib/Templates/paginators.swift index c90247a..be8d86b 100644 --- a/Sources/SotoCodeGeneratorLib/Templates/paginators.swift +++ b/Sources/SotoCodeGeneratorLib/Templates/paginators.swift @@ -20,17 +20,15 @@ extension Templates { @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) extension {{name}} { {{#paginators}} - {{#operation.comment}} - {{>comment}} - {{/operation.comment}} - /// Return PaginatorSequence for operation. + /// Return PaginatorSequence for operation ``{{operation.funcName}}(_:logger:)``. /// /// - Parameters: - /// - input: Input for request - /// - logger: Logger used flot logging + /// - input: Input for operation + /// - logger: Logger used for logging {{#operation.deprecated}} @available(*, deprecated, message: "{{.}}") {{/operation.deprecated}} + @inlinable {{scope}} func {{operation.funcName}}Paginator( _ input: {{operation.inputShape}}, logger: {{logger}} = AWSClient.loggingDisabled @@ -48,6 +46,36 @@ extension Templates { logger: logger ) } + {{#operation}} + {{#inputShape}} + /// Return PaginatorSequence for operation ``{{operation.funcName}}(_:logger:)``. + /// + /// - Parameters: + {{#initParameters}} + /// - {{parameter}}: {{first(comment)}} + {{/initParameters}} + /// - logger: Logger used for logging + {{#deprecated}} + @available(*, deprecated, message: "{{.}}") + {{/deprecated}} + @inlinable + {{scope}} func {{funcName}}Paginator( + {{#initParameters}} + {{parameter}}: {{type}}{{#default}} = {{.}}{{/default}}, + {{/initParameters}} + logger: {{logger}} = AWSClient.loggingDisabled + ) -> AWSClient.PaginatorSequence<{{operation.inputShape}}, {{operation.outputShape}}> { + let input = {{inputShape}}( + {{^empty(initParameters)}} + {{#initParameters}} + {{parameter}}: {{variable}}{{^last()}}, {{/last()}} + {{/initParameters}} + {{/empty(initParameters)}} + ) + return self.{{funcName}}Paginator(input, logger: logger) + } + {{/inputShape}} + {{/operation}} {{^last()}} {{/last()}} @@ -56,6 +84,7 @@ extension Templates { {{#paginatorShapes}} extension {{name}}.{{inputShape}}: {{paginatorProtocol}} { + @inlinable {{scope}} func usingPaginationToken(_ token: {{tokenType}}) -> {{name}}.{{inputShape}} { return .init( {{#initParams}} diff --git a/Sources/SotoCodeGeneratorLib/Templates/shapes.swift b/Sources/SotoCodeGeneratorLib/Templates/shapes.swift index 28578b1..138a564 100644 --- a/Sources/SotoCodeGeneratorLib/Templates/shapes.swift +++ b/Sources/SotoCodeGeneratorLib/Templates/shapes.swift @@ -52,6 +52,9 @@ extension Templates { {{>errors}} {{/errors}} + {{#extraCode}} + {{.}} + {{/extraCode}} """ } diff --git a/Sources/SotoCodeGeneratorLib/Templates/struct.swift b/Sources/SotoCodeGeneratorLib/Templates/struct.swift index b4dfd82..1fe719d 100644 --- a/Sources/SotoCodeGeneratorLib/Templates/struct.swift +++ b/Sources/SotoCodeGeneratorLib/Templates/struct.swift @@ -69,6 +69,7 @@ extension Templates { {{scope}} init() {} {{/empty(members)}} {{^empty(members)}} + @inlinable {{scope}} init({{#initParameters}}{{parameter}}: {{type}}{{#default}} = {{.}}{{/default}}{{^last()}}, {{/last()}}{{/initParameters}}) { {{#members}} {{^deprecated}} @@ -84,6 +85,7 @@ extension Templates { {{^empty(deprecatedMembers)}} @available(*, deprecated, message: "Members {{#deprecatedMembers}}{{.}}{{^last()}}, {{/last()}}{{/deprecatedMembers}} have been deprecated") + @inlinable {{scope}} init({{#members}}{{parameter}}: {{type}}{{#default}} = {{.}}{{/default}}{{^last()}}, {{/last()}}{{/members}}) { {{#members}} self.{{variable}} = {{variable}} diff --git a/Sources/SotoCodeGeneratorLib/Templates/waiters.swift b/Sources/SotoCodeGeneratorLib/Templates/waiters.swift index edacdbe..17b49c2 100644 --- a/Sources/SotoCodeGeneratorLib/Templates/waiters.swift +++ b/Sources/SotoCodeGeneratorLib/Templates/waiters.swift @@ -20,12 +20,18 @@ extension Templates { @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) extension {{name}} { {{#waiters}} + /// Waiter for operation ``{{operation.funcName}}(_:logger:)``. + /// + /// - Parameters: + /// - input: Input for operation + /// - logger: Logger used for logging + @inlinable {{scope}} func waitUntil{{waiterName}}( _ input: {{operation.inputShape}}, maxWaitTime: TimeAmount? = nil, logger: Logger = AWSClient.loggingDisabled ) async throws { - let waiter = AWSClient.Waiter( + let waiter = AWSClient.Waiter<{{operation.inputShape}}, _>( acceptors: [ {{#acceptors}} {{#matcher.jmesPath}} @@ -58,6 +64,31 @@ extension Templates { ) return try await self.client.waitUntil(input, waiter: waiter, maxWaitTime: maxWaitTime, logger: logger) } + {{#inputKey}} + /// Waiter for operation ``{{operation.funcName}}(_:logger:)``. + /// + /// - Parameters: + {{#operation.initParameters}} + /// - {{parameter}}: {{first(comment)}} + {{/operation.initParameters}} + /// - logger: Logger used for logging + @inlinable + {{scope}} func waitUntil{{waiterName}}( + {{#operation.initParameters}} + {{parameter}}: {{type}}{{#default}} = {{.}}{{/default}}, + {{/operation.initParameters}} + logger: {{logger}} = AWSClient.loggingDisabled + ) async throws { + let input = {{operation.inputShape}}( + {{^empty(operation.initParameters)}} + {{#operation.initParameters}} + {{parameter}}: {{variable}}{{^last()}}, {{/last()}} + {{/operation.initParameters}} + {{/empty(operation.initParameters)}} + ) + try await self.waitUntil{{waiterName}}(input, logger: logger) + } + {{/inputKey}} {{^last()}} {{/last()}}