Skip to content

Commit

Permalink
Merge branch 'series/0.18' into update-sbt
Browse files Browse the repository at this point in the history
  • Loading branch information
kubukoz committed Oct 25, 2024
2 parents 78f5490 + 12e7164 commit e00b93b
Show file tree
Hide file tree
Showing 24 changed files with 14,458 additions and 1,084 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Thank you!
# 0.18.26

* Optimises the conversion of empty smithy4s.Blob to fs2.Stream, to avoid performance degradation in Ember (see [#1609](https://github.com/disneystreaming/smithy4s/pull/1609))
* Adds utility types for working with endpoint handlers (see [#1612](https://github.com/disneystreaming/smithy4s/pull/1612))
* Add a more informative error message for repeated namespaces (see [#1608](https://github.com/disneystreaming/smithy4s/pull/1608)).
* Adds `com.disneystreaming.smithy4s:smithy4s-protocol` dependency to the generation of `smithy-build.json` in the `smithy4sUpdateLSPConfig` tasks of the codegen plugins (see [#1610](https://github.com/disneystreaming/smithy4s/pull/1610)).

# 0.18.25

Expand Down
4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,9 @@ lazy val codegen = projectMatrix
"smithyOrg" -> Dependencies.Smithy.org,
"smithyVersion" -> Dependencies.Smithy.smithyVersion,
"alloyOrg" -> Dependencies.Alloy.org,
"alloyVersion" -> Dependencies.Alloy.alloyVersion
"alloyVersion" -> Dependencies.Alloy.alloyVersion,
"smithy4sOrg" -> organization.value,
"protocolArtifactName" -> "smithy4s-protocol",
),
buildInfoPackage := "smithy4s.codegen",
libraryDependencies ++= Seq(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace ns

string Hello

string GoodBye
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace ns

// no hello
// string Hello
//
string GoodBye
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
ThisBuild / scalaVersion := "2.13.10"

lazy val a = project
.enablePlugins(Smithy4sCodegenPlugin)
.settings(
libraryDependencies += "com.disneystreaming.smithy4s" %% "smithy4s-core" % smithy4sVersion.value
)
lazy val b = project
.enablePlugins(Smithy4sCodegenPlugin)
.settings(
libraryDependencies += "com.disneystreaming.smithy4s" %% "smithy4s-core" % smithy4sVersion.value
)

lazy val usage = project
.enablePlugins(Smithy4sCodegenPlugin)
.dependsOn(a, b)

val root = project
.in(file("."))
.aggregate(a, b, usage)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version=1.10.2
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
sys.props.get("plugin.version") match {
case Some(x) =>
addSbtPlugin("com.disneystreaming.smithy4s" % "smithy4s-sbt-codegen" % x)
case _ =>
sys.error(
"""|The system property 'plugin.version' is not defined.
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# smithy4s codegen should fail to compile; both a and b define the same namespace
-> compile
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ val root = project
.aggregate(subproj, subproj2)
.settings(
TaskKey[Unit]("checkSmithyBuild") := {
val generated = IO.readLines(file(".") / "smithy-build.json")
val expected = IO.readLines(file(".") / "expected.json")
val generated = IO.readLines(file(".") / "smithy-build.json").mkString("\n")
val expected = IO.readLines(file(".") / "expected.json").mkString("\n").replace("${SMITHY4S_VERSION}", smithy4sVersion.value)
val compare = s"""|generated:
|${generated.mkString("\n")}
|$generated
|===================================
|expected:
|${expected.mkString("\n")}
|$expected
|===================================
|""".stripMargin

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
],
"maven" : {
"dependencies" : [
"com.disneystreaming.alloy:alloy-core:0.3.13"
"com.disneystreaming.alloy:alloy-core:0.3.13",
"com.disneystreaming.smithy4s:smithy4s-protocol:${SMITHY4S_VERSION}"
],
"repositories" : [
{
Expand Down
6 changes: 4 additions & 2 deletions modules/codegen/src/smithy4s/codegen/CodegenRecord.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ package smithy4s.codegen

import software.amazon.smithy.model.Model
import software.amazon.smithy.model.node.Node
import software.amazon.smithy.model.SourceLocation

import scala.jdk.CollectionConverters._
import scala.jdk.OptionConverters._

private[codegen] final case class CodegenRecord(
namespaces: List[String],
validatedNewtypes: Option[Boolean]
validatedNewtypes: Option[Boolean],
source: SourceLocation
)

private[codegen] object CodegenRecord {
Expand All @@ -50,6 +52,6 @@ private[codegen] object CodegenRecord {
.asScala
.map(_.expectStringNode().getValue())
.toList
CodegenRecord(namespaces, validatedNewtypes)
CodegenRecord(namespaces, validatedNewtypes, node.getSourceLocation())
}
}
6 changes: 5 additions & 1 deletion modules/codegen/src/smithy4s/codegen/SmithyBuildJson.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ import smithy4s.codegen.internals.SmithyBuildMavenRepository
import scala.collection.immutable.ListSet

private[codegen] object SmithyBuildJson {

val protocolDependency =
s"${BuildInfo.smithy4sOrg}:${BuildInfo.protocolArtifactName}:${BuildInfo.version}"

def toJson(
sources: ListSet[String],
dependencies: ListSet[String],
Expand All @@ -35,7 +39,7 @@ private[codegen] object SmithyBuildJson {
version = "1.0",
sources,
SmithyBuildMaven(
dependencies,
dependencies + protocolDependency,
repositories.map(SmithyBuildMavenRepository.apply)
)
)
Expand Down
54 changes: 43 additions & 11 deletions modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import software.amazon.smithy.model.node.Node
import software.amazon.smithy.model.shapes.ModelSerializer
import software.amazon.smithy.model.transform.ModelTransformer
import software.amazon.smithy.openapi.OpenApiConfig
import software.amazon.smithy.model.SourceLocation

import scala.jdk.CollectionConverters._

Expand Down Expand Up @@ -148,18 +149,32 @@ private[codegen] object CodegenImpl { self =>
// Retrieving metadata that indicates what has already been generated by Smithy4s
// in upstream jars.
val alreadyGenerated: Set[String] = {
val allGenerated = CodegenRecord
.recordsFromModel(model)
.flatMap { r =>
r.namespaces
}
for (g <- allGenerated) {
if (allGenerated.count(_ == g) > 1)
throw new IllegalStateException(
s"Multiple artifact manifests indicate containing generated code for namespace $g"
)
val records = CodegenRecord.recordsFromModel(model)

val allGenerated: Seq[String] = records.flatMap { r =>
r.namespaces
}
val allGeneratedSet = allGenerated.toSet

// If there are any duplicates then the set will be smaller than the list
if (allGeneratedSet.size != allGenerated.size) {
// There are duplicates. Find the duplicates and their source, then throw an exception
val duplicates: Seq[(String, Seq[SourceLocation])] = records
.flatMap { r =>
r.namespaces.map(ns => ns -> r.source)
}
.groupBy(_._1)
.collect {
case (s, l) if l.size > 1 => (s, l)
} // just the duplicates
.map { sl => sl._1 -> sl._2.map(_._2) }
.toSeq
.sortBy(_._1)

throw RepeatedNamespaceException(duplicates)
}
allGenerated.toSet

allGeneratedSet
}

val excluded = excludedNS.getOrElse(Set.empty)
Expand Down Expand Up @@ -219,5 +234,22 @@ private[codegen] object CodegenImpl { self =>
OpenEnumTransformer.name :+
KeepOnlyMarkedShapes.name :+
ValidatedNewtypesTransformer.name
}

case class RepeatedNamespaceException(
duplicates: Seq[(String, Seq[SourceLocation])]
) extends IllegalStateException(
RepeatedNamespaceException.createMessage(duplicates)
)

object RepeatedNamespaceException {
private def createMessage(
duplicates: Seq[(String, Seq[SourceLocation])]
): String = {
val duplicateMessages = duplicates.map { d =>
s"${d._1} is contained in ${d._2.size} artifacts:\n ${d._2.mkString("\n ")}"
}
s"""Multiple artifact manifests cannot contain generated code for the same namespace:
| ${duplicateMessages.mkString("\n")}""".stripMargin
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2021-2024 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.codegen.internals

import software.amazon.smithy.model.{Model, SourceLocation}
import software.amazon.smithy.model.node.{
Node,
ArrayNode,
ObjectNode,
StringNode
}

import scala.jdk.CollectionConverters._

final class CodegenImplSpec extends munit.FunSuite {

test("namespace clash") {
val aSmithy =
"""namespace ns
|
|string Hello
|
|string GoodBye
|""".stripMargin

val bSmithy =
"""namespace ns
|
|// no hello
|// string Hello
|//
|string GoodBye
|""".stripMargin

val objectNodeMap = Map[StringNode, Node](
new StringNode(
"namespaces",
new SourceLocation("string node in test code")
) -> new ArrayNode(
List[Node](
new StringNode("ns", new SourceLocation("string node in test code"))
).asJava,
new SourceLocation("objectNodeMap in test code")
)
).asJava
val repeatedNamespaceNode = new ObjectNode(
objectNodeMap,
new SourceLocation("repeatedNamespaceNode in test code")
)
val nodeList =
List[Node](repeatedNamespaceNode, repeatedNamespaceNode).asJava
val smithy4sMetadata = new ArrayNode(
nodeList,
new SourceLocation("smithy4sMetadata in test code")
)

val model = Model
.assembler()
.discoverModels()
.addUnparsedModel("a.smithy", aSmithy)
.addUnparsedModel("b.smithy", bSmithy)
.putMetadata("smithy4sGenerated", smithy4sMetadata)
.assemble()
.unwrap()

def generateScalaCode(model: Model): Map[String, String] = {
CodegenImpl
.generate(model, None, None)
.map { case (_, result) =>
s"${result.namespace}.${result.name}" -> result.content
}
.toMap
}

val expectedDuplicates = Seq(
(
"ns",
Seq(
new SourceLocation("repeatedNamespaceNode in test code"),
new SourceLocation("repeatedNamespaceNode in test code")
)
)
)

val caught: RepeatedNamespaceException =
intercept[RepeatedNamespaceException] {
generateScalaCode(model)
}
assertEquals(caught.duplicates, expectedDuplicates)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@

package smithy4s.codegen.internals

import smithy4s.codegen.SmithyBuildJson
import smithy4s.codegen.{SmithyBuildJson, BuildInfo}
import software.amazon.smithy.model.shapes.ShapeId
import software.amazon.smithy.openapi.OpenApiVersion

import scala.collection.immutable.ListSet

final class SmithyBuildSpec extends munit.FunSuite {
test("generate json") {
test("generate json with SmithyBuild.writeJson") {
val actual = SmithyBuild.writeJson(
SmithyBuild.Serializable(
"1.0",
Expand Down Expand Up @@ -201,4 +201,32 @@ final class SmithyBuildSpec extends munit.FunSuite {
assertEquals(Set.empty[os.FilePath], actual.sources.toSet)
assertEquals(Set.empty[SmithyBuildPlugin], actual.plugins.toSet)
}

test("generate json with SmithyBuildJson.toJson") {
val actual = SmithyBuildJson.toJson(
ListSet("src/"),
ListSet("dep"),
ListSet("repo")
)
assertEquals(
actual,
s"""|{
| "version" : "1.0",
| "sources" : [
| "src/"
| ],
| "maven" : {
| "dependencies" : [
| "dep",
| "com.disneystreaming.smithy4s:smithy4s-protocol:${BuildInfo.version}"
| ],
| "repositories" : [
| {
| "url" : "repo"
| }
| ]
| }
|}""".stripMargin
)
}
}
Loading

0 comments on commit e00b93b

Please sign in to comment.