Code Monkey home page Code Monkey logo

sbt-guardrail's Introduction

sbt-guardrail sbt-guardrail | Join the chat at https://gitter.im/guardrail-dev/guardrail

An SBT plugin for adding clients and servers generated by guardrail to your service.

Installation

Check out the docs

project/plugins.sbt

addSbtPlugin("dev.guardrail" % "sbt-guardrail" % "<Please use the latest available release!>")

build.sbt

/* Available arguments:
    specPath: java.io.File
    pkg: String
    dto: String
    framework: String
    modules: List[String]
    tracing: Boolean
    imports: List[String]
    encodeOptionalAs: Option[CodingConfig]
    decodeOptionalAs: Option[CodingConfig]
*/
guardrailTasks in Compile := List(
  ScalaClient(file("petstore.yaml")),
  ScalaClient(file("github.yaml"), pkg="com.example.clients.github"),
  ScalaClient(file("github.yaml"), pkg="com.example.clients.github", 
              encodeOptionalAs = codingOptional,
              decodeOptionalAs = codingRequiredNullable),
  ScalaServer(file("myserver.yaml"), pkg="com.example.server", tracing=true),
  ScalaModels(file("myserver.yaml"), pkg="com.example.models"),
  JavaClient(file("github.yaml"), pkg="com.example.clients.github")
  ...
)

Alternatively use the guardrailDiscoveredOpenApiFiles setting to automatically discover OpenAPI spec files under src/main/openapi or src/test/openapi (see the test for full example):

Compile / guardrailTasks := (Compile / guardrailDiscoveredOpenApiFiles).value.flatMap { openApiFile =>
  List(          
    ScalaClient(openApiFile.file, pkg = openApiFile.pkg, framework = "http4s"),
    ScalaServer(openApiFile.file, pkg = openApiFile.pkg, framework = "http4s")
  )
}

sbt-guardrail's People

Contributors

andreatp avatar blast-hardcheese avatar bmbferreira avatar kelnos avatar lukaszmarchewka avatar melahub avatar nickhudkins avatar regiskuckaertz avatar sbrunk avatar scala-steward avatar sideeffffect avatar sullis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sbt-guardrail's Issues

Error:Unknown HTTP status code: default

While attempting to reproduce the minimal example with petstore.yaml from swagger, the process crashes:

Warning: Tried to extract the base path for relative glob petstore.yaml/**/*. To disable this warning, re-run the program with java option, -Dsbt.io.implicit.relative.glob.conversion=allow
Warning: Using `tags` to define package membership is deprecated in favor of the `x-jvm-package` vendor extension (.paths./pets.operations.GET.tags)
Warning: Using `tags` to define package membership is deprecated in favor of the `x-jvm-package` vendor extension (.paths./pets.operations.POST.tags)
Warning: Using `tags` to define package membership is deprecated in favor of the `x-jvm-package` vendor extension (.paths./pets/{petId}.operations.GET.tags)
Error:Unknown HTTP status code: default
[error] (Compile / guardrail) com.twilio.guardrail.sbt.CodegenFailedException
[error] Total time: 1 s, completed 13 Jul 2021, 10:03:57

My config is:

guardrailTasks in Compile := List(
  ScalaClient(
    specPath = file("petstore.yaml"),
    pkg = "com.argochamber.eventual.api"
  )
)

Latest version fails build on changed files

The last release included a change that was intended to fail the build in case of errors coming from the underlying components.

Unfortunately, this fails the build when The file $path contained different content than was expected. is printed.

It seems as though unsafeWriteTreeLogged doesn't output any failure information, so it should be sufficient to remove the exception and change nothing else.

Write an example for using `ScalaClient.default`

Spun out of #193, it seems as though the expected functionality regarding defaults(...) didn't produce the same result as not using defaults.

