@@ -9,7 +9,6 @@ import PQueue from 'p-queue';
99import { IGlobalDatabaseContext } from '../../common/application/global-database-context.interface.js' ;
1010import { BaseType } from '../../common/data-injection.tokens.js' ;
1111import { WidgetTypeEnum } from '../../enums/widget-type.enum.js' ;
12- import { isTest } from '../../helpers/app/is-test.js' ;
1312import { getErrorMessage } from '../../helpers/get-error-message.js' ;
1413import { ValidationHelper } from '../../helpers/validators/validation-helper.js' ;
1514import { AiService } from '../ai/ai.service.js' ;
@@ -18,6 +17,11 @@ import { TableSettingsEntity } from '../table-settings/common-table-settings/tab
1817import { buildEmptyTableSettings } from '../table-settings/common-table-settings/utils/build-empty-table-settings.js' ;
1918import { buildNewTableSettingsEntity } from '../table-settings/common-table-settings/utils/build-new-table-settings-entity.js' ;
2019import { TableWidgetEntity } from '../widget/table-widget.entity.js' ;
20+ import { emitSettingsMessages } from './utils/emit-settings-messages.util.js' ;
21+
22+ export type AIScanProgressChunk = { type : 'message' ; text : string } ;
23+
24+ export type AIScanProgressCallback = ( chunk : AIScanProgressChunk ) => void ;
2125
2226@Injectable ( )
2327export class SharedJobsService {
@@ -27,11 +31,22 @@ export class SharedJobsService {
2731 private readonly aiService : AiService ,
2832 ) { }
2933
30- public async scanDatabaseAndCreateSettingsAndWidgetsWithAI ( connection : ConnectionEntity ) : Promise < void > {
31- if ( ! connection || isTest ( ) ) {
34+ public async scanDatabaseAndCreateSettingsAndWidgetsWithAI (
35+ connection : ConnectionEntity ,
36+ onProgress ?: AIScanProgressCallback ,
37+ ) : Promise < void > {
38+ if ( ! connection ) {
3239 return ;
3340 }
41+ const emit : AIScanProgressCallback = ( chunk ) => {
42+ if ( onProgress ) {
43+ onProgress ( chunk ) ;
44+ }
45+ } ;
46+ const message = ( text : string ) : void => emit ( { type : 'message' , text } ) ;
47+
3448 console . info ( `Starting AI scan for connection with id "${ connection . id } "` ) ;
49+ message ( `Starting AI scan for connection "${ connection . title || connection . id } "` ) ;
3550 try {
3651 const existingTableSettings = await this . _dbContext . tableSettingsRepository . find ( {
3752 where : {
@@ -43,14 +58,18 @@ export class SharedJobsService {
4358
4459 const existingTableNames = new Set ( existingTableSettings . map ( ( setting ) => setting . table_name ) ) ;
4560 const dao = getDataAccessObject ( connection ) ;
61+ message ( 'Fetching tables from database' ) ;
4662 const tables : Array < TableDS > = await dao . getTablesFromDB ( ) ;
4763 const tablesToScan = tables . filter ( ( table ) => ! existingTableNames . has ( table . tableName ) ) ;
4864
4965 if ( tablesToScan . length === 0 ) {
5066 console . info ( `No new tables to scan for connection with id "${ connection . id } "` ) ;
67+ message ( 'No new tables to scan — all tables already have settings' ) ;
5168 return ;
5269 }
5370
71+ message ( `Found ${ tablesToScan . length } new ${ tablesToScan . length === 1 ? 'table' : 'tables' } to scan` ) ;
72+
5473 const queue = new PQueue ( { concurrency : 4 } ) ;
5574 const tablesInformationResults = await Promise . all (
5675 tablesToScan . map ( ( table ) =>
@@ -59,13 +78,15 @@ export class SharedJobsService {
5978 const structure = await dao . getTableStructure ( table . tableName , null ) ;
6079 const primaryColumns = await dao . getTablePrimaryColumns ( table . tableName , null ) ;
6180 const foreignKeys = await dao . getTableForeignKeys ( table . tableName , null ) ;
81+ message ( `Inspected structure of table "${ table . tableName } "` ) ;
6282 return {
6383 table_name : table . tableName ,
6484 structure,
6585 primaryColumns,
6686 foreignKeys,
6787 } ;
6888 } catch ( error ) {
89+ message ( `Failed to inspect table "${ table . tableName } ": ${ getErrorMessage ( error ) } ` ) ;
6990 console . error ( `Error getting table information for "${ table . tableName } ": ${ getErrorMessage ( error ) } ` ) ;
7091 return null ;
7192 }
@@ -77,18 +98,26 @@ export class SharedJobsService {
7798
7899 if ( tablesInformation . length === 0 ) {
79100 console . info ( `No valid tables to process for connection with id "${ connection . id } "` ) ;
101+ message ( 'No valid tables to process' ) ;
80102 return ;
81103 }
82104
83105 console . info ( `Processing ${ tablesInformation . length } tables with AI for connection "${ connection . id } "` ) ;
106+ message (
107+ `Generating settings with AI for ${ tablesInformation . length } ${ tablesInformation . length === 1 ? 'table' : 'tables' } ` ,
108+ ) ;
84109 const generatedTableSettings = await this . aiService . generateNewTableSettingsWithAI ( tablesInformation ) ;
85110
86111 if ( generatedTableSettings . length === 0 ) {
87112 console . info ( `No table settings generated by AI for connection with id "${ connection . id } "` ) ;
113+ message ( 'AI did not produce any table settings' ) ;
88114 return ;
89115 }
90116
91117 console . info ( `AI generated settings for ${ generatedTableSettings . length } tables` ) ;
118+ message (
119+ `AI returned settings for ${ generatedTableSettings . length } ${ generatedTableSettings . length === 1 ? 'table' : 'tables' } ` ,
120+ ) ;
92121
93122 const widgetsByTable = new Map < string , Array < TableWidgetEntity > > ( ) ;
94123 for ( const setting of generatedTableSettings ) {
@@ -106,6 +135,7 @@ export class SharedJobsService {
106135 const validateSettingsDS = buildValidateTableSettingsDS ( setting ) ;
107136 const errors = await dao . validateSettings ( validateSettingsDS , setting . table_name , undefined ) ;
108137 if ( errors . length > 0 ) {
138+ message ( `Validation failed for table "${ setting . table_name } ", skipping` ) ;
109139 console . error ( `Validation errors for table "${ setting . table_name } ":` , errors ) ;
110140 return null ;
111141 }
@@ -119,6 +149,7 @@ export class SharedJobsService {
119149 const savedSettings = await this . _dbContext . tableSettingsRepository . save ( settingsToSave ) ;
120150 const widgetsToSave : Array < TableWidgetEntity > = [ ] ;
121151 for ( const savedSetting of savedSettings ) {
152+ emitSettingsMessages ( savedSetting , message ) ;
122153 const widgets = widgetsByTable . get ( savedSetting . table_name ) ;
123154 if ( widgets && widgets . length > 0 ) {
124155 for ( const widget of widgets ) {
@@ -131,17 +162,22 @@ export class SharedJobsService {
131162 widgetEntity . description = widget . description || null ;
132163 widgetEntity . settings = savedSetting ;
133164 widgetsToSave . push ( widgetEntity ) ;
165+ message (
166+ `Added ${ widget . widget_type } widget for table "${ savedSetting . table_name } " on column "${ widget . field_name } "` ,
167+ ) ;
134168 }
135169 }
136170 }
137171
138172 if ( widgetsToSave . length > 0 ) {
139173 await this . _dbContext . tableWidgetsRepository . save ( widgetsToSave ) ;
140174 }
175+ message ( `Finished setup for ${ savedSettings . length } ${ savedSettings . length === 1 ? 'table' : 'tables' } ` ) ;
141176 }
142177 } catch ( error ) {
143178 console . error ( 'Error during AI scan and creation of settings/widgets: ' , error ) ;
144179 Sentry . captureException ( error ) ;
180+ throw error ;
145181 }
146182 }
147183
0 commit comments