@mohsinonxrm/dataverse-sdk-metadata 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/LICENSE +661 -0
- package/README.md +602 -0
- package/dist/index.d.ts +12311 -0
- package/dist/index.js +1287 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
# @mohsinonxrm/dataverse-sdk-metadata
|
|
2
|
+
|
|
3
|
+
Complete TypeScript metadata management for Microsoft Dataverse - create, read, update, and delete entities, columns, relationships, and choices with full type safety.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Entity definitions** - create, read, update, delete custom tables
|
|
8
|
+
- ✅ **Column definitions** - all attribute types with strict typing:
|
|
9
|
+
- Text (Single Line, Multiple Lines)
|
|
10
|
+
- Numbers (Integer, Decimal, Currency)
|
|
11
|
+
- Date & Time
|
|
12
|
+
- Choices (Local & Global)
|
|
13
|
+
- Yes/No (Boolean)
|
|
14
|
+
- Lookup (Single-table & Polymorphic)
|
|
15
|
+
- File & Image
|
|
16
|
+
- ✅ **Relationships** - one-to-many (1:N), many-to-one (N:1), many-to-many (N:N)
|
|
17
|
+
- ✅ **Global choices (option sets)** - create, update, delete global choice lists
|
|
18
|
+
- ✅ **Option value management** - insert, update, delete, reorder choice options
|
|
19
|
+
- ✅ **Polymorphic lookups** - customer and multi-table lookup columns
|
|
20
|
+
- ✅ **Relationship eligibility** - check which entities can participate in relationships
|
|
21
|
+
- ✅ **Metadata change tracking** - RetrieveMetadataChanges with version stamps
|
|
22
|
+
- ✅ **Strict typing** - separate Read/Create/Update types, internal fields excluded
|
|
23
|
+
- ✅ **ESM-only** - works in Node 18+ and browsers
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm add @mohsinonxrm/dataverse-sdk-metadata @mohsinonxrm/dataverse-sdk-core
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { DataverseClient } from "@mohsinonxrm/dataverse-sdk-core";
|
|
35
|
+
import { MetadataClient } from "@mohsinonxrm/dataverse-sdk-metadata";
|
|
36
|
+
|
|
37
|
+
const client = new DataverseClient({
|
|
38
|
+
baseUrl: "https://yourorg.crm.dynamics.com",
|
|
39
|
+
tokenProvider: yourTokenProvider,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const metadataClient = new MetadataClient(client);
|
|
43
|
+
|
|
44
|
+
// Create a custom table
|
|
45
|
+
const entityId = await metadataClient.createEntityDefinition({
|
|
46
|
+
"@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata",
|
|
47
|
+
SchemaName: "new_Project",
|
|
48
|
+
DisplayName: {
|
|
49
|
+
LocalizedLabels: [{ Label: "Project", LanguageCode: 1033 }],
|
|
50
|
+
},
|
|
51
|
+
DisplayCollectionName: {
|
|
52
|
+
LocalizedLabels: [{ Label: "Projects", LanguageCode: 1033 }],
|
|
53
|
+
},
|
|
54
|
+
OwnershipType: 1, // UserOwned
|
|
55
|
+
HasActivities: true,
|
|
56
|
+
HasNotes: true,
|
|
57
|
+
Attributes: [
|
|
58
|
+
// Primary name column (required)
|
|
59
|
+
{
|
|
60
|
+
"@odata.type": "Microsoft.Dynamics.CRM.StringAttributeMetadata",
|
|
61
|
+
SchemaName: "new_Name",
|
|
62
|
+
RequiredLevel: { Value: "ApplicationRequired" },
|
|
63
|
+
MaxLength: 100,
|
|
64
|
+
DisplayName: {
|
|
65
|
+
LocalizedLabels: [{ Label: "Name", LanguageCode: 1033 }],
|
|
66
|
+
},
|
|
67
|
+
IsPrimaryName: true,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Entity Operations
|
|
76
|
+
|
|
77
|
+
### Create Entity
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
const metadataId = await metadataClient.createEntityDefinition({
|
|
81
|
+
"@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata",
|
|
82
|
+
SchemaName: "new_Task",
|
|
83
|
+
DisplayName: { LocalizedLabels: [{ Label: "Task", LanguageCode: 1033 }] },
|
|
84
|
+
DisplayCollectionName: { LocalizedLabels: [{ Label: "Tasks", LanguageCode: 1033 }] },
|
|
85
|
+
Description: { LocalizedLabels: [{ Label: "Project task tracking", LanguageCode: 1033 }] },
|
|
86
|
+
OwnershipType: 1, // UserOwned
|
|
87
|
+
IsActivity: false,
|
|
88
|
+
HasActivities: true,
|
|
89
|
+
HasNotes: true,
|
|
90
|
+
Attributes: [
|
|
91
|
+
{
|
|
92
|
+
"@odata.type": "Microsoft.Dynamics.CRM.StringAttributeMetadata",
|
|
93
|
+
SchemaName: "new_Name",
|
|
94
|
+
DisplayName: { LocalizedLabels: [{ Label: "Task Name", LanguageCode: 1033 }] },
|
|
95
|
+
RequiredLevel: { Value: "ApplicationRequired" },
|
|
96
|
+
MaxLength: 100,
|
|
97
|
+
IsPrimaryName: true,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Read Entities
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// Get all custom entities
|
|
107
|
+
const customEntities = await metadataClient.getEntityDefinitions({
|
|
108
|
+
filter: "IsCustomEntity eq true",
|
|
109
|
+
select: ["LogicalName", "DisplayName", "SchemaName"],
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Get specific entity with attributes expanded
|
|
113
|
+
const projectEntity = await metadataClient.getEntityDefinition("new_project", {
|
|
114
|
+
expand: "Attributes",
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Update Entity
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// Update entity display name
|
|
122
|
+
await metadataClient.updateEntityDefinition("new_project", {
|
|
123
|
+
"@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata",
|
|
124
|
+
DisplayName: { LocalizedLabels: [{ Label: "Project (Updated)", LanguageCode: 1033 }] },
|
|
125
|
+
Description: { LocalizedLabels: [{ Label: "Manage projects and tasks", LanguageCode: 1033 }] },
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Delete Entity
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
await metadataClient.deleteEntityDefinition("new_obsoleteentity");
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Column (Attribute) Operations
|
|
138
|
+
|
|
139
|
+
### Text Columns
|
|
140
|
+
|
|
141
|
+
#### Single Line of Text
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
await metadataClient.createAttributeDefinition("new_project", {
|
|
145
|
+
"@odata.type": "Microsoft.Dynamics.CRM.StringAttributeMetadata",
|
|
146
|
+
SchemaName: "new_ProjectCode",
|
|
147
|
+
DisplayName: { LocalizedLabels: [{ Label: "Project Code", LanguageCode: 1033 }] },
|
|
148
|
+
RequiredLevel: { Value: "None" },
|
|
149
|
+
MaxLength: 50,
|
|
150
|
+
Format: "Text",
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### Multiple Lines of Text
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
await metadataClient.createAttributeDefinition("new_project", {
|
|
158
|
+
"@odata.type": "Microsoft.Dynamics.CRM.MemoAttributeMetadata",
|
|
159
|
+
SchemaName: "new_Description",
|
|
160
|
+
DisplayName: { LocalizedLabels: [{ Label: "Description", LanguageCode: 1033 }] },
|
|
161
|
+
RequiredLevel: { Value: "None" },
|
|
162
|
+
MaxLength: 2000,
|
|
163
|
+
Format: "TextArea",
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Number Columns
|
|
168
|
+
|
|
169
|
+
#### Whole Number
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
await metadataClient.createAttributeDefinition("new_project", {
|
|
173
|
+
"@odata.type": "Microsoft.Dynamics.CRM.IntegerAttributeMetadata",
|
|
174
|
+
SchemaName: "new_Priority",
|
|
175
|
+
DisplayName: { LocalizedLabels: [{ Label: "Priority", LanguageCode: 1033 }] },
|
|
176
|
+
RequiredLevel: { Value: "None" },
|
|
177
|
+
MinValue: 1,
|
|
178
|
+
MaxValue: 100,
|
|
179
|
+
Format: "None",
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Decimal Number
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
await metadataClient.createAttributeDefinition("new_project", {
|
|
187
|
+
"@odata.type": "Microsoft.Dynamics.CRM.DecimalAttributeMetadata",
|
|
188
|
+
SchemaName: "new_CompletionPercentage",
|
|
189
|
+
DisplayName: { LocalizedLabels: [{ Label: "Completion %", LanguageCode: 1033 }] },
|
|
190
|
+
RequiredLevel: { Value: "None" },
|
|
191
|
+
MinValue: 0,
|
|
192
|
+
MaxValue: 100,
|
|
193
|
+
Precision: 2,
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### Currency
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
await metadataClient.createAttributeDefinition("new_project", {
|
|
201
|
+
"@odata.type": "Microsoft.Dynamics.CRM.MoneyAttributeMetadata",
|
|
202
|
+
SchemaName: "new_Budget",
|
|
203
|
+
DisplayName: { LocalizedLabels: [{ Label: "Budget", LanguageCode: 1033 }] },
|
|
204
|
+
RequiredLevel: { Value: "None" },
|
|
205
|
+
MinValue: 0,
|
|
206
|
+
MaxValue: 1000000000,
|
|
207
|
+
Precision: 2,
|
|
208
|
+
PrecisionSource: 2, // Precision = 2
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Date & Time
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
await metadataClient.createAttributeDefinition("new_project", {
|
|
216
|
+
"@odata.type": "Microsoft.Dynamics.CRM.DateTimeAttributeMetadata",
|
|
217
|
+
SchemaName: "new_DueDate",
|
|
218
|
+
DisplayName: { LocalizedLabels: [{ Label: "Due Date", LanguageCode: 1033 }] },
|
|
219
|
+
RequiredLevel: { Value: "None" },
|
|
220
|
+
Format: "DateOnly", // or 'DateAndTime'
|
|
221
|
+
ImeMode: "Disabled",
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Choice (Local)
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
await metadataClient.createAttributeDefinition("new_project", {
|
|
229
|
+
"@odata.type": "Microsoft.Dynamics.CRM.PicklistAttributeMetadata",
|
|
230
|
+
SchemaName: "new_Status",
|
|
231
|
+
DisplayName: { LocalizedLabels: [{ Label: "Status", LanguageCode: 1033 }] },
|
|
232
|
+
RequiredLevel: { Value: "None" },
|
|
233
|
+
OptionSet: {
|
|
234
|
+
"@odata.type": "Microsoft.Dynamics.CRM.OptionSetMetadata",
|
|
235
|
+
IsGlobal: false,
|
|
236
|
+
Options: [
|
|
237
|
+
{ Value: 1, Label: { LocalizedLabels: [{ Label: "Not Started", LanguageCode: 1033 }] } },
|
|
238
|
+
{ Value: 2, Label: { LocalizedLabels: [{ Label: "In Progress", LanguageCode: 1033 }] } },
|
|
239
|
+
{ Value: 3, Label: { LocalizedLabels: [{ Label: "Completed", LanguageCode: 1033 }] } },
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Yes/No (Boolean)
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
await metadataClient.createAttributeDefinition("new_project", {
|
|
249
|
+
"@odata.type": "Microsoft.Dynamics.CRM.BooleanAttributeMetadata",
|
|
250
|
+
SchemaName: "new_IsActive",
|
|
251
|
+
DisplayName: { LocalizedLabels: [{ Label: "Is Active", LanguageCode: 1033 }] },
|
|
252
|
+
RequiredLevel: { Value: "None" },
|
|
253
|
+
DefaultValue: true,
|
|
254
|
+
OptionSet: {
|
|
255
|
+
"@odata.type": "Microsoft.Dynamics.CRM.BooleanOptionSetMetadata",
|
|
256
|
+
TrueOption: { Value: 1, Label: { LocalizedLabels: [{ Label: "Yes", LanguageCode: 1033 }] } },
|
|
257
|
+
FalseOption: { Value: 0, Label: { LocalizedLabels: [{ Label: "No", LanguageCode: 1033 }] } },
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Lookup (Single-table)
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
// Create 1:N relationship (creates lookup column on related entity)
|
|
266
|
+
const relationshipId = await metadataClient.createOneToManyRelationship({
|
|
267
|
+
"@odata.type": "Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",
|
|
268
|
+
SchemaName: "new_project_tasks",
|
|
269
|
+
ReferencedEntity: "new_project",
|
|
270
|
+
ReferencedAttribute: "new_projectid",
|
|
271
|
+
ReferencingEntity: "new_task",
|
|
272
|
+
Lookup: {
|
|
273
|
+
SchemaName: "new_ProjectId",
|
|
274
|
+
DisplayName: { LocalizedLabels: [{ Label: "Project", LanguageCode: 1033 }] },
|
|
275
|
+
RequiredLevel: { Value: "ApplicationRequired" },
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### File Column
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
await metadataClient.createAttributeDefinition("new_project", {
|
|
284
|
+
"@odata.type": "Microsoft.Dynamics.CRM.FileAttributeMetadata",
|
|
285
|
+
SchemaName: "new_Attachment",
|
|
286
|
+
DisplayName: { LocalizedLabels: [{ Label: "Attachment", LanguageCode: 1033 }] },
|
|
287
|
+
RequiredLevel: { Value: "None" },
|
|
288
|
+
MaxSizeInKB: 32768, // 32 MB
|
|
289
|
+
});
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Image Column
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
await metadataClient.createAttributeDefinition("new_project", {
|
|
296
|
+
"@odata.type": "Microsoft.Dynamics.CRM.ImageAttributeMetadata",
|
|
297
|
+
SchemaName: "new_Logo",
|
|
298
|
+
DisplayName: { LocalizedLabels: [{ Label: "Project Logo", LanguageCode: 1033 }] },
|
|
299
|
+
RequiredLevel: { Value: "None" },
|
|
300
|
+
MaxSizeInKB: 10240, // 10 MB
|
|
301
|
+
CanStoreFullImage: true,
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Relationship Operations
|
|
308
|
+
|
|
309
|
+
### One-to-Many (1:N) Relationship
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
// Create relationship (creates lookup on "many" side)
|
|
313
|
+
const relationshipId = await metadataClient.createOneToManyRelationship({
|
|
314
|
+
"@odata.type": "Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",
|
|
315
|
+
SchemaName: "new_account_projects",
|
|
316
|
+
ReferencedEntity: "account", // "One" side (parent)
|
|
317
|
+
ReferencedAttribute: "accountid",
|
|
318
|
+
ReferencingEntity: "new_project", // "Many" side (child)
|
|
319
|
+
Lookup: {
|
|
320
|
+
SchemaName: "new_AccountId",
|
|
321
|
+
DisplayName: { LocalizedLabels: [{ Label: "Account", LanguageCode: 1033 }] },
|
|
322
|
+
RequiredLevel: { Value: "None" },
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Many-to-Many (N:N) Relationship
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
// Create N:N relationship (creates intersect table)
|
|
331
|
+
const relationshipId = await metadataClient.createManyToManyRelationship({
|
|
332
|
+
"@odata.type": "Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata",
|
|
333
|
+
SchemaName: "new_project_contact",
|
|
334
|
+
Entity1LogicalName: "new_project",
|
|
335
|
+
Entity2LogicalName: "contact",
|
|
336
|
+
IntersectEntityName: "new_project_contact",
|
|
337
|
+
Entity1NavigationPropertyName: "new_project_contact_projects",
|
|
338
|
+
Entity2NavigationPropertyName: "new_project_contact_contacts",
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Read Relationships
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// Get all 1:N relationships for an entity
|
|
346
|
+
const oneToManyRels = await metadataClient.getOneToManyRelationships("new_project", {
|
|
347
|
+
select: ["SchemaName", "ReferencedEntity", "ReferencingEntity"],
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Get all N:N relationships
|
|
351
|
+
const manyToManyRels = await metadataClient.getManyToManyRelationships("new_project");
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Global Choice (Option Set) Operations
|
|
357
|
+
|
|
358
|
+
### Create Global Choice
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
const optionSetId = await metadataClient.createGlobalOptionSet({
|
|
362
|
+
"@odata.type": "Microsoft.Dynamics.CRM.OptionSetMetadata",
|
|
363
|
+
Name: "new_projecttype",
|
|
364
|
+
DisplayName: { LocalizedLabels: [{ Label: "Project Type", LanguageCode: 1033 }] },
|
|
365
|
+
IsGlobal: true,
|
|
366
|
+
OptionSetType: "Picklist",
|
|
367
|
+
Options: [
|
|
368
|
+
{ Value: 100000, Label: { LocalizedLabels: [{ Label: "Internal", LanguageCode: 1033 }] } },
|
|
369
|
+
{ Value: 100001, Label: { LocalizedLabels: [{ Label: "External", LanguageCode: 1033 }] } },
|
|
370
|
+
{ Value: 100002, Label: { LocalizedLabels: [{ Label: "Research", LanguageCode: 1033 }] } },
|
|
371
|
+
],
|
|
372
|
+
});
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Use Global Choice in Column
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
// Reference global choice when creating attribute
|
|
379
|
+
await metadataClient.createAttributeDefinition("new_project", {
|
|
380
|
+
"@odata.type": "Microsoft.Dynamics.CRM.PicklistAttributeMetadata",
|
|
381
|
+
SchemaName: "new_Type",
|
|
382
|
+
DisplayName: { LocalizedLabels: [{ Label: "Type", LanguageCode: 1033 }] },
|
|
383
|
+
RequiredLevel: { Value: "None" },
|
|
384
|
+
GlobalOptionSet: {
|
|
385
|
+
"@odata.bind": `/GlobalOptionSetDefinitions(Name='new_projecttype')`,
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Insert Option Value
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
// Add new option to existing global choice
|
|
394
|
+
const result = await metadataClient.insertOptionValue({
|
|
395
|
+
OptionSetName: "new_projecttype",
|
|
396
|
+
Label: { LocalizedLabels: [{ Label: "Maintenance", LanguageCode: 1033 }] },
|
|
397
|
+
Value: 100003, // Optional, auto-assigned if omitted
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
console.log("New option value:", result.NewOptionValue);
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Polymorphic (Multi-table) Lookup Operations
|
|
406
|
+
|
|
407
|
+
### Customer Lookup
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
// Create customer lookup (targets account OR contact)
|
|
411
|
+
const result = await metadataClient.createCustomerRelationships({
|
|
412
|
+
Lookup: {
|
|
413
|
+
"@odata.type": "Microsoft.Dynamics.CRM.LookupAttributeMetadata",
|
|
414
|
+
SchemaName: "new_CustomerId",
|
|
415
|
+
DisplayName: { LocalizedLabels: [{ Label: "Customer", LanguageCode: 1033 }] },
|
|
416
|
+
RequiredLevel: { Value: "ApplicationRequired" },
|
|
417
|
+
},
|
|
418
|
+
OneToManyRelationships: [
|
|
419
|
+
{
|
|
420
|
+
"@odata.type": "Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",
|
|
421
|
+
SchemaName: "new_account_projects",
|
|
422
|
+
ReferencedEntity: "account",
|
|
423
|
+
ReferencedAttribute: "accountid",
|
|
424
|
+
ReferencingEntity: "new_project",
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
"@odata.type": "Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",
|
|
428
|
+
SchemaName: "new_contact_projects",
|
|
429
|
+
ReferencedEntity: "contact",
|
|
430
|
+
ReferencedAttribute: "contactid",
|
|
431
|
+
ReferencingEntity: "new_project",
|
|
432
|
+
},
|
|
433
|
+
],
|
|
434
|
+
});
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Custom Polymorphic Lookup
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
// Create lookup targeting multiple custom entities
|
|
441
|
+
const result = await metadataClient.createPolymorphicLookupAttribute({
|
|
442
|
+
OneToManyRelationships: [
|
|
443
|
+
{
|
|
444
|
+
"@odata.type": "Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",
|
|
445
|
+
SchemaName: "new_task_regarding_project",
|
|
446
|
+
ReferencedEntity: "new_project",
|
|
447
|
+
ReferencedAttribute: "new_projectid",
|
|
448
|
+
ReferencingEntity: "new_task",
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
"@odata.type": "Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",
|
|
452
|
+
SchemaName: "new_task_regarding_milestone",
|
|
453
|
+
ReferencedEntity: "new_milestone",
|
|
454
|
+
ReferencedAttribute: "new_milestoneid",
|
|
455
|
+
ReferencingEntity: "new_task",
|
|
456
|
+
},
|
|
457
|
+
],
|
|
458
|
+
Lookup: {
|
|
459
|
+
SchemaName: "new_RegardingId",
|
|
460
|
+
DisplayName: { LocalizedLabels: [{ Label: "Regarding", LanguageCode: 1033 }] },
|
|
461
|
+
RequiredLevel: { Value: "None" },
|
|
462
|
+
},
|
|
463
|
+
});
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## Relationship Eligibility Checks
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
// Check if entity can be referenced (parent in 1:N)
|
|
472
|
+
const canBeReferenced = await metadataClient.canBeReferenced("new_project");
|
|
473
|
+
console.log("Can be parent:", canBeReferenced.CanBeReferenced);
|
|
474
|
+
|
|
475
|
+
// Check if entity can reference others (child in 1:N)
|
|
476
|
+
const canBeReferencing = await metadataClient.canBeReferencing("new_task");
|
|
477
|
+
console.log("Can be child:", canBeReferencing.CanBeReferencing);
|
|
478
|
+
|
|
479
|
+
// Check if entity can participate in N:N
|
|
480
|
+
const canManyToMany = await metadataClient.canManyToMany("new_project");
|
|
481
|
+
console.log("Can do N:N:", canManyToMany.CanManyToMany);
|
|
482
|
+
|
|
483
|
+
// Get valid entities for N:N relationships
|
|
484
|
+
const validEntities = await metadataClient.getValidManyToMany("new_project");
|
|
485
|
+
console.log("Valid N:N partners:", validEntities.EntityNames);
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## RetrieveMetadataChanges
|
|
491
|
+
|
|
492
|
+
Track metadata changes using version stamps for incremental sync:
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
// Initial query (get all + version stamp)
|
|
496
|
+
const result = await metadataClient.retrieveMetadataChanges(
|
|
497
|
+
{
|
|
498
|
+
Criteria: {
|
|
499
|
+
FilterOperator: "And",
|
|
500
|
+
Conditions: [{ PropertyName: "IsCustomEntity", Value: true }],
|
|
501
|
+
},
|
|
502
|
+
Properties: {
|
|
503
|
+
PropertyNames: ["LogicalName", "DisplayName", "Attributes"],
|
|
504
|
+
},
|
|
505
|
+
},
|
|
506
|
+
"Entity" // Track entity deletions
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
console.log("Entities:", result.EntityMetadata);
|
|
510
|
+
console.log("Server version:", result.ServerVersionStamp);
|
|
511
|
+
|
|
512
|
+
// Later: get only changes since last sync
|
|
513
|
+
const changes = await metadataClient.retrieveMetadataChanges(
|
|
514
|
+
{
|
|
515
|
+
Criteria: {
|
|
516
|
+
FilterOperator: "And",
|
|
517
|
+
Conditions: [{ PropertyName: "IsCustomEntity", Value: true }],
|
|
518
|
+
},
|
|
519
|
+
Properties: {
|
|
520
|
+
PropertyNames: ["LogicalName", "DisplayName"],
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
"Entity",
|
|
524
|
+
result.ServerVersionStamp // Pass previous stamp
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
console.log("Changed entities:", changes.EntityMetadata);
|
|
528
|
+
console.log("Deleted entities:", changes.DeletedMetadata);
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## Important Notes
|
|
534
|
+
|
|
535
|
+
### Update Operations Use PUT
|
|
536
|
+
|
|
537
|
+
Per Microsoft documentation, all metadata update operations use HTTP PUT (not PATCH). This means you must send the complete definition with all properties, not just the changed ones:
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
// Update entity - requires complete definition
|
|
541
|
+
await metadataClient.updateEntityDefinition("new_project", {
|
|
542
|
+
"@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata",
|
|
543
|
+
DisplayName: {
|
|
544
|
+
LocalizedLabels: [{ Label: "Updated Project", LanguageCode: 1033 }],
|
|
545
|
+
},
|
|
546
|
+
Description: {
|
|
547
|
+
LocalizedLabels: [{ Label: "Updated description", LanguageCode: 1033 }],
|
|
548
|
+
},
|
|
549
|
+
// Include all other properties you want to preserve
|
|
550
|
+
});
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
The SDK automatically sets the `MSCRM.MergeLabels: true` header to merge labels instead of replacing them, preserving labels for other languages.
|
|
554
|
+
|
|
555
|
+
### Strict Typing
|
|
556
|
+
|
|
557
|
+
The metadata package uses strict type separation:
|
|
558
|
+
|
|
559
|
+
- **Read types** - include all properties returned from the server (e.g., `EntityMetadata`)
|
|
560
|
+
- **Create types** - exclude read-only and internal properties (e.g., `EntityMetadataCreate`)
|
|
561
|
+
- **Update types** - similar to Create but may have different required fields (e.g., `EntityMetadataUpdate`)
|
|
562
|
+
|
|
563
|
+
This ensures you cannot accidentally send read-only fields like `MetadataId`, `ObjectTypeCode`, or internal-use fields in create/update operations.
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## TypeScript Examples
|
|
568
|
+
|
|
569
|
+
All examples in this README use TypeScript. For pure JavaScript, omit type annotations:
|
|
570
|
+
|
|
571
|
+
```javascript
|
|
572
|
+
// JavaScript equivalent
|
|
573
|
+
const metadataClient = new MetadataClient(client);
|
|
574
|
+
|
|
575
|
+
const entityId = await metadataClient.createEntityDefinition({
|
|
576
|
+
"@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata",
|
|
577
|
+
SchemaName: "new_Project",
|
|
578
|
+
DisplayName: {
|
|
579
|
+
LocalizedLabels: [{ Label: "Project", LanguageCode: 1033 }],
|
|
580
|
+
},
|
|
581
|
+
// ... rest of definition
|
|
582
|
+
});
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
## License
|
|
588
|
+
|
|
589
|
+
This package is part of the `dataverse-sdk-typescript` monorepo and is licensed under **GNU AGPL v3.0**. See [LICENSE](../../LICENSE) for details.
|
|
590
|
+
|
|
591
|
+
### Publishing Changes
|
|
592
|
+
|
|
593
|
+
Metadata changes must be published before they take effect in the application. Use PublishXml or PublishAllXml actions from the messages package.
|
|
594
|
+
|
|
595
|
+
## License
|
|
596
|
+
|
|
597
|
+
GNU AGPL v3.0
|
|
598
|
+
|
|
599
|
+
## Related Packages
|
|
600
|
+
|
|
601
|
+
- [@mohsinonxrm/dataverse-sdk-core](../dataverse-sdk-core) - Core client and request builder
|
|
602
|
+
- [@mohsinonxrm/dataverse-sdk-messages](../dataverse-sdk-messages) - SDK messages including PublishXml
|