Skip to content

Commit

Permalink
Update to smithy4s 0.18 (#255)
Browse files Browse the repository at this point in the history
  • Loading branch information
kubukoz authored Oct 5, 2023
1 parent 30e063d commit 469cd86
Show file tree
Hide file tree
Showing 27 changed files with 426 additions and 254 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,9 @@ We have:
- booleans: `true`, `false`
- lists: `[ true, false, ]`
- structures: `{ k: "value", k2: true, k3: [ 10, 20, ], }` - note that keys aren't quoted, unlike in JSON
- null, but it's not what you think: the `null` literal only typechecks as a Smithy `Document`,
and corresponds to the [Null document](https://awslabs.github.io/smithy/2.0/spec/simple-types.html?highlight=null#document).
- null, but it's not what you think: the `null` literal only typechecks as two things:
- a Smithy `Document`: corresponds to the [Null document](https://awslabs.github.io/smithy/2.0/spec/simple-types.html?highlight=null#document)
- an element of a sparse collection.

### Type representation

Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ lazy val core = module("core")
"com.disneystreaming.smithy4s" %% "smithy4s-aws-http4s" % smithy4sVersion.value,
"com.disneystreaming.smithy4s" % "smithy4s-protocol" % smithy4sVersion.value % Test,
"com.disneystreaming.alloy" % "alloy-core" % "0.2.7" % Test,
"software.amazon.smithy" % "smithy-aws-traits" % "1.34.0" % Test,
"software.amazon.smithy" % "smithy-aws-traits" % "1.39.1" % Test,
),
Smithy4sCodegenPlugin.defaultSettings(Test),
)
Expand Down
47 changes: 4 additions & 43 deletions modules/core/src/main/scala/playground/DynamicServiceProxy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ class DynamicServiceProxy[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]](
val mapOutput = makeProxy(endpointStatic.output, endpoint.output)

def errorMapper[A]: Throwable => F[A] =
endpointStatic.errorable match {
endpointStatic.error match {
case None => _.raiseError[F, A]
case Some(errorableStatic) =>
val errorable = endpoint.errorable.get // should be there at this point
val mapError = makeProxy(errorableStatic.error, errorable.error)
val errorable = endpoint.error.get // should be there at this point
val mapError = makeProxy(errorableStatic.schema, errorable.schema)

e =>
errorableStatic.liftError(e) match {
Expand All @@ -77,47 +77,8 @@ class DynamicServiceProxy[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]](
endpoint: Endpoint[Op, I, E, O, SI, SO]
): I => F[O] = applyWithStatic(endpoint, grp(endpoint.id))
}
.precomputeBy(service.endpoints, _.name)

new FunctorInterpreter[Op, F] {
def apply[I, E, O, SI, SO](
op: Op[I, E, O, SI, SO]
): F[O] = {
val (input, endpoint) = service.endpoint(op)
endpointMapping(endpoint)(input)
}
}
}

private final implicit class PolyFunction5Ops[F[_, _, _, _, _], G[_, _, _, _, _]](
self: PolyFunction5[F, G]
) {

// copied from smithy4s PolyFunction5's unsafeCacheBy
final def precomputeBy[K](
allPossibleInputs: Seq[Kind5.Existential[F]],
getKey: Kind5.Existential[F] => K,
): PolyFunction5[F, G] =
new PolyFunction5[F, G] {

private val map: Map[K, Any] = {
val builder = Map.newBuilder[K, Any]
allPossibleInputs.foreach(input =>
builder += getKey(input) -> self
.apply(input.asInstanceOf[F[Any, Any, Any, Any, Any]])
.asInstanceOf[Any]
)
builder.result()
}

def apply[A0, A1, A2, A3, A4](
input: F[A0, A1, A2, A3, A4]
): G[A0, A1, A2, A3, A4] = map(
getKey(Kind5.existential(input))
).asInstanceOf[G[A0, A1, A2, A3, A4]]

}

service.functorInterpreter(endpointMapping)
}

}
55 changes: 28 additions & 27 deletions modules/core/src/main/scala/playground/NodeEncoderVisitor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import playground.smithyql.NullLiteral
import playground.smithyql.StringLiteral
import playground.smithyql.Struct
import smithy4s.Bijection
import smithy4s.ByteArray
import smithy4s.Document
import smithy4s.Document.DArray
import smithy4s.Document.DBoolean
Expand All @@ -32,12 +31,12 @@ import smithy4s.schema.CollectionTag.IndexedSeqTag
import smithy4s.schema.CollectionTag.ListTag
import smithy4s.schema.CollectionTag.SetTag
import smithy4s.schema.CollectionTag.VectorTag
import smithy4s.schema.EnumTag
import smithy4s.schema.EnumValue
import smithy4s.schema.Field
import smithy4s.schema.Primitive
import smithy4s.schema.Primitive._
import smithy4s.schema.Schema
import smithy4s.schema.SchemaField
import smithy4s.schema.SchemaVisitor

