@mohsinonxrm/dataverse-sdk-messages 1.0.0 → 2.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.
Files changed (5) hide show
  1. package/README.md +349 -183
  2. package/dist/index.d.ts +45885 -21724
  3. package/dist/index.js +26535 -35958
  4. package/dist/index.js.map +1 -1
  5. package/package.json +17 -9
package/README.md CHANGED
@@ -1,13 +1,24 @@
1
1
  # @mohsinonxrm/dataverse-sdk-messages
2
2
 
3
- Organization Service messages for Dataverse, providing typed request/response pairs that map to Web API actions and functions.
3
+ Organization Service messages for Dataverse, providing typed request/response pairs that map to Web API actions, functions, CRUD requests and `$batch`.
4
+
5
+ This package is the SDK's **.NET compatibility layer**, with
6
+ [`@mohsinonxrm/dataverse-sdk-xrm`](../dataverse-sdk-xrm): `<Name>Request` / `<Name>Response`
7
+ classes with the .NET SDK's names, so code written against `IOrganizationService` ports line by
8
+ line. Each message sends the same request as the action or function class of the same name
9
+ (`WinOpportunityRequest` and `WinOpportunityAction`), through the same `DataverseClient`. New code
10
+ should use the main path: those classes from `@mohsinonxrm/dataverse-sdk-actions` and
11
+ `@mohsinonxrm/dataverse-sdk-functions`, run with `client.execute()`, and `client.api()` for CRUD.
12
+ The [.NET mapping](../../docs/mapping/message-to-webapi.md#0-from-the-net-sdk-to-the-main-path)
13
+ gives the main-path equivalent of each .NET call.
4
14
 
5
15
  ## Features
6
16
 
7
- - ✅ **1,356+ typed messages** - Complete coverage of all Dataverse Organization Service messages
8
- - ✅ **Direct exports** - All message classes exported directly (e.g., `ExecuteWorkflowRequest`, `WinOpportunityRequest`)
17
+ - ✅ **<!-- count:messages -->1,281<!-- /count --> typed messages** - The Organization Service messages of the platform, Sales and Customer Service, from the Web API's actions and functions
18
+ - ✅ **CRUD and batch messages** - `CreateRequest`, `RetrieveRequest`, `UpdateRequest`, `DeleteRequest`, `AssociateRequest`, `DisassociateRequest`, `RetrieveMultipleRequest`, `ExecuteMultipleRequest` and `ExecuteTransactionRequest`
19
+ - ✅ **Direct exports** - All message classes exported directly (e.g., `ExecuteWorkflowRequest`, `WhoAmIRequest`)
9
20
  - ✅ **Request/response pairs** - Full type safety for all message parameters and returns
10
- - ✅ **Web API mapping** - Messages map to corresponding Web API actions, functions, or HTTP operations
21
+ - ✅ **Web API mapping** - Each message sends the same request as the Web API action (`POST`) or function (`GET`) of the same name
11
22
  - ✅ **Generic OrganizationRequest** - Escape hatch for custom actions
12
23
  - ✅ **Executable interface** - All messages implement `Executable<TResponse>` for direct execution
13
24
 
@@ -19,7 +30,7 @@ pnpm add @mohsinonxrm/dataverse-sdk-messages @mohsinonxrm/dataverse-sdk-xrm
19
30
 
20
31
  ## Overview
21
32
 
22
- This package provides 1,356+ typed message classes that wrap Web API actions and functions. Each message:
33
+ This package provides <!-- count:messages -->1,281<!-- /count --> typed message classes, one for each Web API action and function, and the hand-written [CRUD and batch messages](#crud-and-batch-messages). Each message:
23
34
 
24
35
  - Extends `OrganizationRequest` base class
25
36
  - Implements `Executable<TResponse>` interface
@@ -28,67 +39,75 @@ This package provides 1,356+ typed message classes that wrap Web API actions and
28
39
 
29
40
  Messages execute via `OrganizationService.execute()` from `@mohsinonxrm/dataverse-sdk-xrm` or directly via `DataverseClient.execute()`.
30
41
 
31
- ## Message Categories
32
-
33
- The SDK includes **1,356+ typed messages** organized into the following categories:
34
-
35
- ### Platform Messages
36
-
37
- **Identity & Security:**
38
-
39
- - `RetrievePrincipalAccessRequest/Response` - Get access rights
40
- - `GrantAccessRequest/Response` - Grant access to record
41
- - `RevokeAccessRequest/Response` - Revoke access to record
42
-
43
- **Entity Operations:**
44
-
45
- - `AssignRequest/Response` - Assign record ownership
46
- - `SetStateRequest/Response` - Set state and status
47
- - `MergeRequest/Response` - Merge duplicate records
48
-
49
- **Metadata:**
50
-
51
- - `RetrieveMetadataChangesRequest/Response` - Get metadata delta
52
- - `CreateEntityRequest/Response` - Create custom entity
53
- - `CreateAttributeRequest/Response` - Create column
54
-
55
- ### Business Messages
56
-
57
- **Sales Module:**
58
-
59
- - `WinOpportunityRequest/Response` - Close opportunity as won
60
- - `LoseOpportunityRequest/Response` - Close opportunity as lost
61
- - `QualifyLeadRequest/Response` - Qualify lead to opportunity
62
- - `ConvertQuoteToSalesOrderRequest/Response` - Convert quote
63
- - `CalculatePriceRequest/Response` - Calculate pricing
64
-
65
- **Customer Service Module:**
66
-
67
- - `CloseIncidentRequest/Response` - Close case
68
- - `ExecuteWorkflowRequest/Response` - Run workflow
69
- - `ApplyRoutingRuleRequest/Response` - Apply routing rules
70
-
71
- **Solution Management:**
72
-
73
- - `ExportSolutionRequest/Response` - Export solution
74
- - `ImportSolutionRequest/Response` - Import solution
75
- - `PublishXmlRequest/Response` - Publish metadata
76
- - `PublishAllXmlRequest/Response` - Publish all changes
77
-
78
- **Field Service (msdyn\_\*):**
79
-
80
- - Work order management messages
81
- - Resource scheduling messages
82
- - Booking operation messages
83
-
84
- **Marketing (msdynmkt\_\*):**
42
+ ## What this package covers
43
+
44
+ A message for each Web API action and function of the **platform** (Dataverse and the Power
45
+ Platform solutions every environment has) and of the two core Dynamics 365 apps of a plain trial,
46
+ **Sales** and **Customer Service**: the same operations as `@mohsinonxrm/dataverse-sdk-actions`
47
+ and `@mohsinonxrm/dataverse-sdk-functions` (but the `$filter` query functions such as `LastXDays`).
48
+ They are chosen by the solution each operation comes from, not by what the org the SDK was
49
+ generated from has installed: the
50
+ [solution table](../../tools/generate-sdk-types/metadata/README.md#which-operations-are-prebuilt)
51
+ lists every solution and whether its operations are in.
52
+
53
+ Not in the package: the messages of Contact Center (Omnichannel, unified routing, voice,
54
+ `CCaaS_*`), Field Service, Project Operations, Universal Resource Scheduling, Customer Insights -
55
+ Journeys (`msdynmkt_*`), Customer Voice, Connected Customer Service (IoT), Power Pages and Process
56
+ Mining, and your own custom APIs. Generate typed classes for those from your org with
57
+ `dataverse-gen` (see
58
+ [Generate the rest with dataverse-gen](../../docs/getting-started.md#generate-the-rest-with-dataverse-gen)),
59
+ or send one with the generic [`OrganizationRequest`](#generic-organizationrequest).
85
60
 
86
- - Campaign operation messages
87
- - Consent management messages
88
-
89
- ### Complete Message Reference
61
+ ## Message Categories
90
62
 
91
- For a complete list of all 1,356+ messages with descriptions, see [docs/mapping/supported-messages.md](../../docs/mapping/supported-messages.md).
63
+ The package has <!-- count:messages -->1,281<!-- /count --> typed messages: a `<Name>Request` /
64
+ `<Name>Response` pair for each Web API **action** and **function** of the platform, Sales and
65
+ Customer Service (see [What this package covers](#what-this-package-covers)). This list is generated
66
+ from the package source (`pnpm --filter generate-sdk-types docs`);
67
+ [supported-messages.md](../../docs/mapping/supported-messages.md) has more.
68
+
69
+ - A function's message sends the function's `GET`, with its parameters as parameter aliases:
70
+ `WhoAmIRequest`, `RetrieveEntityRequest`, `RetrieveMetadataChangesRequest`. A function
71
+ declared for several tables has one message per table: `RetrievePrincipalAccess_systemuserRequest`
72
+ and `RetrievePrincipalAccess_teamRequest` for the .NET `RetrievePrincipalAccessRequest`.
73
+ - There is no AssignRequest or SetStateRequest (.NET names): they aren't Web API actions. Assign by updating
74
+ `ownerid@odata.bind`, and change state by updating `statecode` and `statuscode`.
75
+ - Tables and columns are created with `@mohsinonxrm/dataverse-sdk-metadata`, not with
76
+ the .NET CreateEntityRequest / CreateAttributeRequest.
77
+
78
+ <!-- operations:messages
79
+ GrantAccess: Share a record with a user or team.
80
+ RevokeAccess: Stop sharing a record.
81
+ Merge: Merge duplicate records.
82
+ ExecuteWorkflow: Run an on-demand workflow on a record.
83
+ WinOpportunity: Close an opportunity as won.
84
+ LoseOpportunity: Close an opportunity as lost.
85
+ QualifyLead: Qualify a lead.
86
+ ConvertQuoteToSalesOrder: Create an order from a quote.
87
+ CalculatePrice: Recalculate a quote, order or invoice.
88
+ CloseIncident: Resolve a case.
89
+ ExportSolution: Export a solution.
90
+ ImportSolution: Import a solution.
91
+ PublishXml: Publish the named customizations.
92
+ PublishAllXml: Publish all customizations.
93
+ -->
94
+
95
+ - `new GrantAccessRequest({ Target, PrincipalAccess? })` (`POST /GrantAccess`): Share a record with a user or team.
96
+ - `new RevokeAccessRequest({ Target, Revokee })` (`POST /RevokeAccess`): Stop sharing a record.
97
+ - `new MergeRequest({ Target, Subordinate, UpdateContent?, PerformParentingChecks })` (`POST /Merge`): Merge duplicate records.
98
+ - `new ExecuteWorkflowRequest(workflowId, { EntityId, InputArguments? })` (`POST /workflows({workflowId})/Microsoft.Dynamics.CRM.ExecuteWorkflow`): Run an on-demand workflow on a record.
99
+ - `new WinOpportunityRequest({ OpportunityClose, Caller?, Status })` (`POST /WinOpportunity`): Close an opportunity as won.
100
+ - `new LoseOpportunityRequest({ Caller?, OpportunityClose, Status })` (`POST /LoseOpportunity`): Close an opportunity as lost.
101
+ - `new QualifyLeadRequest(leadId, { CreateAccount, CreateContact, CreateOpportunity, OpportunityCurrencyId?, OpportunityCustomerId?, SourceCampaignId?, Status, ProcessInstanceId?, LeadQualificationV2Param? })` (`POST /leads({leadId})/Microsoft.Dynamics.CRM.QualifyLead`): Qualify a lead.
102
+ - `new ConvertQuoteToSalesOrderRequest({ QuoteId, ColumnSet?, QuoteCloseDate?, QuoteCloseStatus?, QuoteCloseSubject?, QuoteCloseDescription?, ProcessInstanceId? })` (`POST /ConvertQuoteToSalesOrder`): Create an order from a quote.
103
+ - `new CalculatePriceRequest({ Target, ParentId? })` (`POST /CalculatePrice`): Recalculate a quote, order or invoice.
104
+ - `new CloseIncidentRequest({ IncidentResolution, Status })` (`POST /CloseIncident`): Resolve a case.
105
+ - `new ExportSolutionRequest({ SolutionName, Managed, TargetVersion?, ExportAutoNumberingSettings?, ExportCalendarSettings?, ExportCustomizationSettings?, ExportEmailTrackingSettings?, ExportGeneralSettings?, ExportMarketingSettings?, ExportOutlookSynchronizationSettings?, ExportRelationshipRoles?, ExportIsvConfig?, ExportSales?, ExportExternalApplications?, ExportComponentsParams? })` (`POST /ExportSolution`): Export a solution.
106
+ - `new ImportSolutionRequest({ OverwriteUnmanagedCustomizations, PublishWorkflows, CustomizationFile, ImportJobId, ConvertToManaged?, SkipProductUpdateDependencies?, HoldingSolution?, SkipQueueRibbonJob?, LayerDesiredOrder?, AsyncRibbonProcessing?, ComponentParameters?, IsTemplateMode?, TemplateSuffix?, SolutionParameters?, TemplateDisplayNamePrefix?, StageAndUpgrade?, SchemaUpdatesOnly? })` (`POST /ImportSolution`): Import a solution.
107
+ - `new PublishXmlRequest({ ParameterXml })` (`POST /PublishXml`): Publish the named customizations.
108
+ - `new PublishAllXmlRequest()` (`POST /PublishAllXml`): Publish all customizations.
109
+
110
+ <!-- /operations -->
92
111
 
93
112
  ## Quick Start
94
113
 
@@ -99,67 +118,140 @@ import { ExecuteWorkflowRequest } from "@mohsinonxrm/dataverse-sdk-messages";
99
118
  const service = new OrganizationService(client);
100
119
 
101
120
  // Execute workflow message
102
- const request = new ExecuteWorkflowRequest(
103
- workflowId, // Workflow GUID
104
- recordId, // Entity record GUID
105
- {} // Input arguments (optional)
106
- );
121
+ const request = new ExecuteWorkflowRequest(workflowId, { EntityId: recordId });
107
122
 
108
123
  const response = await service.execute(request);
109
- console.log("Async Operation ID:", response.result.asyncoperationid);
124
+ console.log("Async Operation ID:", response.result?.asyncoperationid);
110
125
  ```
111
126
 
112
- ## Common Messages
113
-
114
- ### Assign
115
-
116
- Assigns a record to a user or team.
117
-
118
- **Maps to:** Assign action (POST)
127
+ ## Results
128
+
129
+ A message's `result` is the value the action of the same name returns, with the same type,
130
+ exported as `<Name>Result` (#300). A message without a return type has no `result`.
131
+
132
+ | Return type in `$metadata` | Web API response | Value |
133
+ | ------------------------------------------------ | --------------------------------------------------------- | ----------------------------------------------------------- |
134
+ | none | `204 No Content` | `undefined` (`<Name>Response` is `void`) |
135
+ | primitive or enum (`Edm.Boolean`, …) | `{ "@odata.context", "value": … }` | the value (an enum is its member name) |
136
+ | collection (`Collection(mscrm.role)`, …) | `{ "@odata.context", "value": [ … ] }`, or 204 when unset | the array, each record with `@odata.etag`; `[]` for a 204 |
137
+ | complex type (`mscrm.WhoAmIResponse`) | the properties, with `@odata.context` | the body, with `@odata.context` |
138
+ | entity (`mscrm.asyncoperation`, `crmbaseentity`) | the record, with its annotations, or 204 when unset | the record (`@odata.etag`, `@odata.type`); `null` for a 204 |
139
+
140
+ Dataverse answers `204 No Content` for an entity or entity-collection output that wasn't set,
141
+ although `$metadata` declares the return `Nullable="false"`, so an entity return is
142
+ `<Entity> | null`. `@odata.type` says which table a `crmbaseentity` record is.
143
+
144
+ `<Name>Response.fromResponse(data)` reads `result` from a parsed response body (`undefined` for a
145
+ 204), for example the body of a `$batch` step. Unlike the `Results` of the generic
146
+ `OrganizationRequest`, `result` keeps the annotations, as the action's value does.
147
+
148
+ A function's message reads its value the same way: `(await service.execute(new WhoAmIRequest())).result.UserId`.
149
+
150
+ ## CRUD and batch messages
151
+
152
+ Create, Retrieve, Update, Delete, Associate, Disassociate and RetrieveMultiple aren't Web API
153
+ actions or functions but HTTP requests on entity sets, and ExecuteMultiple and ExecuteTransaction
154
+ are `$batch` requests. These messages are written by hand (`src/handwritten`). A record is a
155
+ reference from `createEntityReference`; a new record is the Web API payload, with `@odata.type`
156
+ naming the table. Each response has `result`, and the .NET property of the same value.
157
+
158
+ | Request | Sends | Response |
159
+ | -------------------------------------------------------------------- | ------------------------------------------------ | --------------------------------------- |
160
+ | `new CreateRequest({ Target, EntitySetName? })` | `POST /{set}` | `id`: the new record's id |
161
+ | `new RetrieveRequest({ Target, ColumnSet? })` | `GET /{set}({id})?$select=…` | `Entity`: the record |
162
+ | `new UpdateRequest({ Target, Attributes, ETag? })` | `PATCH /{set}({id})`, `If-Match` (never creates) | |
163
+ | `new DeleteRequest({ Target, ETag? })` | `DELETE /{set}({id})` | |
164
+ | `new AssociateRequest({ Target, Relationship, RelatedEntities })` | `POST …/{Relationship}/$ref` per record | |
165
+ | `new DisassociateRequest({ Target, Relationship, RelatedEntities })` | `DELETE …/{Relationship}({id})/$ref` per record | |
166
+ | `new RetrieveMultipleRequest({ Query, EntitySetName? })` | `GET /{set}?fetchXml=…` | `EntityCollection`: one page of records |
167
+ | `new ExecuteMultipleRequest({ Requests, Settings? })` | `POST /$batch`, one part per request | `Responses`, `IsFaulted` |
168
+ | `new ExecuteTransactionRequest({ Requests })` | `POST /$batch`, one change set (all or nothing) | `Responses` |
119
169
 
120
170
  ```typescript
121
- import { AssignRequest, createEntityReference } from "@mohsinonxrm/dataverse-sdk-messages";
171
+ import {
172
+ CreateRequest,
173
+ DeleteRequest,
174
+ ExecuteMultipleRequest,
175
+ ExecuteTransactionRequest,
176
+ RetrieveRequest,
177
+ UpdateRequest,
178
+ WhoAmIRequest,
179
+ createEntityReference,
180
+ } from "@mohsinonxrm/dataverse-sdk-messages";
181
+
182
+ const { id } = await service.execute(
183
+ new CreateRequest({
184
+ Target: { "@odata.type": "Microsoft.Dynamics.CRM.account", name: "Contoso" },
185
+ })
186
+ );
187
+ const account = createEntityReference("account", id);
188
+ const { Entity } = await service.execute(
189
+ new RetrieveRequest({ Target: account, ColumnSet: ["name"] })
190
+ );
191
+ await service.execute(
192
+ new UpdateRequest({
193
+ Target: account,
194
+ Attributes: { name: "Fabrikam" },
195
+ ETag: Entity["@odata.etag"],
196
+ })
197
+ );
122
198
 
123
- const request = new AssignRequest(
124
- createEntityReference("account", accountId),
125
- createEntityReference("systemuser", userId)
199
+ // Several requests in one round trip; a failure is an item with a Fault, not a rejection
200
+ const multiple = await service.execute(
201
+ new ExecuteMultipleRequest({
202
+ Requests: [new WhoAmIRequest(), new DeleteRequest({ Target: account })],
203
+ Settings: { ContinueOnError: true },
204
+ })
205
+ );
206
+ console.log(
207
+ multiple.IsFaulted,
208
+ multiple.Responses.map((item) => item.Fault?.message)
126
209
  );
127
210
 
128
- await service.execute(request);
211
+ // All or nothing: a failure rolls back and rejects with that request's error
212
+ const { Responses } = await service.execute(
213
+ new ExecuteTransactionRequest({
214
+ Requests: [
215
+ new CreateRequest({
216
+ Target: { "@odata.type": "Microsoft.Dynamics.CRM.contact", lastname: "A" },
217
+ }),
218
+ new CreateRequest({
219
+ Target: { "@odata.type": "Microsoft.Dynamics.CRM.contact", lastname: "B" },
220
+ }),
221
+ ],
222
+ })
223
+ );
224
+ const [first, second] = Responses; // CreateResponse, CreateResponse
129
225
  ```
130
226
 
131
- **Constructor:**
132
-
133
- - `target: EntityReference` - The record to assign
134
- - `assignee: EntityReference` - The user or team to assign the record to
227
+ - `ExecuteMultipleRequest` takes 1 to 1,000 requests: messages, or the classes of the actions and
228
+ functions packages. Without `Settings.ContinueOnError`, Dataverse stops at the first failure and
229
+ the requests after it have no item; `Settings.ReturnResponses: false` keeps only the failures.
230
+ - `ExecuteTransactionRequest` can't hold a `GET` (a function, Retrieve or RetrieveMultiple): a
231
+ `$batch` change set can't.
232
+ - An Associate or Disassociate of several records is one change set too, so neither it nor
233
+ ExecuteMultiple / ExecuteTransaction can go inside ExecuteMultiple or ExecuteTransaction.
234
+ - `RetrieveMultipleRequest` sends its FetchXML in the URL (up to 32,768 characters); use
235
+ `client.api(entitySet).fetchXml()` for a longer query.
236
+ - For more than 1,000 requests, or change sets mixed with single requests, use
237
+ `BatchRequestBuilder` from `@mohsinonxrm/dataverse-sdk-batch`.
135
238
 
136
- ---
137
-
138
- ### SetState
239
+ ## Common Messages
139
240
 
140
- Sets the state and status of a record.
241
+ ### Assign and SetState
141
242
 
142
- **Maps to:** PATCH with statecode/statuscode
243
+ These aren't Web API actions, so there are no message classes for them. Update the record:
143
244
 
144
245
  ```typescript
145
- import { SetStateRequest, createEntityReference } from "@mohsinonxrm/dataverse-sdk-messages";
146
-
147
- // Deactivate an account
148
- const request = new SetStateRequest(
149
- createEntityReference("account", accountId),
150
- 1, // State: Inactive
151
- 2 // Status: Inactive
152
- );
246
+ // Assign
247
+ await client.api(`accounts(${accountId})`).patch({
248
+ "ownerid@odata.bind": `/systemusers(${userId})`,
249
+ });
153
250
 
154
- await service.execute(request);
251
+ // Deactivate
252
+ await client.api(`accounts(${accountId})`).patch({ statecode: 1, statuscode: 2 });
155
253
  ```
156
254
 
157
- **Constructor:**
158
-
159
- - `entityMoniker: EntityReference` - The record to update
160
- - `state: number` - The state code value (e.g., 0 = Active, 1 = Inactive)
161
- - `status: number` - The status code value
162
-
163
255
  ---
164
256
 
165
257
  ### ExecuteWorkflow
@@ -169,28 +261,26 @@ Executes a workflow against an entity record.
169
261
  **Maps to:** ExecuteWorkflow action (bound to workflow)
170
262
 
171
263
  ```typescript
264
+ import { waitForAsyncOperation } from "@mohsinonxrm/dataverse-sdk-core";
172
265
  import { ExecuteWorkflowRequest } from "@mohsinonxrm/dataverse-sdk-messages";
173
266
 
174
- const request = new ExecuteWorkflowRequest(
175
- workflowId, // Workflow GUID
176
- recordId, // Entity record GUID
177
- {} // Input arguments (optional)
178
- );
267
+ const request = new ExecuteWorkflowRequest(workflowId, { EntityId: recordId, InputArguments: {} });
179
268
 
180
269
  const response = await service.execute(request);
181
- console.log("Async Operation ID:", response.result.asyncoperationid);
182
- // Poll asyncoperation entity to check workflow status
270
+ console.log("Async Operation ID:", response.result?.asyncoperationid);
271
+ // Wait for the workflow to finish
272
+ const job = await waitForAsyncOperation(client, response.result);
183
273
  ```
184
274
 
185
- **Constructor:**
275
+ **Constructor:** `new ExecuteWorkflowRequest(workflowId, parameters)`
186
276
 
187
- - `workflowId: string` - GUID of the workflow to execute
188
- - `entityId: string` - GUID of the entity record to run the workflow against
189
- - `inputArguments: InputArgumentCollection` - Optional input arguments
277
+ - `workflowId: string` - GUID of the workflow to execute (the record the action is bound to)
278
+ - `parameters.EntityId: string` - GUID of the entity record to run the workflow against
279
+ - `parameters.InputArguments?: InputArgumentCollection` - Optional input arguments
190
280
 
191
281
  **Response:**
192
282
 
193
- - `result: asyncoperation` - The async operation record (system job)
283
+ - `result: ExecuteWorkflowResult` - The async operation record (system job), with its annotations, or `null` if the Web API answered 204
194
284
 
195
285
  ---
196
286
 
@@ -211,18 +301,17 @@ const opportunityClose = {
211
301
  actualend: new Date().toISOString(),
212
302
  };
213
303
 
214
- const request = new WinOpportunityRequest(
215
- opportunityClose,
216
- 3 // Status: Won
217
- );
304
+ const request = new WinOpportunityRequest({ OpportunityClose: opportunityClose, Status: 3 });
218
305
 
219
306
  await service.execute(request);
220
307
  ```
221
308
 
222
- **Constructor:**
309
+ **Constructor:** `new WinOpportunityRequest(parameters)`
223
310
 
224
- - `opportunityClose: opportunityclose` - The opportunity close record
225
- - `status: number` - The status code for won opportunities
311
+ - `OpportunityClose: EntityReference | EntityPayload<opportunityclose>` - The opportunity close
312
+ activity, with `"opportunityid@odata.bind"`
313
+ - `Caller?: string` - Optional
314
+ - `Status: number` - The status code for won opportunities
226
315
 
227
316
  ---
228
317
 
@@ -236,26 +325,28 @@ Merges two entity records.
236
325
  ```typescript
237
326
  import { MergeRequest, createEntityReference } from "@mohsinonxrm/dataverse-sdk-messages";
238
327
 
239
- const request = new MergeRequest(
240
- createEntityReference("account", masterAccountId), // Target (keep)
241
- subordinateAccountId, // Subordinate (merge & deactivate)
242
- {
243
- // Update content
328
+ const request = new MergeRequest({
329
+ Target: createEntityReference("account", masterAccountId),
330
+ Subordinate: createEntityReference("account", subordinateAccountId),
331
+ UpdateContent: {
332
+ // UpdateContent: column values to set on the target
333
+ "@odata.type": "Microsoft.Dynamics.CRM.account",
244
334
  name: "Merged Company Name",
245
335
  revenue: 1000000,
246
336
  },
247
- false // PerformParentingChecks
248
- );
337
+ PerformParentingChecks: false,
338
+ });
249
339
 
250
340
  await service.execute(request);
251
341
  ```
252
342
 
253
- **Constructor:**
343
+ **Constructor:** `new MergeRequest(parameters)`
254
344
 
255
- - `target: EntityReference` - The master record (will remain after merge)
256
- - `subordinateId: string` - GUID of the subordinate record (will be deactivated/deleted)
257
- - `updateContent: Record<string, unknown>` - Updates to apply to the master record
258
- - `performParentingChecks: boolean` - Whether to perform parenting checks
345
+ - `Target: EntityReference | AnyEntityPayload` - The master record (will remain after merge)
346
+ - `Subordinate: EntityReference | AnyEntityPayload` - The subordinate record (will be deactivated)
347
+ - `UpdateContent?: EntityReference | AnyEntityPayload` - Column values to set on the master
348
+ record, with `@odata.type` naming the table
349
+ - `PerformParentingChecks: boolean` - Whether to perform parenting checks
259
350
 
260
351
  ---
261
352
 
@@ -266,34 +357,22 @@ Exports a solution from the environment.
266
357
  **Maps to:** ExportSolution action (POST, unbound)
267
358
 
268
359
  ```typescript
360
+ import { writeFileSync } from "node:fs";
269
361
  import { ExportSolutionRequest } from "@mohsinonxrm/dataverse-sdk-messages";
270
362
 
271
- const request = new ExportSolutionRequest(
272
- "MySolution", // Solution name
273
- false, // Managed
274
- false, // ExportAutoNumberingSettings
275
- false, // ExportCalendarSettings
276
- false, // ExportCustomizationSettings
277
- false, // ExportEmailTrackingSettings
278
- false, // ExportGeneralSettings
279
- false, // ExportMarketingSettings
280
- false, // ExportOutlookSynchronizationSettings
281
- false, // ExportRelationshipRoles
282
- false, // ExportIsvConfig
283
- false, // ExportSales
284
- false // ExportExternalApplications
285
- );
363
+ // SolutionName and Managed are required; TargetVersion and the Export* settings are optional
364
+ const request = new ExportSolutionRequest({ SolutionName: "MySolution", Managed: false });
286
365
 
287
366
  const response = await service.execute(request);
288
367
 
289
368
  // Decode base64 to file
290
- const solutionFile = Buffer.from(response.exportSolutionFile, "base64");
291
- fs.writeFileSync("solution.zip", solutionFile);
369
+ const solutionFile = Buffer.from(response.result.ExportSolutionFile ?? "", "base64");
370
+ writeFileSync("solution.zip", solutionFile);
292
371
  ```
293
372
 
294
373
  **Response:**
295
374
 
296
- - `exportSolutionFile: string` - Base64-encoded solution file (zip)
375
+ - `result.ExportSolutionFile: string` - Base64-encoded solution file (zip)
297
376
 
298
377
  ---
299
378
 
@@ -304,26 +383,26 @@ Imports a solution into the environment.
304
383
  **Maps to:** ImportSolution action (POST, unbound)
305
384
 
306
385
  ```typescript
386
+ import { readFileSync } from "node:fs";
307
387
  import { ImportSolutionRequest } from "@mohsinonxrm/dataverse-sdk-messages";
308
388
 
309
- const solutionBytes = fs.readFileSync("solution.zip");
389
+ const solutionBytes = readFileSync("solution.zip");
310
390
 
311
- const request = new ImportSolutionRequest(
312
- solutionBytes.toString("base64"), // CustomizationFile
313
- true, // PublishWorkflows
314
- false, // OverwriteUnmanagedCustomizations
315
- false, // SkipProductUpdateDependencies
316
- false, // HoldingSolution
317
- false // ConvertToManaged
318
- );
391
+ const importJobId = crypto.randomUUID();
392
+ const request = new ImportSolutionRequest({
393
+ OverwriteUnmanagedCustomizations: false,
394
+ PublishWorkflows: true,
395
+ CustomizationFile: solutionBytes.toString("base64"),
396
+ ImportJobId: importJobId,
397
+ });
319
398
 
320
- const response = await service.execute(request);
321
- console.log("Import Job ID:", response.importJobId);
399
+ // ImportSolution returns no content. Follow the import with the importjob record.
400
+ await service.execute(request);
322
401
  ```
323
402
 
324
403
  **Response:**
325
404
 
326
- - `importJobId: string` - GUID of the import job
405
+ - No content. `ImportJobId` is an input: the id to give the importjob record.
327
406
 
328
407
  ---
329
408
 
@@ -343,7 +422,7 @@ const parameterXml = `<importexportxml>
343
422
  </entities>
344
423
  </importexportxml>`;
345
424
 
346
- const request = new PublishXmlRequest(parameterXml);
425
+ const request = new PublishXmlRequest({ ParameterXml: parameterXml });
347
426
  await service.execute(request);
348
427
  ```
349
428
 
@@ -364,25 +443,66 @@ await service.execute(request);
364
443
 
365
444
  ---
366
445
 
446
+ ## Overloads (one class per binding)
447
+
448
+ When `$metadata` declares an operation several times with different bindings, each binding has its
449
+ own class: `<Name>_<table>Request` / `<Name>_<table>Response`, with `_collection` for a collection binding. An unbound
450
+ overload keeps the plain name. A name with no unbound overload has no plain-name class, so you
451
+ pick the binding: there is `AddItemCampaign_listRequest` and `AddItemCampaign_campaignRequest`,
452
+ but no `AddItemCampaign` request without a table.
453
+
454
+ ```typescript
455
+ import {
456
+ AddItemCampaign_listRequest,
457
+ createEntityReference,
458
+ } from "@mohsinonxrm/dataverse-sdk-messages";
459
+
460
+ await service.execute(
461
+ new AddItemCampaign_listRequest(listId, {
462
+ Campaign: createEntityReference("campaign", campaignId),
463
+ })
464
+ );
465
+ ```
466
+
467
+ `ExportToExcelRequest` is generated too, from a supplemental definition (`$metadata` leaves it out);
468
+ see the actions package README.
469
+
367
470
  ## Generic OrganizationRequest
368
471
 
369
472
  For custom actions, custom APIs, or messages not yet implemented, use the generic `OrganizationRequest`.
473
+ It runs as a Web API action: `POST /{messageName}` with `Parameters` as the JSON body, or
474
+ `POST /{entitySetName}({id})/Microsoft.Dynamics.CRM.{messageName}` when bound to a record.
370
475
 
371
476
  ```typescript
372
- import { OrganizationRequest } from "@mohsinonxrm/dataverse-sdk-messages";
477
+ import { OrganizationRequest, OrganizationResponse } from "@mohsinonxrm/dataverse-sdk-messages";
373
478
 
374
479
  const request = new OrganizationRequest("my_CustomAction");
375
480
  request.Parameters["StringParameter"] = "value";
376
481
  request.Parameters["IntParameter"] = 42;
377
- request.Parameters["EntityParameter"] = createEntityReference("account", accountId);
482
+ // Values are sent as given, so an entity parameter uses the Web API shape
483
+ request.Parameters["EntityParameter"] = {
484
+ "@odata.type": "Microsoft.Dynamics.CRM.account",
485
+ accountid: accountId,
486
+ };
378
487
 
379
- const response = await service.execute(request);
488
+ const response = await service.execute<OrganizationResponse>(request);
380
489
  console.log("Results:", response.Results);
490
+
491
+ // Bound to a record
492
+ await service.execute(
493
+ new OrganizationRequest(
494
+ "my_BoundAction",
495
+ {},
496
+ { boundTo: { entitySetName: "accounts", id: accountId } }
497
+ )
498
+ );
381
499
  ```
382
500
 
383
501
  **Constructor:**
384
502
 
385
503
  - `messageName: string` - The name of the action/custom API
504
+ - `parameters?: Record<string, unknown>` - The parameters for the action (default `{}`)
505
+ - `options?: OrganizationRequestOptions` (`{ boundTo?: { entitySetName: string; id: string } }`, exported from the package root) - Bind the action to a record
386
506
 
387
507
  **Properties:**
388
508
 
@@ -390,7 +510,11 @@ console.log("Results:", response.Results);
390
510
 
391
511
  **Response:**
392
512
 
393
- - `Results: Record<string, unknown>` - The result values from the action
513
+ - `Results: Record<string, unknown>` - The response's properties, without the `@odata.*` annotations (empty for `204 No Content`)
514
+
515
+ Functions (GET) are not supported by `OrganizationRequest`: use the function's typed message
516
+ (`WhoAmIRequest`), the typed classes from `@mohsinonxrm/dataverse-sdk-functions`, or
517
+ `client.api("my_Function(...)").get()` for a custom function.
394
518
 
395
519
  ---
396
520
 
@@ -398,8 +522,8 @@ console.log("Results:", response.Results);
398
522
 
399
523
  | Message | Web API Endpoint | HTTP Method | Type |
400
524
  | --------------- | ------------------------------------------------------- | ----------- | -------------- |
401
- | Assign | /Assign | POST | Action |
402
- | SetState | /{entitySet}({id}) | PATCH | HTTP |
525
+ | Assign | /{entitySet}({id}) (`ownerid@odata.bind`) | PATCH | Update |
526
+ | SetState | /{entitySet}({id}) (`statecode`, `statuscode`) | PATCH | Update |
403
527
  | ExecuteWorkflow | /workflows({id})/Microsoft.Dynamics.CRM.ExecuteWorkflow | POST | Action (bound) |
404
528
  | WinOpportunity | /WinOpportunity | POST | Action |
405
529
  | Merge | /Merge | POST | Action |
@@ -407,6 +531,10 @@ console.log("Results:", response.Results);
407
531
  | ImportSolution | /ImportSolution | POST | Action |
408
532
  | PublishXml | /PublishXml | POST | Action |
409
533
  | PublishAllXml | /PublishAllXml | POST | Action |
534
+ | WhoAmI | /WhoAmI | GET | Function |
535
+ | Create | /{entitySet} | POST | CRUD |
536
+ | Retrieve | /{entitySet}({id}) | GET | CRUD |
537
+ | ExecuteMultiple | /$batch | POST | Batch |
410
538
 
411
539
  See [docs/mapping/message-to-webapi.md](../../docs/mapping/message-to-webapi.md) for complete mapping details.
412
540
 
@@ -414,13 +542,45 @@ See [docs/mapping/message-to-webapi.md](../../docs/mapping/message-to-webapi.md)
414
542
 
415
543
  ## EntityReference Type
416
544
 
417
- ```typescript
418
- interface EntityReference {
419
- logicalName: string; // e.g., 'account', 'contact'
420
- id: string; // GUID
421
- name?: string; // Optional display name
422
- }
423
- ```
545
+ A reference to one record. Its enumerable properties are the Web API shape, `@odata.type` and
546
+ the table's primary key (`{ "@odata.type": "Microsoft.Dynamics.CRM.account", accountid: "<id>" }`;
547
+ activities use `activityid`). `logicalName`, `id`, `name` and `entitySetName` are kept on it but
548
+ not sent.
549
+
550
+ It is the SDK's one `EntityReference`, defined in `@mohsinonxrm/dataverse-sdk-entities-runtime`
551
+ and re-exported here, as the actions, functions and xrm packages do. A reference from any of their
552
+ `createEntityReference`s is accepted by every one of them.
553
+
554
+ Only `createEntityReference` makes an `EntityReference`. A hand-written
555
+ `{ "@odata.type", logicalName, id }` doesn't type-check, because it would send `logicalName` and
556
+ `id`, which the Web API rejects. To write the reference by hand, use `WebApiEntityReference`, the
557
+ JSON shape: `{ "@odata.type": "Microsoft.Dynamics.CRM.account", accountid: id }`.
558
+
559
+ ### Entity Payloads
560
+
561
+ An entity-typed parameter also takes the record itself, with column values:
562
+
563
+ - a parameter of one table (`WinOpportunity`'s `OpportunityClose`) is
564
+ `EntityReference | EntityPayload<opportunityclose>`: its columns, `@odata.type`, and lookups set
565
+ with `<navigation property>@odata.bind`;
566
+ - a parameter of any table (`crmbaseentity`, such as `Merge`'s `UpdateContent`) is
567
+ `EntityReference | AnyEntityPayload`, and `@odata.type` names the table.
568
+
569
+ Parameters that `$metadata` marks `Nullable="false"` are required; the others are optional
570
+ (`new ExecuteWorkflowRequest(workflowId, { EntityId })` leaves out `InputArguments`). A parameter
571
+ that is left out or `undefined` isn't sent.
572
+
573
+ ### Enum Values
574
+
575
+ The Web API reads an enum value in a request body only as a string, such as `"ReadAccess"` or
576
+ `"ReadAccess, WriteAccess"` for a flags enum, and rejects a number. The generated enums are
577
+ numeric, so you can combine flags with `|`, and the messages send member names. This covers enum
578
+ parameters and enums inside complex types, such as `PrincipalAccess.AccessMask` and the operators
579
+ of a `QueryExpression`. The enums are exported from the package root.
580
+
581
+ An entity member of a complex type, such as `PrincipalAccess.Principal`, takes a reference or a
582
+ payload, like an entity-typed parameter:
583
+ `new GrantAccessRequest({ Target: createEntityReference("account", id), PrincipalAccess: { Principal: createEntityReference("team", teamId), AccessMask: AccessRights.ReadAccess } })`.
424
584
 
425
585
  ---
426
586
 
@@ -428,13 +588,19 @@ interface EntityReference {
428
588
 
429
589
  ### createEntityReference
430
590
 
431
- Helper to create entity references.
591
+ Helper to create entity references (the same function every package exports).
432
592
 
433
593
  ```typescript
434
594
  import { createEntityReference } from "@mohsinonxrm/dataverse-sdk-messages";
435
595
 
436
596
  const accountRef = createEntityReference("account", accountId);
437
- // { logicalName: 'account', id: '...' }
597
+ // JSON: {"@odata.type":"Microsoft.Dynamics.CRM.account","accountid":"<id>"}
598
+ const named = createEntityReference("contact", contactId, { name: "John Doe" }); // kept, not sent
599
+ // A table the SDK's $metadata snapshot doesn't know:
600
+ createEntityReference("new_project", id, {
601
+ primaryIdAttribute: "new_projectid",
602
+ entitySetName: "new_projects",
603
+ });
438
604
  ```
439
605
 
440
606
  ---