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

Generate NoStackTrace error types #1104

Merged
merged 5 commits into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package smithy4s.example

import scala.util.control.NoStackTrace
import smithy4s.Hints
import smithy4s.Schema
import smithy4s.ShapeId
import smithy4s.ShapeTag
import smithy4s.schema.Schema.string
import smithy4s.schema.Schema.struct

final case class EHFallbackClientError(message: Option[String] = None) extends Throwable {
final case class EHFallbackClientError(message: Option[String] = None) extends NoStackTrace {
override def getMessage(): String = message.orNull
}
object EHFallbackClientError extends ShapeTag.Companion[EHFallbackClientError] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package smithy4s.example

import scala.util.control.NoStackTrace
import smithy4s.Hints
import smithy4s.Schema
import smithy4s.ShapeId
import smithy4s.ShapeTag
import smithy4s.schema.Schema.string
import smithy4s.schema.Schema.struct

final case class EHNotFound(message: Option[String] = None) extends Throwable {
final case class EHNotFound(message: Option[String] = None) extends NoStackTrace {
override def getMessage(): String = message.orNull
}
object EHNotFound extends ShapeTag.Companion[EHNotFound] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package smithy4s.example

import scala.util.control.NoStackTrace
import smithy4s.Hints
import smithy4s.Schema
import smithy4s.ShapeId
import smithy4s.ShapeTag
import smithy4s.schema.Schema.string
import smithy4s.schema.Schema.struct

final case class EHServiceUnavailable(message: Option[String] = None) extends Throwable {
final case class EHServiceUnavailable(message: Option[String] = None) extends NoStackTrace {
override def getMessage(): String = message.orNull
}
object EHServiceUnavailable extends ShapeTag.Companion[EHServiceUnavailable] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ private[internals] object CollisionAvoidance {
val option = NameRef("scala", "Option")
val none = NameRef("scala", "None")
val some = NameRef("scala", "Some")
val noStackTrace = NameRef("scala.util.control", "NoStackTrace")
val throwable = NameRef("java.lang", "Throwable")

}

Expand Down
1 change: 1 addition & 0 deletions modules/codegen/src/smithy4s/codegen/internals/IR.scala
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ private[internals] sealed trait Hint
private[internals] object Hint {
case object Trait extends Hint
case object Error extends Hint
case object NoStackTrace extends Hint
case object PackedInputs extends Hint
case object NoDefault extends Hint
case object ErrorMessage extends Hint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private[codegen] object LineSegment {

def isAutoImported: Boolean = {
val value = pkg.mkString(".")
value.startsWith("scala") || value.equalsIgnoreCase("java.lang")
NameRef.autoImportedNames.exists(_.equalsIgnoreCase(value))
}
def getNamePrefix: String = name.split("\\.").head
def +(piece: String): NameRef = {
Expand All @@ -79,6 +79,12 @@ private[codegen] object LineSegment {
}

object NameRef {
val autoImportedNames: List[String] = List(
"scala",
"java.lang",
"scala.Predef",
"scala.collection.immutable"
)
implicit val nameRefShow: Show[NameRef] = Show.show[NameRef](_.asImport)
def apply(pkg: String, name: String): NameRef =
NameRef(pkg.split("\\.").toList, name, List.empty)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -628,11 +628,15 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self =>

lines(
if (hints.contains(Hint.Error)) {
val exception =
if (hints.contains(Hint.NoStackTrace))
noStackTrace
else throwable
val mixinExtensions = if (mixins.nonEmpty) {
val ext = mixins.map(m => line"$m").intercalate(line" with ")
line" with $ext"
} else Line.empty
block(line"$decl extends Throwable$mixinExtensions") {
block(line"$decl extends $exception$mixinExtensions") {
fields
.find { f =>
f.hints.contains_(Hint.ErrorMessage) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import cats.implicits._
import smithy4s.meta.AdtMemberTrait
import smithy4s.meta.ErrorMessageTrait
import smithy4s.meta.IndexedSeqTrait
import smithy4s.meta.NoStackTraceTrait
import smithy4s.meta.PackedInputsTrait
import smithy4s.meta.RefinementTrait
import smithy4s.meta.VectorTrait
Expand Down Expand Up @@ -913,6 +914,8 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) {
Hint.Deprecated(d.getMessage.asScala, d.getSince.asScala)
case _: ErrorMessageTrait =>
Hint.ErrorMessage
case _: NoStackTraceTrait =>
Hint.NoStackTrace
case _: VectorTrait =>
Hint.SpecializedList.Vector
case _: IndexedSeqTrait =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ smithy4s.meta.AdtMemberTrait$Provider
smithy4s.meta.VectorTrait$Provider
smithy4s.meta.IndexedSeqTrait$Provider
smithy4s.meta.ErrorMessageTrait$Provider
smithy4s.meta.NoStackTraceTrait$Provider
smithy4s.meta.RefinementTrait$Provider
smithy4s.meta.UnwrapTrait$Provider
smithy4s.meta.AdtTrait$Provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,8 @@ structure typeclass {
/// include a Service Product version of the service.
@trait(selector: ":is(service)")
structure generateServiceProduct {}

/// Placing this trait on an error will cause the generated code to exclude the stacktrace
/// via extending scala.util.control.NoStackTrace instead of Throwable.
@trait(selector: "structure :is([trait|error])")
structure noStackTrace {}
48 changes: 48 additions & 0 deletions modules/protocol/src/smithy4s/meta/NoStackTraceTrait.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2021-2022 Disney Streaming
*
* Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://disneystreaming.github.io/TOST-1.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package smithy4s.meta;

import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.AnnotationTrait;
import software.amazon.smithy.model.traits.AbstractTrait;

public class NoStackTraceTrait extends AnnotationTrait {

public static ShapeId ID = ShapeId.from("smithy4s.meta#noStackTrace");

public NoStackTraceTrait(ObjectNode node) {
super(ID, node);
}

public NoStackTraceTrait() {
super(ID, Node.objectNode());
}

public static final class Provider extends AbstractTrait.Provider {
public Provider() {
super(ID);
}

@Override
public NoStackTraceTrait createTrait(ShapeId target, Node node) {
return new NoStackTraceTrait(node.expectObjectNode());
}
}
}

5 changes: 5 additions & 0 deletions sampleSpecs/errorHandling.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ $version: "2"

namespace smithy4s.example

use smithy4s.meta#noStackTrace

// Where the errors are placed in this spec doesn't necessarily "make sense" but is
// mixed up to test some specific scenarios such as falling back to service-level
// errors when no operation-level ones are found.
Expand All @@ -22,17 +24,20 @@ operation ErrorHandlingOperation {
errors: [EHNotFound, EHFallbackServerError]
}

@noStackTrace
@httpError(404)
@error("client")
structure EHNotFound {
message: String
}

@noStackTrace
@error("client")
structure EHFallbackClientError {
message: String
}

@noStackTrace
@httpError(503)
@error("server")
structure EHServiceUnavailable {
Expand Down