@@ -12,6 +12,8 @@ import {
12
12
DBTCommandFactory ,
13
13
DBTCommand ,
14
14
RUN_RESULTS_FILE ,
15
+ RESOURCE_TYPE_MODEL ,
16
+ RESOURCE_TYPE_SOURCE ,
15
17
} from "../dbt_client/dbtIntegration" ;
16
18
import { ProjectHealthcheck } from "../dbt_client/dbtCoreIntegration" ;
17
19
import {
@@ -89,6 +91,7 @@ export class DBTIntegrationAdapter extends EventEmitter implements DBTFacade {
89
91
private targetWatchers : FSWatcher [ ] = [ ] ;
90
92
private currentTargetPath ?: string ;
91
93
private isWatchingTargetFiles = false ;
94
+ private lastParsedManifest ?: ParsedManifest ;
92
95
93
96
constructor (
94
97
private dbtConfiguration : DBTConfiguration ,
@@ -497,7 +500,7 @@ export class DBTIntegrationAdapter extends EventEmitter implements DBTFacade {
497
500
metricMetaMap ,
498
501
) ;
499
502
500
- return {
503
+ const parsedManifest = {
501
504
nodeMetaMap,
502
505
macroMetaMap,
503
506
metricMetaMap,
@@ -508,6 +511,11 @@ export class DBTIntegrationAdapter extends EventEmitter implements DBTFacade {
508
511
exposureMetaMap,
509
512
modelDepthMap,
510
513
} ;
514
+
515
+ // Store reference to last parsed manifest
516
+ this . lastParsedManifest = parsedManifest ;
517
+
518
+ return parsedManifest ;
511
519
}
512
520
513
521
private readAndParseManifestFile ( targetPath : string ) : any {
@@ -1022,25 +1030,164 @@ export class DBTIntegrationAdapter extends EventEmitter implements DBTFacade {
1022
1030
return this . currentIntegration . getCatalog ( ) ;
1023
1031
}
1024
1032
1025
- // Lineage and Relationships - these methods require access to manifest
1026
- // Since the adapter doesn't have direct manifest access, we'd need to implement
1027
- // these by parsing the manifest file or delegating to a manifest service
1028
- getNonEphemeralParents ( _keys : string [ ] ) : string [ ] {
1029
- // This would require manifest parsing logic
1030
- // For now, return empty array
1031
- return [ ] ;
1033
+ // Lineage and Relationships
1034
+ getNonEphemeralParents ( keys : string [ ] ) : string [ ] {
1035
+ if ( ! this . lastParsedManifest ) {
1036
+ throw Error (
1037
+ "No manifest has been generated. Maybe dbt project has not been parsed yet?" ,
1038
+ ) ;
1039
+ }
1040
+ const { nodeMetaMap, graphMetaMap } = this . lastParsedManifest ;
1041
+ const { parents } = graphMetaMap ;
1042
+ const parentSet = new Set < string > ( ) ;
1043
+ const queue = keys ;
1044
+ const visited : Record < string , boolean > = { } ;
1045
+ while ( queue . length > 0 ) {
1046
+ const curr = queue . shift ( ) ! ;
1047
+ if ( visited [ curr ] ) {
1048
+ continue ;
1049
+ }
1050
+ visited [ curr ] = true ;
1051
+ const parent = parents . get ( curr ) ;
1052
+ if ( ! parent ) {
1053
+ continue ;
1054
+ }
1055
+ for ( const n of parent . nodes ) {
1056
+ const splits = n . key . split ( "." ) ;
1057
+ const resource_type = splits [ 0 ] ;
1058
+ if ( resource_type !== RESOURCE_TYPE_MODEL ) {
1059
+ parentSet . add ( n . key ) ;
1060
+ continue ;
1061
+ }
1062
+ if (
1063
+ nodeMetaMap . lookupByUniqueId ( n . key ) ?. config . materialized ===
1064
+ "ephemeral"
1065
+ ) {
1066
+ queue . push ( n . key ) ;
1067
+ } else {
1068
+ parentSet . add ( n . key ) ;
1069
+ }
1070
+ }
1071
+ }
1072
+ return Array . from ( parentSet ) ;
1032
1073
}
1033
1074
1034
- getChildrenModels ( { table : _table } : { table : string } ) : Table [ ] {
1035
- // This would require manifest parsing logic
1036
- // For now, return empty array
1037
- return [ ] ;
1075
+ getChildrenModels ( { table } : { table : string } ) : Table [ ] {
1076
+ return this . getConnectedTables ( "children" , table ) ;
1077
+ }
1078
+
1079
+ getParentModels ( { table } : { table : string } ) : Table [ ] {
1080
+ return this . getConnectedTables ( "parents" , table ) ;
1081
+ }
1082
+
1083
+ private getConnectedTables ( key : keyof GraphMetaMap , table : string ) : Table [ ] {
1084
+ if ( ! this . lastParsedManifest ) {
1085
+ throw Error (
1086
+ "No manifest has been generated. Maybe dbt project has not been parsed yet?" ,
1087
+ ) ;
1088
+ }
1089
+ const { graphMetaMap, nodeMetaMap } = this . lastParsedManifest ;
1090
+ const node = nodeMetaMap . lookupByBaseName ( table ) ;
1091
+ if ( ! node ) {
1092
+ throw Error ( "nodeMetaMap has no entries for " + table ) ;
1093
+ }
1094
+ const dependencyNodes = graphMetaMap [ key ] ;
1095
+ const dependencyNode = dependencyNodes . get ( node . uniqueId ) ;
1096
+ if ( ! dependencyNode ) {
1097
+ throw Error ( "graphMetaMap[" + key + "] has no entries for " + table ) ;
1098
+ }
1099
+ const tables : Map < string , Table > = new Map ( ) ;
1100
+ dependencyNode . nodes . forEach ( ( { url, key } ) => {
1101
+ const _node = this . createTable ( url , key ) ;
1102
+ if ( ! _node ) {
1103
+ return ;
1104
+ }
1105
+ if ( ! tables . has ( _node . table ) ) {
1106
+ tables . set ( _node . table , _node ) ;
1107
+ }
1108
+ } ) ;
1109
+ return Array . from ( tables . values ( ) ) . sort ( ( a , b ) =>
1110
+ a . table . localeCompare ( b . table ) ,
1111
+ ) ;
1112
+ }
1113
+
1114
+ private createTable (
1115
+ tableUrl : string | undefined ,
1116
+ key : string ,
1117
+ ) : Table | undefined {
1118
+ if ( ! this . lastParsedManifest ) {
1119
+ throw new Error ( "The dbt manifest is not available" ) ;
1120
+ }
1121
+ const splits = key . split ( "." ) ;
1122
+ const nodeType = splits [ 0 ] ;
1123
+ const { graphMetaMap, testMetaMap } = this . lastParsedManifest ;
1124
+ const upstreamCount = this . getConnectedNodeCount (
1125
+ graphMetaMap [ "children" ] ,
1126
+ key ,
1127
+ ) ;
1128
+ const downstreamCount = this . getConnectedNodeCount (
1129
+ graphMetaMap [ "parents" ] ,
1130
+ key ,
1131
+ ) ;
1132
+ if ( nodeType === RESOURCE_TYPE_SOURCE ) {
1133
+ const { sourceMetaMap } = this . lastParsedManifest ;
1134
+ const schema = splits [ 2 ] ;
1135
+ const table = splits [ 3 ] ;
1136
+ const _node = sourceMetaMap . get ( schema ) ;
1137
+ if ( ! _node ) {
1138
+ return ;
1139
+ }
1140
+ const _table = _node . tables . find ( ( t ) => t . name === table ) ;
1141
+ if ( ! _table ) {
1142
+ return ;
1143
+ }
1144
+ return {
1145
+ table : key ,
1146
+ label : table ,
1147
+ url : tableUrl ,
1148
+ upstreamCount,
1149
+ downstreamCount,
1150
+ nodeType,
1151
+ isExternalProject : _node . is_external_project ,
1152
+ tests : ( graphMetaMap [ "tests" ] . get ( key ) ?. nodes || [ ] ) . map ( ( n ) => {
1153
+ const testKey = n . label . split ( "." ) [ 0 ] ;
1154
+ return { ...testMetaMap . get ( testKey ) , key : testKey } ;
1155
+ } ) ,
1156
+ columns : _table . columns ,
1157
+ description : _table ?. description ,
1158
+ } ;
1159
+ } else {
1160
+ const { nodeMetaMap } = this . lastParsedManifest ;
1161
+ const node = nodeMetaMap . lookupByUniqueId ( key ) ;
1162
+ if ( ! node ) {
1163
+ return ;
1164
+ }
1165
+ return {
1166
+ table : key ,
1167
+ label : node . name ,
1168
+ url : tableUrl ,
1169
+ upstreamCount,
1170
+ downstreamCount,
1171
+ nodeType,
1172
+ isExternalProject : node . package_name !== this . getProjectName ( ) ,
1173
+ tests : ( graphMetaMap [ "tests" ] . get ( key ) ?. nodes || [ ] ) . map ( ( n ) => {
1174
+ const testKey = n . label . split ( "." ) [ 0 ] ;
1175
+ return { ...testMetaMap . get ( testKey ) , key : testKey } ;
1176
+ } ) ,
1177
+ columns : node . columns ,
1178
+ description : node ?. description ,
1179
+ } ;
1180
+ }
1038
1181
}
1039
1182
1040
- getParentModels ( { table : _table } : { table : string } ) : Table [ ] {
1041
- // This would require manifest parsing logic
1042
- // For now, return empty array
1043
- return [ ] ;
1183
+ private getConnectedNodeCount (
1184
+ connectionGraph : Map <
1185
+ string ,
1186
+ { nodes : { key : string ; label : string ; url ?: string } [ ] }
1187
+ > ,
1188
+ key : string ,
1189
+ ) : number {
1190
+ return connectionGraph . get ( key ) ?. nodes . length || 0 ;
1044
1191
}
1045
1192
1046
1193
// Target File Watcher Operations
0 commit comments