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

fix: handle Enums correctly on Typescript data schema generation #2870

Open
wants to merge 23 commits into
base: main
Choose a base branch
from

Conversation

tejas2008
Copy link

@tejas2008 tejas2008 commented Sep 12, 2024

Description of changes

  • Update Schema generator package to add handling of enums globally
  • Inside the model use ref() to define those enums
  • Added unit test to handle various combination of enum possibilities for Postgresql and mysql

This change would only affect an existing customer only if they re import the schema which is highly unlikely so we have determined this change to be a non breaking change.

CDK / CloudFormation Parameters Changed

Issue #, if available

#2596

Description of how you validated changes

  • Updated a current test snapshot
  • Added more unit tests to validate proper enum handling generating snapshots and passing tests.

Checklist

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@tejas2008 tejas2008 changed the title added enums via ref for single models fix: handle Enums correctly on Typescript data schema generation Sep 17, 2024
@tejas2008 tejas2008 marked this pull request as ready for review September 17, 2024 22:29
@tejas2008 tejas2008 requested a review from a team as a code owner September 17, 2024 22:29
@@ -15,6 +15,7 @@ export class Schema {
}

public addModel(model: Model): void {
console.log(this.engine);
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: remove this logging

Copy link
Author

Choose a reason for hiding this comment

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

Sure I will remove this and add a commit


const combinedEnums = nullEnums.concat(notNullEnums).flat() // making 1 D array

if (engine === 'Postgres') { // for postgresql
Copy link
Contributor

Choose a reason for hiding this comment

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

For future: Instead of introducing these engine based conditionals that bloats up this method, we could move these to separate methods inside an engine specific controller.

Copy link
Author

Choose a reason for hiding this comment

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

Sure I will look into it.

Copy link
Author

Choose a reason for hiding this comment

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

@phani-srikar I have updated the implementation to make it less bloated in the current task as well and more strongly typed.

@@ -15,6 +15,7 @@ export class Schema {
}

public addModel(model: Model): void {
console.log(this.engine);
Copy link
Member

Choose a reason for hiding this comment

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

Is this leftover from dev/debugging? Any reason a customer needs to see this?

Copy link
Author

Choose a reason for hiding this comment

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

Yes its a leftover might have missed it I will remove it

Comment on lines 22 to 25




Copy link
Member

Choose a reason for hiding this comment

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

Run yarn prettier --write . from the top-level project dir to fix this and other linting errors before submitting.

),
],
);
if(engine === 'Postgres'){
Copy link
Member

Choose a reason for hiding this comment

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

Looking at the code without context, it looks like we don't handle enum types for MySQL engine. Since we clearly do, let's add a comment explaining the logic about why we're doing this.

* @returns Typescript data schema type in TS Node format
*/
const createEnums = (type: EnumType, engine: string, modelName: string, enumName: string): ts.Node => {
if (engine === 'Postgres'){
Copy link
Member

Choose a reason for hiding this comment

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

nit: since engine is a string rather than a strongly typed value, let's make the engine handling in this method explicit and fail for unsupported engine types. That'll protect against typos in calling code. Alternately, use a strongly typed engine type to ensure we are operating on a list of values known at compile time.

const tsSchema = ts.factory.createCallExpression(
ts.factory.createPropertyAccessExpression(
createConfigureExpression(schema, config),
ts.factory.createIdentifier(TYPESCRIPT_DATA_SCHEMA_CONSTANTS.SCHEMA_METHOD),
ts.factory.createIdentifier(TYPESCRIPT_DATA_SCHEMA_CONSTANTS.SCHEMA_METHOD), // create a.schema()
Copy link
Member

Choose a reason for hiding this comment

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

prefer to remove dead code rather than leaving it commented

Suggested change
ts.factory.createIdentifier(TYPESCRIPT_DATA_SCHEMA_CONSTANTS.SCHEMA_METHOD), // create a.schema()
ts.factory.createIdentifier(TYPESCRIPT_DATA_SCHEMA_CONSTANTS.SCHEMA_METHOD),

Copy link
Author

Choose a reason for hiding this comment

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

It isn't a dead code its a comment, but its not very necessary since it is existing code so will probably remove it

@@ -147,19 +188,92 @@ export const createSchema = (schema: Schema, config?: DataSourceGenerateConfig):
throw new Error('No valid tables found. Make sure at least one table has a primary key.');
}

const engine = schema.getEngine().type;

const nullEnums = schema // find null enums in all models
Copy link
Member

Choose a reason for hiding this comment

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

naming nit: I think you mean "nullable enum fields" or rather than "null" enums? (The latter implies a concrete value assignment, as opposed to a field type constraint)

Copy link
Author

Choose a reason for hiding this comment

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

I will update the naming to have more clear variable names to avoid confusion. NullEnumFields and RequiredEnumFields makes sense I will go ahead with these naming conventions to provide more readability and understanding

}
}));

