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: load modelina configuration options from a config file #1367

Closed
17 changes: 17 additions & 0 deletions examples/get-config-from-file/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Get config from file

An example of using the `getConfigFromFile` function. We get the config from a file and pass it to a `TypescriptGenerator`

## 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
```
13 changes: 13 additions & 0 deletions examples/get-config-from-file/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;
});

describe('Should be able to render', () => {
afterAll(() => {
jest.restoreAllMocks();
});
test('and should log expected output to console', () => {
expect(spy.mock.calls.length).toEqual(1);
expect(spy.mock.calls[0]).toMatchSnapshot();
});
});
29 changes: 29 additions & 0 deletions examples/get-config-from-file/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { FileHelpers, TypeScriptGenerator, TypeScriptOptions } from '../../src';

const jsonSchemaDraft7 = {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
additionalProperties: false,
properties: {
email: {
type: 'string',
format: 'email'
}
}
};

async function main() {
try {
const config: { config: TypeScriptOptions } =
await FileHelpers.getConfigFromFile('./modelina.config.alternate.js');
Comment on lines +17 to +18
Copy link
Member

Choose a reason for hiding this comment

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

I think we should simplify this.

    const config = await FileHelpers.getConfigFromFile('./modelina.config.alternate.js');

Instead of having to do something like config.config, what do you think? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah definitely, it looks absurd.
We can directly export the config. 🙂
Would that work ?

Copy link
Member

Choose a reason for hiding this comment

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

Yep 👍

const tsGen = new TypeScriptGenerator(config.config);
const models = await tsGen.generate(jsonSchemaDraft7);
for (const model of models) {
console.log(model.result);
}
} catch (err) {
console.log(err);
}
}

main();
48 changes: 48 additions & 0 deletions examples/get-config-from-file/modelina.config.alternate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
TS_COMMON_PRESET,
IndentationTypes,
TypeScriptOptions
} from '../../src';

/** @type {TypeScriptOptions} */
const config = {
enumType: 'union',
modelType: 'interface',
indentation: {
size: 10,
type: IndentationTypes.SPACES
},
mapType: 'record',
renderTypes: false,
moduleSystem: 'CJS',
constraints: {
modelName: (context) => {
return `Custom${context.modelName}`;
},
propertyKey: (context) => {
return `custom_prop_${context.objectPropertyModel.propertyName}`;
}
},
typeMapping: {
Any: (context) => {
// Always map AnyModel to number
return 'number';
}
},
presets: [
{
preset: TS_COMMON_PRESET,
options: {
example: true
}
},
{
preset: TS_COMMON_PRESET,
options: {
marshalling: true
}
}
]
};

export { config };
48 changes: 48 additions & 0 deletions examples/get-config-from-file/modelina.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
TS_COMMON_PRESET,
IndentationTypes,
TypeScriptOptions
} from '../../src';

/** @type {TypeScriptOptions} */
const config = {
enumType: 'union',
modelType: 'class',
indentation: {
size: 10,
type: IndentationTypes.SPACES
},
mapType: 'record',
renderTypes: false,
moduleSystem: 'CJS',
constraints: {
modelName: (context) => {
return `Custom${context.modelName}`;
},
propertyKey: (context) => {
return `custom_prop_${context.objectPropertyModel.propertyName}`;
}
},
typeMapping: {
Any: (context) => {
// Always map AnyModel to number
return 'number';
}
},
presets: [
{
preset: TS_COMMON_PRESET,
options: {
example: true
}
},
{
preset: TS_COMMON_PRESET,
options: {
marshalling: true
}
}
]
};

export { config };
10 changes: 10 additions & 0 deletions examples/get-config-from-file/package-lock.json

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

10 changes: 10 additions & 0 deletions examples/get-config-from-file/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"config" : { "example_name" : "get-config-from-file" },
"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"
}
}
14 changes: 14 additions & 0 deletions src/helpers/FileHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,18 @@ export class FileHelpers {
}
});
}

static async getConfigFromFile(configPath: string): Promise<any> {
Copy link
Member

Choose a reason for hiding this comment

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

You have to write some unit tests here 🙂

Make sure you test error states as well:

  • What if the file exist but is not valid JS?
  • What if the file exist but is TypeScript?
  • What if the file exist and is correct JS, but does not expose the right config?
  • What if the file exist and is correct JS, but does not expose the config file the right way?

Copy link
Member

Choose a reason for hiding this comment

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

Make sure you document this function as well i.e. what it does 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jonaslagoni
I'll make sure to document and write the unit tests.

What if the file exist and is correct JS, but does not expose the right config?

and

What if the file exist and is correct JS, but does not expose the config file the right way?

Should be the same thing right ? I mean we wouldn't know if the file is exposing the config the right way, unless we know what it's exposing ? 🤔

The way i put it here might be confusing, feel free to ignore this, i will let you know later more context on this. 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll try fixing things one after other and contact you back 😄

Copy link
Member

Choose a reason for hiding this comment

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

The way i put it here might be confusing, feel free to ignore this, i will let you know later more context on this. 🙂

The first one is a bit hard to pull off, which is they expose the configuration correctly, but the configuration is incorrect (not sure we actually can do this)

The second one is if they expose it like export { config2: config }; when we expected export config;.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The first one is a bit hard to pull off, which is they expose the configuration correctly, but the configuration is incorrect (not sure we actually can do this)

I'll try to see if we can do the first one

The second one is if they expose it like export { config2: config }; when we expected export config;.

I'll look into this as well 🙂
Seems like a good learning oppurtunity.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jonaslagoni

We can restrict the users to only allow them to use js file for config right ? i hardly saw places where people used ts files for config.
What do you say ?

Copy link
Member

Choose a reason for hiding this comment

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

Agree 👍

It can also be an improvement down the line if ever requested 🙂

const configFilePath = path.resolve(configPath);
try {
await fs.stat(configFilePath);
} catch (err) {
throw new Error(
`No file exists on the specified location ${configFilePath}`
);
}

const configOptions = require(configFilePath);
return configOptions;
}
}