Comments (10)
For completeness, Here is the original message, prior to the message above:
I have generated a JSON Schema from the HelloWorld template....
...then used this to generate the Rust library. (Note: I used an online tool to create this rust lib https://app.quicktype.io/. In the fullness of time, I'll incorporate a Rust crate to do this.)
I then wrote a simple Rust app to parse a Request response using the Rust Lib.
The challenge is that, according to the Schema.json, the valid HelloWorld MyRequest object is:
{
"$class": {
"type": "string",
"default": "org.accordproject.helloworld.MyRequest",
"pattern": "^org\\.accordproject\\.helloworld\\.MyRequest$",
"description": "The class identifier for org.accordproject.helloworld.MyRequest"
},
"input": {
"type": "Accord Project"
}
}
whereas the one we use in TemplateStudio is:
{
"$class": "org.accordproject.helloworld.MyRequest",
"input": "Accord Project"
}
Any ideas why the Schema.json specifies a different MyRequest payload to the one we use with current Ergo?
(FYI... Full repo of this playpen app at: source code)
Am I generating the code from Schema.json incorrectly?
from apap.
I think that I see the problem.
The JSON Schema file that Concerto generates contains definitions but is not a helpful schema for validating on its own. To use the definitions, you need to include a $ref
to the definition that you want to use.
For example, here's an abbreviated JSON Schema definition that matches MyRequest
type.
{
"definitions": {
"org.accordproject.helloworld.MyRequest": {
"title": "MyRequest",
"description": "An instance of org.accordproject.helloworld.MyRequest",
"type": "object",
"properties": {
"$class": {
"type": "string",
"default": "org.accordproject.helloworld.MyRequest",
"pattern": "^org\\.accordproject\\.helloworld\\.MyRequest$",
"description": "The class identifier for org.accordproject.helloworld.MyRequest"
},
"input": {
"type": "string"
}
},
"required": [
"$class",
"input"
]
}
},
"$ref": "#/definitions/org.accordproject.helloworld.MyRequest"
}
Then using the "JSON Schema" source type at https://app.quicktype.io/, I produce a Rust file that looks like this....
use serde::{Serialize, Deserialize};
/// An instance of org.accordproject.helloworld.MyRequest
#[derive(Serialize, Deserialize)]
pub struct MyRequest {
/// The class identifier for org.accordproject.helloworld.MyRequest
#[serde(rename = "$class")]
class: String,
input: String,
}
This better matches what I would expect. My guess is that you generated Rust code using the "JSON" source type rather than "JSON Schema"?
from apap.
Thanks @mttrbrts - That did it!
The "$ref" is the secret ingredient.
I was using the "JSON" source type in https://app.quicktype.io/ because, without the "$ref", no code was being generated when using the "JSON Schema" source type.
SO... I've expanded the "$ref" into a "properties" section with references to all the definitions.
This now allows QuickType to generate Rust structs for all definitions - with the MyRequest
and MyResponse
payloads as expected.
As a thought... It would be nice if we could get concerto compile
to include these properties
in the JSONSchema output, if possible - rather than having to hand modify it. What do you think? (Note: I'm happy to have a crack at this, if that helps?)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"MyRequest": {
"$ref": "#/definitions/org.accordproject.helloworld.MyRequest"
},
"myResponse": {
"$ref": "#/definitions/org.accordproject.helloworld.MyResponse"
},
"helloWorldClause": {
"$ref": "#/definitions/org.accordproject.helloworld.HelloWorldClause"
},
"request": {
"$ref": "#/definitions/org.accordproject.runtime.Request"
},
"response": {
"$ref": "#/definitions/org.accordproject.runtime.Response"
},
"obligation": {
"$ref": "#/definitions/org.accordproject.runtime.Obligation"
},
"state": {
"$ref": "#/definitions/org.accordproject.runtime.State"
},
"contract": {
"$ref": "#/definitions/org.accordproject.runtime.Contract"
},
"clause": {
"$ref": "#/definitions/org.accordproject.runtime.Clause"
}
},
"definitions": {
"org.accordproject.helloworld.MyRequest": {
"title": "MyRequest",
"description": "An instance of org.accordproject.helloworld.MyRequest",
"type": "object",
"properties": {
"$class": {
"type": "string",
"default": "org.accordproject.helloworld.MyRequest",
"pattern": "^org\\.accordproject\\.helloworld\\.MyRequest$",
"description": "The class identifier for org.accordproject.helloworld.MyRequest"
},
"input": {
"type": "string"
}
},
"required": [
"$class",
"input"
]
},
...etc....etc...
from apap.
I don't believe that the JSON Schema output should always contain an object as you describe it, because it is unlikely to validate against a JSON object directly.
Alternatively, I suggest one (or both) of the following options:
-
The JSON Schema code gen should have a parameter to allow a user to optionally specify the concept that they want to want to validate against. Aadrika called this the root type in her work over the summer.
-
We add a roof definition in the JSON Schema that matches against
anyOf
the types in the model. A little like the way that we generate union types for typescript. This should also be optional and controlled by a command line flag.
Otherwise, it's not unreasonable to ask your Rust library to generate structs for all definitions, not just the root?
from apap.
@mttrbrts - I can see your point.
However, I wasn't just trying to generate Rust for just a couple of specific concepts but for all concepts; and I'm not sure I like the idea of the user having to figure out which concepts that they want to validate against.
My thinking was that, by default, the JSON Schema and Rust code generator should generate structs for all the object definitions, not just one or two.
This is what I (manually) did above.
By creating "properties" within the JSON Schema, with different "$ref" values pointing to each, individual definition then this forced QuickType to generate Rust structs for the all JSON Schema definitions.
(Note: this means it is still a valid JSON Schema Definition as QuickType only accepts valid JSON Schema Defs.)
In this way, the contract author doesn't have to decide which concepts that they want to validate against, as all are available in the Rust library to be used at compile time.
Given it's simple to parse the JSON Schema and create the "properties" object containing all the "$ref" values then the suggestion is that we modify the JSONSchema Visitor to include these "properties".
Is this unreasonable?
Would having "properties" in the JSON Schema upset other use cases - even if it is still a valid JSON Schema definition?
Is there another, better, more elegant way?
from apap.
My option two should satisfy what you need, for example, we could generate JSON schema such as...
{
"$schema": "http://json-schema.org/draft-07/schema#",
"oneOf": [
{
"$ref": "#/definitions/org.accordproject.helloworld.MyRequest"
},
{
"$ref": "#/definitions/org.accordproject.helloworld.MyResponse"
},
...
],
"definitions": {
"org.accordproject.helloworld.MyRequest": {
"title": "MyRequest",
"description": "An instance of org.accordproject.helloworld.MyRequest",
"type": "object",
"properties": {
"$class": {
"type": "string",
"default": "org.accordproject.helloworld.MyRequest",
"pattern": "^org\\.accordproject\\.helloworld\\.MyRequest$",
"description": "The class identifier for org.accordproject.helloworld.MyRequest"
},
"input": {
"type": "string"
}
},
"required": [
"$class",
"input"
]
},
"org.accordproject.helloworld.MyResponse": {
"title": "MyRequest",
"description": "An instance of org.accordproject.helloworld.MyResponse",
"type": "object",
"properties": {
"$class": {
"type": "string",
"default": "org.accordproject.helloworld.MyResponse",
"pattern": "^org\\.accordproject\\.helloworld\\.MyResponse$",
"description": "The class identifier for org.accordproject.helloworld.MyResponse"
},
"input": {
"type": "string"
}
},
"required": [
"$class",
"input"
]
}
},
...
}
from apap.
Hi @mttrbrts - I tried using the above and it doesn't seem to generate the Rust code correctly - at least, not with QuickType. It just generates one big struct "HelloWorld" containing a set of optional strings.
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct HelloWorld {
#[serde(rename = "$class")]
pub class: String,
#[serde(rename = "input")]
pub input: Option<String>,
/// The instance identifier for this type
#[serde(rename = "clauseId")]
pub clause_id: Option<String>,
/// The instance identifier for this type
#[serde(rename = "contractId")]
pub contract_id: Option<String>,
#[serde(rename = "name")]
pub name: Option<String>,
#[serde(rename = "output")]
pub output: Option<String>,
#[serde(rename = "contract")]
pub contract: Option<String>,
#[serde(rename = "deadline")]
pub deadline: Option<String>,
#[serde(rename = "promisee")]
pub promisee: Option<String>,
#[serde(rename = "promisor")]
pub promisor: Option<String>,
}
from apap.
@mttrbrts - Of course, the other option is that we forget about generating Rust from JSON Schema and just handcraft a Visitor in Concerto and add --target rust
as an option?
However, I was, kinda, thinking to (lazily) use QuickType to generate the Rust from JSONSchema, instead of handcrafting Rust code gen logic from scratch. This would, obviously, introduce a dependency which, I appreciate, may not be desirable.
What do you think?
from apap.
I say that if you're willing to crack open the code gen code, let's just go direct to rust.
from apap.
@mttrbrts - OK. <<< Insert cracking sound >>>
from apap.
Related Issues (10)
- Alpha release of Agreement Protocol model
- Reference implementation for Agreement Protocol
- Concerto to OpenAPI specification transformation
- Language Choice for Reference Implementation
- JSON to Runtime Binding for Reference Implementation HOT 1
- Error generating OpenAPI client from AP OpenAPI specification file. HOT 4
- Publish API Docs to accordproject.org
- Publish prototype version of APAP to accordproject.org HOT 1
- Publish stateful version of APAP server
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from apap.