Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions csharp/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
3 changes: 3 additions & 0 deletions csharp/Microsoft.Azure.Databricks.Client.sln
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0D112210-7B29-455F-B677-6491F870228B}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
nuget.config = nuget.config
EndProjectSection
EndProject
Global
Expand Down
60 changes: 51 additions & 9 deletions csharp/Microsoft.Azure.Databricks.Client/ApiClient.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Azure.Databricks.Client.Converters;
#nullable enable

using System;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.Azure.Databricks.Client.Converters;

namespace Microsoft.Azure.Databricks.Client;

public abstract class ApiClient : IDisposable
Expand Down Expand Up @@ -52,26 +55,38 @@ protected static ClientApiException CreateApiException(HttpResponseMessage respo
return new ClientApiException(errorContent, statusCode);
}

private static async Task<TResult> SendRequest<TBody, TResult>(HttpClient httpClient, HttpMethod method, string requestUri, TBody body, CancellationToken cancellationToken = default)
protected static async Task<TResult?> SendRequest<TResult>(HttpClient httpClient, HttpMethod method, string requestUri, HttpContent? content, JsonTypeInfo<TResult> typeInfo, CancellationToken cancellationToken = default)
{
using var response = await FetchResponse(httpClient, method, requestUri, content, cancellationToken).ConfigureAwait(false);
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);

return await JsonSerializer.DeserializeAsync(responseStream, typeInfo, cancellationToken).ConfigureAwait(false);
}

[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
private static async Task<TResult?> SendRequest<TBody, TResult>(HttpClient httpClient, HttpMethod method, string requestUri, TBody? body, CancellationToken cancellationToken = default)
{
using var response = await FetchResponse(httpClient, method, requestUri, body, cancellationToken).ConfigureAwait(false);
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);

return await JsonSerializer.DeserializeAsync<TResult>(responseStream, Options, cancellationToken).ConfigureAwait(false);
}

