Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add jsonbinpack preset #854

Merged
merged 19 commits into from
Sep 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions docs/languages/TypeScript.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ There are special use-cases that each language supports; this document pertains
+ [Generate marshalling and unmarshalling functions](#generate-marshalling-and-unmarshalling-functions)
* [To and from XML](#to-and-from-xml)
* [To and from binary](#to-and-from-binary)
+ [Generate models with jsonbinpack support](#generate-models-with-jsonbinpack-support)
- [Generate example data function](#generate-example-data-function)
- [Rendering complete models to a specific module system](#rendering-complete-models-to-a-specific-module-system)
- [Rendering comments from description and example fields](#rendering-comments-from-description-and-example-fields)
Expand Down Expand Up @@ -48,7 +49,7 @@ The most widely used usecase for Modelina is to generate models that include ser
As you normally only need one library to do this, we developers can never get enough with creating new stuff, therefore there might be one specific library you need or want to integrate with. Therefore there is not one specific preset that offers everything. Below is a list of all the supported serialization presets.

### To and from JSON
Here are all the supported presets and the libraries they use:
Here are all the supported presets and the libraries they use for converting to and from JSON:

- [Generate marshalling and unmarshalling functions](#generate-marshalling-and-unmarshalling-functions)

Expand All @@ -62,8 +63,23 @@ Check out this [example out for a live demonstration](../../examples/typescript-
Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)!

### To and from binary
Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)!
Here are all the supported presets and the libraries they use for converting to and from binary:

- [Generate jsonbinpack functions](#generate-models-with-jsonbinpack-support)

#### Generate models with jsonbinpack support

This functionality is for the library [jsonbinpack](https://github.com/sourcemeta/jsonbinpack).

This preset can ONLY be used with AsyncAPI 2.x and JSON Schema draft 4 to 7 inputs.

This functionality has two requirements:
1. You MUST manually install the library `jsonbinpack`.
2. You MUST also use the [Generate un/marshal functions for classes](#generate-unmarshal-functions-for-classes)

This feature allows you to convert models to a buffer, which is highly space-efficient, instead of sending pure JSON data over the wire.

Check out this [example out for a live demonstration](../../examples/typescript-generate-jsonbinpack/).
magicmatatjahu marked this conversation as resolved.
Show resolved Hide resolved

## Generate example data function

Expand All @@ -73,7 +89,6 @@ This can be done by including the preset `TS_COMMON_PRESET` using the option `ex

Check out this [example out for a live demonstration](../../examples/typescript-generate-example).


## Rendering complete models to a specific module system
In some cases you might need to render the complete models to a specific module system such as ESM and CJS.

Expand Down
22 changes: 20 additions & 2 deletions docs/migrations/version-0-to-1.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,23 @@ These are all the preset hook changes:
- C#, enum preset hooks had access to `model: CommonModel` which has now been changed to `model: ConstrainedEnumModel`

General changes:
- Hooks that gave access to properties/fields formally had the arguments `property`, `propertyName` and `type`, these are now wrapped within the [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) and can be accessed through the `property` argument.
- Hooks that gave access to enum items, now has the type [`ConstrainedEnumValueModel`](./internal-model.md#the-constrained-meta-model).
- Hooks that gave access to properties/fields formally had the arguments `property`, `propertyName` and `type`, these are now wrapped within the [`ConstrainedObjectPropertyModel`](../internal-model.md#the-constrained-meta-model) and can be accessed through the `property` argument.
- Hooks that gave access to enum items, now has the type [`ConstrainedEnumValueModel`](../internal-model.md#the-constrained-meta-model).

## TypeScript and JavaScript module system
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OBS: Ended up having to move the module system option around for presets to access it 🙂 Which I had to ripple to the JavaScript generator as well.

In the previous version you only had to define a the module system type (`ESM` or `CJS`) when generating complete models. However, it happens that presets need to import their own dependencies which then need to adapt to the desire module system. Therefore this option have been moved so you have to adapt your code the following way:

```ts
const input = ...;

// Old way
const generator = new JavaScriptGenerator();
const models = await generator.generateCompleteModels(input, {
moduleSystem: 'ESM'
})

// New way
const generator = new JavaScriptGenerator({moduleSystem: 'ESM'});
const models = await generator.generateCompleteModels(input, {});

```
12 changes: 6 additions & 6 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ All [generate functions](./generators.md) return an array of `OutputModel`s, whi
| Property | Type | Description |
|---|---|---|
| `result` | String | The rendered content, that depends on whether you render it as a full model or partial model. |
| `model` | [ConstrainedMetaModel](./processing.md#the-constrained-meta-model) | The constrained meta model that contains many other information about the rendered model. |
| `model` | [ConstrainedMetaModel](./internal-model.md#the-constrained-meta-model) | The constrained meta model that contains many other information about the rendered model. |
| `modelName` | String | The rendered name of the model. |
| `inputModel` | `InputMetaModel` | Contains all the raw models along side the input they where generated for. Check the code for further information. |
| `dependencies` | String[] | List of rendered dependency imports that the model uses. |
Expand All @@ -67,11 +67,11 @@ The message payloads, since it is a JSON Schema variant, is [interpreted as a su
These are the current known limitation of the AsyncAPI input.

#### Message Schema formats
Currently, only a limited number of schema formats are supported and we currently rely on the [AsyncAPI parser](github.com/asyncapi/parser-js/) to handle those schema formats and convert them into [AsyncAPI Schema format](https://github.com/asyncapi/parser-js/#custom-message-parsers). At some point in the future, Modelina will support all native schema formats, so it does not matter which standard you use to define the message payloads, you will be able to generate models from it.
Currently, only a limited number of schema formats are supported and we currently rely on the [AsyncAPI parser](https://github.com/asyncapi/parser-js/) to handle those schema formats and convert them into [AsyncAPI Schema format](https://github.com/asyncapi/parser-js/#custom-message-parsers). At some point in the future, Modelina will support all native schema formats, so it does not matter which standard you use to define the message payloads, you will be able to generate models from it.

#### Polymorphism

Through the AsyncAPI Schema you are able to use `discriminator` for achieving polymorphism. Current version of Modelina does not generate the expected inheritance of models, instead it's current behavior is to [merge the schemas together](./inputs/interpretation_of_JSON_Schema.md#Processing-sub-schemas). This means you will still get access to the properties that missing inherited model has, but without the inheritance.
Through the AsyncAPI Schema you are able to use `discriminator` for achieving polymorphism. Current version of Modelina does not generate the expected inheritance of models, instead it's current behavior is to [merge the schemas together](./inputs/JSON_Schema.md#processing-sub-schemas). This means you will still get access to the properties that missing inherited model has, but without the inheritance.

Long term if this is something you wish was supported, voice your [opionion here](https://github.com/asyncapi/modelina/issues/108).

Expand All @@ -98,7 +98,7 @@ These are the current known limitation of the Swagger 2.0 input.

#### Polymorphism

Through the Swagger 2.0 Schema you are able to use `discriminator` for achieving polymorphism. Current version of Modelina does not generate the expected inheritance of models, instead it's current behavior is to [merge the schemas together](./inputs/interpretation_of_JSON_Schema.md#Processing-sub-schemas). This means you will still get access to the properties that missing inherited model has, but without the inheritance.
Through the Swagger 2.0 Schema you are able to use `discriminator` for achieving polymorphism. Current version of Modelina does not generate the expected inheritance of models, instead it's current behavior is to [merge the schemas together](./inputs/JSON_Schema.md#processing-sub-schemas). This means you will still get access to the properties that missing inherited model has, but without the inheritance.

Long term if this is something you wish was supported, voice your [opionion here](https://github.com/asyncapi/modelina/issues/108).

Expand All @@ -117,7 +117,7 @@ These are the current known limitation of the OpenAPI inputs.

##### Polymorphism

Through the OpenAPI 3.0 Schema you are able to use `discriminator` for achieving polymorphism. Current version of Modelina does not generate the expected inheritance of models, instead it's current behavior is to [merge the schemas together](./inputs/interpretation_of_JSON_Schema.md#Processing-sub-schemas). This means you will still get access to the properties that missing inherited model has, but without the inheritance.
Through the OpenAPI 3.0 Schema you are able to use `discriminator` for achieving polymorphism. Current version of Modelina does not generate the expected inheritance of models, instead it's current behavior is to [merge the schemas together](./inputs/JSON_Schema.md#processing-sub-schemas). This means you will still get access to the properties that missing inherited model has, but without the inheritance.

Long term if this is something you wish was supported, voice your [opionion here](https://github.com/asyncapi/modelina/issues/108).

Expand Down Expand Up @@ -162,4 +162,4 @@ Dart is one of the many output languages we support. Check out this [basic examp

## Generate Rust models

Rust is one of the many output languages we support. Check out this [basic example for a live demonstration](../examples/generate-rust-crate) and the following [Rust documentation for more advanced use-cases](./languages/Rust.md).
Rust is one of the many output languages we support. Check out this [basic example for a live demonstration](../examples/rust-generate-crate) and the following [Rust documentation for more advanced use-cases](./languages/Rust.md).
3 changes: 2 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ This directory contains a series of self-contained examples that you can use as
- [overwrite-naming-formatting](./overwrite-naming-formatting) - A basic example how to overwrite default naming format constraint in this case, overwriting returning a constant case format.
- [overwrite-default-constraint](./overwrite-default-constraint/) - A basic example how to overwrite the entire constraint logic and not just a single single part of the default behavior, in this case overwriting the model naming constraint.
- [integrate with React](./integrate-with-react/) - A basic example that shows how you can integrate Modelina with React.
- [rust-generate-crate](./rust-generate-crate/) - A basic example showing how to generate a Rust package.
- [rust-generate-crate](./rust-generate-crate/) - A basic example showing how to generate a Rust package.
- [typescript-generate-jsonbinpack](./typescript-generate-jsonbinpack) - A basic example showing how to generate models that include [jsonbinpack](https://github.com/sourcemeta/jsonbinpack) functionality.
10 changes: 6 additions & 4 deletions examples/javascript-use-cjs/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { JavaScriptGenerator } from '../../src';

const generator = new JavaScriptGenerator();
const generator = new JavaScriptGenerator(
{
moduleSystem: 'CJS'
}
);
const jsonSchemaDraft7 = {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
Expand All @@ -22,9 +26,7 @@ const jsonSchemaDraft7 = {
export async function generate() : Promise<void> {
const models = await generator.generateCompleteModels(
jsonSchemaDraft7,
{
moduleSystem: 'CJS'
}
{}
);
for (const model of models) {
console.log(model.result);
Expand Down
10 changes: 6 additions & 4 deletions examples/javascript-use-esm/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { JavaScriptGenerator } from '../../src';

const generator = new JavaScriptGenerator();
const generator = new JavaScriptGenerator(
{
moduleSystem: 'ESM'
}
);
const jsonSchemaDraft7 = {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
Expand All @@ -22,9 +26,7 @@ const jsonSchemaDraft7 = {
export async function generate() : Promise<void> {
const models = await generator.generateCompleteModels(
jsonSchemaDraft7,
{
moduleSystem: 'ESM'
}
{}
);
for (const model of models) {
console.log(model.result);
Expand Down
17 changes: 17 additions & 0 deletions examples/typescript-generate-jsonbinpack/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# TypeScript data models with jsonbinpack support

A basic example of how to generate models with [jsonbinpack](https://github.com/sourcemeta/jsonbinpack) support.

## How to run this example

Run this example using:

```sh
npm i && npm run start
```

If you are on Windows, use the `start:windows` script instead:

```sh
npm i && npm run start:windows
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Should be able to generate ts data model with marshal und unmarshal functions and should log expected output to console 1`] = `
Array [
"class Test {
private _email?: string;

constructor(input: {
email?: string,
}) {
this._email = input.email;
}

get email(): string | undefined { return this._email; }
set email(email: string | undefined) { this._email = email; }

public marshal() : string {
let json = '{'
if(this.email !== undefined) {
json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`;
}


//Remove potential last comma
return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`;
}

public static unmarshal(json: string | object): Test {
const obj = typeof json === \\"object\\" ? json : JSON.parse(json);
const instance = new Test({} as any);

if (obj[\\"email\\"] !== undefined) {
instance.email = obj[\\"email\\"];
}



return instance;
}

public async jsonbinSerialize(): Promise<Buffer>{
const jsonData = JSON.parse(this.marshal());
const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\",\\"$id\\":\\"Test\\",\\"type\\":\\"object\\",\\"additionalProperties\\":false,\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"});
return jsonbinpack.serialize(jsonbinpackEncodedSchema, jsonData);
}

public static async jsonbinDeserialize(buffer: Buffer): Promise<Test> {
const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\",\\"$id\\":\\"Test\\",\\"type\\":\\"object\\",\\"additionalProperties\\":false,\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"});
const json = jsonbinpack.deserialize(jsonbinpackEncodedSchema, buffer);
return Test.unmarshal(json);
}
}",
]
`;
13 changes: 13 additions & 0 deletions examples/typescript-generate-jsonbinpack/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; });
import {generate} from './index';

describe('Should be able to generate ts data model with marshal und unmarshal functions', () => {
afterAll(() => {
jest.restoreAllMocks();
});
test('and should log expected output to console', async () => {
await generate();
expect(spy.mock.calls.length).toEqual(1);
expect(spy.mock.calls[0]).toMatchSnapshot();
});
});
35 changes: 35 additions & 0 deletions examples/typescript-generate-jsonbinpack/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { TS_JSONBINPACK_PRESET, TypeScriptGenerator, TS_COMMON_PRESET} from '../../src';

const generator = new TypeScriptGenerator({
presets: [
{
preset: TS_COMMON_PRESET,
options: {
marshalling: true
}
},
TS_JSONBINPACK_PRESET
]
});
const jsonSchemaDraft7 = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'Test',
type: 'object',
additionalProperties: false,
properties: {
email: {
type: 'string',
format: 'email'
}
}
};

export async function generate(): Promise<void> {
const models = await generator.generate(jsonSchemaDraft7);
for (const model of models) {
console.log(model.result);
}
}
if (require.main === module) {
generate();
}
3 changes: 3 additions & 0 deletions examples/typescript-generate-jsonbinpack/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions examples/typescript-generate-jsonbinpack/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"config": {
"example_name": "typescript-generate-jsonbinpack"
},
"scripts": {
"install": "cd ../.. && npm i",
"start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts",
"start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts",
"test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts",
"test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts"
}
}
12 changes: 7 additions & 5 deletions examples/typescript-use-cjs/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { TypeScriptGenerator } from '../../src';

const generator = new TypeScriptGenerator();
const generator = new TypeScriptGenerator(
{
moduleSystem: 'CJS'
}
);
const jsonSchemaDraft7 = {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
Expand All @@ -21,10 +25,8 @@ const jsonSchemaDraft7 = {

export async function generate() : Promise<void> {
const models = await generator.generateCompleteModels(
jsonSchemaDraft7,
{
moduleSystem: 'CJS'
}
jsonSchemaDraft7,
{}
);
for (const model of models) {
console.log(model.result);
Expand Down
10 changes: 6 additions & 4 deletions examples/typescript-use-esm/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { TypeScriptGenerator } from '../../src';

const generator = new TypeScriptGenerator();
const generator = new TypeScriptGenerator(
{
moduleSystem: 'ESM'
}
);
const jsonSchemaDraft7 = {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
Expand All @@ -22,9 +26,7 @@ const jsonSchemaDraft7 = {
export async function generate() : Promise<void> {
const models = await generator.generateCompleteModels(
jsonSchemaDraft7,
{
moduleSystem: 'ESM'
}
{}
);
for (const model of models) {
console.log(model.result);
Expand Down
Loading