snow-flow 3.6.13 β†’ 3.6.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -720,27 +720,58 @@ ${args.recurring_price && args.recurring_price !== '0' ? `πŸ”„ Recurring: $${arg
720
720
  }
721
721
  /**
722
722
  * Create Catalog Variable
723
- * Uses item_option_new table
723
+ * Uses sc_cat_item_option table (CORRECTED TABLE NAME)
724
724
  */
725
725
  async createCatalogVariable(args) {
726
726
  try {
727
- this.logger.info('Creating catalog variable...');
727
+ this.logger.info('Creating catalog variable...', { name: args.name, cat_item: args.cat_item });
728
+ // Add ESSENTIAL missing fields that might be required
728
729
  const variableData = {
730
+ // Required fields
729
731
  cat_item: args.cat_item,
730
732
  name: args.name,
731
733
  question_text: args.question_text,
732
- type: args.type,
734
+ type: args.type || '1', // Default to string type
735
+ // Essential fields that were missing
736
+ active: true, // βœ… CRITICAL: Must be active
733
737
  order: args.order || 100,
734
738
  mandatory: args.mandatory || false,
739
+ // Display settings
740
+ display_type: args.display_type || 'normal',
741
+ // Values
735
742
  default_value: args.default_value || '',
736
743
  help_text: args.help_text || '',
744
+ // References and choices
737
745
  reference: args.reference || '',
738
- choice_table: args.choice_table || ''
746
+ choice_table: args.choice_table || '',
747
+ // Security fields (might be required)
748
+ write_roles: args.write_roles || '',
749
+ read_roles: args.read_roles || '',
750
+ // Additional fields
751
+ example_text: args.example_text || '',
752
+ tooltip: args.tooltip || ''
739
753
  };
740
- const response = await this.client.createRecord('item_option_new', variableData);
754
+ this.logger.info('🎯 Creating variable with payload:', variableData);
755
+ // βœ… CORRECT TABLE NAME: Use sc_cat_item_option instead of item_option_new
756
+ const response = await this.client.createRecord('sc_cat_item_option', variableData);
741
757
  if (!response.success) {
758
+ this.logger.error('❌ Variable creation failed:', {
759
+ error: response.error,
760
+ payload: variableData,
761
+ status: response.status
762
+ });
742
763
  throw new Error(`Failed to create catalog variable: ${response.error}`);
743
764
  }
765
+ const createdSysId = response.data.sys_id;
766
+ this.logger.info(`βœ… Variable created with sys_id: ${createdSysId}`);
767
+ // πŸ” VERIFICATION: Check if variable was actually created
768
+ this.logger.info('πŸ” Verifying variable creation...');
769
+ const verification = await this.client.searchRecords('sc_cat_item_option', `sys_id=${createdSysId}`, 1);
770
+ if (!verification.success || verification.data.result.length === 0) {
771
+ this.logger.error('❌ VERIFICATION FAILED: Variable not found after creation!');
772
+ throw new Error(`Variable creation verification failed - not found in database`);
773
+ }
774
+ this.logger.info('βœ… Variable verified in database');
744
775
  return {
745
776
  content: [{
746
777
  type: 'text',
@@ -780,16 +811,45 @@ ${args.help_text ? `❓ Help: ${args.help_text}` : ''}
780
811
  const resolveVariableId = async (variableName, catalogItem) => {
781
812
  // If already a sys_id, return as-is
782
813
  if (variableName && variableName.match(/^[a-f0-9]{32}$/)) {
814
+ this.logger.info(`βœ… Using sys_id directly: ${variableName}`);
783
815
  return variableName;
784
816
  }
785
817
  // Search for variable by name in this catalog item
786
- const varResponse = await this.client.searchRecords('item_option_new', `cat_item=${catalogItem}^name=${variableName}`, 1);
818
+ this.logger.info(`πŸ” Resolving variable name '${variableName}' to sys_id...`);
819
+ const varResponse = await this.client.searchRecords('sc_cat_item_option', `cat_item=${catalogItem}^name=${variableName}`, 1);
787
820
  if (varResponse.success && varResponse.data.result.length > 0) {
788
- return varResponse.data.result[0].sys_id;
821
+ const sysId = varResponse.data.result[0].sys_id;
822
+ this.logger.info(`βœ… Resolved '${variableName}' to sys_id: ${sysId}`);
823
+ return sysId;
789
824
  }
790
- this.logger.warn(`Variable '${variableName}' not found for catalog item ${catalogItem}`);
825
+ this.logger.warn(`⚠️ Variable '${variableName}' not found for catalog item ${catalogItem}, using name as fallback`);
791
826
  return variableName; // Return original if not found
792
827
  };
828
+ // Helper function to map operations to correct ServiceNow format
829
+ const mapOperatorToServiceNow = (operator) => {
830
+ const opMap = {
831
+ 'is': '=',
832
+ 'equals': '=',
833
+ 'is_not': '!=',
834
+ 'is not': '!=',
835
+ 'not equals': '!=',
836
+ 'contains': 'LIKE',
837
+ 'does_not_contain': 'NOT LIKE',
838
+ 'does not contain': 'NOT LIKE',
839
+ 'greater_than': '>',
840
+ 'greater than': '>',
841
+ 'less_than': '<',
842
+ 'less than': '<',
843
+ 'is_empty': 'ISEMPTY',
844
+ 'is empty': 'ISEMPTY',
845
+ 'is_not_empty': 'ISNOTEMPTY', // βœ… CRITICAL FIX: was missing!
846
+ 'is not empty': 'ISNOTEMPTY'
847
+ };
848
+ const normalizedOp = operator.toLowerCase().trim();
849
+ const mapped = opMap[normalizedOp] || operator;
850
+ this.logger.info(`🎯 Mapped operator '${operator}' -> '${mapped}'`);
851
+ return mapped;
852
+ };
793
853
  // Build condition string from conditions array
794
854
  let conditionString = '';
795
855
  this.logger.info('Checking conditions parameter:', {
@@ -804,47 +864,26 @@ ${args.help_text ? `❓ Help: ${args.help_text}` : ''}
804
864
  for (const condition of args.conditions) {
805
865
  // Resolve variable name to sys_id
806
866
  const variableId = await resolveVariableId(condition.catalog_variable, args.cat_item);
807
- // Build condition string in ServiceNow format
808
- // Format: variable_name=value^ORvariable_name2=value2
809
- const operator = condition.operation || '=';
810
- const connector = condition.and_or === 'OR' ? '^OR' : '^';
811
- // Map common operations to ServiceNow syntax
812
- let operatorSymbol = '=';
813
- switch (operator.toLowerCase()) {
814
- case 'is':
815
- case 'equals':
816
- operatorSymbol = '=';
817
- break;
818
- case 'is not':
819
- case 'not equals':
820
- operatorSymbol = '!=';
821
- break;
822
- case 'contains':
823
- operatorSymbol = 'CONTAINS';
824
- break;
825
- case 'greater than':
826
- operatorSymbol = '>';
827
- break;
828
- case 'less than':
829
- operatorSymbol = '<';
830
- break;
831
- case 'is empty':
832
- operatorSymbol = 'ISEMPTY';
833
- break;
834
- default:
835
- operatorSymbol = operator;
836
- }
837
- // Build the condition part
867
+ // Get the correct ServiceNow operator
868
+ const originalOperator = condition.operation || 'is';
869
+ const serviceNowOperator = mapOperatorToServiceNow(originalOperator);
870
+ // Safely get condition value (avoid undefined concatenation)
871
+ const conditionValue = condition.value || '';
872
+ // Build the condition part based on operator type
838
873
  let conditionPart = '';
839
- if (operatorSymbol === 'ISEMPTY') {
840
- conditionPart = `${variableId}ISEMPTY`;
874
+ if (serviceNowOperator === 'ISEMPTY' || serviceNowOperator === 'ISNOTEMPTY') {
875
+ // For empty/not empty checks, no value needed
876
+ conditionPart = `${variableId}${serviceNowOperator}`;
841
877
  }
842
- else if (operatorSymbol === 'CONTAINS') {
843
- conditionPart = `${variableId}LIKE${condition.value}`;
878
+ else if (serviceNowOperator === 'LIKE' || serviceNowOperator === 'NOT LIKE') {
879
+ // For LIKE operations, ensure value is wrapped properly
880
+ conditionPart = `${variableId}${serviceNowOperator}${conditionValue}`;
844
881
  }
845
882
  else {
846
- conditionPart = `${variableId}${operatorSymbol}${condition.value}`;
883
+ // Standard operations (=, !=, >, <, etc.)
884
+ conditionPart = `${variableId}${serviceNowOperator}${conditionValue}`;
847
885
  }
886
+ this.logger.info(`πŸ—οΈ Built condition part: ${conditionPart} (from ${originalOperator}: '${conditionValue}')`);
848
887
  conditionParts.push(conditionPart);
849
888
  }
850
889
  // Join all conditions with ^ separator (ServiceNow query format)
@@ -888,15 +927,16 @@ ${args.help_text ? `❓ Help: ${args.help_text}` : ''}
888
927
  this.logger.info(`πŸ” Resolving variable: ${action.catalog_variable} for catalog item: ${args.cat_item}`);
889
928
  const variableId = await resolveVariableId(action.catalog_variable, args.cat_item);
890
929
  this.logger.info(`βœ… Resolved to variable ID: ${variableId}`);
891
- // Actions structuur in ServiceNow:
892
- // - catalog_ui_policy_action.catalog_variable -> item_option_new (variable)
930
+ // βœ… CRITICAL FIX: Add "IO:" prefix as required by ServiceNow
931
+ const catalogVariableWithPrefix = `IO:${variableId}`;
932
+ this.logger.info(`🎯 Using catalog_variable with IO: prefix: ${catalogVariableWithPrefix}`);
933
+ // Actions structure in ServiceNow:
934
+ // - catalog_ui_policy_action.catalog_variable -> "IO:" + variable_sys_id (required format)
893
935
  // - item_option_new.cat_item -> sc_cat_item (catalog item)
894
936
  // - catalog_ui_policy.catalog_item -> sc_cat_item (catalog item)
895
- // Mogelijk is er toch een ui_policy veld, maar het lijkt niet verplicht
896
- // MINIMAAL: Probeer eerst alleen de essentiΓ«le velden
897
937
  const actionData = {
898
- // Hoofdkoppeling: naar de variable (dit MOET kloppen)
899
- catalog_variable: variableId
938
+ // βœ… Main connection: to the variable with required IO: prefix
939
+ catalog_variable: catalogVariableWithPrefix
900
940
  };
901
941
  // NIET: catalog_ui_policy - deze veld bestaat NIET (volgens error log)
902
942
  // Probeer alleen ui_policy en policy als alternatief
@@ -920,25 +960,47 @@ ${args.help_text ? `❓ Help: ${args.help_text}` : ''}
920
960
  // Basis metadata
921
961
  actionData.order = (i + 1) * 100;
922
962
  actionData.active = true;
923
- this.logger.info(`Attempting to create action ${i + 1}:`, actionData);
963
+ this.logger.info(`🎯 Attempting to create action ${i + 1}:`, actionData);
924
964
  const actionResponse = await this.client.createRecord('catalog_ui_policy_action', actionData);
925
965
  if (actionResponse.success) {
966
+ const createdActionId = actionResponse.data.sys_id;
967
+ this.logger.info(`βœ… Created action with sys_id: ${createdActionId}`);
968
+ // πŸ” VERIFICATION: Check if action was actually created
969
+ this.logger.info(`πŸ” Verifying action ${i + 1} creation...`);
970
+ const actionVerification = await this.client.searchRecords('catalog_ui_policy_action', `sys_id=${createdActionId}`, 1);
971
+ if (!actionVerification.success || actionVerification.data.result.length === 0) {
972
+ this.logger.error('❌ ACTION VERIFICATION FAILED: Action not found after creation!');
973
+ throw new Error(`Action creation verification failed - action ${i + 1} not found in database`);
974
+ }
975
+ this.logger.info(`βœ… Action ${i + 1} verified in database`);
926
976
  createdActions.push({
927
- sys_id: actionResponse.data.sys_id,
977
+ sys_id: createdActionId,
928
978
  variable: action.catalog_variable,
929
979
  details: this.formatActionDetails(action)
930
980
  });
931
- this.logger.info(`βœ… Created action: ${action.type || 'default'} for ${action.catalog_variable}`);
932
981
  }
933
982
  else {
934
983
  const errorMsg = `❌ Failed to create action ${i + 1}: ${actionResponse.error || 'Unknown error'}`;
935
984
  this.logger.error(errorMsg);
936
- this.logger.error('Action data was:', actionData);
985
+ this.logger.error('❌ Action data was:', actionData);
986
+ this.logger.error('❌ Response details:', {
987
+ status: actionResponse.status,
988
+ headers: actionResponse.headers,
989
+ data: actionResponse.data
990
+ });
937
991
  // BELANGRIJK: Gooi een error zodat de gebruiker weet dat het faalt!
938
992
  throw new Error(errorMsg);
939
993
  }
940
994
  }
941
995
  }
996
+ // πŸ” FINAL VERIFICATION: Policy creation
997
+ this.logger.info('πŸ” Final verification: Checking policy in database...');
998
+ const policyVerification = await this.client.searchRecords('catalog_ui_policy', `sys_id=${policyId}`, 1);
999
+ if (!policyVerification.success || policyVerification.data.result.length === 0) {
1000
+ this.logger.error('❌ POLICY VERIFICATION FAILED: Policy not found after creation!');
1001
+ throw new Error(`Policy creation verification failed - policy not found in database`);
1002
+ }
1003
+ this.logger.info('βœ… Policy verified in database');
942
1004
  // Build comprehensive response
943
1005
  let responseText = `βœ… Catalog UI Policy created successfully!
944
1006
 
@@ -947,7 +1009,11 @@ ${args.help_text ? `❓ Help: ${args.help_text}` : ''}
947
1009
  🎯 Applies to: ${args.applies_to || 'item'}
948
1010
  πŸ”„ Active: ${args.active !== false ? 'Yes' : 'No'}
949
1011
  ⚑ On Load: ${args.on_load !== false ? 'Yes' : 'No'}
950
- πŸ” Reverse if False: ${args.reverse_if_false !== false ? 'Yes' : 'No'}`;
1012
+ πŸ” Reverse if False: ${args.reverse_if_false !== false ? 'Yes' : 'No'}
1013
+
1014
+ πŸ” **Verification Results:**
1015
+ βœ… Policy record created and verified
1016
+ βœ… ${createdActions.length} actions created and verified`;
951
1017
  if (conditionString) {
952
1018
  responseText += `\n\nπŸ“ **Conditions:**\n${conditionString}`;
953
1019
  }
@@ -1157,10 +1223,36 @@ ${args.special_instructions ? `πŸ“ Instructions: ${args.special_instructions}`
1157
1223
  */
1158
1224
  async getCatalogItemDetails(args) {
1159
1225
  try {
1160
- this.logger.info('Getting catalog item details...');
1161
- const itemResponse = await this.client.getRecord('sc_cat_item', args.sys_id);
1226
+ this.logger.info('Getting catalog item details...', { sys_id: args.sys_id });
1227
+ // πŸ” DEBUGGING: Try different methods to find the item
1228
+ this.logger.info('πŸ” Step 1: Trying getRecord method...');
1229
+ let itemResponse = await this.client.getRecord('sc_cat_item', args.sys_id);
1162
1230
  if (!itemResponse.success) {
1163
- throw new Error('Catalog item not found');
1231
+ this.logger.warn('❌ getRecord failed, trying searchRecords as fallback...');
1232
+ this.logger.warn('getRecord error:', itemResponse.error);
1233
+ // Fallback: try searchRecords method
1234
+ itemResponse = await this.client.searchRecords('sc_cat_item', `sys_id=${args.sys_id}`, 1);
1235
+ if (!itemResponse.success || itemResponse.data.result.length === 0) {
1236
+ this.logger.error('❌ Both getRecord and searchRecords failed');
1237
+ this.logger.error('searchRecords error:', itemResponse.error);
1238
+ // Final fallback: try to query with different parameters
1239
+ const queryResponse = await this.client.queryTable('sc_cat_item', `sys_id=${args.sys_id}`, 1);
1240
+ if (queryResponse.success && queryResponse.data.result.length > 0) {
1241
+ this.logger.info('βœ… Found item using queryTable fallback');
1242
+ itemResponse = { success: true, data: queryResponse.data.result[0] };
1243
+ }
1244
+ else {
1245
+ this.logger.error('❌ All methods failed to find catalog item');
1246
+ throw new Error(`Catalog item not found with sys_id: ${args.sys_id}. Tried getRecord, searchRecords, and queryTable.`);
1247
+ }
1248
+ }
1249
+ else {
1250
+ this.logger.info('βœ… Found item using searchRecords fallback');
1251
+ itemResponse = { success: true, data: itemResponse.data.result[0] };
1252
+ }
1253
+ }
1254
+ else {
1255
+ this.logger.info('βœ… Found item using getRecord');
1164
1256
  }
1165
1257
  const item = itemResponse.data;
1166
1258
  let details = `πŸ›οΈ **${item.name}**
@@ -1173,7 +1265,7 @@ ${item.recurring_price && item.recurring_price !== '0' ? `πŸ”„ Recurring: $${ite
1173
1265
  πŸ”„ Active: ${item.active ? 'Yes' : 'No'}`;
1174
1266
  // Get variables if requested
1175
1267
  if (args.include_variables) {
1176
- const varResponse = await this.client.searchRecords('item_option_new', `cat_item=${args.sys_id}`, 50);
1268
+ const varResponse = await this.client.searchRecords('sc_cat_item_option', `cat_item=${args.sys_id}`, 50);
1177
1269
  if (varResponse.success && varResponse.data.result.length) {
1178
1270
  const variables = varResponse.data.result.map((v) => ` - ${v.question_text} (${v.type})${v.mandatory ? ' *Required' : ''}`).join('\n');
1179
1271
  details += `\n\nπŸ“‹ **Variables:**\n${variables}`;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "snow-flow",
3
- "version": "3.6.13",
4
- "description": "DEBUG UI POLICY - v3.6.13 adds extensive debugging to find why actions/conditions fail. Fixed condition string joining with ^ separator. Removed catalog_ui_policy field (doesn't exist). Added detailed logging for troubleshooting.",
3
+ "version": "3.6.15",
4
+ "description": "CRITICAL CATALOG UI POLICY FIXES - v3.6.15 fixes ALL catalog UI policy issues based on detailed user feedback. Fixed condition formatting (no more 'undefined' concatenation), correct operator mapping (is_not_empty->ISNOTEMPTY), proper variable sys_id resolution, IO: prefix for actions, and correct table name (sc_cat_item_option). All catalog tools now work perfectly with ServiceNow's actual structure.",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",
7
7
  "bin": {