Skip to content
Merged
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
14 changes: 11 additions & 3 deletions src/libs/AutoSDK.CSharp/Operations/CSharpEndPointFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ public static EndPoint CreateEndPoint(
OpenApiExtensions.GetExtensionBooleanValue(
operation.Operation.Extensions,
"x-autosdk-response-wrapper");
var servers = CSharpServerFactory.CreateServerOptions(operation.Servers);
var primaryServer = servers.Length > 0
? servers[0]
: default;

return new EndPoint(
Id: endPointId,
Expand All @@ -187,7 +191,7 @@ public static EndPoint CreateEndPoint(
FileNameWithoutExtension: $"{operation.Settings.Namespace}.{className}.{notAsyncMethodName}",
InterfaceFileNameWithoutExtension: $"{operation.Settings.Namespace}.I{className}.{notAsyncMethodName}",
Tag: operation.Tag,
BaseUrl: string.Empty,
BaseUrl: primaryServer.Url ?? string.Empty,
StreamFormat: streamFormat,
Path: preparedPath,
RequestMediaType: requestMediaType,
Expand All @@ -201,7 +205,9 @@ public static EndPoint CreateEndPoint(
ContentType: successResponse.ContentType,
Summary: operation.Operation.GetXmlDocumentationSummary(),
Description: operation.Operation.Description ?? string.Empty,
BaseUrlSummary: string.Empty,
BaseUrlSummary: operation.Servers.Count > 0
? operation.Servers[0].Description?.ClearForXml() ?? string.Empty
: string.Empty,
CliAction:
(OpenApiExtensions.TryGetExtensionStringValue(
operation.Operation.Extensions, "x-cli-action", out var cliActionStr)
Expand All @@ -220,7 +226,9 @@ public static EndPoint CreateEndPoint(
? streamTerminator ?? "[DONE]"
: string.Empty,
Remarks: GetCodeSamplesRemarks(operation.Operation),
GenerateResponseWrapper: generateResponseWrapper);
GenerateResponseWrapper: generateResponseWrapper,
Servers: servers,
HasServerOverride: operation.HasServerOverride);
}

private static void DeduplicateMethodParameterNames(List<MethodParameter> parameters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public static OperationContext CreateOperationContext(
string operationPath,
System.Net.Http.HttpMethod operationType,
IReadOnlyList<SchemaContext>? operationSchemas,
IList<OpenApiServer> effectiveServers,
bool hasServerOverride,
IList<OpenApiSecurityRequirement> globalSecurityRequirements,
IReadOnlyDictionary<string, Tag>? resolvedTags = null)
{
Expand Down Expand Up @@ -45,6 +47,8 @@ public static OperationContext CreateOperationContext(
var context = new OperationContext(settings, globalSettings, operation, operationPath, operationType)
{
Schemas = operationSchemas ?? (IReadOnlyCollection<SchemaContext>)[],
Servers = effectiveServers,
HasServerOverride = hasServerOverride,
Tags = tags,
GlobalSecurityRequirements = globalSecurityRequirements,
Tag = GetOperationTag(operation, settings, firstTag, resolvedTags),
Expand Down
126 changes: 126 additions & 0 deletions src/libs/AutoSDK.CSharp/Operations/CSharpServerFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using System.Collections.Immutable;
using System.Text;
using AutoSDK.Extensions;
using AutoSDK.Models;
using Microsoft.OpenApi;

namespace AutoSDK.Generation;

internal static class CSharpServerFactory
{
public static EquatableArray<ServerOption> CreateServerOptions(
IList<OpenApiServer>? servers)
{
if (servers == null || servers.Count == 0)
{
return [];
}

var builder = ImmutableArray.CreateBuilder<ServerOption>(servers.Count);
var seenUrls = new HashSet<string>(StringComparer.Ordinal);
var seenIds = new HashSet<string>(StringComparer.Ordinal);

foreach (var server in servers)
{
if (server == null)
{
continue;
}

var url = server.ExpandServerTemplate().Trim();
if (string.IsNullOrWhiteSpace(url) || !seenUrls.Add(url))
{
continue;
}

var id = GetServerId(server, url);
if (!seenIds.Add(id))
{
var suffix = 2;
var candidate = id;
while (!seenIds.Add(candidate))
{
candidate = $"{id}-{suffix++}";
}

id = candidate;
}

builder.Add(new ServerOption(
Id: id,
Name: GetServerName(server, url),
Url: url,
Description: server.Description ?? string.Empty));
}

return builder.ToImmutable();
}

private static string GetServerId(
OpenApiServer server,
string url)
{
if (OpenApiExtensions.TryGetExtensionStringValue(server.Extensions, "x-server-id", out var explicitId) &&
!string.IsNullOrWhiteSpace(explicitId))
{
return SanitizeToken(explicitId);
}

return Uri.TryCreate(url, UriKind.Absolute, out var uri)
? SanitizeToken($"{uri.Scheme}-{uri.Host}{uri.AbsolutePath}")
: SanitizeToken(url);
}

private static string GetServerName(
OpenApiServer server,
string url)
{
if (OpenApiExtensions.TryGetExtensionStringValue(server.Extensions, "x-server-name", out var explicitName) &&
!string.IsNullOrWhiteSpace(explicitName))
{
return explicitName.Trim();
}

if (!string.IsNullOrWhiteSpace(server.Description))
{
return (server.Description ?? string.Empty).Trim();
}

if (Uri.TryCreate(url, UriKind.Absolute, out var uri))
{
var path = uri.AbsolutePath.Trim('/').Replace('/', ' ');
return string.IsNullOrWhiteSpace(path)
? uri.Host
: $"{uri.Host} {path}";
}

return url;
}

private static string SanitizeToken(string value)
{
value = value ?? throw new ArgumentNullException(nameof(value));

var builder = new StringBuilder(value.Length);
var previousWasDash = false;

foreach (var c in value)
{
if (char.IsLetterOrDigit(c))
{
builder.Append(char.ToLowerInvariant(c));
previousWasDash = false;
}
else if (!previousWasDash)
{
builder.Append('-');
previousWasDash = true;
}
}

var token = builder.ToString().Trim('-');
return string.IsNullOrWhiteSpace(token)
? "server"
: token;
}
}
86 changes: 86 additions & 0 deletions src/libs/AutoSDK.CSharp/Operations/OpenApiOperationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public static IReadOnlyList<OperationContext> GetOperations(
var schemasByOperation = BuildSchemasByOperation(filteredSchemas);

var globalSecurity = openApiDocument.Security ?? [];
var documentServers = openApiDocument.Servers ?? [];
var results = new List<OperationContext>();
foreach (var path in pathItems)
{
Expand All @@ -43,13 +44,24 @@ public static IReadOnlyList<OperationContext> GetOperations(

schemasByOperation.TryGetValue(op.Value, out var operationSchemas);

var effectiveServers = GetEffectiveServers(
documentServers,
path.Value.Servers,
op.Value.Servers);
var hasServerOverride = HasServerOverride(
documentServers,
path.Value.Servers,
op.Value.Servers);

results.Add(CSharpOperationContextFactory.CreateOperationContext(
settings: settings,
globalSettings: globalSettings,
operation: op.Value,
operationPath: path.Key,
operationType: op.Key,
operationSchemas: operationSchemas,
effectiveServers: effectiveServers,
hasServerOverride: hasServerOverride,
globalSecurityRequirements: globalSecurity,
resolvedTags: resolvedTags));
}
Expand All @@ -76,6 +88,7 @@ public static IReadOnlyList<OperationContext> GetWebhookOperations(

var schemasByOperation = BuildSchemasByOperation(filteredSchemas);
var globalSecurity = openApiDocument.Security ?? [];
var documentServers = openApiDocument.Servers ?? [];
var results = new List<OperationContext>();

foreach (var webhook in webhooks)
Expand All @@ -95,13 +108,24 @@ public static IReadOnlyList<OperationContext> GetWebhookOperations(

schemasByOperation.TryGetValue(op.Value, out var operationSchemas);

var effectiveServers = GetEffectiveServers(
documentServers,
webhook.Value.Servers,
op.Value.Servers);
var hasServerOverride = HasServerOverride(
documentServers,
webhook.Value.Servers,
op.Value.Servers);

results.Add(CSharpOperationContextFactory.CreateOperationContext(
settings: settings,
globalSettings: globalSettings,
operation: op.Value,
operationPath: webhook.Key,
operationType: op.Key,
operationSchemas: operationSchemas,
effectiveServers: effectiveServers,
hasServerOverride: hasServerOverride,
globalSecurityRequirements: globalSecurity,
resolvedTags: resolvedTags));
}
Expand Down Expand Up @@ -140,4 +164,66 @@ private static bool ShouldIgnoreOperation(
return settings.UseExtensionNaming &&
OpenApiExtensions.ShouldIgnoreOperationForDotNet(operation.Extensions);
}

private static IList<OpenApiServer> GetEffectiveServers(
IList<OpenApiServer> documentServers,
IList<OpenApiServer>? pathServers,
IList<OpenApiServer>? operationServers)
{
if (operationServers is { Count: > 0 })
{
return operationServers;
}

if (pathServers is { Count: > 0 })
{
return pathServers;
}

return documentServers;
}

private static bool HasServerOverride(
IList<OpenApiServer> documentServers,
IList<OpenApiServer>? pathServers,
IList<OpenApiServer>? operationServers)
{
if (operationServers is { Count: > 0 })
{
var inheritedServers = pathServers is { Count: > 0 }
? pathServers
: documentServers;
return !AreEquivalent(operationServers, inheritedServers);
}

return pathServers is { Count: > 0 } &&
!AreEquivalent(pathServers, documentServers);
}

private static bool AreEquivalent(
IList<OpenApiServer> left,
IList<OpenApiServer> right)
{
if (ReferenceEquals(left, right))
{
return true;
}

if (left.Count != right.Count)
{
return false;
}

for (var i = 0; i < left.Count; i++)
{
var leftUrl = left[i].ExpandServerTemplate();
var rightUrl = right[i].ExpandServerTemplate();
if (!string.Equals(leftUrl, rightUrl, StringComparison.Ordinal))
{
return false;
}
}

return true;
}
}
4 changes: 4 additions & 0 deletions src/libs/AutoSDK.CSharp/Pipeline/CSharpPipeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ public static IReadOnlyList<FileWithName> GenerateFiles(
.Concat([Sources.Polyfills(settings, cancellationToken)])
.Concat([Sources.Exceptions(settings, cancellationToken)])
.Concat([Sources.PathBuilder(settings, cancellationToken)])
.Concat(data.Clients.Any(static x => x.UsesServerSelectionSupport) ||
data.Methods.Any(static x => x.ClientUsesServerSelectionSupport)
? [Sources.ServerSelectionSupport(settings, cancellationToken)]
: [])
.Concat([Sources.OptionsSupport(settings, cancellationToken)])
.Concat(!data.Authorizations.IsEmpty
? [Sources.SecuritySupport(settings, cancellationToken)]
Expand Down
Loading
Loading