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

fix: Add documentation generation for operations. #578

Merged
merged 13 commits into from
Aug 15, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import software.amazon.smithy.model.Model
import software.amazon.smithy.model.knowledge.OperationIndex
import software.amazon.smithy.model.knowledge.TopDownIndex
import software.amazon.smithy.model.shapes.OperationShape
import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.model.shapes.ShapeId
import software.amazon.smithy.model.shapes.StructureShape
import software.amazon.smithy.model.traits.DocumentationTrait
import software.amazon.smithy.model.traits.StreamingTrait
import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator
import software.amazon.smithy.swift.codegen.model.toLowerCamelCase
Expand Down Expand Up @@ -40,6 +43,7 @@ class ServiceGenerator(
*/
fun renderOperationDefinition(
model: Model,
service: ServiceShape,
symbolProvider: SymbolProvider,
writer: SwiftWriter,
opIndex: OperationIndex,
Expand All @@ -56,8 +60,7 @@ class ServiceGenerator(
val outputShape = opIndex.getOutput(op).get()
val outputShapeName = symbolProvider.toSymbol(outputShape).name

writer.writeShapeDocs(op)
writer.writeAvailableAttribute(model, op)
renderOperationDoc(model, service, op, writer)

val accessSpecifier = if (insideProtocol) "" else "public "

Expand All @@ -68,6 +71,45 @@ class ServiceGenerator(
outputShapeName
)
}

/**
* Helper method for generating in-line documentation for operation
*/
private fun renderOperationDoc(model: Model, service: ServiceShape, op: OperationShape, writer: SwiftWriter) {
writer.writeShapeDocs(op)
writer.writeAvailableAttribute(model, op)

fun writeEmptyLine() {
writer.writeSingleLineDocs { write("") }
}

writeEmptyLine()
writer.writeDocs("\\- Parameter ${op.inputShape.name} : ${retrieveMemberShapeDoc(op.inputShape, model)}")

writeEmptyLine()
writer.writeDocs("\\- Returns: \\`${op.outputShape.name}\\` : ${retrieveMemberShapeDoc(op.outputShape, model)}")

if (op.getErrors(service).isNotEmpty()) {
writeEmptyLine()
writer.writeSingleLineDocs { write("- Throws: One of the exceptions listed below __Possible Exceptions__.") }
writeEmptyLine()
writer.writeSingleLineDocs { write("__Possible Exceptions:__") }
op.getErrors(service).forEach { error ->
writer.writeDocs("\\- \\`${error.name}\\` : ${retrieveMemberShapeDoc(error.toShapeId(), model)}")
}
}
}

/**
* Helper method to grab documentation for operation's member shapes (input, output, error(s)
*/
private fun retrieveMemberShapeDoc(shapeId: ShapeId, model: Model): String {
val docTrait = model.getShape(shapeId).get().getTrait(DocumentationTrait::class.java).getOrNull()
return when {
docTrait == null -> "[no documentation found]"
else -> docTrait.value
}
sichanyoo marked this conversation as resolved.
Show resolved Hide resolved
}
sichanyoo marked this conversation as resolved.
Show resolved Hide resolved
}

fun render() {
Expand Down Expand Up @@ -118,7 +160,7 @@ class ServiceGenerator(
writer.openBlock("public protocol ${serviceSymbol.name}Protocol {")
.call {
operations.forEach { op ->
renderOperationDefinition(model, symbolProvider, writer, operationsIndex, op, true)
renderOperationDefinition(model, service, symbolProvider, writer, operationsIndex, op, true)
}
}
.closeBlock("}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ open class HttpProtocolClientGenerator(

writer.openBlock("extension ${serviceSymbol.name}: ${serviceSymbol.name}Protocol {", "}") {
operations.forEach {
ServiceGenerator.renderOperationDefinition(model, symbolProvider, writer, operationsIndex, it)
ServiceGenerator.renderOperationDefinition(model, serviceShape, symbolProvider, writer, operationsIndex, it)
writer.openBlock("{", "}") {
val operationStackName = "operation"
val generator = MiddlewareExecutionGenerator(ctx, writer, httpBindingResolver, httpProtocolCustomizable, operationMiddleware, operationStackName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ class ContentMd5MiddlewareTests {
val expectedContents =
"""
extension RestXmlProtocolClient: RestXmlProtocolClientProtocol {
/// This is a very cool operation.
///
/// - Parameter IdempotencyTokenWithStructureInput : [no documentation found]
///
/// - Returns: `IdempotencyTokenWithStructureOutputResponse` : [no documentation found]
public func idempotencyTokenWithStructure(input: IdempotencyTokenWithStructureInput) async throws -> IdempotencyTokenWithStructureOutputResponse
{
let context = ClientRuntime.HttpContextBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ class HttpProtocolClientGeneratorTests {
contents.shouldSyntacticSanityCheck()
val expected = """
extension RestJsonProtocolClient: RestJsonProtocolClientProtocol {
/// This is a very cool operation.
///
/// - Parameter AllocateWidgetInput : [no documentation found]
///
/// - Returns: `AllocateWidgetOutputResponse` : [no documentation found]
public func allocateWidget(input: AllocateWidgetInput) async throws -> AllocateWidgetOutputResponse
{
let context = ClientRuntime.HttpContextBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ class IdempotencyTokenTraitTests {
val expectedContents =
"""
extension RestXmlProtocolClient: RestXmlProtocolClientProtocol {
/// This is a very cool operation.
///
/// - Parameter IdempotencyTokenWithStructureInput : [no documentation found]
///
/// - Returns: `IdempotencyTokenWithStructureOutputResponse` : [no documentation found]
public func idempotencyTokenWithStructure(input: IdempotencyTokenWithStructureInput) async throws -> IdempotencyTokenWithStructureOutputResponse
{
let context = ClientRuntime.HttpContextBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ service RestXml {
]
}

@documentation("This is a very cool operation.")
@httpChecksumRequired
@http(uri: "/IdempotencyTokenWithStructure", method: "PUT")
operation IdempotencyTokenWithStructure {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ service RestXml {
}

@http(uri: "/IdempotencyTokenWithStructure", method: "PUT")
@documentation("This is a very cool operation.")
operation IdempotencyTokenWithStructure {
input: IdempotencyToken,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ operation GetFooStreamingInputNoOutput {
}

// https://awslabs.github.io/smithy/1.0/spec/core/behavior-traits.html#idempotencytoken-trait
@documentation("This is a very cool operation.")
@http(method: "POST", uri: "/input/AllocateWidget")
operation AllocateWidget {
input: AllocateWidgetInput
Expand Down