1- import { Model , Enum , DataModel } from '@zenstackhq/language/ast' ;
1+ import { Model , Enum , DataModel , DataField } from '@zenstackhq/language/ast' ;
22import { ZModelCodeGenerator } from '@zenstackhq/sdk' ;
33import fs from 'node:fs' ;
44import path from 'node:path' ;
55import { execPrisma } from '../utils/exec-utils' ;
66import { generateTempPrismaSchema , getSchemaFile , handleSubProcessError , requireDataSourceUrl , loadSchemaDocumentWithServices } from './action-utils' ;
77import { syncEnums , syncRelation , syncTable , type Relation } from './pull' ;
88import { providers } from './pull/provider' ;
9- import { getDatasource , getDbName } from './pull/utils' ;
9+ import { getDatasource , getDbName , getRelationFkName } from './pull/utils' ;
1010import { config } from '@dotenvx/dotenvx' ;
1111
1212type PushOptions = {
@@ -20,7 +20,7 @@ export type PullOptions = {
2020 out ?: string ;
2121 naming ?: 'pascal' | 'camel' | 'snake' | 'kebab' | 'none' ;
2222 alwaysMap ?: boolean ;
23- excludeSchemas : string [ ] ;
23+ excludeSchemas ? : string [ ] ;
2424} ;
2525
2626/**
@@ -91,8 +91,8 @@ async function runPull(options: PullOptions) {
9191 }
9292
9393 const { enums : allEnums , tables : allTables } = await provider . introspect ( datasource . url ) ;
94- const enums = allEnums . filter ( ( e ) => ! options . excludeSchemas . includes ( e . schema_name ) ) ;
95- const tables = allTables . filter ( ( t ) => ! options . excludeSchemas . includes ( t . schema ) ) ;
94+ const enums = allEnums . filter ( ( e ) => ! options . excludeSchemas ? .includes ( e . schema_name ) ) ;
95+ const tables = allTables . filter ( ( t ) => ! options . excludeSchemas ? .includes ( t . schema ) ) ;
9696
9797 const newModel : Model = {
9898 $type : 'Model' ,
@@ -112,79 +112,178 @@ async function runPull(options: PullOptions) {
112112 }
113113
114114 for ( const relation of resolvedRelations ) {
115- syncRelation ( { model : newModel , relation, services, options } ) ;
115+ const simmilarRelations = resolvedRelations . filter ( ( rr ) => {
116+ return (
117+ ( rr . schema === relation . schema &&
118+ rr . table === relation . table &&
119+ rr . references . schema === relation . references . schema &&
120+ rr . references . table === relation . references . table ) ||
121+ ( rr . schema === relation . references . schema &&
122+ rr . column === relation . references . column &&
123+ rr . references . schema === relation . schema &&
124+ rr . references . table === relation . table )
125+ ) ;
126+ } ) . length ;
127+ const selfRelation =
128+ relation . references . schema === relation . schema && relation . references . table === relation . table ;
129+ syncRelation ( {
130+ model : newModel ,
131+ relation,
132+ services,
133+ options,
134+ selfRelation,
135+ simmilarRelations,
136+ } ) ;
116137 }
117138
118139 const cwd = new URL ( `file://${ process . cwd ( ) } ` ) . pathname ;
119140 const docs = services . shared . workspace . LangiumDocuments . all
120141 . filter ( ( { uri } ) => uri . path . toLowerCase ( ) . startsWith ( cwd . toLowerCase ( ) ) )
121142 . toArray ( ) ;
122143 const docsSet = new Set ( docs . map ( ( d ) => d . uri . toString ( ) ) ) ;
123- console . log ( docsSet ) ;
144+
145+ services . shared . workspace . IndexManager . allElements ( 'DataModel' , docsSet )
146+ . filter (
147+ ( declaration ) =>
148+ ! newModel . declarations . find ( ( d ) => getDbName ( d ) === getDbName ( declaration . node as any ) ) ,
149+ )
150+ . forEach ( ( decl ) => {
151+ const model = decl . node ! . $container as Model ;
152+ const index = model . declarations . findIndex ( ( d ) => d === decl . node ) ;
153+ model . declarations . splice ( index , 1 ) ;
154+ console . log ( `Delete model ${ decl . name } ` ) ;
155+ } ) ;
156+ services . shared . workspace . IndexManager . allElements ( 'Enum' , docsSet )
157+ . filter (
158+ ( declaration ) =>
159+ ! newModel . declarations . find ( ( d ) => getDbName ( d ) === getDbName ( declaration . node as any ) ) ,
160+ )
161+ . forEach ( ( decl ) => {
162+ const model = decl . node ! . $container as Model ;
163+ const index = model . declarations . findIndex ( ( d ) => d === decl . node ) ;
164+ model . declarations . splice ( index , 1 ) ;
165+ console . log ( `Delete enum ${ decl . name } ` ) ;
166+ } ) ;
167+
124168 newModel . declarations
125169 . filter ( ( d ) => [ DataModel , Enum ] . includes ( d . $type ) )
126170 . forEach ( ( _declaration ) => {
127171 const declaration = _declaration as DataModel | Enum ;
128- const declarations = services . shared . workspace . IndexManager . allElements ( declaration . $type , docsSet ) ;
172+ const declarations = services . shared . workspace . IndexManager . allElements (
173+ declaration . $type ,
174+ docsSet ,
175+ ) . toArray ( ) ;
129176 const originalModel = declarations . find ( ( d ) => getDbName ( d . node as any ) === getDbName ( declaration ) )
130177 ?. node as DataModel | Enum | undefined ;
131178 if ( ! originalModel ) {
132179 model . declarations . push ( declaration ) ;
133180 ( declaration as any ) . $container = model ;
181+ declaration . fields . forEach ( ( f ) => {
182+ if ( f . $type === 'DataField' && f . type . reference ?. ref ) {
183+ const ref = declarations . find (
184+ ( d ) => getDbName ( d . node as any ) === getDbName ( f . type . reference ! . ref as any ) ,
185+ ) ?. node ;
186+ if ( ref ) ( f . type . reference . ref as any ) = ref ;
187+ }
188+ } ) ;
134189 return ;
135190 }
136191
137192 declaration . fields . forEach ( ( f ) => {
138- const originalField = originalModel . fields . find ( ( d ) => getDbName ( d ) === getDbName ( f ) ) ;
193+ const originalField = originalModel . fields . find (
194+ ( d ) =>
195+ getDbName ( d ) === getDbName ( f ) ||
196+ ( getRelationFkName ( d as any ) === getRelationFkName ( f as any ) &&
197+ ! ! getRelationFkName ( d as any ) &&
198+ ! ! getRelationFkName ( f as any ) ) ,
199+ ) ;
139200
140201 if ( ! originalField ) {
141- console . log ( `Added field ${ f . name } to ${ originalModel . name } ` ) ;
202+ // console.log(`Added field ${f.name} to ${originalModel.name}`);
142203 ( f as any ) . $container = originalModel ;
143204 originalModel . fields . push ( f as any ) ;
205+ if ( f . $type === 'DataField' && f . type . reference ?. ref ) {
206+ const ref = declarations . find (
207+ ( d ) => getDbName ( d . node as any ) === getDbName ( f . type . reference ! . ref as any ) ,
208+ ) ?. node as DataModel | undefined ;
209+ if ( ref ) {
210+ ( f . type . reference . $refText as any ) = ref . name ;
211+ ( f . type . reference . ref as any ) = ref ;
212+ }
213+ }
144214 return ;
145215 }
146- //TODO: update field
216+
217+ if ( originalField . $type === 'DataField' ) {
218+ const field = f as DataField ;
219+ originalField . type = field . type ;
220+ if ( field . type . reference ) {
221+ const ref = declarations . find (
222+ ( d ) => getDbName ( d . node as any ) === getDbName ( field . type . reference ! . ref as any ) ,
223+ ) ?. node as DataModel | undefined ;
224+ if ( ref ) {
225+ ( field . type . reference . $refText as any ) = ref . name ;
226+ ( field . type . reference . ref as any ) = ref ;
227+ }
228+ }
229+
230+ ( originalField . type . $container as any ) = originalField ;
231+ }
232+
233+ f . attributes . forEach ( ( attr ) => {
234+ const originalAttribute = originalField . attributes . find (
235+ ( d ) => d . decl . $refText === attr . decl . $refText ,
236+ ) ;
237+
238+ if ( ! originalAttribute ) {
239+ //console.log(`Added Attribute ${attr.decl.$refText} to ${f.name}`);
240+ ( f as any ) . $container = originalField ;
241+ originalField . attributes . push ( attr as any ) ;
242+ return ;
243+ }
244+
245+ originalAttribute . args = attr . args ;
246+ attr . args . forEach ( ( a ) => {
247+ ( a . $container as any ) = originalAttribute ;
248+ } ) ;
249+ } ) ;
250+
251+ originalField . attributes
252+ . filter ( ( attr ) => ! f . attributes . find ( ( d ) => d . decl . $refText === attr . decl . $refText ) )
253+ . forEach ( ( attr ) => {
254+ const field = attr . $container ;
255+ const index = field . attributes . findIndex ( ( d ) => d === attr ) ;
256+ field . attributes . splice ( index , 1 ) ;
257+ //console.log(`Delete attribute from field:${field.name} ${attr.decl.$refText}`);
258+ } ) ;
147259 } ) ;
148260 originalModel . fields
149- . filter ( ( f ) => ! declaration . fields . find ( ( d ) => getDbName ( d ) === getDbName ( f ) ) )
261+ . filter (
262+ ( f ) =>
263+ ! declaration . fields . find (
264+ ( d ) =>
265+ getDbName ( d ) === getDbName ( f ) ||
266+ ( getRelationFkName ( d as any ) === getRelationFkName ( f as any ) &&
267+ ! ! getRelationFkName ( d as any ) &&
268+ ! ! getRelationFkName ( f as any ) ) ,
269+ ) ,
270+ )
150271 . forEach ( ( f ) => {
151272 const model = f . $container ;
152273 const index = model . fields . findIndex ( ( d ) => d === f ) ;
153274 model . fields . splice ( index , 1 ) ;
154- console . log ( `Delete field ${ f . name } ` ) ;
275+ // console.log(`Delete field ${f.name}`);
155276 } ) ;
156277 } ) ;
157278
158- services . shared . workspace . IndexManager . allElements ( 'DataModel' , docsSet )
159- . filter (
160- ( declaration ) =>
161- ! newModel . declarations . find ( ( d ) => getDbName ( d ) === getDbName ( declaration . node as any ) ) ,
162- )
163- . forEach ( ( decl ) => {
164- const model = decl . node ! . $container as Model ;
165- const index = model . declarations . findIndex ( ( d ) => d === decl . node ) ;
166- model . declarations . splice ( index , 1 ) ;
167- console . log ( `Delete model ${ decl . name } ` ) ;
168- } ) ;
169- services . shared . workspace . IndexManager . allElements ( 'Enum' , docsSet )
170- . filter (
171- ( declaration ) =>
172- ! newModel . declarations . find ( ( d ) => getDbName ( d ) === getDbName ( declaration . node as any ) ) ,
173- )
174- . forEach ( ( decl ) => {
175- const model = decl . node ! . $container as Model ;
176- const index = model . declarations . findIndex ( ( d ) => d === decl . node ) ;
177- model . declarations . splice ( index , 1 ) ;
178- console . log ( `Delete enum ${ decl . name } ` ) ;
179- } ) ;
180-
181279 if ( options . out && ! fs . lstatSync ( options . out ) . isFile ( ) ) {
182280 throw new Error ( `Output path ${ options . out } is not a file` ) ;
183281 }
184282
185283 const generator = new ZModelCodeGenerator ( {
186284 //TODO: make configurable
187285 quote : 'double' ,
286+ indent : 2 ,
188287 } ) ;
189288
190289 if ( options . out ) {
0 commit comments