Skip to content

Commit

Permalink
Merge pull request #21 from dangchinh25/cle/feat/support-unsupported-…
Browse files Browse the repository at this point in the history
…types

Cle/feat/support unsupported types
  • Loading branch information
dangchinh25 authored Nov 28, 2023
2 parents 4f0f47e + 1c6bb40 commit 90f66bc
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 27 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ node_modules
yarn-error.log
build
pnpm-debug.log
.env*
.env*
**/.DS_Store
4 changes: 4 additions & 0 deletions prisma/modelsGraph/ModelsGraph.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
{
"name": "user_type_id",
"type": "Int"
},
{
"name": "lat_lng_geography",
"type": "geography(Point,4326)"
}
],
"relations": [
Expand Down
27 changes: 8 additions & 19 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ generator custom_generator {
exportJSON = true
}

generator client {
provider = "prisma-client-js"
previewFeatures = ["postgresqlExtensions"]
binaryTargets = ["native", "debian-openssl-1.1.x", "linux-musl", "darwin"]
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
Expand All @@ -14,6 +20,8 @@ model User {
name String?
userTypeId Int @map("user_type_id") /// [[UserType.id]]
latLngGeography Unsupported("geography(Point,4326)")? @map("lat_lng_geography")
@@map("users")
}

Expand Down Expand Up @@ -47,22 +55,3 @@ model UserUserGroups {
@@unique([userId, userGroupId])
@@map("users_user_groups")
}

enum NotificationType {
newPosts
newComments
newFollowers
reply
heartOnPost
heartOnComment
heartOnReply
}

enum Language {
Typescript
Javascript
Rust
Go
Python
Cpp
}
2 changes: 1 addition & 1 deletion src/cli/prisma-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { DEFAULT_JSON_FILE_NAME } from './constants';
import { writeFileSafely } from '../utils';

export const generate = async ( options: GeneratorOptions ): Promise<void> => {
const modelsGraph = parseDMMFModels( options.dmmf.datamodel.models );
const modelsGraph = parseDMMFModels( options );

const { exportJSON } = options.generator.config;

Expand Down
110 changes: 109 additions & 1 deletion src/generator/helpers/parser.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { DMMF } from '@prisma/generator-helper';
import { ParsedModel, ParsedModels } from '../types';
import { GeneratorOptions } from '@prisma/generator-helper';

export const parseDMMFModels = (
models: DMMF.Model[]
options: GeneratorOptions
): ParsedModels => {
const models = options.dmmf.datamodel.models;

const parsedModels: ParsedModels = {};
const modelNameDbNameMap: Map<DMMF.Model['name'], string> = new Map();
const attributesDbNameMap: Map<string, string> = new Map();
Expand All @@ -19,6 +22,8 @@ export const parseDMMFModels = (
relations: []
};

const unsupportedFields = getModelUnsupportedFields( options, model.name );

for ( const field of model.fields ) {
const attribute = field.dbName || field.name;
parsedModel.attributes.push( {
Expand All @@ -28,6 +33,14 @@ export const parseDMMFModels = (
attributesDbNameMap.set( `${ model.name }.${ field.name }`, attribute );
}

for ( const field of unsupportedFields ) {
parsedModel.attributes.push( {
name: field.dbName,
type: field.type
} );
attributesDbNameMap.set( `${ model.name }.${ field.name }`, field.dbName );
}

parsedModels[ modelDbName ] = parsedModel;
}

Expand Down Expand Up @@ -94,5 +107,100 @@ const parseDMMFFieldDocumentation = (

const [ , matchWithoutSymbol ] = matches;

return matchWithoutSymbol;
};

export type UnsupportedField = {
name: string;
dbName: string;
type: string;
};

const getModelUnsupportedFields = (
options: GeneratorOptions,
modelName: DMMF.Model['name']
): UnsupportedField[] => {
const datamodel = options.datamodel;

// Split the schema into lines
const lines = datamodel.split( '\n' );

const unsupportedFields: UnsupportedField[] = [];

// Find the model definition
const modelIndex = lines.findIndex( line => line.includes( `model ${ modelName }` ) );

// Find the unsupported attribute within the model definition
for ( let i = modelIndex + 1; i < lines.length; i++ ) {
const line = lines[ i ];

// Check if we've reached the end of the model definition
if ( line.includes( '}' ) ) {
break;
}

// Check if the line defines an unsupported attribute
if ( line.includes( 'Unsupported' ) ) {
// Extract the attribute name (assuming it's the first word in the line)
const attributeDefinitionsParts = line.trim()
.split( ' ' );

const attributeName = attributeDefinitionsParts[ 0 ];
const typeDefinition = attributeDefinitionsParts[ 1 ];

let attributeMappedName: string | null = null;

attributeDefinitionsParts.forEach( part => {
if ( part.includes( '@map' ) ) {
attributeMappedName = parseMappedNameDefinition( part );
}
} );

const trimmedType = parseUnsupportedTypeDefinition( typeDefinition );

unsupportedFields.push( {
name: attributeName,
dbName: attributeMappedName || attributeName,
type: trimmedType
} );
}
}

return unsupportedFields;
};

/**
* Parse Unsupported type definition in Prisma format to extract the correct trimmed DB type.
* E.g: geography(Point, 4326)
* @param typeDefinition Unsupported type definition in Prisma format. E.g: Unsupported("geography(Point,4326)")?
*/
const parseUnsupportedTypeDefinition = ( typeDefinition: string ): string => {
const regex = new RegExp( /Unsupported\("([^"]+)"\)/ );
const matches = regex.exec( typeDefinition );

if ( !matches ) {
throw new Error( 'Invalid Unsupported type definition format.' );
}

const [ , matchWithoutSymbol ] = matches;

return matchWithoutSymbol;
};

/**
* Parse Mapped name definition in Prisma format to extract the correct DB column name.
* @param mappedNameDefinition Mapped name definition in Prisma format. E.g: @map("lat_lng_geography")
*/
const parseMappedNameDefinition = ( mappedNameDefinition: string ): string => {
const regex = new RegExp( /@map\("([^"]+)"\)/ );

const matches = regex.exec( mappedNameDefinition );

if ( !matches ) {
throw new Error( 'Invalid mapped name definition format.' );
}

const [ , matchWithoutSymbol ] = matches;

return matchWithoutSymbol;
};
7 changes: 5 additions & 2 deletions tests/__fixtures__/getSampleDMMF.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { DMMF } from '@prisma/generator-helper';
import { getDMMF, getSchema } from '@prisma/internals';
import path from 'path';


export const getSampleDMMF = async (): Promise<DMMF.Document> => {
const samplePrismaSchema = await getSchema( path.join( __dirname, './sample.prisma' ) );
const samplePrismaSchema = await getSampleSchema();

return getDMMF( { datamodel: samplePrismaSchema } );
};

export const getSampleSchema = async (): Promise<string> => {
return await getSchema( path.join( __dirname, './sample.prisma' ) );
};
7 changes: 4 additions & 3 deletions tests/helpers/parser.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { parseDMMFModels } from '../../src/generator/helpers';
import { getSampleDMMF } from '../__fixtures__/getSampleDMMF';
import { getSampleDMMF, getSampleSchema } from '../__fixtures__/getSampleDMMF';
import { ParsedModels } from '../../src/generator/types';
import { DMMF } from '@prisma/generator-helper';
import { DMMF, GeneratorOptions } from '@prisma/generator-helper';

describe( 'parseDMMFModels', () => {
let parsedModels: ParsedModels;
let dmmfModels: DMMF.Model[];

beforeAll( async () => {
const sampleDMMF = await getSampleDMMF();
const samepleSchema = await getSampleSchema();
dmmfModels = sampleDMMF.datamodel.models;
parsedModels = parseDMMFModels( dmmfModels );
parsedModels = parseDMMFModels( { dmmf: sampleDMMF, datamodel: samepleSchema } as GeneratorOptions );
} );

it( 'generates all models with correct db name', () => {
Expand Down

0 comments on commit 90f66bc

Please sign in to comment.