private static async Task SendRequest<TBody>(HttpClient httpClient, HttpMethod method, string requestUri, TBody body, CancellationToken cancellationToken = default)
[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
private static async Task SendRequest<TBody>(HttpClient httpClient, HttpMethod method, string requestUri, TBody? body, CancellationToken cancellationToken = default)
{
await FetchResponse(httpClient, method, requestUri, body, cancellationToken).ConfigureAwait(false);
}

[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
private static async Task<HttpContentHeaders> SendHeadRequest<TBody>(HttpClient httpClient, HttpMethod method,
string requestUri, TBody body = default, CancellationToken cancellationToken = default)
string requestUri, TBody? body = default, CancellationToken cancellationToken = default)
{
using var response = await FetchResponse(httpClient, method, requestUri, body, cancellationToken).ConfigureAwait(false);
return response.Content.Headers;
}

[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
private static async Task<HttpResponseMessage> FetchResponse<TBody>(HttpClient httpClient, HttpMethod method,
string requestUri, TBody body, CancellationToken cancellationToken = default)
{
Expand All @@ -90,54 +105,81 @@ private static async Task<HttpResponseMessage> FetchResponse<TBody>(HttpClient h
return response;
}

protected static async Task<T> HttpGet<T>(HttpClient httpClient, string requestUri, CancellationToken cancellationToken = default)
private static async Task<HttpResponseMessage> FetchResponse(HttpClient httpClient, HttpMethod method,
string requestUri, HttpContent? content, CancellationToken cancellationToken = default)
{
var request = new HttpRequestMessage(method, requestUri)
{
Content = content,
};

var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
throw CreateApiException(response);
}

return response;
}

[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
protected static async Task<T?> HttpGet<T>(HttpClient httpClient, string requestUri, CancellationToken cancellationToken = default)
{
return await SendRequest<object, T>(httpClient, HttpMethod.Get, requestUri, null, cancellationToken).ConfigureAwait(false);
}

[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
protected static async Task HttpPost<TBody>(HttpClient httpClient, string requestUri, TBody body, CancellationToken cancellationToken = default)
{
await SendRequest(httpClient, HttpMethod.Post, requestUri, body, cancellationToken).ConfigureAwait(false);
}

protected static async Task<TResult> HttpPost<TBody, TResult>(HttpClient httpClient, string requestUri,
[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
protected static async Task<TResult?> HttpPost<TBody, TResult>(HttpClient httpClient, string requestUri,
TBody body, CancellationToken cancellationToken = default)
{
return await SendRequest<TBody, TResult>(httpClient, HttpMethod.Post, requestUri, body, cancellationToken).ConfigureAwait(false);
}

[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
protected static async Task HttpPatch<TBody>(HttpClient httpClient, string requestUri, TBody body, CancellationToken cancellationToken = default)
{
await SendRequest(httpClient, HttpMethod.Patch, requestUri, body, cancellationToken).ConfigureAwait(false);
}

protected static async Task<TResult> HttpPatch<TBody, TResult>(HttpClient httpClient, string requestUri, TBody body, CancellationToken cancellationToken = default)
[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
protected static async Task<TResult?> HttpPatch<TBody, TResult>(HttpClient httpClient, string requestUri, TBody body, CancellationToken cancellationToken = default)
{
return await SendRequest<TBody, TResult>(httpClient, HttpMethod.Patch, requestUri, body, cancellationToken).ConfigureAwait(false);
}

protected static async Task<TResult> HttpPut<TBody, TResult>(HttpClient httpClient, string requestUri, TBody body, CancellationToken cancellationToken = default)
[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
protected static async Task<TResult?> HttpPut<TBody, TResult>(HttpClient httpClient, string requestUri, TBody body, CancellationToken cancellationToken = default)
{
return await SendRequest<TBody, TResult>(httpClient, HttpMethod.Put, requestUri, body, cancellationToken).ConfigureAwait(false);
}

[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
protected static async Task HttpPut<TBody>(HttpClient httpClient, string requestUri, TBody body, CancellationToken cancellationToken = default)
{
await SendRequest(httpClient, HttpMethod.Put, requestUri, body, cancellationToken).ConfigureAwait(false);
}

[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
protected static async Task HttpDelete(HttpClient httpClient, string requestUri,
CancellationToken cancellationToken = default)
{
await SendRequest<object>(httpClient, HttpMethod.Delete, requestUri, null, cancellationToken).ConfigureAwait(false);
}

[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
protected static async Task<HttpContentHeaders> HttpHead<TBody>(HttpClient httpClient, string requestUri, TBody body = default,
CancellationToken cancellationToken = default)
{
return await SendHeadRequest(httpClient, HttpMethod.Head, requestUri, body, cancellationToken).ConfigureAwait(false);
}

[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
protected static async Task<HttpContentHeaders> HttpHead(HttpClient httpClient, string requestUri, CancellationToken cancellationToken = default)
{
return await SendHeadRequest<object>(httpClient, HttpMethod.Head, requestUri, null, cancellationToken).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;

using Microsoft.Azure.Databricks.Client.Converters;
using Microsoft.Azure.Databricks.Client.Models;

namespace Microsoft.Azure.Databricks.Client;

[JsonSerializable(typeof(SqlStatement))]
[JsonSerializable(typeof(StatementExecution))]
[JsonSerializable(typeof(StatementExecutionResultChunk))]
[JsonSourceGenerationOptions(
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
PropertyNameCaseInsensitive = true,
Converters = new System.Type[] {
typeof(JsonStringEnumConverter),
typeof(MillisecondEpochDateTimeConverter),
typeof(LibraryConverter),
typeof(SecretScopeConverter),
typeof(AclPermissionItemConverter),
typeof(DepedencyConverter),
typeof(TableConstraintConverter),
}
)]
internal sealed partial class DatabricksSerializationContext : JsonSerializerContext;
167 changes: 85 additions & 82 deletions csharp/Microsoft.Azure.Databricks.Client/ISQLApi.cs
Original file line number Diff line number Diff line change
@@ -1,92 +1,95 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Azure.Databricks.Client.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Azure.Databricks.Client
using Microsoft.Azure.Databricks.Client.Models;

namespace Microsoft.Azure.Databricks.Client;

public interface ISQLApi : IDisposable
{
IStatementExecutionApi StatementExecution { get; }

IWarehouseApi Warehouse { get; }
}

public interface IWarehouseApi : IDisposable
{
/// <summary>
/// Creates a new SQL warehouse.
/// </summary>
Task<string> Create(WarehouseAttributes warehouseAttributes, CancellationToken cancellationToken = default);

/// <summary>
/// Lists all SQL warehouses that a user has manager permissions on.
/// </summary>
/// <param name="runAsUserId">Service Principal which will be used to fetch the list of warehouses.
/// If not specified, the user from the session header is used.</param>
Task<IEnumerable<WarehouseInfo>> List(int? runAsUserId = default, CancellationToken cancellationToken = default);

/// <summary>
/// Gets the information for a single SQL warehouse.
/// </summary>
/// <param name="id">Required. Id of the SQL warehouse.</param>
Task<WarehouseInfo> Get(string id, CancellationToken cancellationToken = default);

/// <summary>
/// Deletes a SQL warehouse.
/// </summary>
/// <param name="id">Required. Id of the SQL warehouse.</param>
Task Delete(string id, CancellationToken cancellationToken = default);

/// <summary>
/// Updates the configuration for a SQL warehouse.
/// </summary>
/// <param name="id">Required. Id of the warehouse to configure.</param>
Task Update(string id, WarehouseAttributes warehouseAttributes, CancellationToken cancellationToken = default);

/// <summary>
/// Starts a SQL warehouse.
/// </summary>
/// <param name="id">Required. Id of the SQL warehouse.</param>
Task Start(string id, CancellationToken cancellationToken = default);

/// <summary>
/// Stops a SQL warehouse.
/// </summary>
/// <param name="id">Required. Id of the SQL warehouse.</param>
Task Stop(string id, CancellationToken cancellationToken = default);
}

public interface IStatementExecutionApi : IDisposable
{
public interface ISQLApi : IDisposable
{
IStatementExecutionApi StatementExecution { get; }
IWarehouseApi Warehouse { get; }
}

public interface IWarehouseApi : IDisposable
{
/// <summary>
/// Creates a new SQL warehouse.
/// </summary>
Task<string> Create(WarehouseAttributes warehouseAttributes, CancellationToken cancellationToken = default);

/// <summary>
/// Lists all SQL warehouses that a user has manager permissions on.
/// </summary>
/// <param name="runAsUserId">Service Principal which will be used to fetch the list of warehouses.
/// If not specified, the user from the session header is used.</param>
Task<IEnumerable<WarehouseInfo>> List(int? runAsUserId = default, CancellationToken cancellationToken = default);

/// <summary>
/// Gets the information for a single SQL warehouse.
/// </summary>
/// <param name="id">Required. Id of the SQL warehouse.</param>
Task<WarehouseInfo> Get(string id, CancellationToken cancellationToken = default);

/// <summary>
/// Deletes a SQL warehouse.
/// </summary>
/// <param name="id">Required. Id of the SQL warehouse.</param>
Task Delete(string id, CancellationToken cancellationToken = default);

/// <summary>
/// Updates the configuration for a SQL warehouse.
/// </summary>
/// <param name="id">Required. Id of the warehouse to configure.</param>
Task Update(string id, WarehouseAttributes warehouseAttributes, CancellationToken cancellationToken = default);

/// <summary>
/// Starts a SQL warehouse.
/// </summary>
/// <param name="id">Required. Id of the SQL warehouse.</param>
Task Start(string id, CancellationToken cancellationToken = default);

/// <summary>
/// Stops a SQL warehouse.
/// </summary>
/// <param name="id">Required. Id of the SQL warehouse.</param>
Task Stop(string id, CancellationToken cancellationToken = default);
}

public interface IStatementExecutionApi : IDisposable
{
/// <summary>
/// Execute a SQL statement.
/// </summary>
Task<StatementExecution> Execute(SqlStatement statement, CancellationToken cancellationToken = default);

/// <summary>
/// Cancel statement execution.
/// </summary>
/// <param name="id">Requried. Id of statement execution.</param>
Task Cancel(string id, CancellationToken cancellationToken = default);

/// <summary>
/// Get status, manifest, and result first chunk.
/// </summary>
/// <param name="id">Requried. Id of statement execution.</param>
Task<StatementExecution> Get(string id, CancellationToken cancellationToken = default);

/// <summary>
/// Get result chunk by index.
/// </summary>
/// <remarks>
/// After the statement execution has SUCCEEDED, this request can be used to fetch any chunk by index. Whereas the first chunk with chunk_index=0 is typically fetched with statementexecution/executestatement or statementexecution/getstatement, this request can be used to fetch subsequent chunks. The response structure is identical to the nested result element described in the statementexecution/getstatement request, and similarly includes the next_chunk_index and next_chunk_internal_link fields for simple iteration through the result set.
/// </remarks>
/// <param name="id">Requried. Id of statement execution.</param>
/// <param name="chunkIndex">Required. The index of the chunk.</param>
Task<StatementExecutionResultChunk> GetResultChunk(string id, int chunkIndex, CancellationToken cancellationToken = default);
}
/// <summary>
/// Execute a SQL statement.
/// </summary>
Task<StatementExecution> Execute(SqlStatement statement, CancellationToken cancellationToken = default);

/// <summary>
/// Cancel statement execution.
/// </summary>
/// <param name="id">Required. Id of statement execution.</param>
[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
Task Cancel(string id, CancellationToken cancellationToken = default);

/// <summary>
/// Get status, manifest, and result first chunk.
/// </summary>
/// <param name="id">Required. Id of statement execution.</param>
Task<StatementExecution> Get(string id, CancellationToken cancellationToken = default);

/// <summary>
/// Get result chunk by index.
/// </summary>
/// <remarks>
/// After the statement execution has SUCCEEDED, this request can be used to fetch any chunk by index. Whereas the first chunk with chunk_index=0 is typically fetched with statementexecution/executestatement or statementexecution/getstatement, this request can be used to fetch subsequent chunks. The response structure is identical to the nested result element described in the statementexecution/getstatement request, and similarly includes the next_chunk_index and next_chunk_internal_link fields for simple iteration through the result set.
/// </remarks>
/// <param name="id">Required. Id of statement execution.</param>
/// <param name="chunkIndex">Required. The index of the chunk.</param>
Task<StatementExecutionResultChunk> GetResultChunk(string id, int chunkIndex, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<SignAssembly>true</SignAssembly>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Expand Down
Loading