If it turns out that defaults doesn't work, this should be fixed, as well as consolidated into something that isn't ScalaClient.defaults(...)/ScalaServer.defaults(...), since that gives the impression that the kind is actually taken into account with defaults (which isn't actually the case).

Publishing for 2.11

Hello guys!
I still have a bunch of projects in scala 2.11 - I see that guardrail itself is crosscompiled but sbt-guardrail is not.

Any change to have it published for 2.11?

Thanks in advance!

Scala 2.13 support

Quickly tried to just update the version to 2.13.6 and compile the project, it didn't work:
image

image
Error:

sbt-guardrail(master)> compile
[warn]
[warn]  Note: Unresolved dependencies path:
[error] stack trace is suppressed; run last core / update for the full output
[error] (core / update) sbt.librarymanagement.ResolveException: Error downloading org.scala-sbt:scripted-sbt_2.13:1.5.5
[error]   Not found
[error]   Not found
[error]   not found: https://repo1.maven.org/maven2/org/scala-sbt/scripted-sbt_2.13/1.5.5/scripted-sbt_2.13-1.5.5.pom
[error]   not found: C:\Users\User\.ivy2\localorg.scala-sbt\scripted-sbt_2.13\1.5.5\ivys\ivy.xml
[error]   not found: https://repo.scala-sbt.org/scalasbt/sbt-plugin-releases/org.scala-sbt/scripted-sbt_2.13/1.5.5/ivys/ivy.xml
[error]   not found: https://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/scripted-sbt_2.13/1.5.5/ivys/ivy.xml
[error] Total time: 7 s, completed Jul 24, 2021 5:28:02 PM

sbt-sonatype race condition between guardrail-maven-plugin

The way sbt-sonatype works currently is publishSigned doesn't maintain which staging repository it just published to.

As a result, if the maven plugin is published at the same time, sbt-guardrail sonatype release fails because sonatypeClose and sonatypeRelease get confused as it doesn't know which one to use.

MatchError

sbt compile throws scala.MatchError for swagger file https://raw.githubusercontent.com/aeternity/aeternity/master/config/swagger.yaml

scala version: 2.12.8
sbt plugin: addSbtPlugin("com.twilio" % "sbt-guardrail" % "0.37.1-M1")
sbt: 1.2.8

Stack trace:

[error] scala.MatchError: io.swagger.models.ComposedModel@a95c8fbf (of class io.swagger.models.ComposedModel)
[error] 	at com.twilio.guardrail.SwaggerUtil$.$anonfun$modelMetaType$1(SwaggerUtil.scala:114)
[error] 	at cats.data.EitherT.$anonfun$flatMap$1(EitherT.scala:87)
[error] 	at cats.data.Kleisli.$anonfun$flatMap$2(Kleisli.scala:31)
[error] 	at cats.data.WriterT.$anonfun$flatMap$1(WriterT.scala:44)
[error] 	at cats.package$$anon$1.flatMap(package.scala:40)
[error] 	at cats.data.WriterT.flatMap(WriterT.scala:43)
[error] 	at cats.data.WriterTFlatMap1.flatMap(WriterT.scala:324)
[error] 	at cats.data.WriterTFlatMap1.flatMap$(WriterT.scala:323)
[error] 	at cats.data.WriterTInstances3$$anon$4.flatMap(WriterT.scala:185)
[error] 	at cats.data.WriterTInstances3$$anon$4.flatMap(WriterT.scala:185)
[error] 	at cats.data.Kleisli.$anonfun$flatMap$1(Kleisli.scala:31)
[error] 	at cats.data.WriterT.$anonfun$flatMap$1(WriterT.scala:44)
[error] 	at cats.package$$anon$1.flatMap(package.scala:40)
[error] 	at cats.data.WriterT.flatMap(WriterT.scala:43)
[error] 	at cats.data.WriterTFlatMap1.flatMap(WriterT.scala:324)
[error] 	at cats.data.WriterTFlatMap1.flatMap$(WriterT.scala:323)
[error] 	at cats.data.WriterTInstances3$$anon$4.flatMap(WriterT.scala:185)
[error] 	at cats.data.WriterTInstances3$$anon$4.flatMap(WriterT.scala:185)
[error] 	at cats.data.Kleisli$.$anonfun$shift$1(Kleisli.scala:106)
[error] 	at cats.data.Kleisli.$anonfun$map$1(Kleisli.scala:19)
[error] 	at cats.data.Kleisli.$anonfun$flatMap$1(Kleisli.scala:31)
[error] 	at cats.data.WriterT.$anonfun$flatMap$1(WriterT.scala:44)
[error] 	at cats.package$$anon$1.flatMap(package.scala:40)
[error] 	at cats.data.WriterT.flatMap(WriterT.scala:43)
[error] 	at cats.data.WriterTFlatMap1.flatMap(WriterT.scala:324)
[error] 	at cats.data.WriterTFlatMap1.flatMap$(WriterT.scala:323)
[error] 	at cats.data.WriterTInstances3$$anon$4.flatMap(WriterT.scala:185)
[error] 	at cats.data.WriterTInstances3$$anon$4.flatMap(WriterT.scala:185)
[error] 	at cats.data.Kleisli$.$anonfun$shift$1(Kleisli.scala:106)
[error] 	at cats.data.Kleisli.$anonfun$map$1(Kleisli.scala:19)
[error] 	at cats.data.Kleisli.$anonfun$map$1(Kleisli.scala:19)
[error] 	at cats.data.Kleisli.$anonfun$flatMap$2(Kleisli.scala:31)
[error] 	at cats.data.WriterT.$anonfun$flatMap$1(WriterT.scala

Pull guardrail version out of git tag

Currently, sbt-git pulls the sbt version from the current git tag. Similarly to guardrail-dev/guardrail-maven-plugin#16, making sbt pull the primary guardrail version out of the ${major}.${minor}.${guardrail-bugfix}.${plugin-bugfix} versioning scheme would enable a true CI experience, removing the need for bumping guardrail commits.

An open question about this strategy is how the build would react to having a single commit be tagged with more than one release tag.

Syncing packages to Maven Central

Hello,
could you please sync your packages from your custom BinTray repository https://bintray.com/twilio/releases to Maven Central https://repo1.maven.org/maven2/?
It will make them accessible to people behind corporate firewalls.

Ambigous path and query parameters

Hey Devon!

I'm back to the crazy plan to generate a client for the Kubernetes API. Thanks again for all the help last Lambdaconf =]

Here is a snippet of the generated client that does not compile:

Core_v1Client.scala

 def connectCoreV1PostNamespacedPodProxyWithPath(traceBuilder: TraceBuilder[F], name: String, namespace: String, path: String, path: Option[String] = None, methodName: String = "connect-core-v1-post-namespaced-pod-proxy-with-path", headers: List[Header] = List.empty): F[ConnectCoreV1PostNamespacedPodProxyWithPathResponse] = {
    val tracingHttpClient = traceBuilder(s"$clientName:$methodName")(httpClient)
    val allHeaders = headers ++ List[Option[Header]]().flatten
    val req = Request[F](method = Method.POST, uri = Uri.unsafeFromString(host + basePath + "/api/v1/namespaces/" + Formatter.addPath(namespace) + "/pods/" + Formatter.addPath(name) + "/proxy/" + Formatter.addPath(path) + "?" + Formatter.addArg("path", path)), headers = Headers(allHeaders))
...

The path parameter appears twice, as both String and Option[String]. It is used both as a path parameter and query parameter.

Below is the corresponding OpenAPI snippet. What happens is that they define a path parameter with the same name as a query parameter:

/api/v1/namespaces/{namespace}/pods/{name}/proxy/{path}
...
  "parameters": [
   ...
   {
          "description": "Path is the URL path to use for the current proxy request to pod.",
          "in": "query",
          "name": "path",
          "type": "string",
          "uniqueItems": true
        }

I'd be happy to help with resolution, given some direction

Cheers,

Eli

"/api/v1/namespaces/{namespace}/pods/{name}/proxy/{path}": {
      "delete": {
        "consumes": [
          "*/*"
        ],
        "description": "connect DELETE requests to proxy of Pod",
        "operationId": "connectCoreV1DeleteNamespacedPodProxyWithPath",
        "produces": [
          "*/*"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "string"
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "schemes": [
          "https"
        ],
        "tags": [
          "core_v1"
        ],
        "x-kubernetes-action": "connect",
        "x-kubernetes-group-version-kind": {
          "group": "",
          "kind": "PodProxyOptions",
          "version": "v1"
        }
      },
      "get": {
        "consumes": [
          "*/*"
        ],
        "description": "connect GET requests to proxy of Pod",
        "operationId": "connectCoreV1GetNamespacedPodProxyWithPath",
        "produces": [
          "*/*"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "string"
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "schemes": [
          "https"
        ],
        "tags": [
          "core_v1"
        ],
        "x-kubernetes-action": "connect",
        "x-kubernetes-group-version-kind": {
          "group": "",
          "kind": "PodProxyOptions",
          "version": "v1"
        }
      },
      "head": {
        "consumes": [
          "*/*"
        ],
        "description": "connect HEAD requests to proxy of Pod",
        "operationId": "connectCoreV1HeadNamespacedPodProxyWithPath",
        "produces": [
          "*/*"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "string"
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "schemes": [
          "https"
        ],
        "tags": [
          "core_v1"
        ],
        "x-kubernetes-action": "connect",
        "x-kubernetes-group-version-kind": {
          "group": "",
          "kind": "PodProxyOptions",
          "version": "v1"
        }
      },
      "options": {
        "consumes": [
          "*/*"
        ],
        "description": "connect OPTIONS requests to proxy of Pod",
        "operationId": "connectCoreV1OptionsNamespacedPodProxyWithPath",
        "produces": [
          "*/*"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "string"
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "schemes": [
          "https"
        ],
        "tags": [
          "core_v1"
        ],
        "x-kubernetes-action": "connect",
        "x-kubernetes-group-version-kind": {
          "group": "",
          "kind": "PodProxyOptions",
          "version": "v1"
        }
      },
      "parameters": [
        {
          "description": "name of the PodProxyOptions",
          "in": "path",
          "name": "name",
          "required": true,
          "type": "string",
          "uniqueItems": true
        },
        {
          "description": "object name and auth scope, such as for teams and projects",
          "in": "path",
          "name": "namespace",
          "required": true,
          "type": "string",
          "uniqueItems": true
        },
        {
          "description": "path to the resource",
          "in": "path",
          "name": "path",
          "required": true,
          "type": "string",
          "uniqueItems": true
        },
        {
          "description": "Path is the URL path to use for the current proxy request to pod.",
          "in": "query",
          "name": "path",
          "type": "string",
          "uniqueItems": true
        }
      ],
      "patch": {
        "consumes": [
          "*/*"
        ],
        "description": "connect PATCH requests to proxy of Pod",
        "operationId": "connectCoreV1PatchNamespacedPodProxyWithPath",
        "produces": [
          "*/*"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "string"
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "schemes": [
          "https"
        ],
        "tags": [
          "core_v1"
        ],
        "x-kubernetes-action": "connect",
        "x-kubernetes-group-version-kind": {
          "group": "",
          "kind": "PodProxyOptions",
          "version": "v1"
        }
      },
      "post": {
        "consumes": [
          "*/*"
        ],
        "description": "connect POST requests to proxy of Pod",
        "operationId": "connectCoreV1PostNamespacedPodProxyWithPath",
        "produces": [
          "*/*"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "string"
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "schemes": [
          "https"
        ],
        "tags": [
          "core_v1"
        ],
        "x-kubernetes-action": "connect",
        "x-kubernetes-group-version-kind": {
          "group": "",
          "kind": "PodProxyOptions",
          "version": "v1"
        }
      },
      "put": {
        "consumes": [
          "*/*"
        ],
        "description": "connect PUT requests to proxy of Pod",
        "operationId": "connectCoreV1PutNamespacedPodProxyWithPath",
        "produces": [
          "*/*"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "string"
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "schemes": [
          "https"
        ],
        "tags": [
          "core_v1"
        ],
        "x-kubernetes-action": "connect",
        "x-kubernetes-group-version-kind": {
          "group": "",
          "kind": "PodProxyOptions",
          "version": "v1"
        }
      }
    }

Class needs to be a trait to be mixed

It seems there is some issue. Not sure if it is a plugin bug or swagger definition problem.

There are class and companion object generated, but no trait. However, a generated code is mixing some trait in, which does not exist.

target/scala-2.12/src_managed/main/swagger/definitions/ChannelCloseMutualTxJSON.scala:5:216: class ChannelCloseMutualTx needs to be a trait to be mixed in
[error] case class ChannelCloseMutualTxJSON(version: Long, channelId: String, fromId: String, initiatorAmountFinal: Long, responderAmountFinal: Long, ttl: Option[Long] = None, fee: Long, nonce: Long) extends GenericTx with ChannelCloseMutualTx
[error]

This swagger has been used to generate and compile client code: https://raw.githubusercontent.com/aeternity/aeternity/master/config/swagger.yaml

Where to put specs yaml file? server.yaml is either incorrectly formatted or missing

I've got this error when trying to compile project with enabled sbt plugin:

server.yaml is either incorrectly formatted or missing

I have server.yaml in my resources folder. And I have this code:

guardrailTasks in Compile := List(
  Server(file("server.yaml"), pkg="com.nix.weg.server", tracing=true)
)

in build.sbt
Where should I put that server.yaml file?

Unknown method: HEAD

I get the error "Unknown method: HEAD" when the OpenApi file (YAML or JSON) contains a "head" item.
I am attempting to generate an Http4s server with the plugin:

addSbtPlugin("dev.guardrail" % "sbt-guardrail" % "0.75.1")

If I comment out the "head" item, it works.

Project hangs at `sbt`

When I add the dep addSbtPlugin("com.twilio" % "sbt-guardrail" % "0.52.1")

My sbt build hangs at

Updating ProjectRef(uri

Has anyone seen this before?

Only generate and recompile if swagger.yaml changes

I'm using guardrail with play dev mode, and regardless of changes to my implementations or swagger.yaml, the guardrail packages are getting recompiled, causing a full play server restart. I'm thinking it should be possible to avoid touching generated sources if swagger.yaml hasn't changed.

Alternatively, what may be happening is that the sources are being re-generated but not completely identical... of 20 some generated files I'm only recompiling 4 files regularly.

NoSuchMethodError: sbt.package$.singleFileJsonFormatter()Lsjsonnew/JsonFormat

sbt:guardrail-sample-akka-http> compile
[info] Updating {file:/Users/dstewart/Documents/guardrail-sample-akka-http/}guardrail-sample-akka-http...
[info] Done updating.
[error] java.lang.NoSuchMethodError: sbt.package$.singleFileJsonFormatter()Lsjsonnew/JsonFormat;
[error]         at com.twilio.guardrail.sbt.GuardrailAnalysis$.<init>(GuardrailAnalysis.scala:18)
[error]         at com.twilio.guardrail.sbt.GuardrailAnalysis$.<clinit>(GuardrailAnalysis.scala)
[error]         at com.twilio.guardrail.sbt.AbstractGuardrailPlugin.cachedGuardrailTask(AbstractCodegenPlugin.scala:151)
[error]         at com.twilio.guardrail.sbt.AbstractGuardrailPlugin.$anonfun$projectSettings$3(AbstractCodegenPlugin.scala:175)
[error]         at scala.Function1.$anonfun$compose$1(Function1.scala:44)
[error]         at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:42)
[error]         at sbt.std.Transform$$anon$4.work(System.scala:64)
[error]         at sbt.Execute.$anonfun$submit$2(Execute.scala:257)
[error]         at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:16)
[error]         at sbt.Execute.work(Execute.scala:266)
[error]         at sbt.Execute.$anonfun$submit$1(Execute.scala:257)
[error]         at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:167)
[error]         at sbt.CompletionService$$anon$2.call(CompletionService.scala:32)
[error]         at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[error]         at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
[error]         at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[error]         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
[error]         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
[error]         at java.lang.Thread.run(Thread.java:748)
[error] (compile:guardrail) java.lang.NoSuchMethodError: sbt.package$.singleFileJsonFormatter()Lsjsonnew/JsonFormat;
[error] Total time: 1 s, completed May 16, 2020 12:00:24 PM
sbt:guardrail-sample-akka-http>

Investigating a workaround, but this is with sbt 1.0.4 and sbt-guardrail 0.57.2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.