Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into kaizen/harden-suite-for-optional-param
- Loading branch information
Showing
9 changed files
with
315 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,4 +7,6 @@ out | |
mill-bsp.json | ||
|
||
project | ||
target | ||
target | ||
|
||
.bloop |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
0.10.5 | ||
0.10.10 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
97 changes: 97 additions & 0 deletions
97
example/circeOpenRpc/src/io/iohk/armadillo/example/ExampleCirce.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package io.iohk.armadillo.example | ||
|
||
import cats.effect.{ExitCode, IO, IOApp} | ||
import io.circe.generic.semiauto._ | ||
import io.circe.literal._ | ||
import io.circe.syntax.EncoderOps | ||
import io.circe.{Decoder, Encoder} | ||
import io.iohk.armadillo._ | ||
import io.iohk.armadillo.json.circe._ | ||
import io.iohk.armadillo.openrpc.OpenRpcDocsInterpreter | ||
import io.iohk.armadillo.openrpc.circe.ArmadilloOpenRpcCirce | ||
import io.iohk.armadillo.openrpc.circe.yaml.RichOpenRpcDocument | ||
import io.iohk.armadillo.openrpc.model.OpenRpcInfo | ||
import sttp.apispec.AnySchema | ||
import sttp.tapir.{Schema, Validator} | ||
|
||
object ExampleCirce extends IOApp { | ||
|
||
case class DescriptionResponse(msg: String) | ||
|
||
implicit val rpcBlockResponseEncoder: Encoder[DescriptionResponse] = deriveEncoder | ||
implicit val rpcBlockResponseDecoder: Decoder[DescriptionResponse] = deriveDecoder | ||
implicit val rpcBlockResponseSchema: Schema[DescriptionResponse] = Schema.derived | ||
|
||
// Note that only the name is required. Validations, description and example(s) are optionals. | ||
private val nameParam = param[String]("name") | ||
.description("Your name.") | ||
.examples(Set("John Doe", "Jane Doe")) // Multiple examples can be provided | ||
.validate(Validator.minLength(1).and(Validator.maxLength(100))) | ||
|
||
private val ageParam = param[Int]("age") | ||
.example(42) | ||
.description("Your age.") | ||
.validate(Validator.positive) | ||
|
||
private val descriptionResult = result[DescriptionResponse]("description") | ||
.description("A personalized greeting message.") | ||
.examples( | ||
Set( | ||
DescriptionResponse("Hello John Doe, you are 42 years old"), | ||
DescriptionResponse("Hello Jane Doe, you are 42 years old") | ||
) | ||
) | ||
|
||
val describeMeEndpoint: JsonRpcServerEndpoint[IO] = jsonRpcEndpoint(m"describe_me") | ||
.description("Returns a description of a person based on a name and an age.") // An endpoint can also have a description | ||
.summary("A short summary.") | ||
.in(nameParam.and(ageParam)) | ||
.out(descriptionResult) | ||
.serverLogic[IO] { case (name, age) => | ||
IO(Right(DescriptionResponse(s"Hello $name, you are $age years old"))) | ||
} | ||
|
||
case object CustomArmadilloOpenRpcCirce extends ArmadilloOpenRpcCirce { | ||
override val anyObjectEncoding: AnySchema.Encoding = AnySchema.Encoding.Boolean | ||
|
||
override def openApi30: Boolean = true | ||
} | ||
|
||
override def run(args: List[String]): IO[ExitCode] = { | ||
val info: OpenRpcInfo = OpenRpcInfo("1.0.0", "Describe me!") | ||
val document = OpenRpcDocsInterpreter().toOpenRpc(info, List(describeMeEndpoint.endpoint)) | ||
|
||
// Render the OpenRpcDocument in yaml and json. | ||
// Please note the import used, that will allow to render the specification in different version of OpenAPI | ||
for { | ||
_ <- displayStepMessage("Json - Recent version of OpenAPI") | ||
_ <- { | ||
import io.iohk.armadillo.openrpc.circe._ | ||
IO.println(document.asJson) | ||
} | ||
|
||
_ <- displayStepMessage("Json - Version 3.0 of OpenAPI") | ||
_ <- { | ||
import CustomArmadilloOpenRpcCirce._ | ||
IO.println(document.asJson) | ||
} | ||
|
||
_ <- displayStepMessage("Yaml - Recent version of OpenAPI") | ||
_ <- { | ||
import io.iohk.armadillo.openrpc.circe._ | ||
IO.println(document.toYaml) | ||
} | ||
|
||
_ <- displayStepMessage("Yaml - Version 3.0 of OpenAPI") | ||
_ <- { | ||
import CustomArmadilloOpenRpcCirce._ | ||
IO.println(document.toYaml) | ||
} | ||
} yield ExitCode.Success | ||
} | ||
|
||
private def displayStepMessage(step: String): IO[Unit] = { | ||
val separator = IO.println("_" * 50) | ||
separator >> IO.println(step) >> separator | ||
} | ||
} |
96 changes: 96 additions & 0 deletions
96
example/json4sOpenRpc/src/io/iohk/armadillo/example/ExampleJson4s.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package io.iohk.armadillo.example | ||
|
||
import cats.effect.{ExitCode, IO, IOApp} | ||
import io.circe.syntax.EncoderOps | ||
import io.iohk.armadillo._ | ||
import io.iohk.armadillo.json.json4s.{Json4sSupport, jsonRpcCodec} | ||
import io.iohk.armadillo.openrpc.OpenRpcDocsInterpreter | ||
import io.iohk.armadillo.openrpc.circe.ArmadilloOpenRpcCirce | ||
import io.iohk.armadillo.openrpc.circe.yaml.RichOpenRpcDocument | ||
import io.iohk.armadillo.openrpc.model.OpenRpcInfo | ||
import org.json4s.{Formats, NoTypeHints, Serialization} | ||
import sttp.apispec.AnySchema | ||
import sttp.tapir.{Schema, Validator} | ||
|
||
object ExampleJson4s extends IOApp { | ||
|
||
case class DescriptionResponse(msg: String) | ||
|
||
implicit val rpcBlockResponseSchema: Schema[DescriptionResponse] = Schema.derived | ||
implicit val serialization: Serialization = org.json4s.jackson.Serialization | ||
implicit val formats: Formats = org.json4s.jackson.Serialization.formats(NoTypeHints) | ||
implicit val json4sSupport: Json4sSupport = Json4sSupport(org.json4s.jackson.parseJson(_), org.json4s.jackson.compactJson) | ||
|
||
// Note that only the name is required. Validations, description and example(s) are optionals. | ||
private val nameParam = param[String]("name") | ||
.description("Your name.") | ||
.examples(Set("John Doe", "Jane Doe")) // Multiple examples can be provided | ||
.validate(Validator.minLength(1).and(Validator.maxLength(100))) | ||
|
||
private val ageParam = param[Int]("age") | ||
.example(42) | ||
.description("Your age.") | ||
.validate(Validator.positive) | ||
|
||
private val descriptionResult = result[DescriptionResponse]("description") | ||
.description("A personalized greeting message.") | ||
.examples( | ||
Set( | ||
DescriptionResponse("Hello John Doe, you are 42 years old"), | ||
DescriptionResponse("Hello Jane Doe, you are 42 years old") | ||
) | ||
) | ||
|
||
val describeMeEndpoint: JsonRpcServerEndpoint[IO] = jsonRpcEndpoint(m"describe_me") | ||
.description("Returns a description of a person based on a name and an age.") // An endpoint can also have a description | ||
.summary("A short summary.") | ||
.in(nameParam.and(ageParam)) | ||
.out(descriptionResult) | ||
.serverLogic[IO] { case (name, age) => | ||
IO(Right(DescriptionResponse(s"Hello $name, you are $age years old"))) | ||
} | ||
|
||
case object CustomArmadilloOpenRpcCirce extends ArmadilloOpenRpcCirce { | ||
override val anyObjectEncoding: AnySchema.Encoding = AnySchema.Encoding.Boolean | ||
|
||
override def openApi30: Boolean = true | ||
} | ||
|
||
override def run(args: List[String]): IO[ExitCode] = { | ||
val info: OpenRpcInfo = OpenRpcInfo("1.0.0", "Describe me!") | ||
val document = OpenRpcDocsInterpreter().toOpenRpc(info, List(describeMeEndpoint.endpoint)) | ||
|
||
// Render the OpenRpcDocument in yaml and json. | ||
// Please note the import used, that will allow to render the specification in different version of OpenAPI | ||
for { | ||
_ <- displayStepMessage("Json - Recent version of OpenAPI") | ||
_ <- { | ||
import io.iohk.armadillo.openrpc.circe._ | ||
IO.println(document.asJson) | ||
} | ||
|
||
_ <- displayStepMessage("Json - Version 3.0 of OpenAPI") | ||
_ <- { | ||
import CustomArmadilloOpenRpcCirce._ | ||
IO.println(document.asJson) | ||
} | ||
|
||
_ <- displayStepMessage("Yaml - Recent version of OpenAPI") | ||
_ <- { | ||
import io.iohk.armadillo.openrpc.circe._ | ||
IO.println(document.toYaml) | ||
} | ||
|
||
_ <- displayStepMessage("Yaml - Version 3.0 of OpenAPI") | ||
_ <- { | ||
import CustomArmadilloOpenRpcCirce._ | ||
IO.println(document.toYaml) | ||
} | ||
} yield ExitCode.Success | ||
} | ||
|
||
private def displayStepMessage(step: String): IO[Unit] = { | ||
val separator = IO.println("_" * 50) | ||
separator >> IO.println(step) >> separator | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
example/testing/test/src/io/iohk/armadillo/example/ArmadilloStubInterpreterExample.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package io.iohk.armadillo.example | ||
|
||
import cats.effect.IO | ||
import io.circe.Json | ||
import io.circe.generic.auto._ | ||
import io.circe.literal._ | ||
import io.iohk.armadillo._ | ||
import io.iohk.armadillo.json.circe._ | ||
import io.iohk.armadillo.server.ServerInterpreter | ||
import io.iohk.armadillo.server.stub.ArmadilloStubInterpreter | ||
import sttp.client3.HttpError | ||
import sttp.client3.circe._ | ||
import sttp.client3.impl.cats.CatsMonadError | ||
import sttp.client3.testing.SttpBackendStub | ||
import sttp.model.StatusCode | ||
import sttp.model.Uri._ | ||
import weaver.SimpleIOSuite | ||
|
||
object ArmadilloStubInterpreterExample extends SimpleIOSuite { | ||
|
||
private val stubInterpreter: ArmadilloStubInterpreter[IO, Json, Nothing] = | ||
ArmadilloStubInterpreter(SttpBackendStub(new CatsMonadError()), new CirceJsonSupport) | ||
|
||
private val describeMeEndpoint = jsonRpcEndpoint(m"describe_me") | ||
.in(param[String]("name").and(param[Int]("age"))) | ||
.out[String]("description") | ||
.errorOut(errorNoData) | ||
|
||
private val stubbedResponse = "Hello John Doe, you are 42 years old" | ||
|
||
private val InvalidParamsError: JsonRpcError[Unit] = | ||
JsonRpcError(ServerInterpreter.InvalidParams.code, ServerInterpreter.InvalidParams.message, None) | ||
|
||
test("should return a value") { | ||
val backendStub = stubInterpreter | ||
.whenEndpoint(describeMeEndpoint) | ||
.assertInputs(("John Doe", 42)) | ||
.thenRespond(stubbedResponse) | ||
.backend() | ||
|
||
val expected = Right(JsonRpcResponse.v2(stubbedResponse, 1)) | ||
|
||
backendStub | ||
.send( | ||
sttp.client3.basicRequest | ||
.post(uri"http://localhost:1234") | ||
.body(JsonRpcRequest.v2("describe_me", json"""[ "John Doe", 42 ]""", 1)) | ||
.response(asJson[JsonRpcSuccessResponse[String]]) | ||
) | ||
.map(r => expect.same(expected, r.body)) | ||
} | ||
|
||
test("should fail because age is not provided") { | ||
val backendStub = stubInterpreter | ||
.whenEndpoint(describeMeEndpoint) | ||
.thenRespondError(InvalidParamsError) | ||
.backend() | ||
|
||
val expected = Left( | ||
HttpError( | ||
JsonRpcErrorResponse("2.0", json"""{"code": -32602, "message": "Invalid params"}""", Some(1)), | ||
StatusCode(400) | ||
) | ||
) | ||
|
||
backendStub | ||
.send( | ||
sttp.client3.basicRequest | ||
.post(uri"http://localhost:1234") | ||
.body(JsonRpcRequest.v2("describe_me", json"""[ "John Doe" ]""", 1)) | ||
.response(asJsonEither[JsonRpcErrorResponse[Json], JsonRpcSuccessResponse[Json]]) | ||
) | ||
.map(r => expect.same(expected, r.body)) | ||
} | ||
} |