Skip to content

Commit d923e70

Browse files
committed
Restructured the whole solution to leverage interfaces.
1 parent be4f050 commit d923e70

25 files changed

+621
-1009
lines changed

.vscode/launch.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Extension (HTTP)",
6+
"type": "coreclr",
7+
"request": "launch",
8+
"preLaunchTask": "Build Solution",
9+
"program": "${workspaceFolder}/src/bin/Debug/databricks-extension",
10+
"args": [
11+
"--http",
12+
"5001"
13+
],
14+
"cwd": "${workspaceFolder}",
15+
"env": {
16+
"ASPNETCORE_ENVIRONMENT": "Development"
17+
},
18+
"stopAtEntry": false,
19+
"console": "internalConsole"
20+
}
21+
]
22+
}

.vscode/tasks.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "Build Solution",
6+
"command": "dotnet",
7+
"type": "process",
8+
"args": [
9+
"build",
10+
"${workspaceFolder}",
11+
"/property:GenerateFullPaths=true",
12+
"/consoleloggerparameters:NoSummary"
13+
],
14+
"problemMatcher": "$msCompile"
15+
}
16+
]
17+
}

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
<!-- markdownlint-disable MD024 -->
22
# Changelog
33

4+
## [0.1.7] - 2025-08-14
5+
6+
### Changed
7+
8+
- Restructured the whole solution to leverage interfaces.
9+
410
## [0.1.6] - 2025-08-10
511

612
### Added

bicepconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"currentProfile": "AzureCloud"
1212
},
1313
"extensions": {
14-
"databricksExtension": "br:acrdbtextension.azurecr.io/extension/databricks:v0.1.6"
14+
"databricksExtension": "./output/databricks-extension"
1515
},
1616
"implicitExtensions": []
1717
}

examples/compute/cluster.basic.bicep

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ resource cluster 'Cluster' = {
1414
maxWorkers: 8
1515
}
1616
nodeTypeId: 'Standard_DS3_v2'
17-
1817
}
1918

2019
output clusterName string = cluster.clusterName

examples/workspace/repo.basic.bicep

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ extension databricksExtension with {
1111
}
1212

1313
resource gitRepo 'Repo' = {
14-
provider: 'GitHub'
15-
url: 'https://github.com/Azure/bicep'
14+
provider: 'gitHub'
15+
url: 'https://github.com/Gijsreyn/bicep-ext-databricks'
1616
branch: 'main'
1717
}
1818

src/Handlers/BaseHandler.cs

Lines changed: 18 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
using Azure.Identity;
21
using Bicep.Local.Extension.Host.Handlers;
32
using Microsoft.Azure.Databricks.Client;
4-
using System.Net.Http.Headers;
5-
using System.Text;
3+
using Microsoft.Extensions.Logging;
4+
using Bicep.Extension.Databricks.Services;
5+
using System.Net.Http;
66
using System.Text.Json;
77

