snow-flow 4.3.8 ā 4.3.10
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.
- package/dist/mcp/servicenow-knowledge-catalog-mcp.js +660 -667
- package/package.json +1 -1
- package/website/index.html +82 -50
|
@@ -798,7 +798,7 @@ ${args.recurring_price && args.recurring_price !== '0' ? `š Recurring: $${arg
|
|
|
798
798
|
this.logger.error('ā Variable creation failed:', {
|
|
799
799
|
error: response.error,
|
|
800
800
|
payload: variableData,
|
|
801
|
-
|
|
801
|
+
success: response.success
|
|
802
802
|
});
|
|
803
803
|
throw new Error(`Failed to create catalog variable: ${response.error}`);
|
|
804
804
|
}
|
|
@@ -843,435 +843,434 @@ ${args.help_text ? `ā Help: ${args.help_text}` : ''}
|
|
|
843
843
|
this.logger.info('šÆ SIMPLIFIED: Creating catalog UI policy with working approach...');
|
|
844
844
|
// Simplified approach - focus on what actually works in ServiceNow
|
|
845
845
|
try {
|
|
846
|
+
this.logger.info('Creating comprehensive catalog UI policy...');
|
|
847
|
+
// First, let's fetch ALL variables for this catalog item for debugging
|
|
848
|
+
this.logger.info(`š Fetching all variables for catalog item ${args.cat_item} for debugging...`);
|
|
846
849
|
try {
|
|
847
|
-
this.
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
this.logger.info(`š Found ${allVarsResponse.data.result.length} variables for this catalog item:`);
|
|
854
|
-
allVarsResponse.data.result.forEach((v) => {
|
|
855
|
-
this.logger.info(` - Variable: name='${v.name}', sys_id='${v.sys_id}', question='${v.question_text}'`);
|
|
856
|
-
});
|
|
857
|
-
}
|
|
858
|
-
else {
|
|
859
|
-
this.logger.warn(`ā ļø No variables found for catalog item ${args.cat_item} - this might be a problem!`);
|
|
860
|
-
}
|
|
850
|
+
const allVarsResponse = await this.client.searchRecords('sc_cat_item_option', `cat_item=${args.cat_item}`, 100);
|
|
851
|
+
if (allVarsResponse.success && allVarsResponse.data.result.length > 0) {
|
|
852
|
+
this.logger.info(`š Found ${allVarsResponse.data.result.length} variables for this catalog item:`);
|
|
853
|
+
allVarsResponse.data.result.forEach((v) => {
|
|
854
|
+
this.logger.info(` - Variable: name='${v.name}', sys_id='${v.sys_id}', question='${v.question_text}'`);
|
|
855
|
+
});
|
|
861
856
|
}
|
|
862
|
-
|
|
863
|
-
this.logger.
|
|
857
|
+
else {
|
|
858
|
+
this.logger.warn(`ā ļø No variables found for catalog item ${args.cat_item} - this might be a problem!`);
|
|
864
859
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
860
|
+
}
|
|
861
|
+
catch (error) {
|
|
862
|
+
this.logger.error(`ā Failed to fetch variables for debugging:`, error);
|
|
863
|
+
}
|
|
864
|
+
// Helper function to resolve variable names to sys_ids with MULTIPLE FALLBACKS
|
|
865
|
+
const resolveVariableId = async (variableName, catalogItem) => {
|
|
866
|
+
// If already a sys_id, return as-is
|
|
867
|
+
if (variableName && variableName.match(/^[a-f0-9]{32}$/)) {
|
|
868
|
+
this.logger.info(`ā
Using sys_id directly: ${variableName}`);
|
|
869
|
+
return variableName;
|
|
870
|
+
}
|
|
871
|
+
this.logger.info(`š Resolving variable name '${variableName}' to sys_id for catalog item ${catalogItem}...`);
|
|
872
|
+
// Try multiple search strategies for maximum compatibility
|
|
873
|
+
const searchStrategies = [
|
|
874
|
+
// Strategy 1: Search by name field
|
|
875
|
+
{
|
|
876
|
+
query: `cat_item=${catalogItem}^name=${variableName}`,
|
|
877
|
+
description: 'by name field'
|
|
878
|
+
},
|
|
879
|
+
// Strategy 2: Search by name with LIKE operator
|
|
880
|
+
{
|
|
881
|
+
query: `cat_item=${catalogItem}^nameLIKE${variableName}`,
|
|
882
|
+
description: 'by name with LIKE'
|
|
883
|
+
},
|
|
884
|
+
// Strategy 3: Search by question_text
|
|
885
|
+
{
|
|
886
|
+
query: `cat_item=${catalogItem}^question_text=${variableName}`,
|
|
887
|
+
description: 'by question_text field'
|
|
888
|
+
},
|
|
889
|
+
// Strategy 4: Search all variables for this item and match manually
|
|
890
|
+
{
|
|
891
|
+
query: `cat_item=${catalogItem}`,
|
|
892
|
+
description: 'all variables for manual matching'
|
|
871
893
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
query: `cat_item=${catalogItem}^question_text=${variableName}`,
|
|
888
|
-
description: 'by question_text field'
|
|
889
|
-
},
|
|
890
|
-
// Strategy 4: Search all variables for this item and match manually
|
|
891
|
-
{
|
|
892
|
-
query: `cat_item=${catalogItem}`,
|
|
893
|
-
description: 'all variables for manual matching'
|
|
894
|
-
}
|
|
895
|
-
];
|
|
896
|
-
for (const strategy of searchStrategies) {
|
|
897
|
-
this.logger.info(`š Trying strategy: ${strategy.description}`);
|
|
898
|
-
try {
|
|
899
|
-
const varResponse = await this.client.searchRecords('sc_cat_item_option', strategy.query, 50 // Get more results for manual matching
|
|
900
|
-
);
|
|
901
|
-
if (varResponse.success && varResponse.data.result.length > 0) {
|
|
902
|
-
// For the "all variables" strategy, try to match manually
|
|
903
|
-
if (strategy.description === 'all variables for manual matching') {
|
|
904
|
-
const match = varResponse.data.result.find((v) => v.name === variableName ||
|
|
905
|
-
v.question_text === variableName ||
|
|
906
|
-
v.name?.toLowerCase() === variableName.toLowerCase());
|
|
907
|
-
if (match) {
|
|
908
|
-
this.logger.info(`ā
Found variable through manual matching: ${match.sys_id}`);
|
|
909
|
-
return match.sys_id;
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
else {
|
|
913
|
-
// Direct match found
|
|
914
|
-
const sysId = varResponse.data.result[0].sys_id;
|
|
915
|
-
this.logger.info(`ā
Resolved '${variableName}' to sys_id: ${sysId} (${strategy.description})`);
|
|
916
|
-
return sysId;
|
|
894
|
+
];
|
|
895
|
+
for (const strategy of searchStrategies) {
|
|
896
|
+
this.logger.info(`š Trying strategy: ${strategy.description}`);
|
|
897
|
+
try {
|
|
898
|
+
const varResponse = await this.client.searchRecords('sc_cat_item_option', strategy.query, 50 // Get more results for manual matching
|
|
899
|
+
);
|
|
900
|
+
if (varResponse.success && varResponse.data.result.length > 0) {
|
|
901
|
+
// For the "all variables" strategy, try to match manually
|
|
902
|
+
if (strategy.description === 'all variables for manual matching') {
|
|
903
|
+
const match = varResponse.data.result.find((v) => v.name === variableName ||
|
|
904
|
+
v.question_text === variableName ||
|
|
905
|
+
v.name?.toLowerCase() === variableName.toLowerCase());
|
|
906
|
+
if (match) {
|
|
907
|
+
this.logger.info(`ā
Found variable through manual matching: ${match.sys_id}`);
|
|
908
|
+
return match.sys_id;
|
|
917
909
|
}
|
|
918
910
|
}
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
this.logger.warn(`ā ļø Strategy failed: ${strategy.description}`, error);
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
// If all strategies fail, try alternate table names (just in case)
|
|
925
|
-
const alternateTables = ['item_option_new', 'io_set_item_option'];
|
|
926
|
-
for (const table of alternateTables) {
|
|
927
|
-
this.logger.info(`š Trying alternate table: ${table}`);
|
|
928
|
-
try {
|
|
929
|
-
const varResponse = await this.client.searchRecords(table, `cat_item=${catalogItem}^name=${variableName}`, 1);
|
|
930
|
-
if (varResponse.success && varResponse.data.result.length > 0) {
|
|
911
|
+
else {
|
|
912
|
+
// Direct match found
|
|
931
913
|
const sysId = varResponse.data.result[0].sys_id;
|
|
932
|
-
this.logger.info(`ā
|
|
914
|
+
this.logger.info(`ā
Resolved '${variableName}' to sys_id: ${sysId} (${strategy.description})`);
|
|
933
915
|
return sysId;
|
|
934
916
|
}
|
|
935
917
|
}
|
|
936
|
-
catch (error) {
|
|
937
|
-
this.logger.warn(`ā ļø Alternate table ${table} failed:`, error);
|
|
938
|
-
}
|
|
939
918
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
const
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
'does_not_contain': 'NOT LIKE',
|
|
955
|
-
'does not contain': 'NOT LIKE',
|
|
956
|
-
'greater_than': '>',
|
|
957
|
-
'greater than': '>',
|
|
958
|
-
'less_than': '<',
|
|
959
|
-
'less than': '<',
|
|
960
|
-
'is_empty': 'ISEMPTY',
|
|
961
|
-
'is empty': 'ISEMPTY',
|
|
962
|
-
'is_not_empty': 'ISNOTEMPTY', // ā
CRITICAL FIX: was missing!
|
|
963
|
-
'is not empty': 'ISNOTEMPTY'
|
|
964
|
-
};
|
|
965
|
-
const normalizedOp = operator.toLowerCase().trim();
|
|
966
|
-
const mapped = opMap[normalizedOp] || operator;
|
|
967
|
-
this.logger.info(`šÆ Mapped operator '${operator}' -> '${mapped}'`);
|
|
968
|
-
return mapped;
|
|
969
|
-
};
|
|
970
|
-
// Build condition string from conditions array
|
|
971
|
-
let conditionString = '';
|
|
972
|
-
this.logger.info('Checking conditions parameter:', {
|
|
973
|
-
hasConditions: !!args.conditions,
|
|
974
|
-
isArray: Array.isArray(args.conditions),
|
|
975
|
-
length: args.conditions ? args.conditions.length : 0,
|
|
976
|
-
conditionsData: args.conditions
|
|
977
|
-
});
|
|
978
|
-
if (args.conditions && Array.isArray(args.conditions)) {
|
|
979
|
-
this.logger.info(`šÆ Building conditions string from ${args.conditions.length} conditions...`);
|
|
980
|
-
const conditionParts = [];
|
|
981
|
-
for (const condition of args.conditions) {
|
|
982
|
-
// Resolve variable name to sys_id
|
|
983
|
-
const variableId = await resolveVariableId(condition.catalog_variable, args.cat_item);
|
|
984
|
-
// Get the correct ServiceNow operator
|
|
985
|
-
const originalOperator = condition.operation || 'is';
|
|
986
|
-
const serviceNowOperator = mapOperatorToServiceNow(originalOperator);
|
|
987
|
-
// Safely get condition value (avoid undefined concatenation)
|
|
988
|
-
const conditionValue = condition.value || '';
|
|
989
|
-
// Build the condition part based on operator type
|
|
990
|
-
// ā
CRITICAL: Conditions must use IO:sys_id format for catalog variables!
|
|
991
|
-
// BUT only if we successfully resolved the variable to a sys_id
|
|
992
|
-
const isValidSysId = variableId && variableId.match(/^[a-f0-9]{32}$/);
|
|
993
|
-
const variableWithIOPrefix = isValidSysId ? `IO:${variableId}` : variableId;
|
|
994
|
-
if (!isValidSysId) {
|
|
995
|
-
this.logger.warn(`ā ļø Variable '${condition.catalog_variable}' could not be resolved to sys_id - condition may fail!`);
|
|
996
|
-
}
|
|
997
|
-
let conditionPart = '';
|
|
998
|
-
if (serviceNowOperator === 'ISEMPTY' || serviceNowOperator === 'ISNOTEMPTY') {
|
|
999
|
-
// For empty/not empty checks, no value needed
|
|
1000
|
-
conditionPart = `${variableWithIOPrefix}${serviceNowOperator}`;
|
|
1001
|
-
}
|
|
1002
|
-
else if (serviceNowOperator === 'LIKE' || serviceNowOperator === 'NOT LIKE') {
|
|
1003
|
-
// For LIKE operations, ensure value is wrapped properly
|
|
1004
|
-
conditionPart = `${variableWithIOPrefix}${serviceNowOperator}${conditionValue}`;
|
|
1005
|
-
}
|
|
1006
|
-
else {
|
|
1007
|
-
// Standard operations (=, !=, >, <, etc.)
|
|
1008
|
-
conditionPart = `${variableWithIOPrefix}${serviceNowOperator}${conditionValue}`;
|
|
919
|
+
catch (error) {
|
|
920
|
+
this.logger.warn(`ā ļø Strategy failed: ${strategy.description}`, error);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
// If all strategies fail, try alternate table names (just in case)
|
|
924
|
+
const alternateTables = ['item_option_new', 'io_set_item_option'];
|
|
925
|
+
for (const table of alternateTables) {
|
|
926
|
+
this.logger.info(`š Trying alternate table: ${table}`);
|
|
927
|
+
try {
|
|
928
|
+
const varResponse = await this.client.searchRecords(table, `cat_item=${catalogItem}^name=${variableName}`, 1);
|
|
929
|
+
if (varResponse.success && varResponse.data.result.length > 0) {
|
|
930
|
+
const sysId = varResponse.data.result[0].sys_id;
|
|
931
|
+
this.logger.info(`ā
Found in alternate table ${table}: ${sysId}`);
|
|
932
|
+
return sysId;
|
|
1009
933
|
}
|
|
1010
|
-
this.logger.info(`šļø Built condition part: ${conditionPart} (from ${originalOperator}: '${conditionValue}')`);
|
|
1011
|
-
conditionParts.push(conditionPart);
|
|
1012
934
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
935
|
+
catch (error) {
|
|
936
|
+
this.logger.warn(`ā ļø Alternate table ${table} failed:`, error);
|
|
937
|
+
}
|
|
1016
938
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
939
|
+
this.logger.error(`ā CRITICAL: Variable '${variableName}' not found in any table for catalog item ${catalogItem}`);
|
|
940
|
+
this.logger.error(`ā This will cause the action to fail! Please use the sys_id directly instead of the name.`);
|
|
941
|
+
// Return the name but log a warning that this will fail
|
|
942
|
+
return variableName;
|
|
943
|
+
};
|
|
944
|
+
// Helper function to map operations to correct ServiceNow format
|
|
945
|
+
const mapOperatorToServiceNow = (operator) => {
|
|
946
|
+
const opMap = {
|
|
947
|
+
'is': '=',
|
|
948
|
+
'equals': '=',
|
|
949
|
+
'is_not': '!=',
|
|
950
|
+
'is not': '!=',
|
|
951
|
+
'not equals': '!=',
|
|
952
|
+
'contains': 'LIKE',
|
|
953
|
+
'does_not_contain': 'NOT LIKE',
|
|
954
|
+
'does not contain': 'NOT LIKE',
|
|
955
|
+
'greater_than': '>',
|
|
956
|
+
'greater than': '>',
|
|
957
|
+
'less_than': '<',
|
|
958
|
+
'less than': '<',
|
|
959
|
+
'is_empty': 'ISEMPTY',
|
|
960
|
+
'is empty': 'ISEMPTY',
|
|
961
|
+
'is_not_empty': 'ISNOTEMPTY', // ā
CRITICAL FIX: was missing!
|
|
962
|
+
'is not empty': 'ISNOTEMPTY'
|
|
1031
963
|
};
|
|
1032
|
-
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
964
|
+
const normalizedOp = operator.toLowerCase().trim();
|
|
965
|
+
const mapped = opMap[normalizedOp] || operator;
|
|
966
|
+
this.logger.info(`šÆ Mapped operator '${operator}' -> '${mapped}'`);
|
|
967
|
+
return mapped;
|
|
968
|
+
};
|
|
969
|
+
// Build condition string from conditions array
|
|
970
|
+
let conditionString = '';
|
|
971
|
+
this.logger.info('Checking conditions parameter:', {
|
|
972
|
+
hasConditions: !!args.conditions,
|
|
973
|
+
isArray: Array.isArray(args.conditions),
|
|
974
|
+
length: args.conditions ? args.conditions.length : 0,
|
|
975
|
+
conditionsData: args.conditions
|
|
976
|
+
});
|
|
977
|
+
if (args.conditions && Array.isArray(args.conditions)) {
|
|
978
|
+
this.logger.info(`šÆ Building conditions string from ${args.conditions.length} conditions...`);
|
|
979
|
+
const conditionParts = [];
|
|
980
|
+
for (const condition of args.conditions) {
|
|
981
|
+
// Resolve variable name to sys_id
|
|
982
|
+
const variableId = await resolveVariableId(condition.catalog_variable, args.cat_item);
|
|
983
|
+
// Get the correct ServiceNow operator
|
|
984
|
+
const originalOperator = condition.operation || 'is';
|
|
985
|
+
const serviceNowOperator = mapOperatorToServiceNow(originalOperator);
|
|
986
|
+
// Safely get condition value (avoid undefined concatenation)
|
|
987
|
+
const conditionValue = condition.value || '';
|
|
988
|
+
// Build the condition part based on operator type
|
|
989
|
+
// ā
CRITICAL: Conditions must use IO:sys_id format for catalog variables!
|
|
990
|
+
// BUT only if we successfully resolved the variable to a sys_id
|
|
991
|
+
const isValidSysId = variableId && variableId.match(/^[a-f0-9]{32}$/);
|
|
992
|
+
const variableWithIOPrefix = isValidSysId ? `IO:${variableId}` : variableId;
|
|
993
|
+
if (!isValidSysId) {
|
|
994
|
+
this.logger.warn(`ā ļø Variable '${condition.catalog_variable}' could not be resolved to sys_id - condition may fail!`);
|
|
995
|
+
}
|
|
996
|
+
let conditionPart = '';
|
|
997
|
+
if (serviceNowOperator === 'ISEMPTY' || serviceNowOperator === 'ISNOTEMPTY') {
|
|
998
|
+
// For empty/not empty checks, no value needed
|
|
999
|
+
conditionPart = `${variableWithIOPrefix}${serviceNowOperator}`;
|
|
1000
|
+
}
|
|
1001
|
+
else if (serviceNowOperator === 'LIKE' || serviceNowOperator === 'NOT LIKE') {
|
|
1002
|
+
// For LIKE operations, ensure value is wrapped properly
|
|
1003
|
+
conditionPart = `${variableWithIOPrefix}${serviceNowOperator}${conditionValue}`;
|
|
1004
|
+
}
|
|
1005
|
+
else {
|
|
1006
|
+
// Standard operations (=, !=, >, <, etc.)
|
|
1007
|
+
conditionPart = `${variableWithIOPrefix}${serviceNowOperator}${conditionValue}`;
|
|
1008
|
+
}
|
|
1009
|
+
this.logger.info(`šļø Built condition part: ${conditionPart} (from ${originalOperator}: '${conditionValue}')`);
|
|
1010
|
+
conditionParts.push(conditionPart);
|
|
1037
1011
|
}
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1012
|
+
// Join all conditions with ^ separator (ServiceNow query format)
|
|
1013
|
+
conditionString = conditionParts.join('^');
|
|
1014
|
+
this.logger.info(`ā
Built conditions string: "${conditionString}"`);
|
|
1015
|
+
}
|
|
1016
|
+
// ā
CRITICAL FIX: Create in catalog_ui_policy table!
|
|
1017
|
+
// The actions reference catalog_ui_policy, NOT sys_ui_policy!
|
|
1018
|
+
const policyData = {
|
|
1019
|
+
// catalog_ui_policy fields
|
|
1020
|
+
short_description: args.short_description,
|
|
1021
|
+
catalog_item: args.cat_item, // Reference to the catalog item
|
|
1022
|
+
catalog_conditions: conditionString || args.condition || '', // Conditions as string
|
|
1023
|
+
applies_catalog: true, // This is a catalog policy
|
|
1024
|
+
active: args.active !== false,
|
|
1025
|
+
applies_on: args.applies_on || 'true', // When to apply: 'true', 'false', or 'both'
|
|
1026
|
+
reverse_if_false: args.reverse_if_false !== false,
|
|
1027
|
+
// Optional script fields
|
|
1028
|
+
script_true: args.script_true || '',
|
|
1029
|
+
script_false: args.script_false || ''
|
|
1030
|
+
};
|
|
1031
|
+
this.logger.info('šÆ Creating main policy in catalog_ui_policy table with data:', policyData);
|
|
1032
|
+
const policyResponse = await this.client.createRecord('catalog_ui_policy', policyData);
|
|
1033
|
+
if (!policyResponse.success) {
|
|
1034
|
+
this.logger.error('ā Policy creation failed:', policyResponse.error);
|
|
1035
|
+
throw new Error(`Failed to create catalog UI policy: ${policyResponse.error}`);
|
|
1036
|
+
}
|
|
1037
|
+
const policyId = policyResponse.data.sys_id;
|
|
1038
|
+
this.logger.info(`ā
Created main policy with sys_id: ${policyId}`);
|
|
1039
|
+
const createdActions = [];
|
|
1040
|
+
// Step 2: Create action records (dit werkt wel met aparte tabel)
|
|
1041
|
+
this.logger.info('Checking actions parameter:', {
|
|
1042
|
+
hasActions: !!args.actions,
|
|
1043
|
+
isArray: Array.isArray(args.actions),
|
|
1044
|
+
length: args.actions ? args.actions.length : 0,
|
|
1045
|
+
actionsData: args.actions
|
|
1046
|
+
});
|
|
1047
|
+
if (args.actions && Array.isArray(args.actions)) {
|
|
1048
|
+
this.logger.info(`šÆ Starting to create ${args.actions.length} action records...`);
|
|
1049
|
+
for (let i = 0; i < args.actions.length; i++) {
|
|
1050
|
+
const action = args.actions[i];
|
|
1051
|
+
// Resolve variable name to sys_id
|
|
1052
|
+
this.logger.info(`š Resolving variable: ${action.catalog_variable} for catalog item: ${args.cat_item}`);
|
|
1053
|
+
const variableId = await resolveVariableId(action.catalog_variable, args.cat_item);
|
|
1054
|
+
// Check if resolution actually worked (should be a sys_id now)
|
|
1055
|
+
const isValidSysId = variableId && variableId.match(/^[a-f0-9]{32}$/);
|
|
1056
|
+
if (!isValidSysId) {
|
|
1057
|
+
this.logger.error(`ā CRITICAL: Failed to resolve variable '${action.catalog_variable}' to sys_id!`);
|
|
1058
|
+
this.logger.error(`ā Got: ${variableId} (this is not a valid sys_id)`);
|
|
1059
|
+
// Continue anyway but it will likely fail
|
|
1060
|
+
}
|
|
1061
|
+
else {
|
|
1062
|
+
this.logger.info(`ā
Resolved to variable sys_id: ${variableId}`);
|
|
1063
|
+
}
|
|
1064
|
+
// ā
CRITICAL FIX: Add "IO:" prefix as required by ServiceNow
|
|
1065
|
+
// BUT ONLY if we have a valid sys_id!
|
|
1066
|
+
const catalogVariableWithPrefix = isValidSysId ? `IO:${variableId}` : variableId;
|
|
1067
|
+
this.logger.info(`šÆ Using catalog_variable value: ${catalogVariableWithPrefix}`);
|
|
1068
|
+
// Actions structure in ServiceNow:
|
|
1069
|
+
// - catalog_ui_policy_action.catalog_variable -> "IO:" + variable_sys_id (required format)
|
|
1070
|
+
// - catalog_ui_policy_action.ui_policy -> catalog_ui_policy.sys_id
|
|
1071
|
+
// ā
CRITICAL: Build action data according to ServiceNow's exact structure
|
|
1072
|
+
// catalog_ui_policy_action inherits from sys_ui_policy_action
|
|
1073
|
+
// We must set fields in the correct way for ServiceNow to accept them
|
|
1074
|
+
const actionData = {};
|
|
1075
|
+
// STEP 1: ENHANCED VERIFICATION - Verify policy exists before creating actions
|
|
1076
|
+
this.logger.info(`š ENHANCED DEBUG: Verifying policy ${policyId} exists before creating action...`);
|
|
1077
|
+
if (!policyId) {
|
|
1078
|
+
this.logger.error(`ā CRITICAL: No policyId available for action ${i + 1}!`);
|
|
1079
|
+
throw new Error(`Cannot create action without valid policy ID`);
|
|
1080
|
+
}
|
|
1081
|
+
// ā
NEW: Test policy existence before action creation
|
|
1082
|
+
const policyExists = await this.client.searchRecords('catalog_ui_policy', `sys_id=${policyId}`, 1);
|
|
1083
|
+
if (!policyExists.success || policyExists.data.result.length === 0) {
|
|
1084
|
+
this.logger.error(`ā CRITICAL: Policy ${policyId} does not exist in catalog_ui_policy table!`);
|
|
1085
|
+
throw new Error(`Policy verification failed - cannot create action without valid policy`);
|
|
1086
|
+
}
|
|
1087
|
+
const existingPolicy = policyExists.data.result[0];
|
|
1088
|
+
this.logger.info(`ā
Policy verification successful:`);
|
|
1089
|
+
this.logger.info(` - Policy sys_id: ${existingPolicy.sys_id}`);
|
|
1090
|
+
this.logger.info(` - Policy name: ${existingPolicy.short_description || 'N/A'}`);
|
|
1091
|
+
this.logger.info(` - Policy active: ${existingPolicy.active}`);
|
|
1092
|
+
// STEP 2: Set reference fields with enhanced validation
|
|
1093
|
+
// ā
ui_policy is a reference to catalog_ui_policy - test multiple formats
|
|
1094
|
+
this.logger.info(`š Setting ui_policy reference to: ${policyId}`);
|
|
1095
|
+
// Try setting the reference in the most explicit way possible
|
|
1096
|
+
actionData.ui_policy = policyId;
|
|
1097
|
+
// ā
catalog_item is a reference to sc_cat_item - use sys_id directly
|
|
1098
|
+
if (!args.cat_item) {
|
|
1099
|
+
this.logger.error(`ā CRITICAL: No catalog item ID provided!`);
|
|
1100
|
+
throw new Error(`Cannot create action without catalog item ID`);
|
|
1101
|
+
}
|
|
1102
|
+
this.logger.info(`š Setting catalog_item reference to: ${args.cat_item}`);
|
|
1103
|
+
actionData.catalog_item = args.cat_item;
|
|
1104
|
+
// STEP 3: Set the catalog_variable with IO: prefix (STRING field, not reference)
|
|
1105
|
+
this.logger.info(`š Setting catalog_variable to: ${catalogVariableWithPrefix}`);
|
|
1106
|
+
actionData.catalog_variable = catalogVariableWithPrefix;
|
|
1107
|
+
// STEP 4: Set action properties with correct values
|
|
1108
|
+
// ā
CRITICAL: Use "ignore" instead of not setting or using false
|
|
1109
|
+
// This is how ServiceNow differentiates between "don't change" and "set to false"
|
|
1110
|
+
if (action.visible !== undefined) {
|
|
1111
|
+
actionData.visible = action.visible === true ? 'true' :
|
|
1112
|
+
action.visible === false ? 'false' : 'ignore';
|
|
1113
|
+
}
|
|
1114
|
+
else {
|
|
1115
|
+
actionData.visible = 'ignore'; // Default to ignore if not specified
|
|
1116
|
+
}
|
|
1117
|
+
if (action.mandatory !== undefined) {
|
|
1118
|
+
actionData.mandatory = action.mandatory === true ? 'true' :
|
|
1119
|
+
action.mandatory === false ? 'false' : 'ignore';
|
|
1120
|
+
}
|
|
1121
|
+
else {
|
|
1122
|
+
actionData.mandatory = 'ignore'; // Default to ignore if not specified
|
|
1123
|
+
}
|
|
1124
|
+
if (action.readonly !== undefined) {
|
|
1125
|
+
actionData.disabled = action.readonly === true ? 'true' :
|
|
1126
|
+
action.readonly === false ? 'false' : 'ignore';
|
|
1127
|
+
}
|
|
1128
|
+
else {
|
|
1129
|
+
actionData.disabled = 'ignore'; // Default to ignore if not specified
|
|
1130
|
+
}
|
|
1131
|
+
// STEP 5: Set optional value field
|
|
1132
|
+
if (action.value !== undefined && action.value !== null && action.value !== '') {
|
|
1133
|
+
actionData.value = String(action.value);
|
|
1134
|
+
}
|
|
1135
|
+
// STEP 6: Set other metadata
|
|
1136
|
+
actionData.order = (i + 1) * 100;
|
|
1137
|
+
actionData.active = true;
|
|
1138
|
+
this.logger.info(`š Creating action with VALIDATED structure:`);
|
|
1139
|
+
this.logger.info(` - ui_policy (ref): ${policyId} [VERIFIED EXISTS]`);
|
|
1140
|
+
this.logger.info(` - catalog_item (ref): ${args.cat_item}`);
|
|
1141
|
+
this.logger.info(` - catalog_variable: ${catalogVariableWithPrefix}`);
|
|
1142
|
+
this.logger.info(` - visible: ${actionData.visible}`);
|
|
1143
|
+
this.logger.info(` - mandatory: ${actionData.mandatory}`);
|
|
1144
|
+
this.logger.info(` - disabled: ${actionData.disabled}`);
|
|
1145
|
+
// ā
ENHANCED DEBUG: Log complete action data being sent
|
|
1146
|
+
this.logger.info(`š Complete action data being sent to ServiceNow:`, JSON.stringify(actionData, null, 2));
|
|
1147
|
+
this.logger.info(`šÆ Attempting to create action ${i + 1} in catalog_ui_policy_action table...`);
|
|
1148
|
+
const actionResponse = await this.client.createRecord('catalog_ui_policy_action', actionData);
|
|
1149
|
+
if (actionResponse.success) {
|
|
1150
|
+
const createdActionId = actionResponse.data.sys_id;
|
|
1151
|
+
this.logger.info(`ā
Created action with sys_id: ${createdActionId}`);
|
|
1152
|
+
// š VERIFICATION: Check if action was actually created AND fields are populated
|
|
1153
|
+
this.logger.info(`š Verifying action ${i + 1} creation and field population...`);
|
|
1154
|
+
const actionVerification = await this.client.searchRecords('catalog_ui_policy_action', `sys_id=${createdActionId}`, 1);
|
|
1155
|
+
if (!actionVerification.success || actionVerification.data.result.length === 0) {
|
|
1156
|
+
this.logger.error('ā ACTION VERIFICATION FAILED: Action not found after creation!');
|
|
1157
|
+
throw new Error(`Action creation verification failed - action ${i + 1} not found in database`);
|
|
1102
1158
|
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1159
|
+
// ā
NEW: Verify that critical fields are actually populated
|
|
1160
|
+
const createdAction = actionVerification.data.result[0];
|
|
1161
|
+
this.logger.info(`š Created action data:`, {
|
|
1162
|
+
sys_id: createdAction.sys_id,
|
|
1163
|
+
ui_policy: createdAction.ui_policy || 'ā EMPTY',
|
|
1164
|
+
catalog_variable: createdAction.catalog_variable || 'ā EMPTY',
|
|
1165
|
+
catalog_item: createdAction.catalog_item || 'ā EMPTY',
|
|
1166
|
+
visible: createdAction.visible,
|
|
1167
|
+
mandatory: createdAction.mandatory,
|
|
1168
|
+
disabled: createdAction.disabled
|
|
1169
|
+
});
|
|
1170
|
+
// ā
ENHANCED VERIFICATION: Check if critical fields are populated correctly
|
|
1171
|
+
const uiPolicyValue = createdAction.ui_policy;
|
|
1172
|
+
const catalogVariableValue = createdAction.catalog_variable;
|
|
1173
|
+
const catalogItemValue = createdAction.catalog_item;
|
|
1174
|
+
// Log the raw response to understand what ServiceNow returns
|
|
1175
|
+
this.logger.info(`š Raw action verification response:`, JSON.stringify(createdAction, null, 2));
|
|
1176
|
+
// Check ui_policy reference - ServiceNow might return it as an object
|
|
1177
|
+
let actualUiPolicyId = uiPolicyValue;
|
|
1178
|
+
if (typeof uiPolicyValue === 'object' && uiPolicyValue !== null) {
|
|
1179
|
+
actualUiPolicyId = uiPolicyValue.value || uiPolicyValue.sys_id || '';
|
|
1180
|
+
this.logger.info(`š ui_policy returned as object: ${JSON.stringify(uiPolicyValue)}`);
|
|
1114
1181
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1182
|
+
if (!actualUiPolicyId || actualUiPolicyId === '' || actualUiPolicyId === '{}') {
|
|
1183
|
+
this.logger.error(`ā CRITICAL: ui_policy field is EMPTY for action ${i + 1}!`);
|
|
1184
|
+
this.logger.error(`ā Expected policy ID: ${policyId}`);
|
|
1185
|
+
this.logger.error(`ā Raw ui_policy value: ${JSON.stringify(uiPolicyValue)}`);
|
|
1186
|
+
this.logger.error(`ā Parsed ui_policy value: ${actualUiPolicyId}`);
|
|
1187
|
+
this.logger.error(`ā Action data sent:`, JSON.stringify(actionData, null, 2));
|
|
1188
|
+
this.logger.error(`ā Full action verification response:`, JSON.stringify(createdAction, null, 2));
|
|
1189
|
+
// ā
ENHANCED ERROR: Try to understand ServiceNow's response pattern
|
|
1190
|
+
this.logger.error(`ā¹ļø DIAGNOSTIC INFO:`);
|
|
1191
|
+
this.logger.error(` - Policy verified to exist: YES (${policyId})`);
|
|
1192
|
+
this.logger.error(` - Action created successfully: YES (${createdActionId})`);
|
|
1193
|
+
this.logger.error(` - ui_policy field type: ${typeof uiPolicyValue}`);
|
|
1194
|
+
this.logger.error(` - ui_policy field value length: ${String(uiPolicyValue || '').length}`);
|
|
1195
|
+
this.logger.error(` - All action fields:`, Object.keys(createdAction));
|
|
1196
|
+
// Check if it's a ServiceNow API timing issue
|
|
1197
|
+
this.logger.warn(`š ATTEMPTING SECONDARY VERIFICATION (possible timing issue)...`);
|
|
1198
|
+
// Wait a moment and try again
|
|
1199
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
1200
|
+
const secondVerification = await this.client.searchRecords('catalog_ui_policy_action', `sys_id=${createdActionId}`, 1);
|
|
1201
|
+
if (secondVerification.success && secondVerification.data.result.length > 0) {
|
|
1202
|
+
const reCheckedAction = secondVerification.data.result[0];
|
|
1203
|
+
const reCheckedUiPolicy = reCheckedAction.ui_policy;
|
|
1204
|
+
this.logger.error(`š Secondary verification ui_policy: ${JSON.stringify(reCheckedUiPolicy)}`);
|
|
1205
|
+
if (reCheckedUiPolicy && reCheckedUiPolicy !== '' && reCheckedUiPolicy !== '{}') {
|
|
1206
|
+
this.logger.warn(`ā ļø This was a timing issue - ui_policy populated after delay`);
|
|
1207
|
+
// Continue with the re-checked value
|
|
1208
|
+
return; // Skip the error throwing
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
throw new Error(`Action ${i + 1} created but ui_policy field is empty - action will not work! This may be a ServiceNow API or table structure issue.`);
|
|
1117
1212
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1213
|
+
// For reference fields, ServiceNow might return an object - extract the value
|
|
1214
|
+
const uiPolicySysId = typeof uiPolicyValue === 'object' && uiPolicyValue.value ?
|
|
1215
|
+
uiPolicyValue.value : uiPolicyValue;
|
|
1216
|
+
this.logger.info(`ā
ui_policy field populated successfully: ${actualUiPolicyId}`);
|
|
1217
|
+
if (uiPolicySysId !== policyId) {
|
|
1218
|
+
this.logger.warn(`ā ļø ui_policy mismatch - Expected: ${policyId}, Got: ${uiPolicySysId}`);
|
|
1121
1219
|
}
|
|
1122
1220
|
else {
|
|
1123
|
-
|
|
1221
|
+
this.logger.info(`ā
ui_policy reference matches expected value`);
|
|
1124
1222
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1223
|
+
// Check catalog_variable (should have IO: prefix)
|
|
1224
|
+
if (!catalogVariableValue || catalogVariableValue === '') {
|
|
1225
|
+
this.logger.error(`ā CRITICAL: catalog_variable field is EMPTY for action ${i + 1}!`);
|
|
1226
|
+
this.logger.error(`ā Expected: ${catalogVariableWithPrefix}, Got: ${catalogVariableValue}`);
|
|
1227
|
+
throw new Error(`Action ${i + 1} created but catalog_variable field is empty - action will not work!`);
|
|
1128
1228
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
}
|
|
1132
|
-
// STEP 5: Set optional value field
|
|
1133
|
-
if (action.value !== undefined && action.value !== null && action.value !== '') {
|
|
1134
|
-
actionData.value = String(action.value);
|
|
1229
|
+
if (!catalogVariableValue.startsWith('IO:')) {
|
|
1230
|
+
this.logger.warn(`ā ļø catalog_variable missing IO: prefix - Got: ${catalogVariableValue}`);
|
|
1135
1231
|
}
|
|
1136
|
-
//
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
this.logger.info(` - catalog_item (ref): ${args.cat_item}`);
|
|
1142
|
-
this.logger.info(` - catalog_variable: ${catalogVariableWithPrefix}`);
|
|
1143
|
-
this.logger.info(` - visible: ${actionData.visible}`);
|
|
1144
|
-
this.logger.info(` - mandatory: ${actionData.mandatory}`);
|
|
1145
|
-
this.logger.info(` - disabled: ${actionData.disabled}`);
|
|
1146
|
-
// ā
ENHANCED DEBUG: Log complete action data being sent
|
|
1147
|
-
this.logger.info(`š Complete action data being sent to ServiceNow:`, JSON.stringify(actionData, null, 2));
|
|
1148
|
-
this.logger.info(`šÆ Attempting to create action ${i + 1} in catalog_ui_policy_action table...`);
|
|
1149
|
-
const actionResponse = await this.client.createRecord('catalog_ui_policy_action', actionData);
|
|
1150
|
-
if (actionResponse.success) {
|
|
1151
|
-
const createdActionId = actionResponse.data.sys_id;
|
|
1152
|
-
this.logger.info(`ā
Created action with sys_id: ${createdActionId}`);
|
|
1153
|
-
// š VERIFICATION: Check if action was actually created AND fields are populated
|
|
1154
|
-
this.logger.info(`š Verifying action ${i + 1} creation and field population...`);
|
|
1155
|
-
const actionVerification = await this.client.searchRecords('catalog_ui_policy_action', `sys_id=${createdActionId}`, 1);
|
|
1156
|
-
if (!actionVerification.success || actionVerification.data.result.length === 0) {
|
|
1157
|
-
this.logger.error('ā ACTION VERIFICATION FAILED: Action not found after creation!');
|
|
1158
|
-
throw new Error(`Action creation verification failed - action ${i + 1} not found in database`);
|
|
1159
|
-
}
|
|
1160
|
-
// ā
NEW: Verify that critical fields are actually populated
|
|
1161
|
-
const createdAction = actionVerification.data.result[0];
|
|
1162
|
-
this.logger.info(`š Created action data:`, {
|
|
1163
|
-
sys_id: createdAction.sys_id,
|
|
1164
|
-
ui_policy: createdAction.ui_policy || 'ā EMPTY',
|
|
1165
|
-
catalog_variable: createdAction.catalog_variable || 'ā EMPTY',
|
|
1166
|
-
catalog_item: createdAction.catalog_item || 'ā EMPTY',
|
|
1167
|
-
visible: createdAction.visible,
|
|
1168
|
-
mandatory: createdAction.mandatory,
|
|
1169
|
-
disabled: createdAction.disabled
|
|
1170
|
-
});
|
|
1171
|
-
// ā
ENHANCED VERIFICATION: Check if critical fields are populated correctly
|
|
1172
|
-
const uiPolicyValue = createdAction.ui_policy;
|
|
1173
|
-
const catalogVariableValue = createdAction.catalog_variable;
|
|
1174
|
-
const catalogItemValue = createdAction.catalog_item;
|
|
1175
|
-
// Log the raw response to understand what ServiceNow returns
|
|
1176
|
-
this.logger.info(`š Raw action verification response:`, JSON.stringify(createdAction, null, 2));
|
|
1177
|
-
// Check ui_policy reference - ServiceNow might return it as an object
|
|
1178
|
-
let actualUiPolicyId = uiPolicyValue;
|
|
1179
|
-
if (typeof uiPolicyValue === 'object' && uiPolicyValue !== null) {
|
|
1180
|
-
actualUiPolicyId = uiPolicyValue.value || uiPolicyValue.sys_id || '';
|
|
1181
|
-
this.logger.info(`š ui_policy returned as object: ${JSON.stringify(uiPolicyValue)}`);
|
|
1182
|
-
}
|
|
1183
|
-
if (!actualUiPolicyId || actualUiPolicyId === '' || actualUiPolicyId === '{}') {
|
|
1184
|
-
this.logger.error(`ā CRITICAL: ui_policy field is EMPTY for action ${i + 1}!`);
|
|
1185
|
-
this.logger.error(`ā Expected policy ID: ${policyId}`);
|
|
1186
|
-
this.logger.error(`ā Raw ui_policy value: ${JSON.stringify(uiPolicyValue)}`);
|
|
1187
|
-
this.logger.error(`ā Parsed ui_policy value: ${actualUiPolicyId}`);
|
|
1188
|
-
this.logger.error(`ā Action data sent:`, JSON.stringify(actionData, null, 2));
|
|
1189
|
-
this.logger.error(`ā Full action verification response:`, JSON.stringify(createdAction, null, 2));
|
|
1190
|
-
// ā
ENHANCED ERROR: Try to understand ServiceNow's response pattern
|
|
1191
|
-
this.logger.error(`ā¹ļø DIAGNOSTIC INFO:`);
|
|
1192
|
-
this.logger.error(` - Policy verified to exist: YES (${policyId})`);
|
|
1193
|
-
this.logger.error(` - Action created successfully: YES (${createdActionId})`);
|
|
1194
|
-
this.logger.error(` - ui_policy field type: ${typeof uiPolicyValue}`);
|
|
1195
|
-
this.logger.error(` - ui_policy field value length: ${String(uiPolicyValue || '').length}`);
|
|
1196
|
-
this.logger.error(` - All action fields:`, Object.keys(createdAction));
|
|
1197
|
-
// Check if it's a ServiceNow API timing issue
|
|
1198
|
-
this.logger.warn(`š ATTEMPTING SECONDARY VERIFICATION (possible timing issue)...`);
|
|
1199
|
-
// Wait a moment and try again
|
|
1200
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
1201
|
-
const secondVerification = await this.client.searchRecords('catalog_ui_policy_action', `sys_id=${createdActionId}`, 1);
|
|
1202
|
-
if (secondVerification.success && secondVerification.data.result.length > 0) {
|
|
1203
|
-
const reCheckedAction = secondVerification.data.result[0];
|
|
1204
|
-
const reCheckedUiPolicy = reCheckedAction.ui_policy;
|
|
1205
|
-
this.logger.error(`š Secondary verification ui_policy: ${JSON.stringify(reCheckedUiPolicy)}`);
|
|
1206
|
-
if (reCheckedUiPolicy && reCheckedUiPolicy !== '' && reCheckedUiPolicy !== '{}') {
|
|
1207
|
-
this.logger.warn(`ā ļø This was a timing issue - ui_policy populated after delay`);
|
|
1208
|
-
// Continue with the re-checked value
|
|
1209
|
-
return; // Skip the error throwing
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
throw new Error(`Action ${i + 1} created but ui_policy field is empty - action will not work! This may be a ServiceNow API or table structure issue.`);
|
|
1213
|
-
}
|
|
1214
|
-
// For reference fields, ServiceNow might return an object - extract the value
|
|
1215
|
-
const uiPolicySysId = typeof uiPolicyValue === 'object' && uiPolicyValue.value ?
|
|
1216
|
-
uiPolicyValue.value : uiPolicyValue;
|
|
1217
|
-
this.logger.info(`ā
ui_policy field populated successfully: ${actualUiPolicyId}`);
|
|
1218
|
-
if (uiPolicySysId !== policyId) {
|
|
1219
|
-
this.logger.warn(`ā ļø ui_policy mismatch - Expected: ${policyId}, Got: ${uiPolicySysId}`);
|
|
1220
|
-
}
|
|
1221
|
-
else {
|
|
1222
|
-
this.logger.info(`ā
ui_policy reference matches expected value`);
|
|
1223
|
-
}
|
|
1224
|
-
// Check catalog_variable (should have IO: prefix)
|
|
1225
|
-
if (!catalogVariableValue || catalogVariableValue === '') {
|
|
1226
|
-
this.logger.error(`ā CRITICAL: catalog_variable field is EMPTY for action ${i + 1}!`);
|
|
1227
|
-
this.logger.error(`ā Expected: ${catalogVariableWithPrefix}, Got: ${catalogVariableValue}`);
|
|
1228
|
-
throw new Error(`Action ${i + 1} created but catalog_variable field is empty - action will not work!`);
|
|
1229
|
-
}
|
|
1230
|
-
if (!catalogVariableValue.startsWith('IO:')) {
|
|
1231
|
-
this.logger.warn(`ā ļø catalog_variable missing IO: prefix - Got: ${catalogVariableValue}`);
|
|
1232
|
-
}
|
|
1233
|
-
// Check catalog_item reference
|
|
1234
|
-
if (!catalogItemValue || catalogItemValue === '' || catalogItemValue === '{}') {
|
|
1235
|
-
this.logger.error(`ā CRITICAL: catalog_item field is EMPTY for action ${i + 1}!`);
|
|
1236
|
-
this.logger.error(`ā Expected: ${args.cat_item}, Got: ${catalogItemValue}`);
|
|
1237
|
-
throw new Error(`Action ${i + 1} created but catalog_item field is empty - action will not work!`);
|
|
1238
|
-
}
|
|
1239
|
-
const catalogItemSysId = typeof catalogItemValue === 'object' && catalogItemValue.value ?
|
|
1240
|
-
catalogItemValue.value : catalogItemValue;
|
|
1241
|
-
if (catalogItemSysId !== args.cat_item) {
|
|
1242
|
-
this.logger.warn(`ā ļø catalog_item mismatch - Expected: ${args.cat_item}, Got: ${catalogItemSysId}`);
|
|
1243
|
-
}
|
|
1244
|
-
this.logger.info(`ā
Action ${i + 1} verified in database with all fields populated`);
|
|
1245
|
-
createdActions.push({
|
|
1246
|
-
sys_id: createdActionId,
|
|
1247
|
-
variable: action.catalog_variable,
|
|
1248
|
-
details: this.formatActionDetails(action)
|
|
1249
|
-
});
|
|
1232
|
+
// Check catalog_item reference
|
|
1233
|
+
if (!catalogItemValue || catalogItemValue === '' || catalogItemValue === '{}') {
|
|
1234
|
+
this.logger.error(`ā CRITICAL: catalog_item field is EMPTY for action ${i + 1}!`);
|
|
1235
|
+
this.logger.error(`ā Expected: ${args.cat_item}, Got: ${catalogItemValue}`);
|
|
1236
|
+
throw new Error(`Action ${i + 1} created but catalog_item field is empty - action will not work!`);
|
|
1250
1237
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
this.logger.
|
|
1255
|
-
this.logger.error('ā Response details:', {
|
|
1256
|
-
status: actionResponse.status,
|
|
1257
|
-
headers: actionResponse.headers,
|
|
1258
|
-
data: actionResponse.data
|
|
1259
|
-
});
|
|
1260
|
-
// BELANGRIJK: Gooi een error zodat de gebruiker weet dat het faalt!
|
|
1261
|
-
throw new Error(errorMsg);
|
|
1238
|
+
const catalogItemSysId = typeof catalogItemValue === 'object' && catalogItemValue.value ?
|
|
1239
|
+
catalogItemValue.value : catalogItemValue;
|
|
1240
|
+
if (catalogItemSysId !== args.cat_item) {
|
|
1241
|
+
this.logger.warn(`ā ļø catalog_item mismatch - Expected: ${args.cat_item}, Got: ${catalogItemSysId}`);
|
|
1262
1242
|
}
|
|
1243
|
+
this.logger.info(`ā
Action ${i + 1} verified in database with all fields populated`);
|
|
1244
|
+
createdActions.push({
|
|
1245
|
+
sys_id: createdActionId,
|
|
1246
|
+
variable: action.catalog_variable,
|
|
1247
|
+
details: this.formatActionDetails(action)
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1250
|
+
else {
|
|
1251
|
+
const errorMsg = `ā Failed to create action ${i + 1}: ${actionResponse.error || 'Unknown error'}`;
|
|
1252
|
+
this.logger.error(errorMsg);
|
|
1253
|
+
this.logger.error('ā Action data was:', actionData);
|
|
1254
|
+
this.logger.error('ā Response details:', {
|
|
1255
|
+
success: actionResponse.success,
|
|
1256
|
+
error: actionResponse.error,
|
|
1257
|
+
data: actionResponse.data
|
|
1258
|
+
});
|
|
1259
|
+
// BELANGRIJK: Gooi een error zodat de gebruiker weet dat het faalt!
|
|
1260
|
+
throw new Error(errorMsg);
|
|
1263
1261
|
}
|
|
1264
1262
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1263
|
+
}
|
|
1264
|
+
// š FINAL VERIFICATION: Policy creation in catalog_ui_policy table
|
|
1265
|
+
this.logger.info('š Final verification: Checking policy in catalog_ui_policy table...');
|
|
1266
|
+
const policyVerification = await this.client.searchRecords('catalog_ui_policy', `sys_id=${policyId}`, 1);
|
|
1267
|
+
if (!policyVerification.success || policyVerification.data.result.length === 0) {
|
|
1268
|
+
this.logger.error('ā POLICY VERIFICATION FAILED: Policy not found in catalog_ui_policy table!');
|
|
1269
|
+
throw new Error(`Policy creation verification failed - policy not found in catalog_ui_policy table`);
|
|
1270
|
+
}
|
|
1271
|
+
this.logger.info('ā
Policy verified in catalog_ui_policy table');
|
|
1272
|
+
// Build comprehensive response
|
|
1273
|
+
let responseText = `ā
Catalog UI Policy created successfully!
|
|
1275
1274
|
|
|
1276
1275
|
š **${args.short_description}**
|
|
1277
1276
|
š Policy sys_id: ${policyId}
|
|
@@ -1284,81 +1283,71 @@ ${args.help_text ? `ā Help: ${args.help_text}` : ''}
|
|
|
1284
1283
|
š **Verification Results:**
|
|
1285
1284
|
ā
Policy record created in catalog_ui_policy table
|
|
1286
1285
|
ā
${createdActions.length} actions created in catalog_ui_policy_action table`;
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
}
|
|
1290
|
-
if (createdActions.length > 0) {
|
|
1291
|
-
responseText += `\n\nā” **Actions Created (${createdActions.length}):**\n`;
|
|
1292
|
-
createdActions.forEach((action, i) => {
|
|
1293
|
-
responseText += ` ${i + 1}. ${action.details} on ${action.variable}\n`;
|
|
1294
|
-
});
|
|
1295
|
-
}
|
|
1296
|
-
responseText += `\n\n⨠UI policy configured successfully with ${createdActions.length} actions!`;
|
|
1297
|
-
return {
|
|
1298
|
-
content: [{
|
|
1299
|
-
type: 'text',
|
|
1300
|
-
text: responseText
|
|
1301
|
-
}]
|
|
1302
|
-
};
|
|
1286
|
+
if (conditionString) {
|
|
1287
|
+
responseText += `\n\nš **Conditions:**\n${conditionString}`;
|
|
1303
1288
|
}
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1289
|
+
if (createdActions.length > 0) {
|
|
1290
|
+
responseText += `\n\nā” **Actions Created (${createdActions.length}):**\n`;
|
|
1291
|
+
createdActions.forEach((action, i) => {
|
|
1292
|
+
responseText += ` ${i + 1}. ${action.details} on ${action.variable}\n`;
|
|
1293
|
+
});
|
|
1307
1294
|
}
|
|
1295
|
+
responseText += `\n\n⨠UI policy configured successfully with ${createdActions.length} actions!`;
|
|
1296
|
+
return {
|
|
1297
|
+
content: [{
|
|
1298
|
+
type: 'text',
|
|
1299
|
+
text: responseText
|
|
1300
|
+
}]
|
|
1301
|
+
};
|
|
1308
1302
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
finally {
|
|
1303
|
+
catch (error) {
|
|
1304
|
+
this.logger.error('Failed to create catalog UI policy:', error);
|
|
1305
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create catalog UI policy: ${error}`);
|
|
1313
1306
|
}
|
|
1314
|
-
/**
|
|
1315
|
-
* Helper function to format action details for display
|
|
1316
|
-
*/
|
|
1317
|
-
}
|
|
1318
|
-
catch(error) { n; this.logger.error('Failed to create catalog UI policy:', error); n; return { content: [{ type: 'text', text: `ā Failed to create catalog UI policy: ${error.message || error}` }] }; n; }
|
|
1319
|
-
}
|
|
1320
|
-
n;
|
|
1321
|
-
n;
|
|
1322
|
-
formatActionDetails(action, any);
|
|
1323
|
-
string;
|
|
1324
|
-
{
|
|
1325
|
-
const details = [];
|
|
1326
|
-
if (action.mandatory !== undefined) {
|
|
1327
|
-
details.push(`Mandatory: ${action.mandatory}`);
|
|
1328
|
-
}
|
|
1329
|
-
if (action.visible !== undefined) {
|
|
1330
|
-
details.push(`Visible: ${action.visible}`);
|
|
1331
1307
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
details
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
const scriptData = {
|
|
1346
|
-
cat_item: args.cat_item,
|
|
1347
|
-
name: args.name,
|
|
1348
|
-
script: args.script,
|
|
1349
|
-
type: args.type,
|
|
1350
|
-
applies_to: args.applies_to || 'item',
|
|
1351
|
-
cat_variable: args.variable || '',
|
|
1352
|
-
active: args.active !== false
|
|
1353
|
-
};
|
|
1354
|
-
const response = await this.client.createRecord('catalog_script_client', scriptData);
|
|
1355
|
-
if (!response.success) {
|
|
1356
|
-
throw new Error(`Failed to create catalog client script: ${response.error}`);
|
|
1308
|
+
/**
|
|
1309
|
+
* Helper function to format action details for display
|
|
1310
|
+
*/
|
|
1311
|
+
formatActionDetails(action) {
|
|
1312
|
+
const details = [];
|
|
1313
|
+
if (action.mandatory !== undefined) {
|
|
1314
|
+
details.push(`Mandatory: ${action.mandatory}`);
|
|
1315
|
+
}
|
|
1316
|
+
if (action.visible !== undefined) {
|
|
1317
|
+
details.push(`Visible: ${action.visible}`);
|
|
1318
|
+
}
|
|
1319
|
+
if (action.readonly !== undefined) {
|
|
1320
|
+
details.push(`Read-only: ${action.readonly}`);
|
|
1357
1321
|
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1322
|
+
if (action.value !== undefined && action.value !== '') {
|
|
1323
|
+
details.push(`Value: "${action.value}"`);
|
|
1324
|
+
}
|
|
1325
|
+
return details.length > 0 ? details.join(', ') : 'No specific action';
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Create Catalog Client Script
|
|
1329
|
+
* Uses catalog_script_client table
|
|
1330
|
+
*/
|
|
1331
|
+
async createCatalogClientScript(args) {
|
|
1332
|
+
try {
|
|
1333
|
+
this.logger.info('Creating catalog client script...');
|
|
1334
|
+
const scriptData = {
|
|
1335
|
+
cat_item: args.cat_item,
|
|
1336
|
+
name: args.name,
|
|
1337
|
+
script: args.script,
|
|
1338
|
+
type: args.type,
|
|
1339
|
+
applies_to: args.applies_to || 'item',
|
|
1340
|
+
cat_variable: args.variable || '',
|
|
1341
|
+
active: args.active !== false
|
|
1342
|
+
};
|
|
1343
|
+
const response = await this.client.createRecord('catalog_script_client', scriptData);
|
|
1344
|
+
if (!response.success) {
|
|
1345
|
+
throw new Error(`Failed to create catalog client script: ${response.error}`);
|
|
1346
|
+
}
|
|
1347
|
+
return {
|
|
1348
|
+
content: [{
|
|
1349
|
+
type: 'text',
|
|
1350
|
+
text: `ā
Catalog Client Script created successfully!
|
|
1362
1351
|
|
|
1363
1352
|
š **${args.name}**
|
|
1364
1353
|
š sys_id: ${response.data.sys_id}
|
|
@@ -1368,115 +1357,118 @@ ${args.variable ? `š Variable: ${args.variable}` : ''}
|
|
|
1368
1357
|
š Active: ${args.active !== false ? 'Yes' : 'No'}
|
|
1369
1358
|
|
|
1370
1359
|
⨠Client script added to catalog item!`
|
|
1371
|
-
}]
|
|
1372
|
-
};
|
|
1373
|
-
}
|
|
1374
|
-
catch (error) {
|
|
1375
|
-
this.logger.error('Failed to create catalog client script:', error);
|
|
1376
|
-
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create catalog client script: ${error}`);
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
async;
|
|
1380
|
-
searchCatalog(args, any);
|
|
1381
|
-
{
|
|
1382
|
-
try {
|
|
1383
|
-
this.logger.info('Searching service catalog...');
|
|
1384
|
-
let query = args.query ? `nameLIKE${args.query}^ORshort_descriptionLIKE${args.query}` : '';
|
|
1385
|
-
if (args.category) {
|
|
1386
|
-
query += query ? '^' : '';
|
|
1387
|
-
query += `category=${args.category}`;
|
|
1388
|
-
}
|
|
1389
|
-
if (args.catalog) {
|
|
1390
|
-
query += query ? '^' : '';
|
|
1391
|
-
query += `sc_catalogs=${args.catalog}`;
|
|
1392
|
-
}
|
|
1393
|
-
if (args.active_only) {
|
|
1394
|
-
query += query ? '^' : '';
|
|
1395
|
-
query += 'active=true';
|
|
1396
|
-
}
|
|
1397
|
-
const limit = args.limit || 20;
|
|
1398
|
-
this.logger.trackAPICall('SEARCH', 'sc_cat_item', limit);
|
|
1399
|
-
const response = await this.client.searchRecords('sc_cat_item', query, limit);
|
|
1400
|
-
if (!response.success) {
|
|
1401
|
-
throw new Error('Failed to search catalog');
|
|
1402
|
-
}
|
|
1403
|
-
const items = response.data.result;
|
|
1404
|
-
if (!items.length) {
|
|
1405
|
-
return {
|
|
1406
|
-
content: [{
|
|
1407
|
-
type: 'text',
|
|
1408
|
-
text: `ā No catalog items found${args.query ? ` matching "${args.query}"` : ''}`
|
|
1409
1360
|
}]
|
|
1410
1361
|
};
|
|
1411
1362
|
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1363
|
+
catch (error) {
|
|
1364
|
+
this.logger.error('Failed to create catalog client script:', error);
|
|
1365
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create catalog client script: ${error}`);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Search Service Catalog
|
|
1370
|
+
*/
|
|
1371
|
+
async searchCatalog(args) {
|
|
1372
|
+
try {
|
|
1373
|
+
this.logger.info('Searching service catalog...');
|
|
1374
|
+
let query = args.query ? `nameLIKE${args.query}^ORshort_descriptionLIKE${args.query}` : '';
|
|
1375
|
+
if (args.category) {
|
|
1376
|
+
query += query ? '^' : '';
|
|
1377
|
+
query += `category=${args.category}`;
|
|
1378
|
+
}
|
|
1379
|
+
if (args.catalog) {
|
|
1380
|
+
query += query ? '^' : '';
|
|
1381
|
+
query += `sc_catalogs=${args.catalog}`;
|
|
1382
|
+
}
|
|
1383
|
+
if (args.active_only) {
|
|
1384
|
+
query += query ? '^' : '';
|
|
1385
|
+
query += 'active=true';
|
|
1386
|
+
}
|
|
1387
|
+
const limit = args.limit || 20;
|
|
1388
|
+
this.logger.trackAPICall('SEARCH', 'sc_cat_item', limit);
|
|
1389
|
+
const response = await this.client.searchRecords('sc_cat_item', query, limit);
|
|
1390
|
+
if (!response.success) {
|
|
1391
|
+
throw new Error('Failed to search catalog');
|
|
1392
|
+
}
|
|
1393
|
+
const items = response.data.result;
|
|
1394
|
+
if (!items.length) {
|
|
1395
|
+
return {
|
|
1396
|
+
content: [{
|
|
1397
|
+
type: 'text',
|
|
1398
|
+
text: `ā No catalog items found${args.query ? ` matching "${args.query}"` : ''}`
|
|
1399
|
+
}]
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
const itemList = items.map((item) => {
|
|
1403
|
+
return `šļø **${item.name}**
|
|
1414
1404
|
š ${item.sys_id}
|
|
1415
1405
|
š ${item.short_description}
|
|
1416
1406
|
${item.price && item.price !== '0' ? `š° Price: $${item.price}` : ''}
|
|
1417
1407
|
š Active: ${item.active ? 'Yes' : 'No'}`;
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1408
|
+
}).join('\n\n');
|
|
1409
|
+
return {
|
|
1410
|
+
content: [{
|
|
1411
|
+
type: 'text',
|
|
1412
|
+
text: `š Catalog Search Results${args.query ? ` for "${args.query}"` : ''}:
|
|
1423
1413
|
|
|
1424
1414
|
${itemList}
|
|
1425
1415
|
|
|
1426
1416
|
⨠Found ${items.length} catalog item(s)`
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
}
|
|
1430
|
-
catch (error) {
|
|
1431
|
-
this.logger.error('Failed to search catalog:', error);
|
|
1432
|
-
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to search catalog: ${error}`);
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
async;
|
|
1436
|
-
orderCatalogItem(args, any);
|
|
1437
|
-
{
|
|
1438
|
-
try {
|
|
1439
|
-
this.logger.info('Ordering catalog item...');
|
|
1440
|
-
// Create service catalog request
|
|
1441
|
-
const requestData = {
|
|
1442
|
-
requested_for: args.requested_for,
|
|
1443
|
-
opened_by: args.requested_for,
|
|
1444
|
-
special_instructions: args.special_instructions || ''
|
|
1445
|
-
};
|
|
1446
|
-
const requestResponse = await this.client.createRecord('sc_request', requestData);
|
|
1447
|
-
if (!requestResponse.success) {
|
|
1448
|
-
throw new Error(`Failed to create request: ${requestResponse.error}`);
|
|
1417
|
+
}]
|
|
1418
|
+
};
|
|
1449
1419
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
request: requestId,
|
|
1454
|
-
cat_item: args.cat_item,
|
|
1455
|
-
requested_for: args.requested_for,
|
|
1456
|
-
quantity: args.quantity || 1,
|
|
1457
|
-
delivery_address: args.delivery_address || ''
|
|
1458
|
-
};
|
|
1459
|
-
const ritmResponse = await this.client.createRecord('sc_req_item', ritmData);
|
|
1460
|
-
if (!ritmResponse.success) {
|
|
1461
|
-
throw new Error(`Failed to create requested item: ${ritmResponse.error}`);
|
|
1420
|
+
catch (error) {
|
|
1421
|
+
this.logger.error('Failed to search catalog:', error);
|
|
1422
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to search catalog: ${error}`);
|
|
1462
1423
|
}
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Order Catalog Item
|
|
1427
|
+
* Creates sc_request and sc_req_item records
|
|
1428
|
+
*/
|
|
1429
|
+
async orderCatalogItem(args) {
|
|
1430
|
+
try {
|
|
1431
|
+
this.logger.info('Ordering catalog item...');
|
|
1432
|
+
// Create service catalog request
|
|
1433
|
+
const requestData = {
|
|
1434
|
+
requested_for: args.requested_for,
|
|
1435
|
+
opened_by: args.requested_for,
|
|
1436
|
+
special_instructions: args.special_instructions || ''
|
|
1437
|
+
};
|
|
1438
|
+
const requestResponse = await this.client.createRecord('sc_request', requestData);
|
|
1439
|
+
if (!requestResponse.success) {
|
|
1440
|
+
throw new Error(`Failed to create request: ${requestResponse.error}`);
|
|
1474
1441
|
}
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1442
|
+
const requestId = requestResponse.data.sys_id;
|
|
1443
|
+
// Create requested item (RITM)
|
|
1444
|
+
const ritmData = {
|
|
1445
|
+
request: requestId,
|
|
1446
|
+
cat_item: args.cat_item,
|
|
1447
|
+
requested_for: args.requested_for,
|
|
1448
|
+
quantity: args.quantity || 1,
|
|
1449
|
+
delivery_address: args.delivery_address || ''
|
|
1450
|
+
};
|
|
1451
|
+
const ritmResponse = await this.client.createRecord('sc_req_item', ritmData);
|
|
1452
|
+
if (!ritmResponse.success) {
|
|
1453
|
+
throw new Error(`Failed to create requested item: ${ritmResponse.error}`);
|
|
1454
|
+
}
|
|
1455
|
+
const ritmId = ritmResponse.data.sys_id;
|
|
1456
|
+
const ritmNumber = ritmResponse.data.number;
|
|
1457
|
+
// Set variable values if provided
|
|
1458
|
+
if (args.variables) {
|
|
1459
|
+
for (const [varName, varValue] of Object.entries(args.variables)) {
|
|
1460
|
+
const varData = {
|
|
1461
|
+
request_item: ritmId,
|
|
1462
|
+
name: varName,
|
|
1463
|
+
value: varValue
|
|
1464
|
+
};
|
|
1465
|
+
await this.client.createRecord('sc_item_option_mtom', varData);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
return {
|
|
1469
|
+
content: [{
|
|
1470
|
+
type: 'text',
|
|
1471
|
+
text: `ā
Catalog Item ordered successfully!
|
|
1480
1472
|
|
|
1481
1473
|
šļø **Order Placed**
|
|
1482
1474
|
š Request: ${requestId}
|
|
@@ -1487,51 +1479,52 @@ ${args.delivery_address ? `š Delivery: ${args.delivery_address}` : ''}
|
|
|
1487
1479
|
${args.special_instructions ? `š Instructions: ${args.special_instructions}` : ''}
|
|
1488
1480
|
|
|
1489
1481
|
⨠Order submitted for fulfillment!`
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1482
|
+
}]
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
catch (error) {
|
|
1486
|
+
this.logger.error('Failed to order catalog item:', error);
|
|
1487
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to order catalog item: ${error}`);
|
|
1488
|
+
}
|
|
1496
1489
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
{
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1490
|
+
/**
|
|
1491
|
+
* Get Catalog Item Details
|
|
1492
|
+
*/
|
|
1493
|
+
async getCatalogItemDetails(args) {
|
|
1494
|
+
try {
|
|
1495
|
+
this.logger.info('Getting catalog item details...', { sys_id: args.sys_id });
|
|
1496
|
+
// š DEBUGGING: Try different methods to find the item
|
|
1497
|
+
this.logger.info('š Step 1: Trying getRecord method...');
|
|
1498
|
+
let itemResponse = await this.client.getRecord('sc_cat_item', args.sys_id);
|
|
1499
|
+
if (!itemResponse.success) {
|
|
1500
|
+
this.logger.warn('ā getRecord failed, trying searchRecords as fallback...');
|
|
1501
|
+
this.logger.warn('getRecord error:', itemResponse.error);
|
|
1502
|
+
// Fallback: try searchRecords method
|
|
1503
|
+
itemResponse = await this.client.searchRecords('sc_cat_item', `sys_id=${args.sys_id}`, 1);
|
|
1504
|
+
if (!itemResponse.success || itemResponse.data.result.length === 0) {
|
|
1505
|
+
this.logger.error('ā Both getRecord and searchRecords failed');
|
|
1506
|
+
this.logger.error('searchRecords error:', itemResponse.error);
|
|
1507
|
+
// Final fallback: try to query with different parameters
|
|
1508
|
+
const queryResponse = await this.client.searchRecords('sc_cat_item', `sys_id=${args.sys_id}`, 1);
|
|
1509
|
+
if (queryResponse.success && queryResponse.data.result.length > 0) {
|
|
1510
|
+
this.logger.info('ā
Found item using queryTable fallback');
|
|
1511
|
+
itemResponse = { success: true, data: queryResponse.data.result[0] };
|
|
1512
|
+
}
|
|
1513
|
+
else {
|
|
1514
|
+
this.logger.error('ā All methods failed to find catalog item');
|
|
1515
|
+
throw new Error(`Catalog item not found with sys_id: ${args.sys_id}. Tried getRecord, searchRecords, and queryTable.`);
|
|
1516
|
+
}
|
|
1519
1517
|
}
|
|
1520
1518
|
else {
|
|
1521
|
-
this.logger.
|
|
1522
|
-
|
|
1519
|
+
this.logger.info('ā
Found item using searchRecords fallback');
|
|
1520
|
+
itemResponse = { success: true, data: itemResponse.data.result[0] };
|
|
1523
1521
|
}
|
|
1524
1522
|
}
|
|
1525
1523
|
else {
|
|
1526
|
-
this.logger.info('ā
Found item using
|
|
1527
|
-
itemResponse = { success: true, data: itemResponse.data.result[0] };
|
|
1524
|
+
this.logger.info('ā
Found item using getRecord');
|
|
1528
1525
|
}
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
this.logger.info('ā
Found item using getRecord');
|
|
1532
|
-
}
|
|
1533
|
-
const item = itemResponse.data;
|
|
1534
|
-
let details = `šļø **${item.name}**
|
|
1526
|
+
const item = itemResponse.data;
|
|
1527
|
+
let details = `šļø **${item.name}**
|
|
1535
1528
|
š sys_id: ${item.sys_id}
|
|
1536
1529
|
š ${item.short_description}
|
|
1537
1530
|
š ${item.description || 'No detailed description'}
|
|
@@ -1539,82 +1532,82 @@ ${item.price && item.price !== '0' ? `š° Price: $${item.price}` : ''}
|
|
|
1539
1532
|
${item.recurring_price && item.recurring_price !== '0' ? `š Recurring: $${item.recurring_price}` : ''}
|
|
1540
1533
|
š¦ Delivery: ${item.delivery_time || '3 business days'}
|
|
1541
1534
|
š Active: ${item.active ? 'Yes' : 'No'}`;
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1535
|
+
// Get variables if requested
|
|
1536
|
+
if (args.include_variables) {
|
|
1537
|
+
const varResponse = await this.client.searchRecords('sc_cat_item_option', `cat_item=${args.sys_id}`, 50);
|
|
1538
|
+
if (varResponse.success && varResponse.data.result.length) {
|
|
1539
|
+
const variables = varResponse.data.result.map((v) => ` - ${v.question_text} (${v.type})${v.mandatory ? ' *Required' : ''}`).join('\n');
|
|
1540
|
+
details += `\n\nš **Variables:**\n${variables}`;
|
|
1541
|
+
}
|
|
1548
1542
|
}
|
|
1543
|
+
return {
|
|
1544
|
+
content: [{
|
|
1545
|
+
type: 'text',
|
|
1546
|
+
text: details + '\n\n⨠Catalog item details retrieved!'
|
|
1547
|
+
}]
|
|
1548
|
+
};
|
|
1549
1549
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
text: details + '\n\n⨠Catalog item details retrieved!'
|
|
1554
|
-
}]
|
|
1555
|
-
};
|
|
1556
|
-
}
|
|
1557
|
-
catch (error) {
|
|
1558
|
-
this.logger.error('Failed to get catalog item details:', error);
|
|
1559
|
-
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get catalog item details: ${error}`);
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
async;
|
|
1563
|
-
discoverCatalogs(args, any);
|
|
1564
|
-
{
|
|
1565
|
-
try {
|
|
1566
|
-
this.logger.info('Discovering service catalogs...');
|
|
1567
|
-
let query = '';
|
|
1568
|
-
if (args.active_only) {
|
|
1569
|
-
query = 'active=true';
|
|
1570
|
-
}
|
|
1571
|
-
const catalogResponse = await this.client.searchRecords('sc_catalog', query, 50);
|
|
1572
|
-
if (!catalogResponse.success) {
|
|
1573
|
-
throw new Error('Failed to discover catalogs');
|
|
1550
|
+
catch (error) {
|
|
1551
|
+
this.logger.error('Failed to get catalog item details:', error);
|
|
1552
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get catalog item details: ${error}`);
|
|
1574
1553
|
}
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Discover Service Catalogs
|
|
1557
|
+
*/
|
|
1558
|
+
async discoverCatalogs(args) {
|
|
1559
|
+
try {
|
|
1560
|
+
this.logger.info('Discovering service catalogs...');
|
|
1561
|
+
let query = '';
|
|
1562
|
+
if (args.active_only) {
|
|
1563
|
+
query = 'active=true';
|
|
1582
1564
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1565
|
+
const catalogResponse = await this.client.searchRecords('sc_catalog', query, 50);
|
|
1566
|
+
if (!catalogResponse.success) {
|
|
1567
|
+
throw new Error('Failed to discover catalogs');
|
|
1568
|
+
}
|
|
1569
|
+
const catalogs = catalogResponse.data.result;
|
|
1570
|
+
// Get categories if requested
|
|
1571
|
+
const catalogsWithDetails = await Promise.all(catalogs.map(async (catalog) => {
|
|
1572
|
+
if (args.include_categories) {
|
|
1573
|
+
const catResponse = await this.client.searchRecords('sc_category', `sc_catalog=${catalog.sys_id}`, 20);
|
|
1574
|
+
const categories = catResponse.success ? catResponse.data.result : [];
|
|
1575
|
+
return { ...catalog, categories };
|
|
1576
|
+
}
|
|
1577
|
+
return catalog;
|
|
1578
|
+
}));
|
|
1579
|
+
const catalogText = catalogsWithDetails.map((catalog) => {
|
|
1580
|
+
let text = `šļø **${catalog.title}** ${catalog.active ? 'ā
' : 'ā'}
|
|
1587
1581
|
š ${catalog.sys_id}
|
|
1588
1582
|
š ${catalog.description || 'No description'}`;
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1583
|
+
if (catalog.categories) {
|
|
1584
|
+
const categoryList = catalog.categories.map((cat) => ` - ${cat.title}`).join('\n');
|
|
1585
|
+
text += `\nš Categories:\n${categoryList || ' No categories'}`;
|
|
1586
|
+
}
|
|
1587
|
+
return text;
|
|
1588
|
+
}).join('\n\n');
|
|
1589
|
+
return {
|
|
1590
|
+
content: [{
|
|
1591
|
+
type: 'text',
|
|
1592
|
+
text: `š Discovered Service Catalogs:
|
|
1599
1593
|
|
|
1600
1594
|
${catalogText}
|
|
1601
1595
|
|
|
1602
1596
|
⨠Found ${catalogs.length} catalog(s)`
|
|
1603
|
-
|
|
1604
|
-
|
|
1597
|
+
}]
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
catch (error) {
|
|
1601
|
+
this.logger.error('Failed to discover catalogs:', error);
|
|
1602
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to discover catalogs: ${error}`);
|
|
1603
|
+
}
|
|
1605
1604
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1605
|
+
async run() {
|
|
1606
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
1607
|
+
await this.server.connect(transport);
|
|
1608
|
+
this.logger.info('ServiceNow Knowledge & Catalog MCP Server running on stdio');
|
|
1609
1609
|
}
|
|
1610
1610
|
}
|
|
1611
|
-
async;
|
|
1612
|
-
run();
|
|
1613
|
-
{
|
|
1614
|
-
const transport = new stdio_js_1.StdioServerTransport();
|
|
1615
|
-
await this.server.connect(transport);
|
|
1616
|
-
this.logger.info('ServiceNow Knowledge & Catalog MCP Server running on stdio');
|
|
1617
|
-
}
|
|
1618
1611
|
const server = new ServiceNowKnowledgeCatalogMCP();
|
|
1619
1612
|
server.run().catch(console.error);
|
|
1620
1613
|
//# sourceMappingURL=servicenow-knowledge-catalog-mcp.js.map
|