@@ -72,41 +72,120 @@ CostOptimizationAnalyzer.prototype = {
7272 var threshold = gs . getProperty ( 'cost.analyzer.integration.threshold' , '30' ) ;
7373 var cutoffDate = gs . daysAgoStart ( parseInt ( threshold ) ) ;
7474
75- // Check REST Messages
76- var restGr = new GlideRecord ( 'sys_rest_message' ) ;
77- restGr . query ( ) ;
75+ // Check IntegrationHub Spoke usage
76+ var unusedSpokes = this . findUnusedSpokes ( cutoffDate ) ;
77+ redundantIntegrations = redundantIntegrations . concat ( unusedSpokes ) ;
7878
79- while ( restGr . next ( ) ) {
80- var lastUsed = this . getIntegrationLastUsed ( restGr . sys_id . toString ( ) , 'rest' ) ;
81- if ( lastUsed && lastUsed < cutoffDate ) {
82- redundantIntegrations . push ( {
83- name : restGr . name . toString ( ) ,
84- type : 'REST' ,
85- endpoint : restGr . endpoint . toString ( ) ,
86- lastUsed : lastUsed ,
79+ // Check SOAP Web Services
80+ var unusedSoap = this . findUnusedSoapServices ( cutoffDate ) ;
81+ redundantIntegrations = redundantIntegrations . concat ( unusedSoap ) ;
82+
83+ // Check for duplicate endpoints (still valuable)
84+ var duplicates = this . findDuplicateEndpoints ( ) ;
85+ redundantIntegrations = redundantIntegrations . concat ( duplicates ) ;
86+
87+ return redundantIntegrations ;
88+ } ,
89+
90+ findUnusedSpokes : function ( cutoffDate ) {
91+ var unusedSpokes = [ ] ;
92+
93+ // Get all installed spokes
94+ var spokeGr = new GlideRecord ( 'sys_app' ) ;
95+ spokeGr . addQuery ( 'source' , 'sn_app_store' ) ;
96+ spokeGr . addQuery ( 'name' , 'CONTAINS' , 'spoke' ) ;
97+ spokeGr . query ( ) ;
98+
99+ while ( spokeGr . next ( ) ) {
100+ var spokeId = spokeGr . sys_id . toString ( ) ;
101+ var spokeName = spokeGr . name . toString ( ) ;
102+
103+ // Check usage in ua_ih_usage table
104+ var usageGr = new GlideRecord ( 'ua_ih_usage' ) ;
105+ usageGr . addQuery ( 'spoke' , spokeId ) ;
106+ usageGr . addQuery ( 'sys_created_on' , '>=' , cutoffDate ) ;
107+ usageGr . setLimit ( 1 ) ;
108+ usageGr . query ( ) ;
109+
110+ if ( ! usageGr . hasNext ( ) ) {
111+ // No recent usage found
112+ var lastUsage = this . getLastSpokeUsage ( spokeId ) ;
113+ unusedSpokes . push ( {
114+ name : spokeName ,
115+ type : 'IntegrationHub Spoke' ,
116+ spokeId : spokeId ,
117+ lastUsed : lastUsage ,
87118 status : 'Potentially Unused'
88119 } ) ;
89120 }
90121 }
91122
92- // Check for duplicate endpoints
93- var duplicates = this . findDuplicateEndpoints ( ) ;
94- redundantIntegrations = redundantIntegrations . concat ( duplicates ) ;
123+ return unusedSpokes ;
124+ } ,
125+
126+ getLastSpokeUsage : function ( spokeId ) {
127+ var usageGr = new GlideRecord ( 'ua_ih_usage' ) ;
128+ usageGr . addQuery ( 'spoke' , spokeId ) ;
129+ usageGr . orderByDesc ( 'sys_created_on' ) ;
130+ usageGr . setLimit ( 1 ) ;
131+ usageGr . query ( ) ;
95132
96- return redundantIntegrations ;
133+ if ( usageGr . next ( ) ) {
134+ return usageGr . sys_created_on . getDisplayValue ( ) ;
135+ }
136+ return 'Never used' ;
137+ } ,
138+
139+ findUnusedSoapServices : function ( cutoffDate ) {
140+ var unusedSoap = [ ] ;
141+
142+ var soapGr = new GlideRecord ( 'sys_web_service' ) ;
143+ soapGr . query ( ) ;
144+
145+ while ( soapGr . next ( ) ) {
146+ var soapId = soapGr . sys_id . toString ( ) ;
147+ var soapName = soapGr . name . toString ( ) ;
148+
149+ // Check if SOAP service has been used recently
150+ var usageCount = this . getSoapUsageCount ( soapId , cutoffDate ) ;
151+ if ( usageCount === 0 ) {
152+ unusedSoap . push ( {
153+ name : soapName ,
154+ type : 'SOAP Web Service' ,
155+ endpoint : soapGr . endpoint . toString ( ) ,
156+ lastUsed : this . getLastSoapUsage ( soapId ) ,
157+ status : 'Potentially Unused'
158+ } ) ;
159+ }
160+ }
161+
162+ return unusedSoap ;
163+ } ,
164+
165+ getSoapUsageCount : function ( soapId , cutoffDate ) {
166+ var usageGr = new GlideAggregate ( 'sys_soap_log' ) ;
167+ usageGr . addQuery ( 'web_service' , soapId ) ;
168+ usageGr . addQuery ( 'sys_created_on' , '>=' , cutoffDate ) ;
169+ usageGr . addAggregate ( 'COUNT' ) ;
170+ usageGr . query ( ) ;
171+
172+ if ( usageGr . next ( ) ) {
173+ return parseInt ( usageGr . getAggregate ( 'COUNT' ) ) ;
174+ }
175+ return 0 ;
97176 } ,
98177
99- getIntegrationLastUsed : function ( integrationId , type ) {
100- var logGr = new GlideRecord ( 'syslog ' ) ;
101- logGr . addQuery ( 'message ' , 'CONTAINS' , integrationId ) ;
178+ getLastSoapUsage : function ( soapId ) {
179+ var logGr = new GlideRecord ( 'sys_soap_log ' ) ;
180+ logGr . addQuery ( 'web_service ' , soapId ) ;
102181 logGr . orderByDesc ( 'sys_created_on' ) ;
103182 logGr . setLimit ( 1 ) ;
104183 logGr . query ( ) ;
105184
106185 if ( logGr . next ( ) ) {
107186 return logGr . sys_created_on . getDisplayValue ( ) ;
108187 }
109- return null ;
188+ return 'Never used' ;
110189 } ,
111190
112191 findDuplicateEndpoints : function ( ) {
@@ -147,7 +226,7 @@ CostOptimizationAnalyzer.prototype = {
147226 var recordCount = this . getTableRecordCount ( tableName ) ;
148227
149228 if ( recordCount > parseInt ( threshold ) ) {
150- var sizeInfo = this . estimateTableSize ( tableName , recordCount ) ;
229+ var sizeInfo = this . getActualTableSize ( tableName , recordCount ) ;
151230 oversizedTables . push ( {
152231 table : tableName ,
153232 recordCount : recordCount ,
@@ -162,7 +241,7 @@ CostOptimizationAnalyzer.prototype = {
162241 systemTables . forEach ( function ( tableName ) {
163242 var recordCount = this . getTableRecordCount ( tableName ) ;
164243 if ( recordCount > parseInt ( threshold ) ) {
165- var sizeInfo = this . estimateTableSize ( tableName , recordCount ) ;
244+ var sizeInfo = this . getActualTableSize ( tableName , recordCount ) ;
166245 oversizedTables . push ( {
167246 table : tableName ,
168247 recordCount : recordCount ,
@@ -190,9 +269,25 @@ CostOptimizationAnalyzer.prototype = {
190269 return 0 ;
191270 } ,
192271
193- estimateTableSize : function ( tableName , recordCount ) {
194- var avgRecordSize = 2 ; // KB per record (estimate)
195- var sizeGB = ( recordCount * avgRecordSize ) / ( 1024 * 1024 ) ;
272+ getActualTableSize : function ( tableName , recordCount ) {
273+ var sizeGB = 0 ;
274+
275+ // Get actual table size from sys_physical_table_stats
276+ var statsGr = new GlideRecord ( 'sys_physical_table_stats' ) ;
277+ statsGr . addQuery ( 'table_name' , tableName ) ;
278+ statsGr . orderByDesc ( 'sys_created_on' ) ;
279+ statsGr . setLimit ( 1 ) ;
280+ statsGr . query ( ) ;
281+
282+ if ( statsGr . next ( ) ) {
283+ // Convert bytes to GB
284+ var sizeBytes = parseInt ( statsGr . size_bytes || 0 ) ;
285+ sizeGB = sizeBytes / ( 1024 * 1024 * 1024 ) ;
286+ } else {
287+ // Fallback to estimate if stats not available
288+ var avgRecordSize = 2 ; // KB per record (estimate)
289+ sizeGB = ( recordCount * avgRecordSize ) / ( 1024 * 1024 ) ;
290+ }
196291
197292 var recommendation = 'Consider archiving old records' ;
198293 if ( tableName === 'sys_audit' ) {
@@ -235,4 +330,4 @@ CostOptimizationAnalyzer.prototype = {
235330 } ,
236331
237332 type : 'CostOptimizationAnalyzer'
238- } ;
333+ }
0 commit comments