Skip to content

Commit 8f689bb

Browse files
Fixes onboard APIs with OpenAPI specs. Closes #786 (#788)
Adds tracing to ApiCenterClient. Closes #785 Fixes generating OpenAPI specs. Closes #787
1 parent 16c89a6 commit 8f689bb

File tree

8 files changed

+74
-11
lines changed

8 files changed

+74
-11
lines changed

dev-proxy-abstractions/ProxyUtils.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ public static class ProxyUtils
3939

4040
public static readonly string ReportsKey = "Reports";
4141

42+
static ProxyUtils()
43+
{
44+
// convert enum values to camelCase
45+
jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
46+
}
47+
4248
public static bool IsGraphRequest(Request request) => IsGraphUrl(request.RequestUri);
4349

4450
public static bool IsGraphUrl(Uri uri) =>

dev-proxy-plugins/ApiCenter/ApiCenterClient.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ internal ApiCenterClient(ApiCenterClientConfiguration configuration, ILogger log
8282

8383
_authenticationHandler = new AuthenticationDelegatingHandler(_credential, _scopes)
8484
{
85-
InnerHandler = new HttpClientHandler()
85+
InnerHandler = new TracingDelegatingHandler(logger) {
86+
InnerHandler = new HttpClientHandler()
87+
}
8688
};
8789
_httpClient = new HttpClient(_authenticationHandler);
8890

@@ -232,6 +234,6 @@ internal async Task<HttpResponseMessage> PostImportSpecification(ApiSpecImport a
232234
internal async Task<ApiSpecExportResult?> PostExportSpecification(string definitionId)
233235
{
234236
var definitionRes = await _httpClient.PostAsync($"https://management.azure.com{definitionId}/exportSpecification?api-version=2024-03-01", null);
235-
return await definitionRes.Content.ReadFromJsonAsync<ApiSpecExportResult>();
237+
return await definitionRes.Content.ReadFromJsonAsync<ApiSpecExportResult>(ProxyUtils.JsonSerializerOptions);
236238
}
237239
}

dev-proxy-plugins/ApiCenter/Models.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
using System.Text.Json.Serialization;
54
using Microsoft.OpenApi.Models;
65

76
namespace Microsoft.DevProxy.Plugins.RequestLogs.ApiCenter;
@@ -25,9 +24,7 @@ internal class ApiProperties
2524
public ApiContact[] Contacts { get; set; } = [];
2625
public dynamic CustomProperties { get; set; } = new object();
2726
public string? Description { get; set; }
28-
[JsonConverter(typeof(JsonStringEnumConverter))]
2927
public ApiKind? Kind { get; set; }
30-
[JsonConverter(typeof(JsonStringEnumConverter))]
3128
public ApiLifecycleStage? LifecycleStage { get; set; }
3229
public string? Title { get; set; }
3330
public string? Summary { get; set; }
@@ -79,7 +76,6 @@ internal class ApiDefinitionPropertiesSpecification
7976

8077
internal class ApiSpecImport
8178
{
82-
[JsonConverter(typeof(JsonStringEnumConverter))]
8379
public ApiSpecImportResultFormat Format { get; set; }
8480
public ApiSpecImportRequestSpecification? Specification { get; set; }
8581
public string? Value { get; set; }
@@ -93,7 +89,6 @@ internal class ApiSpecImportRequestSpecification
9389

9490
internal class ApiSpecExportResult
9591
{
96-
[JsonConverter(typeof(JsonStringEnumConverter))]
9792
public ApiSpecExportResultFormat? Format { get; set; }
9893
public string? Value { get; set; }
9994
}
@@ -108,7 +103,6 @@ internal class ApiVersion
108103

109104
internal class ApiVersionProperties
110105
{
111-
[JsonConverter(typeof(JsonStringEnumConverter))]
112106
public ApiLifecycleStage LifecycleStage { get; set; }
113107
public string? Title { get; set; }
114108
}

dev-proxy-plugins/Reporters/PlainTextReporter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public PlainTextReporter(IPluginEvents pluginEvents, IProxyContext context, ILog
4141

4242
if (_transformers.TryGetValue(reportType, out var transform))
4343
{
44-
Logger.LogDebug("Transforming {reportType} using {transform}...", reportType.Name, transform.Method);
44+
Logger.LogDebug("Transforming {reportType} using {transform}...", reportType.Name, transform.Method.Name);
4545

4646
return transform(report.Value);
4747
}

dev-proxy-plugins/RequestLogs/ApiCenterOnboardingPlugin.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,16 @@ async Task ImportApiDefinition(string apiDefinitionId, string openApiSpecFilePat
384384
}
385385
else
386386
{
387-
Logger.LogError("Failed to import API definition for {apiDefinition}. Status: {status}, reason: {reason}", apiDefinitionId, res.StatusCode, res.ReasonPhrase);
387+
var resContent = res.ReasonPhrase;
388+
try
389+
{
390+
resContent = await res.Content.ReadAsStringAsync();
391+
}
392+
catch
393+
{
394+
}
395+
396+
Logger.LogError("Failed to import API definition for {apiDefinition}. Status: {status}, reason: {reason}", apiDefinitionId, res.StatusCode, resContent);
388397
}
389398
}
390399
}

dev-proxy-plugins/RequestLogs/OpenApiSpecGeneratorPlugin.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ private string ParametrizePath(OpenApiPathItem pathItem, Uri requestUri)
404404
if (IsParametrizable(segment))
405405
{
406406
var parameterName = $"{previousSegment}-id";
407-
segments[i] = "{" + parameterName + "}/";
407+
segments[i] = $"{{{parameterName}}}{(requestUri.Segments[i].EndsWith('/') ? "/" : "")}";
408408

409409
pathItem.Parameters.Add(new OpenApiParameter
410410
{
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace Microsoft.DevProxy.Plugins;
7+
8+
internal class TracingDelegatingHandler(ILogger logger) : DelegatingHandler
9+
{
10+
private readonly ILogger _logger = logger;
11+
12+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
13+
{
14+
using var scope = _logger.BeginScope(request.GetHashCode().ToString());
15+
16+
_logger.LogTrace("Request: {method} {uri}", request.Method, request.RequestUri);
17+
foreach (var (header, value) in request.Headers)
18+
{
19+
_logger.LogTrace("{header}: {value}", header, string.Join(", ", value));
20+
}
21+
if (request.Content is not null)
22+
{
23+
var body = await request.Content.ReadAsStringAsync();
24+
_logger.LogTrace("Body: {body}", body);
25+
}
26+
27+
var response = await base.SendAsync(request, cancellationToken);
28+
29+
_logger.LogTrace("Response");
30+
foreach (var (header, value) in response.Headers)
31+
{
32+
_logger.LogTrace("{header}: {value}", header, string.Join(", ", value));
33+
}
34+
if (response.Content is not null)
35+
{
36+
var body = await response.Content.ReadAsStringAsync();
37+
_logger.LogTrace("Body: {body}", body);
38+
}
39+
40+
return response;
41+
}
42+
}

dev-proxy/Logging/ProxyConsoleFormatter.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,21 @@ private void WriteMessageBoxedWithInvertedLabels(string? message, LogLevel logLe
129129
{
130130
scopeProvider.ForEachScope((scope, state) =>
131131
{
132+
if (scope is null)
133+
{
134+
return;
135+
}
136+
132137
if (scope is string scopeString)
133138
{
134139
textWriter.Write(scopeString);
135140
textWriter.Write(": ");
136141
}
142+
else if (scope.GetType().Name == "FormattedLogValues")
143+
{
144+
textWriter.Write(scope.ToString());
145+
textWriter.Write(": ");
146+
}
137147
}, textWriter);
138148
}
139149

0 commit comments

Comments
 (0)