trait NodeEncoder[A] {
Expand Down Expand Up @@ -111,16 +110,24 @@ object NodeEncoderVisitor extends SchemaVisitor[NodeEncoder] { self =>
case PBigInt => bigint
case PBoolean => boolean
case PBigDecimal => bigdecimal
case PBlob => string.contramap((_: ByteArray).toString)
case PBlob => string.contramap(_.toBase64String)
case PDouble => double
case PDocument => document
case PFloat => float
case PUnit => _ => obj(Nil)
case PUUID => string.contramap(_.toString())
case PByte => byte
case PTimestamp => string.contramap(_.toString)
}

def option[A](
schema: Schema[A]
): NodeEncoder[Option[A]] = {
val base = schema.compile(this)
val nullDoc = document.toNode(Document.nullDoc)

_.fold(nullDoc)(base.toNode)
}

def collection[C[_], A](
shapeId: ShapeId,
hints: Hints,
Expand Down Expand Up @@ -162,46 +169,40 @@ object NodeEncoderVisitor extends SchemaVisitor[NodeEncoder] { self =>
def enumeration[E](
shapeId: ShapeId,
hints: Hints,
tag: EnumTag[E],
values: List[EnumValue[E]],
total: E => EnumValue[E],
): NodeEncoder[E] = string.contramap(total(_).name)

def struct[S](
shapeId: ShapeId,
hints: Hints,
fieldsRaw: Vector[SchemaField[S, _]],
fieldsRaw: Vector[Field[S, _]],
make: IndexedSeq[Any] => S,
): NodeEncoder[S] = {
val fields = fieldsRaw.map(_.mapK(this))

def go[A](
f: Field[NodeEncoder, S, A],
s: S,
): Option[Binding[Id]] = f.fold(
new Field.Folder[NodeEncoder, S, Option[Binding[Id]]] {
def onRequired[F](
label: String,
instance: NodeEncoder[F],
get: S => F,
): Option[Binding[Id]] = Binding[Id](Identifier(label), instance.toNode(get(s))).some

def onOptional[F](
label: String,
instance: NodeEncoder[F],
get: S => Option[F],
): Option[Binding[Id]] = get(s).map(f => Binding[Id](Identifier(label), instance.toNode(f)))
}
)
f: Field[S, A]
): S => Option[Binding[Id]] = {
val instance = f.schema.compile(this)

s =>
f.getUnlessDefault(s).map(instance.toNode(_)).map { v =>
Binding[Id](Identifier(f.label), v)
}
}

val fields = fieldsRaw.map(go(_))

s => obj(fields.flatMap(go(_, s)).toList)
s => obj(fields.mapFilter(_.apply(s)).toList)
}

def union[U](
shapeId: ShapeId,
hints: Hints,
alternatives: Vector[Alt[Schema, U, _]],
dispatcher: Alt.Dispatcher[Schema, U],
): NodeEncoder[U] = dispatcher.compile(new Alt.Precompiler[Schema, NodeEncoder] {
alternatives: Vector[Alt[U, _]],
dispatcher: Alt.Dispatcher[U],
): NodeEncoder[U] = dispatcher.compile(new Alt.Precompiler[NodeEncoder] {

def apply[A](
label: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ object OperationCompiler {

def fromSchemaIndex(
dsi: DynamicSchemaIndex
): OperationCompiler[Eff] = fromServices(dsi.allServices)
): OperationCompiler[Eff] = fromServices(dsi.allServices.toList)

def fromServices(
services: List[DynamicSchemaIndex.ServiceWrapper]
Expand Down Expand Up @@ -154,7 +154,7 @@ private class ServiceCompiler[Alg[_[_, _, _, _, _]]](
): QueryCompiler[CompiledInput] = {
val inputCompiler = e.input.compile(QueryCompilerVisitor.full)
val outputEncoder = NodeEncoder.derive(e.output)
val errorEncoder = e.errorable.map(e => NodeEncoder.derive(e.error))
val errorEncoder = e.error.map(e => NodeEncoder.derive(e.schema))

ast =>
inputCompiler
Expand All @@ -177,6 +177,7 @@ private class ServiceCompiler[Alg[_[_, _, _, _, _]]](
// map of endpoint names to (endpoint, input compiler)
private val endpoints = service
.endpoints
.toList
.groupByNel(_.name)
.map(_.map(_.head).map(e => (e, compileEndpoint(e))))

Expand Down
19 changes: 12 additions & 7 deletions modules/core/src/main/scala/playground/OperationRunner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@ package playground

import aws.protocols.AwsJson1_0
import aws.protocols.AwsJson1_1
import aws.protocols.AwsQuery
import aws.protocols.Ec2Query
import aws.protocols.RestJson1
import aws.protocols.RestXml
import cats.Defer
import cats.Id
import cats.data.IorNel
import cats.data.NonEmptyList
import cats.effect.Concurrent
import cats.effect.Async
import cats.effect.MonadCancelThrow
import cats.effect.Resource
import cats.effect.implicits._
import cats.effect.std
import cats.implicits._
import fs2.compression.Compression
import org.http4s.Uri
import org.http4s.client.Client
import playground._
Expand Down Expand Up @@ -135,22 +140,22 @@ object OperationRunner {
}
}

def forSchemaIndex[F[_]: StdlibRuntime: Concurrent: Defer: std.Console](
def forSchemaIndex[F[_]: StdlibRuntime: Async: Compression: std.Console](
dsi: DynamicSchemaIndex,
client: Client[F],
baseUri: F[Uri],
awsEnv: Resource[F, AwsEnvironment[F]],
plugins: List[PlaygroundPlugin],
): Map[QualifiedIdentifier, Resolver[F]] = forServices(
services = dsi.allServices,
services = dsi.allServices.toList,
getSchema = dsi.getSchema,
client = client,
baseUri = baseUri,
awsEnv = awsEnv,
plugins = plugins,
)

def forServices[F[_]: StdlibRuntime: Concurrent: Defer: std.Console](
def forServices[F[_]: StdlibRuntime: Async: Compression: std.Console](
services: List[DynamicSchemaIndex.ServiceWrapper],
getSchema: ShapeId => Option[Schema[_]],
client: Client[F],
Expand Down Expand Up @@ -194,7 +199,7 @@ object OperationRunner {

}

def forService[Alg[_[_, _, _, _, _]], F[_]: StdlibRuntime: Concurrent: Defer: std.Console](
def forService[Alg[_[_, _, _, _, _]], F[_]: StdlibRuntime: Async: Compression: std.Console](
service: Service[Alg],
client: Client[F],
baseUri: F[Uri],
Expand Down Expand Up @@ -256,14 +261,14 @@ object OperationRunner {
.prepare(service)
.map { builder =>
awsEnv
.map(builder.buildSimple(_))
.map(builder.build(_))
.map(service.toPolyFunction(_))
}
.map(liftFunctorInterpreterResource(_))
.toIor
.leftMap(_ =>
NonEmptyList
.of(AwsJson1_0.id, AwsJson1_1.id)
.of(AwsJson1_0.id, AwsJson1_1.id, RestJson1.id, AwsQuery.id, RestXml.id, Ec2Query.id)
.map(Issue.InvalidProtocol(_, serviceProtocols))
)

Expand Down
Loading

0 comments on commit 469cd86

Please sign in to comment.