const notNullEnums = schema // find Non null enums in all models
Copy link
Member

Choose a reason for hiding this comment

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

naming nit: I think you mean "required" enums or "non null enum types"?

Copy link
Author

Choose a reason for hiding this comment

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

I will update the naming to have more clear variable names to avoid confusion.

@@ -190,33 +195,35 @@ export const createSchema = (schema: Schema, config?: DataSourceGenerateConfig):

const engine = schema.getEngine().type;

const nullEnums = schema // find null enums in all models
const nullEnumFields = schema // find null enum fields in all models
Copy link
Member

Choose a reason for hiding this comment

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

nit

Suggested change
const nullEnumFields = schema // find null enum fields in all models
const nullableEnumFields = schema

Copy link
Author

Choose a reason for hiding this comment

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

addressed it


const notNullEnums = schema // find Non null enums in all models
const RequiredEnumFields = schema // find required enum fields in all models
Copy link
Member

Choose a reason for hiding this comment

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

nit: don't PascalCase non-type, non-Class variables.

Suggested change
const RequiredEnumFields = schema // find required enum fields in all models
const requiredEnumFields = schema

Copy link
Author

Choose a reason for hiding this comment

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

Done

Comment on lines 238 to 239
for postgresql since enums are declared globally to void declaring duplicate enums we
declare find unique enums and then pass it to the schema
Copy link
Member

Choose a reason for hiding this comment

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

I can't tell what you mean with this explanation. Can you reword to clarify? (I mean to say, based on our discussion and reading through the code, I know what you're trying to do--it's the comment wording that is unclear.)

Also, nit: reserve the double-asterisk block comment structure (/** ... */ for docComments (such as we put at the beginning of an exported symbol). Other comments should be delimited with a double-slash (//)

Copy link
Author

Choose a reason for hiding this comment

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

Sure I will re word this and and remove the double-asterisk block structure

Copy link
Author

Choose a reason for hiding this comment

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

Added appropriate comment structure and comments to explain in brief the use case

@@ -1,6 +1,7 @@
import * as ts from 'typescript';
import { Schema } from '../schema-representation';
import { createImportExpression, createSchema, DataSourceGenerateConfig } from './helpers';
import { LogFormat } from '@aws-sdk/client-lambda';
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Are you using this somewhere since there are no other changes in this file.

Copy link
Author

Choose a reason for hiding this comment

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

Removed it

let enumName = enumNamePrefix;
let counter = 0;
while (this.enums.has(enumName)) {
enumName = [enumNamePrefix, counter.toString()].join('_');
enumName = toPascalCase([enumNamePrefix, counter.toString()]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you confirm this wouldn't be a breaking change if we change the naming of enums from table_field to TableField?

Copy link
Author

Choose a reason for hiding this comment

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

As per the discussion @palpatim the change could affect customers only if they reimport the schema which is highly unlikely, but we still are waiting for conformation from Josef on the same

@tejas2008 tejas2008 requested a review from a team as a code owner October 2, 2024 17:55
Comment on lines +184 to +190
.map((field) => {
if (field.type.kind === 'NonNull' && field.type.type.kind === 'Enum') {
return createEnums(field.type.type);
} else {
return undefined;
}
}),
Copy link
Member

Choose a reason for hiding this comment

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

Is checking field.type.kind and field.type.type.kind redundant because of the filter?

Suggested change
.map((field) => {
if (field.type.kind === 'NonNull' && field.type.type.kind === 'Enum') {
return createEnums(field.type.type);
} else {
return undefined;
}
}),
.map((field) => createEnums(field.type.type))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants