diff --git a/.github/skills/connection-setup/SKILL.md b/.github/skills/connection-setup/SKILL.md index e3a8da6..8a5e5fc 100644 --- a/.github/skills/connection-setup/SKILL.md +++ b/.github/skills/connection-setup/SKILL.md @@ -65,7 +65,7 @@ Remove-Item $tempFile -ErrorAction SilentlyContinue ### Step 2: Create Connection -Supported SDK connector names: `arm`, `azuread`, `azureblob`, `azureeventgrid`, `azureiotcentral`, `azuremonitorlogs`, `azurequeues`, `azuretables`, `box`, `campfire`, `clicksendsms`, `cloudmersiveconvert`, `docusign`, `documentdb`, `docuware`, `dropbox`, `dynamicsax`, `elfsquaddata`, `etsy`, `eventbrite`, `eventhubs`, `excelonline`, `excelonlinebusiness`, `formstackforms`, `freshservice`, `ftp`, `github`, `googlecalendar`, `googledrive`, `googletasks`, `impexium`, `infusionsoft`, `insightly`, `jedoxodatahub`, `jira`, `kusto`, `mailchimp`, `meetingroommap`, `microsoftforms`, `monday`, `mq`, `msgraphgroupsanduser`, `office365`, `office365users`, `onedrive`, `onedriveforbusiness`, `orderful`, `outlook`, `pdfco`, `pipedrive`, `plivo`, `plumsail`, `projectplace`, `replicon`, `revai`, `rss`, `salesforce`, `seismicplanner`, `sendgrid`, `servicebus`, `sharepointonline`, `signinghub`, `slack`, `smtp`, `sql`, `starmind`, `starrezrestv1`, `tallyfy`, `teams`, `textrequest`, `ticketmaster`, `trello`, `twitter`, `typeform`, `universalprint`, `waywedo`, `wdatp`, `webex`, `wordpress`, `wordonlinebusiness`, `yammer`, `zendesk`, `azureautomation`, `azuredatafactory`, `azuredigitaltwins`, `azurevm`, `keyvault`, `microsoftbookings`, `office365groups`, `office365groupsmail`, `onenote`, `planner`, `powerbi`, `shifts`, `todo`, `zohosign` (and any `Microsoft.Web/connections` connector name). +Supported SDK connector names: `arm`, `azuread`, `azureblob`, `azureeventgrid`, `azureiotcentral`, `azuremonitorlogs`, `azurequeues`, `azuretables`, `box`, `campfire`, `clicksendsms`, `cloudmersiveconvert`, `commondataservice`, `docusign`, `documentdb`, `docuware`, `dropbox`, `dynamicsax`, `elfsquaddata`, `etsy`, `eventbrite`, `eventhubs`, `excelonline`, `excelonlinebusiness`, `formstackforms`, `freshservice`, `ftp`, `github`, `googlecalendar`, `googledrive`, `googletasks`, `impexium`, `infusionsoft`, `insightly`, `jedoxodatahub`, `jira`, `kusto`, `mailchimp`, `meetingroommap`, `microsoftforms`, `monday`, `mq`, `msgraphgroupsanduser`, `office365`, `office365users`, `onedrive`, `onedriveforbusiness`, `orderful`, `outlook`, `pdfco`, `pipedrive`, `plivo`, `plumsail`, `projectplace`, `replicon`, `revai`, `rss`, `salesforce`, `seismicplanner`, `sendgrid`, `servicebus`, `sharepointonline`, `signinghub`, `slack`, `smtp`, `sql`, `starmind`, `starrezrestv1`, `tallyfy`, `teams`, `textrequest`, `ticketmaster`, `trello`, `twitter`, `typeform`, `universalprint`, `waywedo`, `wdatp`, `webex`, `wordpress`, `wordonlinebusiness`, `yammer`, `zendesk`, `azureautomation`, `azuredatafactory`, `azuredigitaltwins`, `azurevm`, `keyvault`, `microsoftbookings`, `office365groups`, `office365groupsmail`, `onenote`, `planner`, `powerbi`, `shifts`, `todo`, `zohosign` (and any `Microsoft.Web/connections` connector name). ```powershell $connectorName = "" # e.g., "arm", "azureblob", "azuremonitorlogs", "kusto", "mq", "msgraphgroupsanduser", "office365", "office365users", "onedriveforbusiness", "sharepointonline", "smtp", "teams" diff --git a/CHANGELOG.md b/CHANGELOG.md index c835e96..4151fba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 the same content into release_notes.md for NuGet packaging. Do NOT put HTML comments in release_notes.md — it is packed verbatim into . --> +### Added + +- **Microsoft Dataverse client (`commondataservice`)** — generated typed `CommondataserviceClient` for the current-environment Microsoft Dataverse connector. Covers row operations (`ListRecordsAsync`, `CreateRecordAsync`, `GetItemCodelessAsync`, `UpdateRecordAsync`, `DeleteRecordAsync`), bound/unbound actions (`PerformBoundActionAsync`, `PerformUnboundActionAsync`), relate/unrelate (`AssociateEntitiesAsync`, `DisassociateEntitiesAsync`), relevance search (`GetRelevantRowsAsync`), and file/image column upload & download. Deprecated legacy dataset/table operations are excluded by the generator's deprecation filter. + ## [0.12.0-preview.1] - 2026-06-02 ### Breaking Changes diff --git a/ROADMAP.md b/ROADMAP.md index 608de3e..1d832a8 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -42,7 +42,7 @@ High-volume connectors for enterprise data and integration scenarios. | Priority | Connector | Usage Rank | Partner Tier | 90-Day Executions | Notes | Status | |----------|-----------|------------|--------------|-------------------|-------|--------| -| 2.1 | **Dataverse (CDS)** | #12 | Tier 1 | 2.93B | Power Platform integration | ⬜ Not started | +| 2.1 | **Dataverse** (`commondataservice`) | #12 | Tier 1 | 2.93B | Current-environment Microsoft Dataverse — rows, bound/unbound actions, relate/unrelate, relevance search | ✅ Complete | | 2.2 | **Excel Online Business** | #9 | Tier 2 | 957M | Spreadsheet operations | ⬜ Not started | | 2.3 | **Dynamics 365** | — | Tier 2 | 315M | CRM record operations | ⬜ Not started | | 2.4 | **Salesforce** | — | Tier 2 | 127M | CRM operations | ⬜ Not started | diff --git a/src/Azure.Connectors.Sdk/Generated/CommondataserviceExtensions.cs b/src/Azure.Connectors.Sdk/Generated/CommondataserviceExtensions.cs new file mode 100644 index 0000000..bb640e2 --- /dev/null +++ b/src/Azure.Connectors.Sdk/Generated/CommondataserviceExtensions.cs @@ -0,0 +1,1620 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// CommondataserviceExtensions.cs - Auto-generated Connectors SDK +// Do not edit this file directly. + +#nullable disable +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.Commondataservice.Models; +using Azure.Core; +using Azure.Identity; + +namespace Azure.Connectors.Sdk.Commondataservice.Models +{ + + #region Types + + /// + /// Response for GetDataSetsMetadata + /// + public class DataSetsMetadata + { + /// tabular + [JsonPropertyName("tabular")] + public TabularDataSetsMetadata Tabular { get; set; } + + /// blob + [JsonPropertyName("blob")] + public BlobDataSetsMetadata Blob { get; set; } + } + + /// + /// tabular + /// + public class TabularDataSetsMetadata + { + /// Dataset source + [JsonPropertyName("source")] + public string Source { get; set; } + + /// Dataset display name + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } + + /// Dataset url encoding + [JsonPropertyName("urlEncoding")] + public string UrlEncoding { get; set; } + + /// Table display name + [JsonPropertyName("tableDisplayName")] + public string TableDisplayName { get; set; } + + /// Table plural display name + [JsonPropertyName("tablePluralName")] + public string TablePluralName { get; set; } + } + + /// + /// blob + /// + public class BlobDataSetsMetadata + { + /// Blob dataset source + [JsonPropertyName("source")] + public string Source { get; set; } + + /// Blob dataset display name + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } + + /// Blob dataset url encoding + [JsonPropertyName("urlEncoding")] + public string UrlEncoding { get; set; } + } + + /// + /// Associates one row to another on the provided relationship + /// + [DynamicSchema("GetMetadataForPostItem_V2")] + public class AssociateRecordsPatchItemInput + { + /// + /// Dynamic properties determined at runtime by the connector's schema discovery endpoint. + /// Populate this dictionary with the properties returned by the schema API. + /// + [JsonExtensionData] + public Dictionary AdditionalProperties { get; set; } = new(); + } + + /// + /// Response for Associates one row to another on the provided relationship + /// + [DynamicSchema("GetTable")] + public class Item + { + /// + /// Dynamic properties determined at runtime by the connector's schema discovery endpoint. + /// Populate this dictionary with the properties returned by the schema API. + /// + [JsonExtensionData] + public Dictionary AdditionalProperties { get; set; } = new(); + + /// dynamicProperties + [JsonPropertyName("dynamicProperties")] + public JsonElement? DynamicProperties { get; set; } + } + + /// + /// Response for Retrieves all collection valued relationship items as an expand would + /// + public class ItemsList + { + /// List of Items + [JsonPropertyName("value")] + public List Value { get; set; } + } + + /// + /// Response for GetDataSets_V2 + /// + public class DataSetsList + { + /// List of datasets + [JsonPropertyName("value")] + public List Value { get; set; } + } + + /// + /// Item in List of datasets + /// + public class DataSet + { + /// Dataset name + [JsonPropertyName("Name")] + public string Name { get; set; } + + /// Dataset display name + [JsonPropertyName("DisplayName")] + public string DisplayName { get; set; } + + /// Pass-through Native Queries + [JsonPropertyName("query")] + [JsonInclude] + public List Query { get; init; } + } + + /// + /// Item in Pass-through Native Queries + /// + public class PassThroughNativeQuery + { + /// Query language + [JsonPropertyName("Language")] + public string Language { get; set; } + } + + /// + /// Response for Get row (legacy) + /// + [DynamicSchema("GetTable_V2")] + public class GetItemResponse + { + /// + /// Dynamic properties determined at runtime by the connector's schema discovery endpoint. + /// Populate this dictionary with the properties returned by the schema API. + /// + [JsonExtensionData] + public Dictionary AdditionalProperties { get; set; } = new(); + } + + /// + /// Response for Get table metadata - Patch + /// + public class TableMetadata + { + /// Table name + [JsonPropertyName("name")] + public string Name { get; set; } + + /// Table title + [JsonPropertyName("title")] + public string Title { get; set; } + + /// Table permission + [JsonPropertyName("x-ms-permission")] + public string XMsPermission { get; set; } + + /// x-ms-capabilities + [JsonPropertyName("x-ms-capabilities")] + public TableCapabilitiesMetadata XMsCapabilities { get; set; } + + /// schema + [JsonPropertyName("schema")] + public ObjectEntity Schema { get; set; } + + /// referencedEntities + [JsonPropertyName("referencedEntities")] + public ObjectEntity ReferencedEntities { get; set; } + + /// Url link + [JsonPropertyName("webUrl")] + public string WebUrl { get; set; } + } + + /// + /// x-ms-capabilities + /// + public class TableCapabilitiesMetadata + { + /// sortRestrictions + [JsonPropertyName("sortRestrictions")] + public TableSortRestrictionsMetadata SortRestrictions { get; set; } + + /// filterRestrictions + [JsonPropertyName("filterRestrictions")] + public TableFilterRestrictionsMetadata FilterRestrictions { get; set; } + + /// selectRestrictions + [JsonPropertyName("selectRestrictions")] + public TableSelectRestrictionsMetadata SelectRestrictions { get; set; } + + /// Server paging restrictions + [JsonPropertyName("isOnlyServerPagable")] + public bool? IsOnlyServerPagable { get; set; } + + /// List of supported filter capabilities + [JsonPropertyName("filterFunctionSupport")] + public List FilterFunctionSupport { get; set; } + + /// List of supported server-driven paging capabilities + [JsonPropertyName("serverPagingOptions")] + public List ServerPagingOptions { get; set; } + } + + /// + /// sortRestrictions + /// + public class TableSortRestrictionsMetadata + { + /// Indicates whether this table has sortable columns + [JsonPropertyName("sortable")] + public bool? Sortable { get; set; } + + /// List of unsortable properties + [JsonPropertyName("unsortableProperties")] + public List UnsortableProperties { get; set; } + + /// List of properties which support ascending order only + [JsonPropertyName("ascendingOnlyProperties")] + public List AscendingOnlyProperties { get; set; } + } + + /// + /// filterRestrictions + /// + public class TableFilterRestrictionsMetadata + { + /// Indicates whether this table has filterable columns + [JsonPropertyName("filterable")] + public bool? Filterable { get; set; } + + /// List of non filterable properties + [JsonPropertyName("nonFilterableProperties")] + public List NonFilterableProperties { get; set; } + + /// List of required properties + [JsonPropertyName("requiredProperties")] + public List RequiredProperties { get; set; } + } + + /// + /// selectRestrictions + /// + public class TableSelectRestrictionsMetadata + { + /// Indicates whether this table has selectable columns + [JsonPropertyName("selectable")] + public bool? Selectable { get; set; } + } + + /// + /// schema + /// + public class ObjectEntity + { + /// + /// Arbitrary properties. This type has no static schema; any JSON properties will be captured here. + /// + [JsonExtensionData] + public Dictionary AdditionalProperties { get; set; } = new(); + } + + /// + /// Response for GetTables_V2 + /// + public class TablesList + { + /// List of Tables + [JsonPropertyName("value")] + public List Value { get; set; } + } + + /// + /// Item in List of Tables + /// + public class Table + { + /// The name of the table. The name is used at runtime. + [JsonPropertyName("Name")] + public string Name { get; set; } + + /// The display name of the table. + [JsonPropertyName("DisplayName")] + public string DisplayName { get; set; } + + /// Additional table properties provided by the connector to the clients. + [JsonPropertyName("DynamicProperties")] + [JsonInclude] + public JsonElement? DynamicProperties { get; init; } + } + + /// + /// Update a row (legacy) + /// + [DynamicSchema("GetMetadataForPatchItem_V2")] + public class PatchItemInput + { + /// + /// Dynamic properties determined at runtime by the connector's schema discovery endpoint. + /// Populate this dictionary with the properties returned by the schema API. + /// + [JsonExtensionData] + public Dictionary AdditionalProperties { get; set; } = new(); + } + + /// + /// Response for Update a row (legacy) + /// + [DynamicSchema("GetMetadataForPatchItem_V2")] + public class PatchItemResponse + { + /// + /// Dynamic properties determined at runtime by the connector's schema discovery endpoint. + /// Populate this dictionary with the properties returned by the schema API. + /// + [JsonExtensionData] + public Dictionary AdditionalProperties { get; set; } = new(); + } + + /// + /// Add a new row (legacy) + /// + [DynamicSchema("GetMetadataForPostItem_V2")] + public class PostItemInput + { + /// + /// Dynamic properties determined at runtime by the connector's schema discovery endpoint. + /// Populate this dictionary with the properties returned by the schema API. + /// + [JsonExtensionData] + public Dictionary AdditionalProperties { get; set; } = new(); + } + + /// + /// Response for Add a new row (legacy) + /// + [DynamicSchema("GetMetadataForPostItem_V2")] + public class PostItemResponse + { + /// + /// Dynamic properties determined at runtime by the connector's schema discovery endpoint. + /// Populate this dictionary with the properties returned by the schema API. + /// + [JsonExtensionData] + public Dictionary AdditionalProperties { get; set; } = new(); + } + + #endregion Types + + #region Model Factory + + /// + /// Model factory for creating instances of Commondataservice models. + /// Use these factory methods to construct model instances in tests and scenarios + /// where output-only properties (with init-only setters) need to be populated. + /// + public static class CommondataserviceModelFactory + { + /// + /// Creates a new instance of . + /// + public static DataSetsMetadata DataSetsMetadata( + TabularDataSetsMetadata tabular = default, + BlobDataSetsMetadata blob = default) + { + return new DataSetsMetadata + { + Tabular = tabular, + Blob = blob, + }; + } + + /// + /// Creates a new instance of . + /// + public static TabularDataSetsMetadata TabularDataSetsMetadata( + string source = default, + string displayName = default, + string urlEncoding = default, + string tableDisplayName = default, + string tablePluralName = default) + { + return new TabularDataSetsMetadata + { + Source = source, + DisplayName = displayName, + UrlEncoding = urlEncoding, + TableDisplayName = tableDisplayName, + TablePluralName = tablePluralName, + }; + } + + /// + /// Creates a new instance of . + /// + public static BlobDataSetsMetadata BlobDataSetsMetadata( + string source = default, + string displayName = default, + string urlEncoding = default) + { + return new BlobDataSetsMetadata + { + Source = source, + DisplayName = displayName, + UrlEncoding = urlEncoding, + }; + } + + /// + /// Creates a new instance of . + /// + public static Item Item( + JsonElement? dynamicProperties = default) + { + return new Item + { + DynamicProperties = dynamicProperties, + }; + } + + /// + /// Creates a new instance of . + /// + public static ItemsList ItemsList( + List value = default) + { + return new ItemsList + { + Value = value, + }; + } + + /// + /// Creates a new instance of . + /// + public static DataSetsList DataSetsList( + List value = default) + { + return new DataSetsList + { + Value = value, + }; + } + + /// + /// Creates a new instance of . + /// + public static DataSet DataSet( + string name = default, + string displayName = default, + List query = default) + { + return new DataSet + { + Name = name, + DisplayName = displayName, + Query = query, + }; + } + + /// + /// Creates a new instance of . + /// + public static PassThroughNativeQuery PassThroughNativeQuery( + string language = default) + { + return new PassThroughNativeQuery + { + Language = language, + }; + } + + /// + /// Creates a new instance of . + /// + public static TableMetadata TableMetadata( + string name = default, + string title = default, + string xMsPermission = default, + TableCapabilitiesMetadata xMsCapabilities = default, + ObjectEntity schema = default, + ObjectEntity referencedEntities = default, + string webUrl = default) + { + return new TableMetadata + { + Name = name, + Title = title, + XMsPermission = xMsPermission, + XMsCapabilities = xMsCapabilities, + Schema = schema, + ReferencedEntities = referencedEntities, + WebUrl = webUrl, + }; + } + + /// + /// Creates a new instance of . + /// + public static TableCapabilitiesMetadata TableCapabilitiesMetadata( + TableSortRestrictionsMetadata sortRestrictions = default, + TableFilterRestrictionsMetadata filterRestrictions = default, + TableSelectRestrictionsMetadata selectRestrictions = default, + bool? isOnlyServerPagable = default, + List filterFunctionSupport = default, + List serverPagingOptions = default) + { + return new TableCapabilitiesMetadata + { + SortRestrictions = sortRestrictions, + FilterRestrictions = filterRestrictions, + SelectRestrictions = selectRestrictions, + IsOnlyServerPagable = isOnlyServerPagable, + FilterFunctionSupport = filterFunctionSupport, + ServerPagingOptions = serverPagingOptions, + }; + } + + /// + /// Creates a new instance of . + /// + public static TableSortRestrictionsMetadata TableSortRestrictionsMetadata( + bool? sortable = default, + List unsortableProperties = default, + List ascendingOnlyProperties = default) + { + return new TableSortRestrictionsMetadata + { + Sortable = sortable, + UnsortableProperties = unsortableProperties, + AscendingOnlyProperties = ascendingOnlyProperties, + }; + } + + /// + /// Creates a new instance of . + /// + public static TableFilterRestrictionsMetadata TableFilterRestrictionsMetadata( + bool? filterable = default, + List nonFilterableProperties = default, + List requiredProperties = default) + { + return new TableFilterRestrictionsMetadata + { + Filterable = filterable, + NonFilterableProperties = nonFilterableProperties, + RequiredProperties = requiredProperties, + }; + } + + /// + /// Creates a new instance of . + /// + public static TableSelectRestrictionsMetadata TableSelectRestrictionsMetadata( + bool? selectable = default) + { + return new TableSelectRestrictionsMetadata + { + Selectable = selectable, + }; + } + + /// + /// Creates a new instance of . + /// + public static TablesList TablesList( + List
value = default) + { + return new TablesList + { + Value = value, + }; + } + + /// + /// Creates a new instance of . + /// + public static Table Table( + string name = default, + string displayName = default, + JsonElement? dynamicProperties = default) + { + return new Table + { + Name = name, + DisplayName = displayName, + DynamicProperties = dynamicProperties, + }; + } + } + + #endregion Model Factory + + #region Trigger Payloads + + /// + /// Typed trigger payload for the OnDeletedItems trigger (Commondataservice "When a row is deleted (Admin Only) (Deprecated)", operationId: GetOnDeletedItems_V2). + /// Deserialize Connector Namespace callbacks directly: JsonSerializer.Deserialize<CommondataserviceOnDeletedItemsTriggerPayload>(body). + /// + public class CommondataserviceOnDeletedItemsTriggerPayload : TriggerCallbackPayload + { + } + + /// + /// Typed trigger payload for the OnNewItems trigger (Commondataservice "When a row is added (Admin Only) (Deprecated)", operationId: GetOnNewItems_V2). + /// Deserialize Connector Namespace callbacks directly: JsonSerializer.Deserialize<CommondataserviceOnNewItemsTriggerPayload>(body). + /// + public class CommondataserviceOnNewItemsTriggerPayload : TriggerCallbackPayload + { + } + + /// + /// Typed trigger payload for the OnUpdatedItems trigger (Commondataservice "When a row is modified (Admin Only) (Deprecated)", operationId: GetOnUpdatedItems_V2). + /// Deserialize Connector Namespace callbacks directly: JsonSerializer.Deserialize<CommondataserviceOnUpdatedItemsTriggerPayload>(body). + /// + public class CommondataserviceOnUpdatedItemsTriggerPayload : TriggerCallbackPayload + { + } + + /// + /// Static registry of trigger operations for the Commondataservice connector that have typed payloads. + /// Maps operation names to their typed subtypes. + /// Triggers that return binary content (e.g., file downloads) are not included here + /// because they have no JSON-deserializable payload type. See + /// for the complete list of trigger operation name constants. + /// + public static class CommondataserviceTriggers + { + /// + /// Trigger operations with typed payloads for the Commondataservice connector. + /// This is a subset of all triggers — see for the full list. + /// + public static IReadOnlyDictionary Operations { get; } = new ReadOnlyDictionary( + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + ["GetOnDeletedItems_V2"] = typeof(CommondataserviceOnDeletedItemsTriggerPayload), + ["GetOnNewItems_V2"] = typeof(CommondataserviceOnNewItemsTriggerPayload), + ["GetOnUpdatedItems_V2"] = typeof(CommondataserviceOnUpdatedItemsTriggerPayload), + }); + } + + #endregion Trigger Payloads + +} + +namespace Azure.Connectors.Sdk.Commondataservice +{ + + #region Trigger Operation Constants + + /// + /// Trigger operation name constants for the Commondataservice connector. + /// Use these constants with the [ConnectorTrigger] attribute's OperationName property + /// and with the Connector Namespace TriggerConfig operationName field. + /// + public static class CommondataserviceTriggerOperations + { + /// + /// When a row is deleted (Admin Only) (Deprecated). + /// Payload type: . + /// + public const string OnDeletedItems = "GetOnDeletedItems_V2"; + + /// + /// When a row is added (Admin Only) (Deprecated). + /// Payload type: . + /// + public const string OnNewItems = "GetOnNewItems_V2"; + + /// + /// When a row is modified (Admin Only) (Deprecated). + /// Payload type: . + /// + public const string OnUpdatedItems = "GetOnUpdatedItems_V2"; + + } + + #endregion Trigger Operation Constants + + #region Trigger Parameter Metadata + + /// + /// Trigger input parameter name constants for the Commondataservice connector. + /// These correspond to the Connector Namespace TriggerConfig parameters array. + /// + public static class CommondataserviceTriggerParameters + { + /// + /// Input parameters for the OnDeletedItems trigger operation (operationId: GetOnDeletedItems_V2). + /// + public static class OnDeletedItems + { + /// + /// An ODATA filter query to restrict the entries returned (e.g. stringColumn eq 'string' OR numberColumn lt 123). + /// + public const string Filter = "$filter"; + + /// + /// An ODATA orderBy query for specifying the order of entries. + /// + public const string Orderby = "$orderby"; + + /// + /// Total number of entries to retrieve (default = all). + /// + public const string Top = "$top"; + + /// + /// The number of entries to skip (default = 0). + /// + public const string Skip = "$skip"; + + } + + /// + /// Input parameters for the OnNewItems trigger operation (operationId: GetOnNewItems_V2). + /// + public static class OnNewItems + { + /// + /// An ODATA filter query to restrict the entries returned (e.g. stringColumn eq 'string' OR numberColumn lt 123). + /// + public const string Filter = "$filter"; + + /// + /// An ODATA orderBy query for specifying the order of entries. + /// + public const string Orderby = "$orderby"; + + /// + /// Total number of entries to retrieve (default = all). + /// + public const string Top = "$top"; + + /// + /// The number of entries to skip (default = 0). + /// + public const string Skip = "$skip"; + + } + + /// + /// Input parameters for the OnUpdatedItems trigger operation (operationId: GetOnUpdatedItems_V2). + /// + public static class OnUpdatedItems + { + /// + /// An ODATA filter query to restrict the entries returned (e.g. stringColumn eq 'string' OR numberColumn lt 123). + /// + public const string Filter = "$filter"; + + /// + /// An ODATA orderBy query for specifying the order of entries. + /// + public const string Orderby = "$orderby"; + + /// + /// Total number of entries to retrieve (default = all). + /// + public const string Top = "$top"; + + /// + /// The number of entries to skip (default = 0). + /// + public const string Skip = "$skip"; + + } + + } + + #endregion Trigger Parameter Metadata + + #region Client + + /// + /// Typed client for commondataservice connector. + /// + public class CommondataserviceClient : ConnectorClientBase + { + /// + /// Creates a new CommondataserviceClient with the specified connection runtime URL. + /// Uses by default. + /// + /// The connection runtime URL from Azure Portal. + public CommondataserviceClient(Uri connectionRuntimeUrl) + : base(connectionRuntimeUrl) + { + } + + /// + /// Creates a new CommondataserviceClient with the specified connection runtime URL and credential. + /// + /// The connection runtime URL from Azure Portal. + /// The Azure credential for authentication. + /// Optional client options for retry, timeout, etc. + public CommondataserviceClient(Uri connectionRuntimeUrl, TokenCredential credential, ConnectorClientOptions options = null) + : base(connectionRuntimeUrl, credential, options) + { + } + + /// + /// Creates a new CommondataserviceClient with the specified connection runtime URL and credential. + /// + /// The connection runtime URL from Azure Portal. + /// The Azure credential for authentication. + public CommondataserviceClient(Uri connectionRuntimeUrl, TokenCredential credential) + : base(connectionRuntimeUrl, credential) + { + } + + /// + /// Creates a new CommondataserviceClient with the specified connection runtime URL string. + /// Uses by default. + /// + /// The connection runtime URL from Azure Portal. + public CommondataserviceClient(string connectionRuntimeUrl) + : base(connectionRuntimeUrl) + { + } + + protected CommondataserviceClient() : base() { } + + public override string ConnectorName => "commondataservice"; + + private static readonly System.Diagnostics.ActivitySource ConnectorActivitySource = new System.Diagnostics.ActivitySource("Azure.Connectors.Sdk.commondataservice"); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => base.Equals(obj); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => base.GetHashCode(); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override string ToString() => base.ToString(); + + // NOTE(daviburg): Double-encodes a value for URL path segments. + // The commondataservice connector uses x-ms-url-encoding: double on path + // parameters because dataset values are full URLs with slashes and colons. + private static string DoubleEscape(string value) => Uri.EscapeDataString(Uri.EscapeDataString(value)); + + /// + /// GetDataSetsMetadata + /// + /// Cancellation token. + /// The GetDataSetsMetadata response. + public virtual async Task GetDataSetsMetadataAsync(CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetDataSetsMetadataAsync"); + try + { + var path = $"/$metadata.json/datasets"; + return await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Follows nextLink to retrieve next page of data + /// + /// This operation gets the next page of data. + /// nextLink value + /// Cancellation token. + public virtual async Task GetNextPageAsync(string nextLinkValue, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetNextPageAsync"); + try + { + if (nextLinkValue is null) + throw new ArgumentNullException(nameof(nextLinkValue)); + var path = $"/nextLink/{Uri.EscapeDataString(nextLinkValue.ToString())}"; + await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Associates one row to another on the provided relationship + /// + /// Associates one row to another on the provided relationship + /// Dataset + /// Table + /// Id + /// Relationship + /// The request body. + /// Cancellation token. + /// The Associates one row to another on the provided relationship response. + public virtual async Task AssociateRecordsPatchItemAsync([DynamicValues("GetDataSets")] string dataset, [DynamicValues("GetTables")] string table, string id, string relationship, AssociateRecordsPatchItemInput input, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.AssociateRecordsPatchItemAsync"); + try + { + if (dataset is null) + throw new ArgumentNullException(nameof(dataset)); + if (table is null) + throw new ArgumentNullException(nameof(table)); + if (id is null) + throw new ArgumentNullException(nameof(id)); + if (relationship is null) + throw new ArgumentNullException(nameof(relationship)); + var path = $"/v2/datasets/{DoubleEscape(dataset.ToString())}/tables/{DoubleEscape(table.ToString())}/items/{DoubleEscape(id.ToString())}/Relationship/{DoubleEscape(relationship.ToString())}"; + return await this + .CallConnectorAsync(HttpMethod.Patch, path, input, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Creates Note (annotation) for specified table row + /// + /// Creates Note (annotation) for specified table row. + /// Dataset + /// Table + /// Id + /// The request body. + /// File Name + /// Cancellation token. + /// The Creates Note (annotation) for specified table row response. + public virtual async Task CreateAttachmentAsync([DynamicValues("GetDataSets")] string dataset, [DynamicValues("GetTables")] string table, int id, byte[] input, string fileName, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.CreateAttachmentAsync"); + try + { + if (dataset is null) + throw new ArgumentNullException(nameof(dataset)); + if (table is null) + throw new ArgumentNullException(nameof(table)); + var queryParams = new List(); + if (fileName is null) + throw new ArgumentNullException(nameof(fileName)); + queryParams.Add($"displayName={Uri.EscapeDataString(fileName.ToString())}"); + var path = $"/v2/datasets/{DoubleEscape(dataset.ToString())}/tables/{DoubleEscape(table.ToString())}/items/{DoubleEscape(id.ToString())}/attachments" + (queryParams.Count > 0 ? "?" + string.Join("&", queryParams) : ""); + return await this + .CallConnectorAsync(HttpMethod.Post, path, input, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Deletes specified attachment on the Note (annotation) + /// + /// Deletes specified attachment on the Note (annotation). + /// Dataset + /// Table + /// Id + /// AttachmentId + /// Cancellation token. + public virtual async Task DeleteAttachmentAsync([DynamicValues("GetDataSets")] string dataset, [DynamicValues("GetTables")] string table, string id, string attachmentId, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.DeleteAttachmentAsync"); + try + { + if (dataset is null) + throw new ArgumentNullException(nameof(dataset)); + if (table is null) + throw new ArgumentNullException(nameof(table)); + if (id is null) + throw new ArgumentNullException(nameof(id)); + if (attachmentId is null) + throw new ArgumentNullException(nameof(attachmentId)); + var path = $"/v2/datasets/{DoubleEscape(dataset.ToString())}/tables/{DoubleEscape(table.ToString())}/items/{DoubleEscape(id.ToString())}/attachments/{DoubleEscape(attachmentId.ToString())}"; + await this + .CallConnectorAsync(HttpMethod.Delete, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Delete a row (legacy) + /// + /// This operation deletes a row from a table collection + /// Environment + /// Table Name + /// Item identifier + /// Cancellation token. + public virtual async Task DeleteItemAsync([DynamicValues("GetDataSets_V2")] string environment, [DynamicValues("GetTables")] string tableName, string itemIdentifier, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.DeleteItemAsync"); + try + { + if (environment is null) + throw new ArgumentNullException(nameof(environment)); + if (tableName is null) + throw new ArgumentNullException(nameof(tableName)); + if (itemIdentifier is null) + throw new ArgumentNullException(nameof(itemIdentifier)); + var path = $"/v2/datasets/{DoubleEscape(environment.ToString())}/tables/{DoubleEscape(tableName.ToString())}/items/{DoubleEscape(itemIdentifier.ToString())}"; + await this + .CallConnectorAsync(HttpMethod.Delete, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Disassociates a row from a multi-valued relationship + /// + /// Disassociates a row from a multi-valued relationship + /// Dataset + /// Table + /// Id + /// Relationship + /// RelatedId + /// Cancellation token. + /// The Disassociates a row from a multi-valued relationship response. + public virtual async Task DisassociateRecordsPostItemAsync([DynamicValues("GetDataSets")] string dataset, [DynamicValues("GetTables")] string table, string id, string relationship, string relatedId, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.DisassociateRecordsPostItemAsync"); + try + { + if (dataset is null) + throw new ArgumentNullException(nameof(dataset)); + if (table is null) + throw new ArgumentNullException(nameof(table)); + if (id is null) + throw new ArgumentNullException(nameof(id)); + if (relationship is null) + throw new ArgumentNullException(nameof(relationship)); + if (relatedId is null) + throw new ArgumentNullException(nameof(relatedId)); + var path = $"/v2/datasets/{DoubleEscape(dataset.ToString())}/tables/{DoubleEscape(table.ToString())}/items/{DoubleEscape(id.ToString())}/Relationship/{DoubleEscape(relationship.ToString())}/RelatedId/{DoubleEscape(relatedId.ToString())}"; + return await this + .CallConnectorAsync(HttpMethod.Delete, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Disassociates a row from a single-valued relationship + /// + /// Disassociates a row from a single-valued relationship + /// Dataset + /// Table + /// Id + /// Relationship + /// Cancellation token. + /// The Disassociates a row from a single-valued relationship response. + public virtual async Task DisassociateSingleValueRecordDeleteItemAsync([DynamicValues("GetDataSets")] string dataset, [DynamicValues("GetTables")] string table, string id, string relationship, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.DisassociateSingleValueRecordDeleteItemAsync"); + try + { + if (dataset is null) + throw new ArgumentNullException(nameof(dataset)); + if (table is null) + throw new ArgumentNullException(nameof(table)); + if (id is null) + throw new ArgumentNullException(nameof(id)); + if (relationship is null) + throw new ArgumentNullException(nameof(relationship)); + var path = $"/v2/datasets/{DoubleEscape(dataset.ToString())}/tables/{DoubleEscape(table.ToString())}/items/{DoubleEscape(id.ToString())}/Relationship/{DoubleEscape(relationship.ToString())}"; + return await this + .CallConnectorAsync(HttpMethod.Delete, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Retrieves file content for specified Note (annotation) + /// + /// Retrieves file content for specified Note (annotation). + /// Dataset + /// Table + /// Id + /// AttachmentId + /// Cancellation token. + /// The Retrieves file content for specified Note (annotation) response. + public virtual async Task GetAttachmentContentAsync([DynamicValues("GetDataSets")] string dataset, [DynamicValues("GetTables")] string table, string id, string attachmentId, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetAttachmentContentAsync"); + try + { + if (dataset is null) + throw new ArgumentNullException(nameof(dataset)); + if (table is null) + throw new ArgumentNullException(nameof(table)); + if (id is null) + throw new ArgumentNullException(nameof(id)); + if (attachmentId is null) + throw new ArgumentNullException(nameof(attachmentId)); + var path = $"/v2/datasets/{DoubleEscape(dataset.ToString())}/tables/{DoubleEscape(table.ToString())}/items/{DoubleEscape(id.ToString())}/attachments/{DoubleEscape(attachmentId.ToString())}/$value"; + return await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Retrieves all collection valued relationship items as an expand would + /// + /// Retrieves all collection valued relationship items as an expand would + /// Dataset + /// Table + /// Id + /// Collection + /// Relationship + /// Target + /// Cancellation token. + /// The Retrieves all collection valued relationship items as an expand would response. + public virtual async Task GetCollectionRelationshipsAsync([DynamicValues("GetDataSets")] string dataset, [DynamicValues("GetTables")] string table, string id, string collection, string relationship, string target, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetCollectionRelationshipsAsync"); + try + { + if (dataset is null) + throw new ArgumentNullException(nameof(dataset)); + if (table is null) + throw new ArgumentNullException(nameof(table)); + if (id is null) + throw new ArgumentNullException(nameof(id)); + if (collection is null) + throw new ArgumentNullException(nameof(collection)); + if (relationship is null) + throw new ArgumentNullException(nameof(relationship)); + if (target is null) + throw new ArgumentNullException(nameof(target)); + var path = $"/v2/datasets/{DoubleEscape(dataset.ToString())}/tables/{DoubleEscape(table.ToString())}/items/{DoubleEscape(id.ToString())}/Collection/{DoubleEscape(collection.ToString())}/{DoubleEscape(relationship.ToString())}/TargetTable/{DoubleEscape(target.ToString())}"; + return await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// GetDataSets_V2 + /// + /// Cancellation token. + /// The GetDataSets_V2 response. + public virtual async Task GetDataSetsAsync(CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetDataSetsAsync"); + try + { + var path = $"/v2/datasets"; + return await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Get row (legacy) + /// + /// This operation retrieves the specified row for a table + /// Environment + /// Table Name + /// Item identifier + /// Cancellation token. + /// The Get row (legacy) response. + public virtual async Task GetItemAsync([DynamicValues("GetDataSets_V2")] string environment, [DynamicValues("GetTables")] string tableName, string itemIdentifier, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetItemAsync"); + try + { + if (environment is null) + throw new ArgumentNullException(nameof(environment)); + if (tableName is null) + throw new ArgumentNullException(nameof(tableName)); + if (itemIdentifier is null) + throw new ArgumentNullException(nameof(itemIdentifier)); + var path = $"/v2/datasets/{DoubleEscape(environment.ToString())}/tables/{DoubleEscape(tableName.ToString())}/items/{DoubleEscape(itemIdentifier.ToString())}"; + return await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Retrieves all Notes (annotations) for the provided table row Id + /// + /// Retrieves all Notes (annotations) for the provided table row Id. + /// Dataset + /// Table + /// Id + /// Cancellation token. + /// The Retrieves all Notes (annotations) for the provided table row Id response. + public virtual async Task GetItemAttachmentsAsync([DynamicValues("GetDataSets")] string dataset, [DynamicValues("GetTables")] string table, string id, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetItemAttachmentsAsync"); + try + { + if (dataset is null) + throw new ArgumentNullException(nameof(dataset)); + if (table is null) + throw new ArgumentNullException(nameof(table)); + if (id is null) + throw new ArgumentNullException(nameof(id)); + var path = $"/v2/datasets/{DoubleEscape(dataset.ToString())}/tables/{DoubleEscape(table.ToString())}/items/{DoubleEscape(id.ToString())}/attachments"; + return await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// List rows (legacy) + /// + /// This operation gets rows for a table + /// Environment + /// Table Name + /// Aggregation transformation + /// Filter Query + /// Order By + /// Top Count + /// Expand Query + /// Cancellation token. + /// The List rows (legacy) response. + public virtual async Task GetItemsAsync([DynamicValues("GetDataSets_V2")] string environment, [DynamicValues("GetTables")] string tableName, string aggregationTransformation = default, string filterQuery = default, string orderBy = default, int? topCount = default, string expandQuery = default, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetItemsAsync"); + try + { + if (environment is null) + throw new ArgumentNullException(nameof(environment)); + if (tableName is null) + throw new ArgumentNullException(nameof(tableName)); + var queryParams = new List(); + if (aggregationTransformation != default) + queryParams.Add($"$apply={Uri.EscapeDataString(aggregationTransformation.ToString())}"); + if (filterQuery != default) + queryParams.Add($"$filter={Uri.EscapeDataString(filterQuery.ToString())}"); + if (orderBy != default) + queryParams.Add($"$orderby={Uri.EscapeDataString(orderBy.ToString())}"); + if (topCount.HasValue) + queryParams.Add($"$top={Uri.EscapeDataString(topCount.Value.ToString())}"); + if (expandQuery != default) + queryParams.Add($"$expand={Uri.EscapeDataString(expandQuery.ToString())}"); + var path = $"/v2/datasets/{DoubleEscape(environment.ToString())}/tables/{DoubleEscape(tableName.ToString())}/items" + (queryParams.Count > 0 ? "?" + string.Join("&", queryParams) : ""); + return await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Get table metadata - Patch + /// + /// Gets table metadata for patch operation + /// Environment + /// Table Name + /// Cancellation token. + /// The Get table metadata - Patch response. + public virtual async Task GetMetadataForPatchItemAsync([DynamicValues("GetDataSets")] string environment, [DynamicValues("GetTables")] string tableName, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetMetadataForPatchItemAsync"); + try + { + if (environment is null) + throw new ArgumentNullException(nameof(environment)); + if (tableName is null) + throw new ArgumentNullException(nameof(tableName)); + var path = $"/v2/$metadata.json/datasets/{DoubleEscape(environment.ToString())}/tables/{DoubleEscape(tableName.ToString())}/patchitem"; + return await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Get table metadata - Post + /// + /// Gets table metadata for post operation + /// Environment + /// Table Name + /// Cancellation token. + /// The Get table metadata - Post response. + public virtual async Task GetMetadataForPostItemAsync([DynamicValues("GetDataSets")] string environment, [DynamicValues("GetTables")] string tableName, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetMetadataForPostItemAsync"); + try + { + if (environment is null) + throw new ArgumentNullException(nameof(environment)); + if (tableName is null) + throw new ArgumentNullException(nameof(tableName)); + var path = $"/v2/$metadata.json/datasets/{DoubleEscape(environment.ToString())}/tables/{DoubleEscape(tableName.ToString())}/postitem"; + return await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Retrieves the metadata for multi select column metadata + /// + /// Retrieves the metadata for multi select column metadata. + /// org name + /// Table name + /// id + /// Cancellation token. + /// The Retrieves the metadata for multi select column metadata response. + public virtual async Task GetMultiSelectMetadataAsync([DynamicValues("GetDataSets")] string orgName, [DynamicValues("GetTables")] string tableName, string id, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetMultiSelectMetadataAsync"); + try + { + if (orgName is null) + throw new ArgumentNullException(nameof(orgName)); + if (tableName is null) + throw new ArgumentNullException(nameof(tableName)); + if (id is null) + throw new ArgumentNullException(nameof(id)); + var path = $"/v2/datasets/{DoubleEscape(orgName.ToString())}/tables/{DoubleEscape(tableName.ToString())}/items/{DoubleEscape(id.ToString())}/MultiSelect"; + return await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Retrieves the metadata for choice column metadata + /// + /// Retrieves the metadata for choice column metadata. + /// Dataset + /// Table + /// id + /// type of choice + /// Cancellation token. + /// The Retrieves the metadata for choice column metadata response. + public virtual async Task GetOptionSetMetadataAsync([DynamicValues("GetDataSets")] string dataset, [DynamicValues("GetTables")] string table, string id, string typeOfChoice, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetOptionSetMetadataAsync"); + try + { + if (dataset is null) + throw new ArgumentNullException(nameof(dataset)); + if (table is null) + throw new ArgumentNullException(nameof(table)); + if (id is null) + throw new ArgumentNullException(nameof(id)); + if (typeOfChoice is null) + throw new ArgumentNullException(nameof(typeOfChoice)); + var path = $"/v2/datasets/{DoubleEscape(dataset.ToString())}/tables/{DoubleEscape(table.ToString())}/items/{DoubleEscape(id.ToString())}/OptionSet/{DoubleEscape(typeOfChoice.ToString())}"; + return await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Get table metadata + /// + /// Gets table metadata + /// Environment + /// Table Name + /// Cancellation token. + /// The Get table metadata response. + public virtual async Task GetTableAsync([DynamicValues("GetDataSets")] string environment, [DynamicValues("GetTables")] string tableName, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetTableAsync"); + try + { + if (environment is null) + throw new ArgumentNullException(nameof(environment)); + if (tableName is null) + throw new ArgumentNullException(nameof(tableName)); + var path = $"/v2/$metadata.json/datasets/{DoubleEscape(environment.ToString())}/tables/{DoubleEscape(tableName.ToString())}"; + return await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// GetTables_V2 + /// + /// dataset + /// Cancellation token. + /// The GetTables_V2 response. + public virtual async Task GetTablesAsync([DynamicValues("GetDataSets")] string dataset, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.GetTablesAsync"); + try + { + if (dataset is null) + throw new ArgumentNullException(nameof(dataset)); + var path = $"/v2/datasets/{DoubleEscape(dataset.ToString())}/tables"; + return await this + .CallConnectorAsync(HttpMethod.Get, path, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Update a row (legacy) + /// + /// This operation updates an existing row for a table + /// Environment + /// Table Name + /// Row identifier + /// The request body. + /// Cancellation token. + /// The Update a row (legacy) response. + public virtual async Task PatchItemAsync([DynamicValues("GetDataSets_V2")] string environment, [DynamicValues("GetTables")] string tableName, string rowIdentifier, PatchItemInput input, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.PatchItemAsync"); + try + { + if (environment is null) + throw new ArgumentNullException(nameof(environment)); + if (tableName is null) + throw new ArgumentNullException(nameof(tableName)); + if (rowIdentifier is null) + throw new ArgumentNullException(nameof(rowIdentifier)); + var path = $"/v2/datasets/{DoubleEscape(environment.ToString())}/tables/{DoubleEscape(tableName.ToString())}/items/{DoubleEscape(rowIdentifier.ToString())}"; + return await this + .CallConnectorAsync(HttpMethod.Patch, path, input, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + /// + /// Add a new row (legacy) + /// + /// This operation adds a new row of a table + /// Environment + /// Table Name + /// The request body. + /// Cancellation token. + /// The Add a new row (legacy) response. + public virtual async Task PostItemAsync([DynamicValues("GetDataSets_V2")] string environment, [DynamicValues("GetTables")] string tableName, PostItemInput input, CancellationToken cancellationToken = default) + { + using var activity = CommondataserviceClient.ConnectorActivitySource.StartActivity("CommondataserviceClient.PostItemAsync"); + try + { + if (environment is null) + throw new ArgumentNullException(nameof(environment)); + if (tableName is null) + throw new ArgumentNullException(nameof(tableName)); + var path = $"/v2/datasets/{DoubleEscape(environment.ToString())}/tables/{DoubleEscape(tableName.ToString())}/items"; + return await this + .CallConnectorAsync(HttpMethod.Post, path, input, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + } + catch (Exception ex) when (!ex.IsFatal()) + { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); + throw; + } + } + + } + + #endregion Client +} + diff --git a/tests/Azure.Connectors.Sdk.Tests/CommondataserviceClientTests.cs b/tests/Azure.Connectors.Sdk.Tests/CommondataserviceClientTests.cs new file mode 100644 index 0000000..0488728 --- /dev/null +++ b/tests/Azure.Connectors.Sdk.Tests/CommondataserviceClientTests.cs @@ -0,0 +1,185 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System; +using System.Net; +using System.Net.Http; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Azure.Connectors.Sdk.Commondataservice; +using Azure.Connectors.Sdk.Commondataservice.Models; +using global::Azure.Core; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace Azure.Connectors.Sdk.Tests +{ + /// + /// Tests for the generated class (Microsoft Dataverse). + /// + [TestClass] + public class CommondataserviceClientTests + { + private static CommondataserviceClient CreateMockedClient(Func responseFactory) + { + var (credential, options) = ConnectorTestHelpers.CreateMockedClientSetup(responseFactory); + return new CommondataserviceClient( + connectionRuntimeUrl: new Uri("https://test.azure.com/connection"), + credential: credential, + options: options); + } + + [TestMethod] + public void Constructor_WithValidConnectionRuntimeUrl_ShouldCreateInstance() + { + // Arrange & Act + using var client = new CommondataserviceClient("https://test.azure.com/connection"); + + // Assert + Assert.IsNotNull(client); + } + + [TestMethod] + public void Constructor_WithNullConnectionRuntimeUrl_ShouldThrowArgumentNullException() + { + // Arrange & Act & Assert + Assert.ThrowsExactly(() => new CommondataserviceClient((string)null!)); + } + + [TestMethod] + public void ConnectorName_ReturnsCommondataservice() + { + // Arrange + using var client = new CommondataserviceClient("https://test.azure.com/connection"); + + // Act & Assert + Assert.AreEqual("commondataservice", client.ConnectorName, ignoreCase: false); + } + + [TestMethod] + public void Dispose_ShouldNotThrow() + { + // Arrange + var client = new CommondataserviceClient("https://test.azure.com/connection"); + + // Act & Assert - should not throw + client.Dispose(); + } + + [TestMethod] + public void Dispose_CalledTwice_ShouldNotThrow() + { + // Arrange + var mockCredential = new Mock(); + var client = new CommondataserviceClient( + connectionRuntimeUrl: new Uri("https://test.azure.com/connection"), + credential: mockCredential.Object); + + // Act & Assert - calling Dispose twice should not throw (idempotent) + client.Dispose(); + client.Dispose(); + } + + [TestMethod] + public async Task GetDataSetsAsync_WithMockedResponse_ReturnsExpectedResult() + { + // Arrange + var expectedResponse = new DataSetsList + { + Value = + [ + new DataSet + { + Name = "https://contoso.crm.dynamics.com", + DisplayName = "Contoso (contoso)" + }, + new DataSet + { + Name = "https://fabrikam.crm.dynamics.com", + DisplayName = "Fabrikam (fabrikam)" + } + ] + }; + + using var client = CreateMockedClient(() => new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(JsonSerializer.Serialize(expectedResponse)) + }); + + // Act + var result = await client + .GetDataSetsAsync(cancellationToken: CancellationToken.None) + .ConfigureAwait(continueOnCapturedContext: false); + + // Assert + Assert.IsNotNull(result); + Assert.IsNotNull(result.Value); + Assert.AreEqual(expected: 2, actual: result.Value.Count); + Assert.AreEqual(expected: "Contoso (contoso)", actual: result.Value[0].DisplayName); + Assert.AreEqual(expected: "https://fabrikam.crm.dynamics.com", actual: result.Value[1].Name); + } + + [TestMethod] + public async Task GetItemAsync_WithErrorResponse_ThrowsConnectorException() + { + // Arrange + using var client = CreateMockedClient(() => new HttpResponseMessage + { + StatusCode = HttpStatusCode.BadRequest, + Content = new StringContent("{\"error\": \"Invalid request\"}") + }); + + // Act & Assert + var exception = await Assert + .ThrowsExactlyAsync(async () => + await client + .GetItemAsync( + environment: "https://contoso.crm.dynamics.com", + tableName: "accounts", + itemIdentifier: "00000000-0000-0000-0000-000000000000", + cancellationToken: CancellationToken.None) + .ConfigureAwait(continueOnCapturedContext: false)) + .ConfigureAwait(continueOnCapturedContext: false); + + Assert.AreEqual(expected: 400, actual: exception.Status); + Assert.IsTrue(exception.ResponseBody.Contains("Invalid request", StringComparison.Ordinal)); + } + + [TestMethod] + public void DataSet_JsonSerialization_RoundTrips() + { + // Arrange + var dataSet = new DataSet + { + Name = "https://contoso.crm.dynamics.com", + DisplayName = "Contoso (contoso)" + }; + + // Act + var json = JsonSerializer.Serialize(dataSet); + var deserialized = JsonSerializer.Deserialize(json); + + // Assert + Assert.IsNotNull(deserialized); + Assert.AreEqual(expected: dataSet.Name, actual: deserialized.Name); + Assert.AreEqual(expected: dataSet.DisplayName, actual: deserialized.DisplayName); + } + + [TestMethod] + public void PostItemInput_JsonSerialization_RoundTrips() + { + // Arrange + var input = new PostItemInput(); + + // Act + var json = JsonSerializer.Serialize(input); + var deserialized = JsonSerializer.Deserialize(json); + + // Assert + Assert.IsNotNull(deserialized); + } + } +}