Skip to content

Commit

Permalink
feat: add generate example preset for JS (#629)
Browse files Browse the repository at this point in the history
  • Loading branch information
Samridhi-98 authored Feb 15, 2022
1 parent 5567586 commit c9980fc
Show file tree
Hide file tree
Showing 13 changed files with 358 additions and 1 deletion.
7 changes: 7 additions & 0 deletions docs/languages/JavaScript.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ There are special use-cases that each language supports; this document pertains

- [Rendering complete models to a specific module system](#rendering-complete-models-to-a-specific-module-system)
- [Generate un/marshal functions for classes](#generate-unmarshal-functions-for-classes)
- [Generate example data function](#generate-example-data-function)

<!-- tocstop -->

Expand All @@ -25,3 +26,9 @@ Sometimes you want to use the models for data transfers, and while most cases wo
Here, this can be done by including the preset `JS_COMMON_PRESET` using the option `marshalling`.

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

## Generate example data function

Generate example instance of the data model including the preset `JS_COMMON_PRESET` using the option `example`.

Check out this [example out for a live demonstration](../../examples/javascript-generate-example).
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ This directory contains a series of self-contained examples that you can use as
- [javascript-use-esm](./javascript-use-esm) - A basic example that generate the models to use ESM module system.
- [javascript-use-cjs](./javascript-use-cjs) - A basic example that generate the models to use CJS module system.
- [javascript-generate-marshalling](./javascript-generate-marshalling) - A basic example of how to use the un/marshalling functionality of the javascript class.
- [javascript-generate-example](./javascript-generate-example) - A basic example of how to use Modelina and output a JavaScript class with an example function.
- [generate-java-models](./generate-java-models) - A basic example to generate Java data models.
- [generate-go-models](./generate-go-models) - A basic example to generate Go data models
- [include-custom-function](./include-custom-function) - A basic example where a custom function is included.
Expand Down
18 changes: 18 additions & 0 deletions examples/javascript-generate-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# JavaScript Data Models with example function

A basic example of how to use Modelina and output a TypeScript class with an example function.


## 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,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Should be able to generate ts data model with example function and should log expected output to console 1`] = `
Array [
"class Root {
email;
constructor(input) {
if (input.hasOwnProperty('email')) {
this.email = input.email;
}
}
get email() { return this.email; }
set email(email) { this.email = email; }
example(){
const instance = new Root({});
instance.email = \\"string\\";
return instance;
}
}",
]
`;
14 changes: 14 additions & 0 deletions examples/javascript-generate-example/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; });
import { generate } from './index';

describe('Should be able to generate ts data model with example function', () => {
afterAll(() => {
jest.restoreAllMocks();
});
test('and should log expected output to console', async () => {
await generate();
//Generate is called 2x, so even though we expect 1 model, we double it
expect(spy.mock.calls.length).toEqual(2);
expect(spy.mock.calls[1]).toMatchSnapshot();
});
});
32 changes: 32 additions & 0 deletions examples/javascript-generate-example/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { JavaScriptGenerator } from '../../src';
import { JS_COMMON_PRESET } from '../../src';

const generator = new JavaScriptGenerator({
presets: [
{
preset: JS_COMMON_PRESET,
options: {
example: true
}
}
]
});
const jsonSchemaDraft7 = {
$schema: 'http://json-schema.org/draft-07/schema#',
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);
}
}
generate();
10 changes: 10 additions & 0 deletions examples/javascript-generate-example/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/javascript-generate-example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"config": {
"example_name": "javascript-generate-example"
},
"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"
}
}
6 changes: 5 additions & 1 deletion src/generators/javascript/presets/CommonPreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { JavaScriptRenderer } from '../JavaScriptRenderer';
import { JavaScriptPreset } from '../JavaScriptPreset';
import { getUniquePropertyName, DefaultPropertyNames, TypeHelpers, ModelKind } from '../../../helpers';
import { CommonInputModel, CommonModel } from '../../../models';
import renderExampleFunction from './utils/ExampleFunction';

export interface JavaScriptCommonPresetOptions {
marshalling: boolean;
example: boolean;
}

function realizePropertyFactory(prop: string) {
Expand Down Expand Up @@ -200,7 +202,9 @@ export const JS_COMMON_PRESET: JavaScriptPreset = {
blocks.push(renderMarshal({ renderer, model, inputModel }));
blocks.push(renderUnmarshal({ renderer, model, inputModel }));
}

if (options.example === true) {
blocks.push(renderExampleFunction({ renderer, model }));
}
return renderer.renderBlock([content, ...blocks], 2);
},
}
Expand Down
66 changes: 66 additions & 0 deletions src/generators/javascript/presets/utils/ExampleFunction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { JavaScriptRenderer } from '../../JavaScriptRenderer';
import { CommonModel } from '../../../../models';

export function renderValueFromModel(model: CommonModel, renderer: JavaScriptRenderer): string | undefined {
if (model.$ref !== undefined) {
return `${renderer.nameType(model.$ref)}.example()`;
}
if (Array.isArray(model.type)) {
if (model.type.length > 0) {
return renderValueFromType(model.type[0], model, renderer);
}
return undefined;
}
return renderValueFromType(model.type, model, renderer);
}

export function renderValueFromType(modelType: string | undefined, model: CommonModel, renderer: JavaScriptRenderer): string | undefined {
if (modelType === undefined) {
return undefined;
}
switch (modelType) {
case 'string':
return '"string"';
case 'integer':
case 'number':
return '0';
case 'boolean':
return 'true';
case 'array': {
if (model.items === undefined) {
return '[]';
}
if (Array.isArray(model.items)) {
const arrayValues = model.items.map((item) => {
return renderValueFromModel(item, renderer);
});
return `[${arrayValues.join(', ')}]`;
}
const arrayType = renderValueFromModel(model.items, renderer);
return `[${arrayType}]`;
}
}
return undefined;
}

export default function renderExampleFunction({ renderer, model }: {
renderer: JavaScriptRenderer,
model: CommonModel
}): string {
const properties = model.properties || {};
const setProperties = [];
for (const [propertyName, property] of Object.entries(properties)) {
const formattedPropertyName = renderer.nameProperty(propertyName, property);
const potentialRenderedValue = renderValueFromModel(property, renderer);
if (potentialRenderedValue === undefined) {
continue;
}
setProperties.push(` instance.${formattedPropertyName} = ${potentialRenderedValue};`);
}
const formattedModelName = renderer.nameType(model.$id);
return `example(){
const instance = new ${formattedModelName}({});
${(setProperties.join('\n'))}
return instance;
}`;
}
40 changes: 40 additions & 0 deletions test/generators/javascript/preset/ExamplePreset.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { JavaScriptGenerator, JS_COMMON_PRESET } from '../../../../src/generators';
const doc = {
$id: 'Test',
type: 'object',
additionalProperties: true,
required: ['string prop'],
properties: {
'string prop': { type: 'string' },
numberProp: { type: 'number' },
objectProp: { type: 'object', $id: 'NestedTest', properties: { stringProp: { type: 'string' } } }
},
patternProperties: {
'^S(.?)test': {
type: 'string'
}
},
};
describe('Example function generation', () => {
test('should render example function for model', async () => {
const generator = new JavaScriptGenerator({
presets: [
{
preset: JS_COMMON_PRESET,
options: {
example: true
}
}
]
});
const inputModel = await generator.process(doc);
const testModel = inputModel.models['Test'];
const nestedTestModel = inputModel.models['NestedTest'];

const testClass = await generator.renderClass(testModel, inputModel);
const nestedTestClass = await generator.renderClass(nestedTestModel, inputModel);

expect(testClass.result).toMatchSnapshot();
expect(nestedTestClass.result).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Example function generation should render example function for model 1`] = `
"class Test {
stringProp;
numberProp;
objectProp;
additionalProperties;
sTestPatternProperties;
constructor(input) {
this.stringProp = input.stringProp;
if (input.hasOwnProperty('numberProp')) {
this.numberProp = input.numberProp;
}
if (input.hasOwnProperty('objectProp')) {
this.objectProp = input.objectProp;
}
}
get stringProp() { return this.stringProp; }
set stringProp(stringProp) { this.stringProp = stringProp; }
get numberProp() { return this.numberProp; }
set numberProp(numberProp) { this.numberProp = numberProp; }
get objectProp() { return this.objectProp; }
set objectProp(objectProp) { this.objectProp = objectProp; }
get additionalProperties() { return this.additionalProperties; }
set additionalProperties(additionalProperties) { this.additionalProperties = additionalProperties; }
get sTestPatternProperties() { return this.sTestPatternProperties; }
set sTestPatternProperties(sTestPatternProperties) { this.sTestPatternProperties = sTestPatternProperties; }
example(){
const instance = new Test({});
instance.stringProp = \\"string\\";
instance.numberProp = 0;
instance.objectProp = NestedTest.example();
return instance;
}
}"
`;

exports[`Example function generation should render example function for model 2`] = `
"class NestedTest {
stringProp;
additionalProperties;
constructor(input) {
if (input.hasOwnProperty('stringProp')) {
this.stringProp = input.stringProp;
}
}
get stringProp() { return this.stringProp; }
set stringProp(stringProp) { this.stringProp = stringProp; }
get additionalProperties() { return this.additionalProperties; }
set additionalProperties(additionalProperties) { this.additionalProperties = additionalProperties; }
example(){
const instance = new NestedTest({});
instance.stringProp = \\"string\\";
return instance;
}
}"
`;
Loading

0 comments on commit c9980fc

Please sign in to comment.