aishfenton / argus Goto Github PK
View Code? Open in Web Editor NEWBuilds models from JSON Schemas
License: MIT License
Builds models from JSON Schemas
License: MIT License
The following schema:
{
"$ref": "#/definitions/FooBar",
"definitions": {
"FooBar": {
"oneOf": [
{ "$ref": "#/definitions/Foo" },
{ "$ref": "#/definitions/Bar" }
]
},
"Foo": {
"type": "object",
"properties": {
"foo": {
"type": "string"
}
}
},
"Bar": {
"type": "object",
"properties": {
"bar": {
"type": "integer"
}
}
}
}
}
will yield this:
sealed trait FooBarUnion
case class FooBarFoo(x: Foo) extends FooBarUnion
case class FooBarBar(x: Bar) extends FooBarUnion
case class Foo(foo: Option[String] = None)
case class Bar(bar: Option[Int] = None)
It would be nice if the intermediary wrappers could be eliminated and this could be simplified to:
sealed trait FooBarUnion
case class Foo(foo: Option[String] = None) extends FooBarUnion
case class Bar(bar: Option[Int] = None) extends FooBarUnion
The Vegas library can't be updated to Circe 0.9 until this library is, the current master branch already has Circe 0.9 and clearly there is no development in progress. @aishfenton any chance you would be willing to cut a release for this project in it's current state?
IntelliJ recently added support for scala macros which would eliminate the syntax errors reported by using macro-generated code.
Is there plans to add this feature to Argus?
I'm interested in working with the implementation of this, since I would like to use it with Vegas to support Vega (and newer versions of Vega-Lite) to be able to use some of the more advanced visualizations that Vega-Lite lacks.
Is there anyone actively working on this? Or would it be OK if I work on it?
I would be using the proposal from https://github.com/joelittlejohn/jsonschema2pojo/wiki/Proposal-for-allOf,-anyOf-and-oneOf as a guide to implement this on Scala, unless there's an objection to it.
We would like to pattern match against multiple Argus-generated case classes which all extend from a common sealed trait. Is there a way to make the generated code extend a predefined trait?
I'd like to parse a file, convert it to a scala AST with case classes, and then output the resulting code as a file. I'd like to do this without macros, if possible.
Tests are not executable from IntelliJ.
Add MIT license to project.
When trying to use the @fromSchemaResource
macro, I got an Input stream is null
exception even though the schema was in the resources directory and the path was correct. I found that this was due to the resources not being available at compile time when the macro runs.
Adding the following to my build.sbt solved the issue:
unmanagedClasspath in Compile ++= (unmanagedResources in Compile).value
Perhaps it would be good to mention this in the documentation.
I think a very good test-case is the FHIR schema, for example, this version:
http://hl7.org/fhir/STU3/fhir.schema.json.zip
There I use the file fhir.schema.json
as stated in the doc:
@fromSchemaResource("/fhir/fhir.schema.json")
object SchemaDef
and results in the compiler error:
SchemaDef.scala:5:2: ResourceListParameters is already defined as case class ResourceListParameters
I have to manually remove the resourceList
at the bottom of the file and then compiles.
I noticed that definitions in the JSON schema must be capitalized for Argus to work. For this example adapted from the Argus readme:
package test
import argus.macros._
import io.circe._
import io.circe.syntax._
@fromSchemaJson("""
{
"title": "Example Schema",
"type": "object",
"definitions" : {
"address": {
"type": "object",
"properties": {
"number" : { "type": "integer" },
"street" : { "type": "string" }
}
},
"ErdosNumber": {
"type": "integer"
}
},
"properties": {
"name": {
"type": "array",
"items": { "type": "string" }
},
"age": {
"description": "Age in years",
"type": "integer"
},
"address" : { "$ref" : "#/definitions/address" },
"erdosNumber" : { "$ref" : "#/definitions/ErdosNumber" }
}
}
""", name = "Person")
object Schema
object TestArgus {
import Schema._
import Schema.Implicits._
val json = """
|{
| "name" : ["Bob", "Smith"],
| "age" : 26,
| "address" : {
| "number" : 31,
| "street" : "Main St"
| },
| "erdosNumber": 123
|}
""".stripMargin
// Decode into generated case class
val person = parser.decode[Person](json).right.toOption.get
// Update address
val personAddress = address(number = Some(42), street = Some("Alt St"))
val newPerson = person.copy(address = Some(personAddress))
// Encode base to json
newPerson.asJson
}
I get a compile error:
[error] .../src/main/scala/test/TestArgus.scala:7: not found: type address
[error] @fromSchemaJson("""
[error] ^
If I change the schema to use "Address" (and also the case class name to match), everything works. Is this intended?
any plans on supporting scala 2.13?
A fresh clone and running sbt
produces a bunch of errors:
[ERROR] Failed to construct terminal; falling back to unsupported
java.lang.NumberFormatException: For input string: "0x100"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.valueOf(Integer.java:766)
at jline.internal.InfoCmp.parseInfoCmp(InfoCmp.java:59)
at jline.UnixTerminal.parseInfoCmp(UnixTerminal.java:233)
at jline.UnixTerminal.<init>(UnixTerminal.java:64)
at jline.UnixTerminal.<init>(UnixTerminal.java:49)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at jline.TerminalFactory.getFlavor(TerminalFactory.java:209)
at jline.TerminalFactory.create(TerminalFactory.java:100)
at jline.TerminalFactory.get(TerminalFactory.java:184)
at jline.TerminalFactory.get(TerminalFactory.java:190)
at sbt.ConsoleLogger$.ansiSupported(ConsoleLogger.scala:123)
at sbt.ConsoleLogger$.<init>(ConsoleLogger.scala:117)
at sbt.ConsoleLogger$.<clinit>(ConsoleLogger.scala)
at sbt.GlobalLogging$.initial(GlobalLogging.scala:43)
at sbt.StandardMain$.initialGlobalLogging(Main.scala:64)
at sbt.StandardMain$.initialState(Main.scala:73)
at sbt.xMain.run(Main.scala:29)
at xsbt.boot.Launch$$anonfun$run$1.apply(Launch.scala:109)
at xsbt.boot.Launch$.withContextLoader(Launch.scala:128)
at xsbt.boot.Launch$.run(Launch.scala:109)
at xsbt.boot.Launch$$anonfun$apply$1.apply(Launch.scala:35)
at xsbt.boot.Launch$.launch(Launch.scala:117)
at xsbt.boot.Launch$.apply(Launch.scala:18)
at xsbt.boot.Boot$.runImpl(Boot.scala:56)
at xsbt.boot.Boot$.main(Boot.scala:18)
at xsbt.boot.Boot.main(Boot.scala)
[info] Loading project definition from /home/bravegag/code/Argus/project
[info] Set current project to Argus (in build file:/home/bravegag/code/Argus/)
[ERROR] Failed to construct terminal; falling back to unsupported
java.lang.NumberFormatException: For input string: "0x100"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.valueOf(Integer.java:766)
at jline.internal.InfoCmp.parseInfoCmp(InfoCmp.java:59)
at jline.UnixTerminal.parseInfoCmp(UnixTerminal.java:233)
at jline.UnixTerminal.<init>(UnixTerminal.java:64)
at jline.UnixTerminal.<init>(UnixTerminal.java:49)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at jline.TerminalFactory.getFlavor(TerminalFactory.java:209)
at jline.TerminalFactory.create(TerminalFactory.java:100)
at jline.TerminalFactory.get(TerminalFactory.java:184)
at jline.TerminalFactory.get(TerminalFactory.java:190)
at sbt.JLine$.sbt$JLine$$terminal(LineReader.scala:85)
at sbt.JLine$.withTerminal(LineReader.scala:88)
at sbt.JLine$.usingTerminal(LineReader.scala:96)
at sbt.JLine$.createReader(LineReader.scala:102)
at sbt.FullReader.<init>(LineReader.scala:132)
at sbt.BasicCommands$$anonfun$shell$1.apply(BasicCommands.scala:184)
at sbt.BasicCommands$$anonfun$shell$1.apply(BasicCommands.scala:181)
at sbt.Command$$anonfun$command$1$$anonfun$apply$1.apply(Command.scala:30)
at sbt.Command$$anonfun$command$1$$anonfun$apply$1.apply(Command.scala:30)
at sbt.Command$.process(Command.scala:93)
at sbt.MainLoop$$anonfun$1$$anonfun$apply$1.apply(MainLoop.scala:96)
at sbt.MainLoop$$anonfun$1$$anonfun$apply$1.apply(MainLoop.scala:96)
at sbt.State$$anon$1.process(State.scala:184)
at sbt.MainLoop$$anonfun$1.apply(MainLoop.scala:96)
at sbt.MainLoop$$anonfun$1.apply(MainLoop.scala:96)
at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)
at sbt.MainLoop$.next(MainLoop.scala:96)
at sbt.MainLoop$.run(MainLoop.scala:89)
at sbt.MainLoop$$anonfun$runWithNewLog$1.apply(MainLoop.scala:68)
at sbt.MainLoop$$anonfun$runWithNewLog$1.apply(MainLoop.scala:63)
at sbt.Using.apply(Using.scala:24)
at sbt.MainLoop$.runWithNewLog(MainLoop.scala:63)
at sbt.MainLoop$.runAndClearLast(MainLoop.scala:46)
at sbt.MainLoop$.runLoggedLoop(MainLoop.scala:30)
at sbt.MainLoop$.runLogged(MainLoop.scala:22)
at sbt.StandardMain$.runManaged(Main.scala:57)
at sbt.xMain.run(Main.scala:29)
at xsbt.boot.Launch$$anonfun$run$1.apply(Launch.scala:109)
at xsbt.boot.Launch$.withContextLoader(Launch.scala:128)
at xsbt.boot.Launch$.run(Launch.scala:109)
at xsbt.boot.Launch$$anonfun$apply$1.apply(Launch.scala:35)
at xsbt.boot.Launch$.launch(Launch.scala:117)
at xsbt.boot.Launch$.apply(Launch.scala:18)
at xsbt.boot.Boot$.runImpl(Boot.scala:56)
at xsbt.boot.Boot$.main(Boot.scala:18)
at xsbt.boot.Boot.main(Boot.scala)
[ERROR] Failed to construct terminal; falling back to unsupported
java.lang.NumberFormatException: For input string: "0x100"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.valueOf(Integer.java:766)
at jline.internal.InfoCmp.parseInfoCmp(InfoCmp.java:59)
at jline.UnixTerminal.parseInfoCmp(UnixTerminal.java:233)
at jline.UnixTerminal.<init>(UnixTerminal.java:64)
at jline.UnixTerminal.<init>(UnixTerminal.java:49)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at jline.TerminalFactory.getFlavor(TerminalFactory.java:209)
at jline.TerminalFactory.create(TerminalFactory.java:100)
at jline.TerminalFactory.get(TerminalFactory.java:184)
at jline.TerminalFactory.get(TerminalFactory.java:190)
at jline.console.ConsoleReader.<init>(ConsoleReader.java:240)
at jline.console.ConsoleReader.<init>(ConsoleReader.java:232)
at jline.console.ConsoleReader.<init>(ConsoleReader.java:220)
at sbt.JLine$$anonfun$createReader$1.apply(LineReader.scala:103)
at sbt.JLine$$anonfun$createReader$1.apply(LineReader.scala:102)
at sbt.JLine$$anonfun$usingTerminal$1.apply(LineReader.scala:98)
at sbt.JLine$$anonfun$usingTerminal$1.apply(LineReader.scala:96)
at sbt.JLine$.withTerminal(LineReader.scala:89)
at sbt.JLine$.usingTerminal(LineReader.scala:96)
at sbt.JLine$.createReader(LineReader.scala:102)
at sbt.FullReader.<init>(LineReader.scala:132)
at sbt.BasicCommands$$anonfun$shell$1.apply(BasicCommands.scala:184)
at sbt.BasicCommands$$anonfun$shell$1.apply(BasicCommands.scala:181)
at sbt.Command$$anonfun$command$1$$anonfun$apply$1.apply(Command.scala:30)
at sbt.Command$$anonfun$command$1$$anonfun$apply$1.apply(Command.scala:30)
at sbt.Command$.process(Command.scala:93)
at sbt.MainLoop$$anonfun$1$$anonfun$apply$1.apply(MainLoop.scala:96)
at sbt.MainLoop$$anonfun$1$$anonfun$apply$1.apply(MainLoop.scala:96)
at sbt.State$$anon$1.process(State.scala:184)
at sbt.MainLoop$$anonfun$1.apply(MainLoop.scala:96)
at sbt.MainLoop$$anonfun$1.apply(MainLoop.scala:96)
at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)
at sbt.MainLoop$.next(MainLoop.scala:96)
at sbt.MainLoop$.run(MainLoop.scala:89)
at sbt.MainLoop$$anonfun$runWithNewLog$1.apply(MainLoop.scala:68)
at sbt.MainLoop$$anonfun$runWithNewLog$1.apply(MainLoop.scala:63)
at sbt.Using.apply(Using.scala:24)
at sbt.MainLoop$.runWithNewLog(MainLoop.scala:63)
at sbt.MainLoop$.runAndClearLast(MainLoop.scala:46)
at sbt.MainLoop$.runLoggedLoop(MainLoop.scala:30)
at sbt.MainLoop$.runLogged(MainLoop.scala:22)
at sbt.StandardMain$.runManaged(Main.scala:57)
at sbt.xMain.run(Main.scala:29)
at xsbt.boot.Launch$$anonfun$run$1.apply(Launch.scala:109)
at xsbt.boot.Launch$.withContextLoader(Launch.scala:128)
at xsbt.boot.Launch$.run(Launch.scala:109)
at xsbt.boot.Launch$$anonfun$apply$1.apply(Launch.scala:35)
at xsbt.boot.Launch$.launch(Launch.scala:117)
at xsbt.boot.Launch$.apply(Launch.scala:18)
at xsbt.boot.Boot$.runImpl(Boot.scala:56)
at xsbt.boot.Boot$.main(Boot.scala:18)
at xsbt.boot.Boot.main(Boot.scala)
I'm interested in writing a Scala competitor to swagger-codegen using Argus and scala.meta.
Currently unsupported:
anyOf
/ allOf
"Should be simple to add, just haven't done it yet."allOf
is all that's needed, but anyOf
should be implemented for completeness
default
"Schemas can specify the default value to use for a field. Currently we just ignore these."Needed, but would prefer to implement default values in a way that doesn't leverage case class default parameters. Open to discussion, but it violates a wartremover wart (and a convincing argument against default properties).
not
"Not sure how you could specify this in a type language. Needs more thoughts"As with default
values, validation could be generated as a separate structure that could be optionally applied by consumers of a generated argus case class schema.
additionalProperties
/ additionalItems
"Json schema lets you specify free-form properties too. These are unsupported for now (although I think this should be easy now there's supprot(sic) for Any types)"Only additionalProperties
are required, but additionalItems
should be supported as well.
patternProperties
"What should be the objects fields in this case? Maybe we just make it a Map?"Unnecessary
dependencies
. "Dependencies can also extend the schema... sigh."Doesn't seem necessary
Could be combined with the validation structure generated by not
I'd be interested in feedback for some of these ideas before I start trying to implement some of them.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.