@mcp-consultant-tools/powerplatform-customization 21.0.0 → 25.0.0-beta.2
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/build/PowerPlatformService.d.ts +23 -0
- package/build/PowerPlatformService.d.ts.map +1 -1
- package/build/PowerPlatformService.js +92 -8
- package/build/PowerPlatformService.js.map +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +303 -39
- package/build/index.js.map +1 -1
- package/build/utils/bestPractices.d.ts +73 -3
- package/build/utils/bestPractices.d.ts.map +1 -1
- package/build/utils/bestPractices.js +99 -81
- package/build/utils/bestPractices.js.map +1 -1
- package/build/utils/iconManager.d.ts.map +1 -1
- package/build/utils/iconManager.js +7 -3
- package/build/utils/iconManager.js.map +1 -1
- package/build/utils/publisherConfig.d.ts +33 -0
- package/build/utils/publisherConfig.d.ts.map +1 -0
- package/build/utils/publisherConfig.js +54 -0
- package/build/utils/publisherConfig.js.map +1 -0
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { pathToFileURL } from 'node:url';
|
|
|
5
5
|
import { realpathSync } from 'node:fs';
|
|
6
6
|
import { createMcpServer, createEnvLoader } from '@mcp-consultant-tools/core';
|
|
7
7
|
import { PowerPlatformService } from './PowerPlatformService.js';
|
|
8
|
+
import { initializePublisherPrefix } from './utils/publisherConfig.js';
|
|
8
9
|
const POWERPLATFORM_DEFAULT_SOLUTION = process.env.POWERPLATFORM_DEFAULT_SOLUTION || "";
|
|
9
10
|
/**
|
|
10
11
|
* Register powerplatform-customization tools with an MCP server
|
|
@@ -19,12 +20,15 @@ export function registerPowerplatformCustomizationTools(server, service) {
|
|
|
19
20
|
'POWERPLATFORM_URL',
|
|
20
21
|
'POWERPLATFORM_CLIENT_ID',
|
|
21
22
|
'POWERPLATFORM_CLIENT_SECRET',
|
|
22
|
-
'POWERPLATFORM_TENANT_ID'
|
|
23
|
+
'POWERPLATFORM_TENANT_ID',
|
|
24
|
+
'PUBLISHER_PREFIX'
|
|
23
25
|
];
|
|
24
26
|
const missing = requiredVars.filter(v => !process.env[v]);
|
|
25
27
|
if (missing.length > 0) {
|
|
26
28
|
throw new Error(`Missing required PowerPlatform configuration: ${missing.join(', ')}`);
|
|
27
29
|
}
|
|
30
|
+
// Initialize publisher prefix for validation
|
|
31
|
+
initializePublisherPrefix();
|
|
28
32
|
const config = {
|
|
29
33
|
organizationUrl: process.env.POWERPLATFORM_URL,
|
|
30
34
|
clientId: process.env.POWERPLATFORM_CLIENT_ID,
|
|
@@ -788,12 +792,39 @@ export function registerPowerplatformCustomizationTools(server, service) {
|
|
|
788
792
|
};
|
|
789
793
|
}
|
|
790
794
|
});
|
|
791
|
-
server.tool("update-attribute", "Update an existing attribute on an entity. Supports
|
|
795
|
+
server.tool("update-attribute", "Update an existing attribute on an entity. Supports updating display properties, numeric constraints, date/time format, string format, and auditing settings.", {
|
|
792
796
|
entityLogicalName: z.string().describe("Entity logical name"),
|
|
793
797
|
attributeLogicalName: z.string().describe("Attribute logical name"),
|
|
794
798
|
displayName: z.string().optional().describe("New display name"),
|
|
795
799
|
description: z.string().optional().describe("New description"),
|
|
796
800
|
requiredLevel: z.enum(["None", "Recommended", "ApplicationRequired"]).optional().describe("Required level"),
|
|
801
|
+
// String/Memo properties
|
|
802
|
+
maxLength: z.number().optional().describe("Maximum length for String/Memo attributes. " +
|
|
803
|
+
"String (single-line): 1-4000 characters. " +
|
|
804
|
+
"Memo (multi-line): 1-1048576 characters. " +
|
|
805
|
+
"Note: Reducing maxLength won't truncate existing data."),
|
|
806
|
+
formatName: z.enum(["Text", "TextArea", "Email", "Phone", "Url", "TickerSymbol"]).optional().describe("Format for String attributes. Text=single line, TextArea=multi-line, " +
|
|
807
|
+
"Email/Phone/Url/TickerSymbol=formatted input with validation."),
|
|
808
|
+
// Numeric properties (Integer, Decimal, Money)
|
|
809
|
+
minValue: z.number().optional().describe("Minimum value for Integer/Decimal/Money attributes. " +
|
|
810
|
+
"Integer: -2147483648 to 2147483647. " +
|
|
811
|
+
"Decimal: -100000000000 to 100000000000. " +
|
|
812
|
+
"Money: -922337203685477 to 922337203685477."),
|
|
813
|
+
maxValue: z.number().optional().describe("Maximum value for Integer/Decimal/Money attributes. " +
|
|
814
|
+
"Integer: -2147483648 to 2147483647. " +
|
|
815
|
+
"Decimal: -100000000000 to 100000000000. " +
|
|
816
|
+
"Money: -922337203685477 to 922337203685477."),
|
|
817
|
+
precision: z.number().optional().describe("Decimal precision for Decimal/Money attributes (0-10 decimal places)."),
|
|
818
|
+
precisionSource: z.enum(["Precision", "Pricing", "Currency"]).optional().describe("Precision source for Money attributes. " +
|
|
819
|
+
"Precision=use precision property, Pricing=org pricing setting, Currency=currency precision."),
|
|
820
|
+
// DateTime properties
|
|
821
|
+
format: z.enum(["DateAndTime", "DateOnly"]).optional().describe("Display format for DateTime attributes."),
|
|
822
|
+
dateTimeBehavior: z.enum(["UserLocal", "DateOnly", "TimeZoneIndependent"]).optional().describe("Time zone behavior for DateTime attributes. Only changeable if CanChangeDateTimeBehavior=true. " +
|
|
823
|
+
"UserLocal=adjusted to user timezone, DateOnly=date without time, TimeZoneIndependent=stored as-is."),
|
|
824
|
+
// Auditing and search properties
|
|
825
|
+
isAuditEnabled: z.boolean().optional().describe("Enable/disable auditing for this attribute. Tracks changes to field values."),
|
|
826
|
+
isValidForAdvancedFind: z.boolean().optional().describe("Show this attribute in Advanced Find queries."),
|
|
827
|
+
// AutoNumber conversion
|
|
797
828
|
autoNumberFormat: z.string().optional().describe("Auto-number format string to convert String attribute to AutoNumber. " +
|
|
798
829
|
"Use placeholders: {SEQNUM:n} for sequential number (min length n), " +
|
|
799
830
|
"{RANDSTRING:n} for random alphanumeric (length 1-6 only), " +
|
|
@@ -819,6 +850,69 @@ export function registerPowerplatformCustomizationTools(server, service) {
|
|
|
819
850
|
if (params.requiredLevel) {
|
|
820
851
|
updates.RequiredLevel = { Value: params.requiredLevel, CanBeChanged: true };
|
|
821
852
|
}
|
|
853
|
+
// Handle MaxLength update for String/Memo attributes
|
|
854
|
+
if (params.maxLength !== undefined) {
|
|
855
|
+
if (params.maxLength < 1) {
|
|
856
|
+
throw new Error("maxLength must be at least 1 character.");
|
|
857
|
+
}
|
|
858
|
+
if (params.maxLength > 1048576) {
|
|
859
|
+
throw new Error("maxLength cannot exceed 1,048,576 characters (Memo maximum).");
|
|
860
|
+
}
|
|
861
|
+
updates.MaxLength = params.maxLength;
|
|
862
|
+
}
|
|
863
|
+
// Handle FormatName update for String attributes
|
|
864
|
+
if (params.formatName) {
|
|
865
|
+
updates.FormatName = { Value: params.formatName };
|
|
866
|
+
}
|
|
867
|
+
// Handle MinValue update for Integer/Decimal/Money attributes
|
|
868
|
+
if (params.minValue !== undefined) {
|
|
869
|
+
updates.MinValue = params.minValue;
|
|
870
|
+
}
|
|
871
|
+
// Handle MaxValue update for Integer/Decimal/Money attributes
|
|
872
|
+
if (params.maxValue !== undefined) {
|
|
873
|
+
updates.MaxValue = params.maxValue;
|
|
874
|
+
}
|
|
875
|
+
// Handle Precision update for Decimal/Money attributes
|
|
876
|
+
if (params.precision !== undefined) {
|
|
877
|
+
if (params.precision < 0 || params.precision > 10) {
|
|
878
|
+
throw new Error("precision must be between 0 and 10 decimal places.");
|
|
879
|
+
}
|
|
880
|
+
updates.Precision = params.precision;
|
|
881
|
+
}
|
|
882
|
+
// Handle PrecisionSource update for Money attributes
|
|
883
|
+
if (params.precisionSource) {
|
|
884
|
+
const precisionSourceMap = {
|
|
885
|
+
"Precision": 0,
|
|
886
|
+
"Pricing": 1,
|
|
887
|
+
"Currency": 2
|
|
888
|
+
};
|
|
889
|
+
updates.PrecisionSource = precisionSourceMap[params.precisionSource];
|
|
890
|
+
}
|
|
891
|
+
// Handle Format update for DateTime attributes
|
|
892
|
+
if (params.format) {
|
|
893
|
+
updates.Format = params.format;
|
|
894
|
+
}
|
|
895
|
+
// Handle DateTimeBehavior update for DateTime attributes
|
|
896
|
+
if (params.dateTimeBehavior) {
|
|
897
|
+
updates.DateTimeBehavior = {
|
|
898
|
+
"@odata.type": "Microsoft.Dynamics.CRM.DateTimeBehavior",
|
|
899
|
+
Value: params.dateTimeBehavior
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
// Handle IsAuditEnabled update
|
|
903
|
+
if (params.isAuditEnabled !== undefined) {
|
|
904
|
+
updates.IsAuditEnabled = {
|
|
905
|
+
Value: params.isAuditEnabled,
|
|
906
|
+
CanBeChanged: true
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
// Handle IsValidForAdvancedFind update
|
|
910
|
+
if (params.isValidForAdvancedFind !== undefined) {
|
|
911
|
+
updates.IsValidForAdvancedFind = {
|
|
912
|
+
Value: params.isValidForAdvancedFind,
|
|
913
|
+
CanBeChanged: true
|
|
914
|
+
};
|
|
915
|
+
}
|
|
822
916
|
// Handle AutoNumber format conversion
|
|
823
917
|
if (params.autoNumberFormat) {
|
|
824
918
|
// Validate RANDSTRING lengths (common error - API rejects length > 6)
|
|
@@ -841,10 +935,45 @@ export function registerPowerplatformCustomizationTools(server, service) {
|
|
|
841
935
|
}
|
|
842
936
|
await service.updateAttribute(params.entityLogicalName, params.attributeLogicalName, updates, params.solutionUniqueName);
|
|
843
937
|
let successMessage = `✅ Successfully updated attribute '${params.attributeLogicalName}' on entity '${params.entityLogicalName}'`;
|
|
938
|
+
// Build list of changes made
|
|
939
|
+
const changes = [];
|
|
940
|
+
if (params.displayName)
|
|
941
|
+
changes.push(`Display name: "${params.displayName}"`);
|
|
942
|
+
if (params.description)
|
|
943
|
+
changes.push(`Description updated`);
|
|
944
|
+
if (params.requiredLevel)
|
|
945
|
+
changes.push(`Required level: ${params.requiredLevel}`);
|
|
946
|
+
if (params.maxLength !== undefined)
|
|
947
|
+
changes.push(`Max length: ${params.maxLength} characters`);
|
|
948
|
+
if (params.formatName)
|
|
949
|
+
changes.push(`Format: ${params.formatName}`);
|
|
950
|
+
if (params.minValue !== undefined)
|
|
951
|
+
changes.push(`Min value: ${params.minValue}`);
|
|
952
|
+
if (params.maxValue !== undefined)
|
|
953
|
+
changes.push(`Max value: ${params.maxValue}`);
|
|
954
|
+
if (params.precision !== undefined)
|
|
955
|
+
changes.push(`Precision: ${params.precision} decimal places`);
|
|
956
|
+
if (params.precisionSource)
|
|
957
|
+
changes.push(`Precision source: ${params.precisionSource}`);
|
|
958
|
+
if (params.format)
|
|
959
|
+
changes.push(`DateTime format: ${params.format}`);
|
|
960
|
+
if (params.dateTimeBehavior)
|
|
961
|
+
changes.push(`DateTime behavior: ${params.dateTimeBehavior}`);
|
|
962
|
+
if (params.isAuditEnabled !== undefined)
|
|
963
|
+
changes.push(`Auditing: ${params.isAuditEnabled ? 'enabled' : 'disabled'}`);
|
|
964
|
+
if (params.isValidForAdvancedFind !== undefined)
|
|
965
|
+
changes.push(`Advanced Find: ${params.isValidForAdvancedFind ? 'enabled' : 'disabled'}`);
|
|
966
|
+
if (params.autoNumberFormat)
|
|
967
|
+
changes.push(`AutoNumber format: ${params.autoNumberFormat}`);
|
|
968
|
+
if (changes.length > 0) {
|
|
969
|
+
successMessage += `\n\n📝 Changes made:\n${changes.map(c => ` • ${c}`).join('\n')}`;
|
|
970
|
+
}
|
|
844
971
|
if (params.autoNumberFormat) {
|
|
845
|
-
successMessage += `\n\n📋 Auto-number format set to: ${params.autoNumberFormat}`;
|
|
846
972
|
successMessage += `\n\n⚠️ NOTE: Converting to AutoNumber is irreversible. The attribute will now auto-generate values based on the format.`;
|
|
847
973
|
}
|
|
974
|
+
if (params.dateTimeBehavior) {
|
|
975
|
+
successMessage += `\n\n⚠️ NOTE: DateTimeBehavior changes may affect existing data interpretation. Review dependent workflows and business rules.`;
|
|
976
|
+
}
|
|
848
977
|
successMessage += `\n\n⚠️ IMPORTANT: You must publish this customization using the 'publish-customizations' tool before it becomes active.`;
|
|
849
978
|
return {
|
|
850
979
|
content: [{ type: "text", text: successMessage }]
|
|
@@ -1979,43 +2108,51 @@ export function registerPowerplatformCustomizationTools(server, service) {
|
|
|
1979
2108
|
solutionUniqueName: z.string().optional(),
|
|
1980
2109
|
replaceExisting: z.boolean().optional().describe("Update existing assembly vs. create new"),
|
|
1981
2110
|
}, async (params) => {
|
|
2111
|
+
const service = getPowerPlatformService();
|
|
2112
|
+
// Track created resources for potential rollback
|
|
2113
|
+
let createdAssemblyId = null;
|
|
2114
|
+
let isNewAssembly = false;
|
|
2115
|
+
const createdStepIds = [];
|
|
2116
|
+
const summary = {
|
|
2117
|
+
phases: {
|
|
2118
|
+
deploy: {},
|
|
2119
|
+
register: { stepsCreated: 0, imagesCreated: 0 },
|
|
2120
|
+
},
|
|
2121
|
+
};
|
|
2122
|
+
let dllBuffer;
|
|
1982
2123
|
try {
|
|
1983
|
-
const service = getPowerPlatformService();
|
|
1984
|
-
const summary = {
|
|
1985
|
-
phases: {
|
|
1986
|
-
deploy: {},
|
|
1987
|
-
register: { stepsCreated: 0, imagesCreated: 0 },
|
|
1988
|
-
},
|
|
1989
|
-
};
|
|
1990
2124
|
// Read DLL file
|
|
1991
2125
|
const fs = await import('fs/promises');
|
|
1992
2126
|
const normalizedPath = params.assemblyPath.replace(/\\/g, '/');
|
|
1993
|
-
|
|
2127
|
+
dllBuffer = await fs.readFile(normalizedPath);
|
|
1994
2128
|
const dllBase64 = dllBuffer.toString('base64');
|
|
1995
2129
|
const version = await service.extractAssemblyVersion(params.assemblyPath);
|
|
1996
|
-
// Phase 1: Deploy assembly
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
}
|
|
2008
|
-
else {
|
|
2009
|
-
throw new Error(`Assembly '${params.assemblyName}' not found for update`);
|
|
2010
|
-
}
|
|
2130
|
+
// Phase 1: Deploy assembly (UPSERT logic)
|
|
2131
|
+
const existingAssemblyId = await service.queryPluginAssemblyByName(params.assemblyName);
|
|
2132
|
+
if (existingAssemblyId) {
|
|
2133
|
+
// Assembly exists - update it (no rollback needed for updates)
|
|
2134
|
+
await service.updatePluginAssembly(existingAssemblyId, dllBase64, version, params.solutionUniqueName || POWERPLATFORM_DEFAULT_SOLUTION);
|
|
2135
|
+
summary.phases.deploy = {
|
|
2136
|
+
action: 'updated',
|
|
2137
|
+
assemblyId: existingAssemblyId,
|
|
2138
|
+
version,
|
|
2139
|
+
pluginTypes: await service.getPluginTypesForAssembly(existingAssemblyId),
|
|
2140
|
+
};
|
|
2011
2141
|
}
|
|
2012
2142
|
else {
|
|
2143
|
+
// Assembly doesn't exist - create it
|
|
2144
|
+
if (params.replaceExisting) {
|
|
2145
|
+
// User expected update but assembly not found - warn but proceed with create
|
|
2146
|
+
console.error(`Warning: Assembly '${params.assemblyName}' not found for update. Creating new assembly instead.`);
|
|
2147
|
+
}
|
|
2013
2148
|
const uploadResult = await service.createPluginAssembly({
|
|
2014
2149
|
name: params.assemblyName,
|
|
2015
2150
|
content: dllBase64,
|
|
2016
2151
|
version,
|
|
2017
2152
|
solutionUniqueName: params.solutionUniqueName || POWERPLATFORM_DEFAULT_SOLUTION,
|
|
2018
2153
|
});
|
|
2154
|
+
createdAssemblyId = uploadResult.pluginAssemblyId; // Track for rollback
|
|
2155
|
+
isNewAssembly = true;
|
|
2019
2156
|
summary.phases.deploy = {
|
|
2020
2157
|
action: 'created',
|
|
2021
2158
|
assemblyId: uploadResult.pluginAssemblyId,
|
|
@@ -2023,7 +2160,7 @@ export function registerPowerplatformCustomizationTools(server, service) {
|
|
|
2023
2160
|
pluginTypes: uploadResult.pluginTypes,
|
|
2024
2161
|
};
|
|
2025
2162
|
}
|
|
2026
|
-
// Phase 2: Register steps
|
|
2163
|
+
// Phase 2: Register steps (with rollback tracking)
|
|
2027
2164
|
if (params.stepConfigurations) {
|
|
2028
2165
|
const stageMap = {
|
|
2029
2166
|
PreValidation: 10,
|
|
@@ -2046,6 +2183,7 @@ export function registerPowerplatformCustomizationTools(server, service) {
|
|
|
2046
2183
|
filteringAttributes: stepConfig.filteringAttributes?.join(','),
|
|
2047
2184
|
solutionUniqueName: params.solutionUniqueName || POWERPLATFORM_DEFAULT_SOLUTION,
|
|
2048
2185
|
});
|
|
2186
|
+
createdStepIds.push(stepResult.stepId); // Track for rollback
|
|
2049
2187
|
summary.phases.register.stepsCreated++;
|
|
2050
2188
|
// Register pre-image
|
|
2051
2189
|
if (stepConfig.preImage) {
|
|
@@ -2077,28 +2215,154 @@ export function registerPowerplatformCustomizationTools(server, service) {
|
|
|
2077
2215
|
return {
|
|
2078
2216
|
content: [{
|
|
2079
2217
|
type: "text",
|
|
2080
|
-
text:
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
(summary.phases.deploy.pluginTypes ?
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2218
|
+
text: `Plugin deployment completed successfully!\n\n` +
|
|
2219
|
+
`Assembly: ${summary.phases.deploy.action === 'created' ? 'Created' : 'Updated'}\n` +
|
|
2220
|
+
`Assembly ID: ${summary.phases.deploy.assemblyId}\n` +
|
|
2221
|
+
`Version: ${summary.phases.deploy.version}\n` +
|
|
2222
|
+
`Size: ${(dllBuffer.length / 1024).toFixed(2)} KB\n` +
|
|
2223
|
+
(summary.phases.deploy.pluginTypes ? `Plugin Types: ${summary.phases.deploy.pluginTypes.length}\n` : '') +
|
|
2224
|
+
`Steps Created: ${summary.phases.register.stepsCreated}\n` +
|
|
2225
|
+
`Images Created: ${summary.phases.register.imagesCreated}\n` +
|
|
2226
|
+
`Published: ${summary.phases.publish.success ? 'Yes' : 'No'}\n\n` +
|
|
2227
|
+
`Deployment is complete and active in the environment!`
|
|
2090
2228
|
}]
|
|
2091
2229
|
};
|
|
2092
2230
|
}
|
|
2093
2231
|
catch (error) {
|
|
2232
|
+
// ROLLBACK: Clean up created resources on failure
|
|
2233
|
+
let rollbackMessage = '';
|
|
2234
|
+
if (createdStepIds.length > 0 || (createdAssemblyId && isNewAssembly)) {
|
|
2235
|
+
rollbackMessage = '\n\nRollback initiated:\n';
|
|
2236
|
+
// Delete created steps first (reverse order)
|
|
2237
|
+
for (const stepId of createdStepIds.reverse()) {
|
|
2238
|
+
try {
|
|
2239
|
+
await service.deletePluginStep(stepId);
|
|
2240
|
+
rollbackMessage += `- Deleted step: ${stepId}\n`;
|
|
2241
|
+
}
|
|
2242
|
+
catch (rollbackError) {
|
|
2243
|
+
rollbackMessage += `- Failed to delete step ${stepId}: ${rollbackError.message}\n`;
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
// Delete the assembly if we created it (cascade deletes remaining components)
|
|
2247
|
+
if (createdAssemblyId && isNewAssembly) {
|
|
2248
|
+
try {
|
|
2249
|
+
await service.deletePluginAssembly(createdAssemblyId);
|
|
2250
|
+
rollbackMessage += `- Deleted assembly: ${createdAssemblyId}\n`;
|
|
2251
|
+
}
|
|
2252
|
+
catch (rollbackError) {
|
|
2253
|
+
rollbackMessage += `- Failed to delete assembly ${createdAssemblyId}: ${rollbackError.message}\n`;
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
rollbackMessage += '\nPlease verify cleanup in Power Platform.';
|
|
2257
|
+
}
|
|
2094
2258
|
console.error("Error deploying plugin:", error);
|
|
2095
2259
|
return {
|
|
2096
|
-
content: [{ type: "text", text:
|
|
2260
|
+
content: [{ type: "text", text: `Failed to deploy plugin: ${error.message}${rollbackMessage}` }],
|
|
2261
|
+
isError: true
|
|
2262
|
+
};
|
|
2263
|
+
}
|
|
2264
|
+
});
|
|
2265
|
+
server.tool("get-plugin-deployment-status", "Get the current deployment status of a plugin assembly, including all registered types, steps, and images. Useful for verifying deployments and troubleshooting.", {
|
|
2266
|
+
assemblyName: z.string().describe("Name of the plugin assembly to check"),
|
|
2267
|
+
includeDisabled: z.boolean().optional().describe("Include disabled steps (default: false)"),
|
|
2268
|
+
}, async (params) => {
|
|
2269
|
+
try {
|
|
2270
|
+
const service = getPowerPlatformService();
|
|
2271
|
+
// Check if assembly exists
|
|
2272
|
+
const assemblyId = await service.queryPluginAssemblyByName(params.assemblyName);
|
|
2273
|
+
if (!assemblyId) {
|
|
2274
|
+
return {
|
|
2275
|
+
content: [{
|
|
2276
|
+
type: "text",
|
|
2277
|
+
text: `Assembly '${params.assemblyName}' not found in Dataverse.\n\n` +
|
|
2278
|
+
`Possible reasons:\n` +
|
|
2279
|
+
`- Assembly has not been deployed yet\n` +
|
|
2280
|
+
`- Assembly name is incorrect (case-sensitive)\n` +
|
|
2281
|
+
`- Assembly was deleted\n\n` +
|
|
2282
|
+
`Use 'create-plugin-assembly' or 'deploy-plugin-complete' to deploy.`
|
|
2283
|
+
}]
|
|
2284
|
+
};
|
|
2285
|
+
}
|
|
2286
|
+
// Get complete assembly information
|
|
2287
|
+
const result = await service.getPluginAssemblyComplete(params.assemblyName, params.includeDisabled || false);
|
|
2288
|
+
// Build status report
|
|
2289
|
+
let statusReport = `PLUGIN DEPLOYMENT STATUS\n`;
|
|
2290
|
+
statusReport += `${'='.repeat(50)}\n\n`;
|
|
2291
|
+
// Assembly info
|
|
2292
|
+
statusReport += `ASSEMBLY\n`;
|
|
2293
|
+
statusReport += `---------\n`;
|
|
2294
|
+
statusReport += `Name: ${result.assembly.name}\n`;
|
|
2295
|
+
statusReport += `Version: ${result.assembly.version}\n`;
|
|
2296
|
+
statusReport += `ID: ${result.assembly.pluginassemblyid}\n`;
|
|
2297
|
+
statusReport += `Isolation Mode: ${result.assembly.isolationmode === 2 ? 'Sandbox' : 'None'}\n`;
|
|
2298
|
+
statusReport += `Is Managed: ${result.assembly.ismanaged ? 'Yes' : 'No'}\n`;
|
|
2299
|
+
statusReport += `Modified: ${result.assembly.modifiedon}\n`;
|
|
2300
|
+
statusReport += `Modified By: ${result.assembly.modifiedby?.fullname || 'Unknown'}\n\n`;
|
|
2301
|
+
// Plugin types
|
|
2302
|
+
statusReport += `PLUGIN TYPES (${result.pluginTypes.length})\n`;
|
|
2303
|
+
statusReport += `-------------\n`;
|
|
2304
|
+
if (result.pluginTypes.length === 0) {
|
|
2305
|
+
statusReport += `No plugin types found. This may indicate:\n`;
|
|
2306
|
+
statusReport += `- Dataverse is still processing the assembly\n`;
|
|
2307
|
+
statusReport += `- The DLL does not contain any IPlugin implementations\n\n`;
|
|
2308
|
+
}
|
|
2309
|
+
else {
|
|
2310
|
+
for (const type of result.pluginTypes) {
|
|
2311
|
+
statusReport += `- ${type.typename}\n`;
|
|
2312
|
+
statusReport += ` ID: ${type.plugintypeid}\n`;
|
|
2313
|
+
}
|
|
2314
|
+
statusReport += `\n`;
|
|
2315
|
+
}
|
|
2316
|
+
// Steps
|
|
2317
|
+
const stageNames = { 10: 'PreValidation', 20: 'PreOperation', 40: 'PostOperation' };
|
|
2318
|
+
const modeNames = { 0: 'Sync', 1: 'Async' };
|
|
2319
|
+
statusReport += `REGISTERED STEPS (${result.steps.length})\n`;
|
|
2320
|
+
statusReport += `------------------\n`;
|
|
2321
|
+
if (result.steps.length === 0) {
|
|
2322
|
+
statusReport += `No steps registered.\n\n`;
|
|
2323
|
+
}
|
|
2324
|
+
else {
|
|
2325
|
+
for (const step of result.steps) {
|
|
2326
|
+
const status = step.statuscode === 1 ? 'Active' : 'Disabled';
|
|
2327
|
+
statusReport += `- ${step.name}\n`;
|
|
2328
|
+
statusReport += ` Message: ${step.sdkmessageid?.name || 'Unknown'} on ${step.sdkmessagefilterid?.primaryobjecttypecode || 'Unknown'}\n`;
|
|
2329
|
+
statusReport += ` Stage: ${stageNames[step.stage] || step.stage}, Mode: ${modeNames[step.mode] || step.mode}\n`;
|
|
2330
|
+
statusReport += ` Status: ${status}, Rank: ${step.rank}\n`;
|
|
2331
|
+
statusReport += ` ID: ${step.sdkmessageprocessingstepid}\n`;
|
|
2332
|
+
if (step.images && step.images.length > 0) {
|
|
2333
|
+
statusReport += ` Images:\n`;
|
|
2334
|
+
for (const img of step.images) {
|
|
2335
|
+
const imgType = img.imagetype === 0 ? 'Pre' : img.imagetype === 1 ? 'Post' : 'Both';
|
|
2336
|
+
statusReport += ` - ${img.name} (${imgType}Image, alias: ${img.entityalias})\n`;
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
statusReport += `\n`;
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
// Validation
|
|
2343
|
+
statusReport += `VALIDATION\n`;
|
|
2344
|
+
statusReport += `----------\n`;
|
|
2345
|
+
if (result.validation.potentialIssues.length === 0) {
|
|
2346
|
+
statusReport += `No issues detected.\n`;
|
|
2347
|
+
}
|
|
2348
|
+
else {
|
|
2349
|
+
for (const issue of result.validation.potentialIssues) {
|
|
2350
|
+
statusReport += `Warning: ${issue}\n`;
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
return {
|
|
2354
|
+
content: [{ type: "text", text: statusReport }]
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
catch (error) {
|
|
2358
|
+
console.error("Error getting plugin deployment status:", error);
|
|
2359
|
+
return {
|
|
2360
|
+
content: [{ type: "text", text: `Failed to get plugin deployment status: ${error.message}` }],
|
|
2097
2361
|
isError: true
|
|
2098
2362
|
};
|
|
2099
2363
|
}
|
|
2100
2364
|
});
|
|
2101
|
-
console.error(
|
|
2365
|
+
console.error(`powerplatform-customization tools registered (${46} tools)`);
|
|
2102
2366
|
}
|
|
2103
2367
|
// CLI entry point (standalone execution)
|
|
2104
2368
|
// Uses realpathSync to resolve symlinks created by npx
|