powerplatform-mcp 0.4.4 → 1.0.0
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/README.md +38 -49
- package/build/PowerPlatformClient.js +83 -0
- package/build/index.js +47 -632
- package/build/models/ApiCollectionResponse.js +1 -0
- package/build/models/index.js +1 -0
- package/build/prompts/entityPrompts.js +244 -0
- package/build/prompts/index.js +9 -0
- package/build/prompts/templates.js +34 -0
- package/build/services/EntityService.js +128 -0
- package/build/services/OptionSetService.js +18 -0
- package/build/services/PluginService.js +288 -0
- package/build/services/RecordService.js +29 -0
- package/build/services/index.js +4 -0
- package/build/tools/entityTools.js +156 -0
- package/build/tools/index.js +17 -0
- package/build/tools/optionSetTools.js +43 -0
- package/build/tools/pluginTools.js +188 -0
- package/build/tools/recordTools.js +87 -0
- package/build/types.js +1 -0
- package/package.json +12 -7
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { powerPlatformPrompts } from "./templates.js";
|
|
3
|
+
/**
|
|
4
|
+
* Register entity prompts with the MCP server.
|
|
5
|
+
*/
|
|
6
|
+
export function registerEntityPrompts(server, ctx) {
|
|
7
|
+
// Entity Overview Prompt
|
|
8
|
+
server.registerPrompt("entity-overview", {
|
|
9
|
+
title: "Entity Overview",
|
|
10
|
+
description: "Get an overview of a Power Platform entity",
|
|
11
|
+
argsSchema: {
|
|
12
|
+
entityName: z.string().describe("The logical name of the entity"),
|
|
13
|
+
},
|
|
14
|
+
}, async (args) => {
|
|
15
|
+
try {
|
|
16
|
+
const service = ctx.getEntityService();
|
|
17
|
+
const entityName = args.entityName;
|
|
18
|
+
// Get entity metadata and key attributes
|
|
19
|
+
const [metadata, attributes] = await Promise.all([
|
|
20
|
+
service.getEntityMetadata(entityName),
|
|
21
|
+
service.getEntityAttributes(entityName),
|
|
22
|
+
]);
|
|
23
|
+
// Format entity details
|
|
24
|
+
const entityDetails = `- Display Name: ${metadata.DisplayName?.UserLocalizedLabel?.Label || entityName}\n` +
|
|
25
|
+
`- Schema Name: ${metadata.SchemaName}\n` +
|
|
26
|
+
`- Description: ${metadata.Description?.UserLocalizedLabel?.Label || "No description"}\n` +
|
|
27
|
+
`- Primary Key: ${metadata.PrimaryIdAttribute}\n` +
|
|
28
|
+
`- Primary Name: ${metadata.PrimaryNameAttribute}`;
|
|
29
|
+
// Get key attributes
|
|
30
|
+
const keyAttributes = attributes.value
|
|
31
|
+
.map((attr) => {
|
|
32
|
+
const attrType = attr["@odata.type"] || attr.odata?.type || "Unknown type";
|
|
33
|
+
return `- ${attr.LogicalName}: ${attrType}`;
|
|
34
|
+
})
|
|
35
|
+
.join("\n");
|
|
36
|
+
// Get relationships summary
|
|
37
|
+
const relationships = await service.getEntityRelationships(entityName);
|
|
38
|
+
const oneToManyCount = relationships.oneToMany.value.length;
|
|
39
|
+
const manyToManyCount = relationships.manyToMany.value.length;
|
|
40
|
+
const relationshipsSummary = `- One-to-Many Relationships: ${oneToManyCount}\n` +
|
|
41
|
+
`- Many-to-Many Relationships: ${manyToManyCount}`;
|
|
42
|
+
let promptContent = powerPlatformPrompts.ENTITY_OVERVIEW(entityName);
|
|
43
|
+
promptContent = promptContent
|
|
44
|
+
.replace("{{entity_details}}", entityDetails)
|
|
45
|
+
.replace("{{key_attributes}}", keyAttributes)
|
|
46
|
+
.replace("{{relationships}}", relationshipsSummary);
|
|
47
|
+
return {
|
|
48
|
+
messages: [
|
|
49
|
+
{
|
|
50
|
+
role: "assistant",
|
|
51
|
+
content: {
|
|
52
|
+
type: "text",
|
|
53
|
+
text: promptContent,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error(`Error handling entity-overview prompt:`, error);
|
|
61
|
+
return {
|
|
62
|
+
messages: [
|
|
63
|
+
{
|
|
64
|
+
role: "assistant",
|
|
65
|
+
content: {
|
|
66
|
+
type: "text",
|
|
67
|
+
text: `Error: ${error.message}`,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
// Attribute Details Prompt
|
|
75
|
+
server.registerPrompt("attribute-details", {
|
|
76
|
+
title: "Attribute Details",
|
|
77
|
+
description: "Get detailed information about a specific entity attribute/field",
|
|
78
|
+
argsSchema: {
|
|
79
|
+
entityName: z.string().describe("The logical name of the entity"),
|
|
80
|
+
attributeName: z.string().describe("The logical name of the attribute"),
|
|
81
|
+
},
|
|
82
|
+
}, async (args) => {
|
|
83
|
+
try {
|
|
84
|
+
const service = ctx.getEntityService();
|
|
85
|
+
const { entityName, attributeName } = args;
|
|
86
|
+
// Get attribute details
|
|
87
|
+
const attribute = await service.getEntityAttribute(entityName, attributeName);
|
|
88
|
+
// Format attribute details
|
|
89
|
+
const attrDetails = `- Display Name: ${attribute.DisplayName?.UserLocalizedLabel?.Label || attributeName}\n` +
|
|
90
|
+
`- Description: ${attribute.Description?.UserLocalizedLabel?.Label || "No description"}\n` +
|
|
91
|
+
`- Type: ${attribute.AttributeType}\n` +
|
|
92
|
+
`- Format: ${attribute.Format || "N/A"}\n` +
|
|
93
|
+
`- Is Required: ${attribute.RequiredLevel?.Value || "No"}\n` +
|
|
94
|
+
`- Is Searchable: ${attribute.IsValidForAdvancedFind || false}`;
|
|
95
|
+
let promptContent = powerPlatformPrompts.ATTRIBUTE_DETAILS(entityName, attributeName);
|
|
96
|
+
promptContent = promptContent
|
|
97
|
+
.replace("{{attribute_details}}", attrDetails)
|
|
98
|
+
.replace("{{data_type}}", attribute.AttributeType)
|
|
99
|
+
.replace("{{required}}", attribute.RequiredLevel?.Value || "No")
|
|
100
|
+
.replace("{{max_length}}", attribute.MaxLength || "N/A");
|
|
101
|
+
return {
|
|
102
|
+
messages: [
|
|
103
|
+
{
|
|
104
|
+
role: "assistant",
|
|
105
|
+
content: {
|
|
106
|
+
type: "text",
|
|
107
|
+
text: promptContent,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
console.error(`Error handling attribute-details prompt:`, error);
|
|
115
|
+
return {
|
|
116
|
+
messages: [
|
|
117
|
+
{
|
|
118
|
+
role: "assistant",
|
|
119
|
+
content: {
|
|
120
|
+
type: "text",
|
|
121
|
+
text: `Error: ${error.message}`,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
// Query Template Prompt
|
|
129
|
+
server.registerPrompt("query-template", {
|
|
130
|
+
title: "Query Template",
|
|
131
|
+
description: "Get a template for querying a Power Platform entity",
|
|
132
|
+
argsSchema: {
|
|
133
|
+
entityName: z.string().describe("The logical name of the entity"),
|
|
134
|
+
},
|
|
135
|
+
}, async (args) => {
|
|
136
|
+
try {
|
|
137
|
+
const service = ctx.getEntityService();
|
|
138
|
+
const entityName = args.entityName;
|
|
139
|
+
// Get entity metadata to determine plural name
|
|
140
|
+
const metadata = await service.getEntityMetadata(entityName);
|
|
141
|
+
const entityNamePlural = metadata.EntitySetName;
|
|
142
|
+
// Get a few important fields for the select example
|
|
143
|
+
const attributes = await service.getEntityAttributes(entityName);
|
|
144
|
+
const selectFields = attributes.value
|
|
145
|
+
.filter((attr) => attr.IsValidForRead === true && !attr.AttributeOf)
|
|
146
|
+
.slice(0, 5) // Just take first 5 for example
|
|
147
|
+
.map((attr) => attr.LogicalName)
|
|
148
|
+
.join(",");
|
|
149
|
+
let promptContent = powerPlatformPrompts.QUERY_TEMPLATE(entityNamePlural);
|
|
150
|
+
promptContent = promptContent
|
|
151
|
+
.replace("{{selected_fields}}", selectFields)
|
|
152
|
+
.replace("{{filter_conditions}}", `${metadata.PrimaryNameAttribute} eq 'Example'`)
|
|
153
|
+
.replace("{{order_by}}", `${metadata.PrimaryNameAttribute} asc`)
|
|
154
|
+
.replace("{{max_records}}", "50");
|
|
155
|
+
return {
|
|
156
|
+
messages: [
|
|
157
|
+
{
|
|
158
|
+
role: "assistant",
|
|
159
|
+
content: {
|
|
160
|
+
type: "text",
|
|
161
|
+
text: promptContent,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
console.error(`Error handling query-template prompt:`, error);
|
|
169
|
+
return {
|
|
170
|
+
messages: [
|
|
171
|
+
{
|
|
172
|
+
role: "assistant",
|
|
173
|
+
content: {
|
|
174
|
+
type: "text",
|
|
175
|
+
text: `Error: ${error.message}`,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
// Relationship Map Prompt
|
|
183
|
+
server.registerPrompt("relationship-map", {
|
|
184
|
+
title: "Relationship Map",
|
|
185
|
+
description: "Get a list of relationships for a Power Platform entity",
|
|
186
|
+
argsSchema: {
|
|
187
|
+
entityName: z.string().describe("The logical name of the entity"),
|
|
188
|
+
},
|
|
189
|
+
}, async (args) => {
|
|
190
|
+
try {
|
|
191
|
+
const service = ctx.getEntityService();
|
|
192
|
+
const entityName = args.entityName;
|
|
193
|
+
// Get relationships
|
|
194
|
+
const relationships = await service.getEntityRelationships(entityName);
|
|
195
|
+
// Format one-to-many relationships where this entity is primary
|
|
196
|
+
const oneToManyPrimary = relationships.oneToMany.value
|
|
197
|
+
.filter((rel) => rel.ReferencingEntity !== entityName)
|
|
198
|
+
.map((rel) => `- ${rel.SchemaName}: ${entityName} (1) → ${rel.ReferencingEntity} (N)`)
|
|
199
|
+
.join("\n");
|
|
200
|
+
// Format one-to-many relationships where this entity is related
|
|
201
|
+
const oneToManyRelated = relationships.oneToMany.value
|
|
202
|
+
.filter((rel) => rel.ReferencingEntity === entityName)
|
|
203
|
+
.map((rel) => `- ${rel.SchemaName}: ${rel.ReferencedEntity} (1) → ${entityName} (N)`)
|
|
204
|
+
.join("\n");
|
|
205
|
+
// Format many-to-many relationships
|
|
206
|
+
const manyToMany = relationships.manyToMany.value
|
|
207
|
+
.map((rel) => {
|
|
208
|
+
const otherEntity = rel.Entity1LogicalName === entityName ? rel.Entity2LogicalName : rel.Entity1LogicalName;
|
|
209
|
+
return `- ${rel.SchemaName}: ${entityName} (N) ↔ ${otherEntity} (N)`;
|
|
210
|
+
})
|
|
211
|
+
.join("\n");
|
|
212
|
+
let promptContent = powerPlatformPrompts.RELATIONSHIP_MAP(entityName);
|
|
213
|
+
promptContent = promptContent
|
|
214
|
+
.replace("{{one_to_many_primary}}", oneToManyPrimary || "None found")
|
|
215
|
+
.replace("{{one_to_many_related}}", oneToManyRelated || "None found")
|
|
216
|
+
.replace("{{many_to_many}}", manyToMany || "None found");
|
|
217
|
+
return {
|
|
218
|
+
messages: [
|
|
219
|
+
{
|
|
220
|
+
role: "assistant",
|
|
221
|
+
content: {
|
|
222
|
+
type: "text",
|
|
223
|
+
text: promptContent,
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
console.error(`Error handling relationship-map prompt:`, error);
|
|
231
|
+
return {
|
|
232
|
+
messages: [
|
|
233
|
+
{
|
|
234
|
+
role: "assistant",
|
|
235
|
+
content: {
|
|
236
|
+
type: "text",
|
|
237
|
+
text: `Error: ${error.message}`,
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { registerEntityPrompts } from "./entityPrompts.js";
|
|
2
|
+
export { registerEntityPrompts } from "./entityPrompts.js";
|
|
3
|
+
export { powerPlatformPrompts } from "./templates.js";
|
|
4
|
+
/**
|
|
5
|
+
* Register all prompts with the MCP server.
|
|
6
|
+
*/
|
|
7
|
+
export function registerAllPrompts(server, ctx) {
|
|
8
|
+
registerEntityPrompts(server, ctx);
|
|
9
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-defined PowerPlatform prompt templates.
|
|
3
|
+
*/
|
|
4
|
+
export const powerPlatformPrompts = {
|
|
5
|
+
// Entity exploration prompts
|
|
6
|
+
ENTITY_OVERVIEW: (entityName) => `## Power Platform Entity: ${entityName}\n\n` +
|
|
7
|
+
`This is an overview of the '${entityName}' entity in Microsoft Power Platform/Dataverse:\n\n` +
|
|
8
|
+
`### Entity Details\n{{entity_details}}\n\n` +
|
|
9
|
+
`### Attributes\n{{key_attributes}}\n\n` +
|
|
10
|
+
`### Relationships\n{{relationships}}\n\n` +
|
|
11
|
+
`You can query this entity using OData filters against the plural name.`,
|
|
12
|
+
ATTRIBUTE_DETAILS: (entityName, attributeName) => `## Attribute: ${attributeName}\n\n` +
|
|
13
|
+
`Details for the '${attributeName}' attribute of the '${entityName}' entity:\n\n` +
|
|
14
|
+
`{{attribute_details}}\n\n` +
|
|
15
|
+
`### Usage Notes\n` +
|
|
16
|
+
`- Data Type: {{data_type}}\n` +
|
|
17
|
+
`- Required: {{required}}\n` +
|
|
18
|
+
`- Max Length: {{max_length}}`,
|
|
19
|
+
// Query builder prompts
|
|
20
|
+
QUERY_TEMPLATE: (entityNamePlural) => `## OData Query Template for ${entityNamePlural}\n\n` +
|
|
21
|
+
`Use this template to build queries against the ${entityNamePlural} entity:\n\n` +
|
|
22
|
+
`\`\`\`\n${entityNamePlural}?$select={{selected_fields}}&$filter={{filter_conditions}}&$orderby={{order_by}}&$top={{max_records}}\n\`\`\`\n\n` +
|
|
23
|
+
`### Common Filter Examples\n` +
|
|
24
|
+
`- Equals: \`name eq 'Contoso'\`\n` +
|
|
25
|
+
`- Contains: \`contains(name, 'Contoso')\`\n` +
|
|
26
|
+
`- Greater than date: \`createdon gt 2023-01-01T00:00:00Z\`\n` +
|
|
27
|
+
`- Multiple conditions: \`name eq 'Contoso' and statecode eq 0\``,
|
|
28
|
+
// Relationship exploration prompts
|
|
29
|
+
RELATIONSHIP_MAP: (entityName) => `## Relationship Map for ${entityName}\n\n` +
|
|
30
|
+
`This shows all relationships for the '${entityName}' entity:\n\n` +
|
|
31
|
+
`### One-to-Many Relationships (${entityName} as Primary)\n{{one_to_many_primary}}\n\n` +
|
|
32
|
+
`### One-to-Many Relationships (${entityName} as Related)\n{{one_to_many_related}}\n\n` +
|
|
33
|
+
`### Many-to-Many Relationships\n{{many_to_many}}\n\n`,
|
|
34
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service for entity metadata operations.
|
|
3
|
+
* Handles entity definitions, attributes, and relationships.
|
|
4
|
+
*/
|
|
5
|
+
export class EntityService {
|
|
6
|
+
client;
|
|
7
|
+
constructor(client) {
|
|
8
|
+
this.client = client;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get metadata about an entity
|
|
12
|
+
* @param entityName The logical name of the entity
|
|
13
|
+
*/
|
|
14
|
+
async getEntityMetadata(entityName) {
|
|
15
|
+
const response = await this.client.get(`api/data/v9.2/EntityDefinitions(LogicalName='${entityName}')`);
|
|
16
|
+
// Remove Privileges property if it exists
|
|
17
|
+
if (response && typeof response === 'object' && 'Privileges' in response) {
|
|
18
|
+
delete response.Privileges;
|
|
19
|
+
}
|
|
20
|
+
return response;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get metadata about entity attributes/fields
|
|
24
|
+
* @param entityName The logical name of the entity
|
|
25
|
+
*/
|
|
26
|
+
async getEntityAttributes(entityName) {
|
|
27
|
+
const selectProperties = [
|
|
28
|
+
'LogicalName',
|
|
29
|
+
].join(',');
|
|
30
|
+
// Make the request to get attributes
|
|
31
|
+
const response = await this.client.get(`api/data/v9.2/EntityDefinitions(LogicalName='${entityName}')/Attributes?$select=${selectProperties}&$filter=AttributeType ne 'Virtual'`);
|
|
32
|
+
if (response && response.value) {
|
|
33
|
+
// First pass: Filter out attributes that end with 'yominame'
|
|
34
|
+
response.value = response.value.filter((attribute) => {
|
|
35
|
+
const logicalName = attribute.LogicalName || '';
|
|
36
|
+
return !logicalName.endsWith('yominame');
|
|
37
|
+
});
|
|
38
|
+
// Filter out attributes that end with 'name' if there is another attribute with the same name without the 'name' suffix
|
|
39
|
+
const baseNames = new Set();
|
|
40
|
+
const namesAttributes = new Map();
|
|
41
|
+
for (const attribute of response.value) {
|
|
42
|
+
const logicalName = attribute.LogicalName || '';
|
|
43
|
+
if (logicalName.endsWith('name') && logicalName.length > 4) {
|
|
44
|
+
const baseName = logicalName.slice(0, -4); // Remove 'name' suffix
|
|
45
|
+
namesAttributes.set(baseName, attribute);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// This is a potential base attribute
|
|
49
|
+
baseNames.add(logicalName);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Find attributes to remove that match the pattern
|
|
53
|
+
const attributesToRemove = new Set();
|
|
54
|
+
for (const [baseName, nameAttribute] of namesAttributes.entries()) {
|
|
55
|
+
if (baseNames.has(baseName)) {
|
|
56
|
+
attributesToRemove.add(nameAttribute);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
response.value = response.value.filter(attribute => !attributesToRemove.has(attribute));
|
|
60
|
+
}
|
|
61
|
+
return response;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get metadata about a specific entity attribute/field
|
|
65
|
+
* @param entityName The logical name of the entity
|
|
66
|
+
* @param attributeName The logical name of the attribute
|
|
67
|
+
*/
|
|
68
|
+
async getEntityAttribute(entityName, attributeName) {
|
|
69
|
+
return this.client.get(`api/data/v9.2/EntityDefinitions(LogicalName='${entityName}')/Attributes(LogicalName='${attributeName}')`);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get one-to-many relationships for an entity
|
|
73
|
+
* @param entityName The logical name of the entity
|
|
74
|
+
*/
|
|
75
|
+
async getEntityOneToManyRelationships(entityName) {
|
|
76
|
+
const selectProperties = [
|
|
77
|
+
'SchemaName',
|
|
78
|
+
'RelationshipType',
|
|
79
|
+
'ReferencedAttribute',
|
|
80
|
+
'ReferencedEntity',
|
|
81
|
+
'ReferencingAttribute',
|
|
82
|
+
'ReferencingEntity',
|
|
83
|
+
'ReferencedEntityNavigationPropertyName',
|
|
84
|
+
'ReferencingEntityNavigationPropertyName'
|
|
85
|
+
].join(',');
|
|
86
|
+
// Only filter by ReferencingAttribute in the OData query since startswith isn't supported
|
|
87
|
+
const response = await this.client.get(`api/data/v9.2/EntityDefinitions(LogicalName='${entityName}')/OneToManyRelationships?$select=${selectProperties}&$filter=ReferencingAttribute ne 'regardingobjectid'`);
|
|
88
|
+
// Filter the response to exclude relationships with ReferencingEntity starting with 'msdyn_' or 'adx_'
|
|
89
|
+
if (response && response.value) {
|
|
90
|
+
response.value = response.value.filter((relationship) => {
|
|
91
|
+
const referencingEntity = relationship.ReferencingEntity || '';
|
|
92
|
+
return !(referencingEntity.startsWith('msdyn_') || referencingEntity.startsWith('adx_'));
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return response;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get many-to-many relationships for an entity
|
|
99
|
+
* @param entityName The logical name of the entity
|
|
100
|
+
*/
|
|
101
|
+
async getEntityManyToManyRelationships(entityName) {
|
|
102
|
+
const selectProperties = [
|
|
103
|
+
'SchemaName',
|
|
104
|
+
'RelationshipType',
|
|
105
|
+
'Entity1LogicalName',
|
|
106
|
+
'Entity2LogicalName',
|
|
107
|
+
'Entity1IntersectAttribute',
|
|
108
|
+
'Entity2IntersectAttribute',
|
|
109
|
+
'Entity1NavigationPropertyName',
|
|
110
|
+
'Entity2NavigationPropertyName'
|
|
111
|
+
].join(',');
|
|
112
|
+
return this.client.get(`api/data/v9.2/EntityDefinitions(LogicalName='${entityName}')/ManyToManyRelationships?$select=${selectProperties}`);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get all relationships (one-to-many and many-to-many) for an entity
|
|
116
|
+
* @param entityName The logical name of the entity
|
|
117
|
+
*/
|
|
118
|
+
async getEntityRelationships(entityName) {
|
|
119
|
+
const [oneToMany, manyToMany] = await Promise.all([
|
|
120
|
+
this.getEntityOneToManyRelationships(entityName),
|
|
121
|
+
this.getEntityManyToManyRelationships(entityName)
|
|
122
|
+
]);
|
|
123
|
+
return {
|
|
124
|
+
oneToMany,
|
|
125
|
+
manyToMany
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service for option set operations.
|
|
3
|
+
* Handles global option set definitions.
|
|
4
|
+
*/
|
|
5
|
+
export class OptionSetService {
|
|
6
|
+
client;
|
|
7
|
+
constructor(client) {
|
|
8
|
+
this.client = client;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get a global option set definition by name
|
|
12
|
+
* @param optionSetName The name of the global option set
|
|
13
|
+
* @returns The global option set definition
|
|
14
|
+
*/
|
|
15
|
+
async getGlobalOptionSet(optionSetName) {
|
|
16
|
+
return this.client.get(`api/data/v9.2/GlobalOptionSetDefinitions(Name='${optionSetName}')`);
|
|
17
|
+
}
|
|
18
|
+
}
|