88
namespace Bicep.Extension.Databricks.Handlers;
@@ -11,121 +11,30 @@ public abstract class BaseHandler<TResource, TIdentifiers> : TypedResourceHandle
1111
where TResource : class, TIdentifiers
1212
where TIdentifiers : class
1313
{
14-
private DatabricksClient? _databricksClient;
15-
private readonly HttpClient _httpClient;
14+
protected readonly ILogger _logger;
15+
private readonly IDatabricksClientFactory _factory;
1616

17-
protected BaseHandler()
17+
protected BaseHandler(IDatabricksClientFactory factory, ILogger logger)
1818
{
19-
_httpClient = new HttpClient();
19+
_factory = factory;
20+
_logger = logger;
2021
}
2122

22-
private async Task<string> GetAccessTokenAsync(CancellationToken cancellationToken)
23-
{
24-
var envToken = Environment.GetEnvironmentVariable("DATABRICKS_ACCESS_TOKEN");
25-
if (!string.IsNullOrEmpty(envToken))
26-
{
27-
Console.WriteLine("[TRACE] Using DATABRICKS_ACCESS_TOKEN from environment variable");
28-
return envToken;
29-
}
30-
else
31-
{
32-
Console.WriteLine("[TRACE] Getting token using DefaultAzureCredential");
33-
var credential = new DefaultAzureCredential();
34-
var tokenResponse = await credential.GetTokenAsync(
35-
new Azure.Core.TokenRequestContext(["2ff814a6-3304-4ab8-85cb-cd0e6f879c1d/.default"]),
36-
cancellationToken);
37-
return tokenResponse.Token;
38-
}
39-
}
40-
41-
private async Task<DatabricksClient> GetDatabricksClient(ResourceRequest request, CancellationToken cancellationToken)
42-
{
43-
if (_databricksClient != null)
44-
{
45-
return _databricksClient;
46-
}
47-
48-
var accessToken = await GetAccessTokenAsync(cancellationToken);
23+
protected Task<DatabricksClient> GetClientAsync(string workspaceUrl, CancellationToken ct, int? timeoutSeconds = null)
24+
=> _factory.GetClientAsync(workspaceUrl, ct, timeoutSeconds);
4925

50-
var baseUrl = request.Config.WorkspaceUrl.TrimEnd('/');
51-
Console.WriteLine($"[TRACE] Creating DatabricksClient for workspace: {baseUrl}");
52-
53-
_databricksClient = DatabricksClient.CreateClient(baseUrl, accessToken);
54-
return _databricksClient;
55-
}
56-
57-
protected async Task<DatabricksClient> GetClientAsync(ResourceRequest request, CancellationToken cancellationToken)
26+
protected async Task<T?> CallDatabricksApiForResponse<T>(string workspaceUrl, HttpMethod method, string relativePath, CancellationToken ct, object? payload = null, int? timeoutSeconds = null)
5827
{
59-
return await GetDatabricksClient(request, cancellationToken);
60-
}
61-
62-
protected async Task<string> CallDatabricksApiForResponse(
63-
ResourceRequest request,
64-
string apiPath,
65-
object requestBody,
66-
CancellationToken cancellationToken)
67-
{
68-
var accessToken = await GetAccessTokenAsync(cancellationToken);
69-
70-
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
71-
72-
var databricksUrl = request.Config.WorkspaceUrl.TrimEnd('/');
73-
var endpoint = $"{databricksUrl}{apiPath}";
74-
75-
var json = JsonSerializer.Serialize(requestBody, new JsonSerializerOptions
76-
{
77-
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
78-
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
79-
});
80-
81-
var content = new StringContent(json, Encoding.UTF8, "application/json");
82-
83-
HttpResponseMessage response;
84-
if (string.IsNullOrEmpty(json) || json == "{}")
85-
{
86-
// For GET requests or requests with empty body, use GET method
87-
response = await _httpClient.GetAsync(endpoint, cancellationToken);
88-
}
89-
else
90-
{
91-
// For requests with payload, use POST method
92-
response = await _httpClient.PostAsync(endpoint, content, cancellationToken);
93-
}
94-
95-
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
96-
28+
var response = await (_factory as IDatabricksClientFactory).CallApiAsync(workspaceUrl, method, relativePath, ct, payload, timeoutSeconds);
9729
if (!response.IsSuccessStatusCode)
9830
{
99-
throw new InvalidOperationException($"Failed to call Databricks API {apiPath}. Status: {response.StatusCode}, Error: {responseContent}");
31+
var body = await response.Content.ReadAsStringAsync(ct);
32+
throw new InvalidOperationException($"Databricks API call failed: {(int)response.StatusCode} {response.ReasonPhrase} Body={body}");
10033
}
101-
102-
return responseContent;
34+
if (typeof(T) == typeof(object) || response.Content.Headers.ContentLength == 0)
35+
return default;
36+
var json = await response.Content.ReadAsStringAsync(ct);
37+
return JsonSerializer.Deserialize<T>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
10338
}
10439

105-
protected async Task<ResourceResponse> CallDatabricksApi(
106-
ResourceRequest request,
107-
string apiPath,
108-
object requestBody,
109-
CancellationToken cancellationToken)
110-
{
111-
// Call the API and get the response content
112-
await CallDatabricksApiForResponse(request, apiPath, requestBody, cancellationToken);
113-
114-
return GetResponse(request);
115-
}
116-
117-
protected ResourceResponse CreateResourceResponse<TDatabricksResult>(
118-
TDatabricksResult databricksResult,
119-
string resourceType,
120-
Func<TDatabricksResult, TIdentifiers> createIdentifiers,
121-
Func<TDatabricksResult, TResource> createResource)
122-
{
123-
return new ResourceResponse
124-
{
125-
Type = resourceType,
126-
ApiVersion = "2.0",
127-
Identifiers = createIdentifiers(databricksResult),
128-
Properties = createResource(databricksResult)
129-
};
130-
}
13140
}

0 commit comments

Comments
 (0)