-
-
Notifications
You must be signed in to change notification settings - Fork 6
Codex/experimental stage detection #158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -154,7 +154,7 @@ | |
| return (decimal.MinValue, decimal.MaxValue); | ||
| } | ||
|
|
||
| return schema.Format?.ToLowerInvariant() switch | ||
|
Check warning on line 157 in src/libs/AutoSDK/Extensions/OpenApiExtensions.cs
|
||
| { | ||
| "int32" => (int.MinValue, int.MaxValue), | ||
| _ => (long.MinValue, long.MaxValue), | ||
|
|
@@ -217,14 +217,14 @@ | |
|
|
||
| if (schemeType == SecuritySchemeType.Http) | ||
| { | ||
| schemeName = $"http_{namePart.ToLowerInvariant()}"; | ||
|
Check warning on line 220 in src/libs/AutoSDK/Extensions/OpenApiExtensions.cs
|
||
| securityScheme.Type = SecuritySchemeType.Http; | ||
| securityScheme.Scheme = namePart; | ||
| securityScheme.In = location; | ||
| } | ||
| else | ||
| { | ||
| schemeName = $"apikey_{namePart.ToLowerInvariant()}"; | ||
|
Check warning on line 227 in src/libs/AutoSDK/Extensions/OpenApiExtensions.cs
|
||
| securityScheme.Type = schemeType; | ||
| securityScheme.In = location; | ||
| securityScheme.Name = namePart; | ||
|
|
@@ -769,29 +769,90 @@ | |
| { | ||
| operation = operation ?? throw new ArgumentNullException(nameof(operation)); | ||
|
|
||
| // In Microsoft.OpenApi 3.0, extension values are IOpenApiExtension, not JsonNode directly | ||
| // Check for specific extension keys instead | ||
| if ((operation.Extensions?.TryGetValue("x-stage", out var stage) ?? false) && | ||
| stage is JsonValue stageJsonValue && stageJsonValue.TryGetValue<string>(out var stageString)) | ||
| if (TryGetExtensionString(operation.Extensions, "x-stage", out var stageString)) | ||
| { | ||
| return stageString; | ||
| return NormalizeExperimentalStage(stageString); | ||
| } | ||
|
|
||
| if ((operation.Extensions?.TryGetValue("x-alpha", out var alpha) ?? false) && | ||
| alpha is JsonValue alphaJsonValue && alphaJsonValue.TryGetValue<bool>(out var alphaBoolean) && alphaBoolean) | ||
| if (TryGetExtensionBoolean(operation.Extensions, "x-alpha")) | ||
| { | ||
| return "Alpha"; | ||
| } | ||
|
|
||
| if ((operation.Extensions?.TryGetValue("x-beta", out var beta) ?? false) && | ||
| beta is JsonValue betaJsonValue && betaJsonValue.TryGetValue<bool>(out var betaBoolean) && betaBoolean) | ||
| if (TryGetExtensionBoolean(operation.Extensions, "x-beta")) | ||
| { | ||
| return "Beta"; | ||
| } | ||
|
|
||
| if (TryGetAvailability(operation.Extensions, out var availability)) | ||
| { | ||
| return NormalizeExperimentalStage(availability); | ||
| } | ||
|
|
||
| return GetExperimentalStageFromSummary(operation.Summary); | ||
| } | ||
|
|
||
| public static bool IsDeprecated(this OpenApiOperation operation) | ||
| { | ||
| operation = operation ?? throw new ArgumentNullException(nameof(operation)); | ||
|
|
||
| return operation.Deprecated || | ||
| TryGetAvailability(operation.Extensions, out var availability) && | ||
| string.Equals(availability, "Deprecated", StringComparison.Ordinal); | ||
| } | ||
|
|
||
| public static bool IsDeprecated(this IOpenApiSchema schema) | ||
| { | ||
| schema = schema ?? throw new ArgumentNullException(nameof(schema)); | ||
|
|
||
| return schema.Deprecated || | ||
| TryGetAvailability(schema.Extensions, out var availability) && | ||
| string.Equals(availability, "Deprecated", StringComparison.Ordinal); | ||
| } | ||
|
|
||
| public static string GetExperimentalStageFromSummary(this string? summary) | ||
| { | ||
| if (string.IsNullOrWhiteSpace(summary)) | ||
| { | ||
| return string.Empty; | ||
| } | ||
|
|
||
| var trimmed = (summary ?? string.Empty).TrimStart(); | ||
|
Check warning on line 820 in src/libs/AutoSDK/Extensions/OpenApiExtensions.cs
|
||
|
|
||
| foreach (var (prefix, stage) in ExperimentalStagePrefixes) | ||
| { | ||
| if (trimmed.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) | ||
| { | ||
| return stage; | ||
| } | ||
|
Comment on lines
+822
to
+827
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use token-boundary checks for stage prefixes to avoid false positives. Current 💡 Proposed fix foreach (var (prefix, stage) in ExperimentalStagePrefixes)
{
- if (trimmed.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
+ if (trimmed.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) &&
+ (prefix.StartsWith("[", StringComparison.Ordinal) ||
+ trimmed.Length == prefix.Length ||
+ " \t:-_".Contains(trimmed[prefix.Length])))
{
return stage;
}
} foreach (var (prefix, _) in ExperimentalStagePrefixes)
{
- if (!trimmed.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
+ if (!trimmed.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) ||
+ (!prefix.StartsWith("[", StringComparison.Ordinal) &&
+ trimmed.Length > prefix.Length &&
+ !" \t:-_".Contains(trimmed[prefix.Length])))
{
continue;
}
var remainder = trimmed.Substring(prefix.Length).TrimStart(' ', '\t', ':', '-', '_');
return string.IsNullOrWhiteSpace(remainder) ? trimmed : remainder;
}Also applies to: 842-850, 953-961 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| return string.Empty; | ||
| } | ||
|
|
||
| public static string StripExperimentalStagePrefix(this string? summary) | ||
| { | ||
| if (string.IsNullOrWhiteSpace(summary)) | ||
| { | ||
| return string.Empty; | ||
| } | ||
|
|
||
| var trimmed = (summary ?? string.Empty).Trim(); | ||
|
Check warning on line 840 in src/libs/AutoSDK/Extensions/OpenApiExtensions.cs
|
||
|
|
||
| foreach (var (prefix, _) in ExperimentalStagePrefixes) | ||
| { | ||
| if (!trimmed.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| var remainder = trimmed.Substring(prefix.Length).TrimStart(' ', '\t', ':', '-', '_'); | ||
| return string.IsNullOrWhiteSpace(remainder) ? trimmed : remainder; | ||
| } | ||
|
|
||
| return trimmed; | ||
| } | ||
|
|
||
| public static string ClearForXml(this string text) | ||
| { | ||
| text = text ?? throw new ArgumentNullException(nameof(text)); | ||
|
|
@@ -888,6 +949,142 @@ | |
|
|
||
| return @enum; | ||
| } | ||
|
|
||
| private static readonly (string Prefix, string Stage)[] ExperimentalStagePrefixes = | ||
| [ | ||
| ("[Alpha]", "Alpha"), | ||
| ("[Beta]", "Beta"), | ||
| ("[Experimental]", "Experimental"), | ||
| ("Alpha", "Alpha"), | ||
| ("Beta", "Beta"), | ||
| ("Experimental", "Experimental"), | ||
| ]; | ||
|
|
||
| private static string NormalizeExperimentalStage(string? stage) | ||
| { | ||
| var normalized = stage?.Trim() ?? string.Empty; | ||
| if (normalized.Length == 0) | ||
| { | ||
| return string.Empty; | ||
| } | ||
|
|
||
| return normalized.ToUpperInvariant() switch | ||
| { | ||
| "ALPHA" => "Alpha", | ||
| "BETA" => "Beta", | ||
| "EXPERIMENTAL" => "Experimental", | ||
| "GENERALLY-AVAILABLE" => string.Empty, | ||
| "DEPRECATED" => string.Empty, | ||
| _ => normalized, | ||
| }; | ||
| } | ||
|
|
||
| private static string NormalizeAvailability(string? availability) | ||
| { | ||
| var normalized = availability?.Trim() ?? string.Empty; | ||
| if (normalized.Length == 0) | ||
| { | ||
| return string.Empty; | ||
| } | ||
|
|
||
| return normalized.ToUpperInvariant() switch | ||
| { | ||
| "ALPHA" => "Alpha", | ||
| "BETA" => "Beta", | ||
| "DEPRECATED" => "Deprecated", | ||
| "GENERALLY-AVAILABLE" => "GenerallyAvailable", | ||
| _ => normalized, | ||
| }; | ||
| } | ||
|
|
||
| private static bool TryGetAvailability(IDictionary<string, IOpenApiExtension>? extensions, out string availability) | ||
| { | ||
| availability = string.Empty; | ||
|
|
||
| if (!TryGetExtensionString(extensions, "x-fern-availability", out var rawAvailability)) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| availability = NormalizeAvailability(rawAvailability); | ||
| return !string.IsNullOrWhiteSpace(availability); | ||
| } | ||
|
|
||
| private static bool TryGetExtensionString( | ||
| IDictionary<string, IOpenApiExtension>? extensions, | ||
| string name, | ||
| out string value) | ||
| { | ||
| value = string.Empty; | ||
|
|
||
| if (!(extensions?.TryGetValue(name, out var extension) ?? false)) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| if (TryGetJsonString(extension, out var stringValue)) | ||
| { | ||
| value = stringValue; | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private static bool TryGetExtensionBoolean( | ||
| IDictionary<string, IOpenApiExtension>? extensions, | ||
| string name) | ||
| { | ||
| if (!(extensions?.TryGetValue(name, out var extension) ?? false)) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return TryGetJsonBoolean(extension, out var booleanValue) && booleanValue; | ||
| } | ||
|
|
||
| private static bool TryGetJsonString(IOpenApiExtension extension, out string value) | ||
| { | ||
| value = string.Empty; | ||
|
|
||
| var node = extension switch | ||
| { | ||
| JsonValue jsonValue => jsonValue, | ||
| JsonNodeExtension jsonNodeExtension => jsonNodeExtension.Node, | ||
| _ => null, | ||
| }; | ||
|
|
||
| if (node is JsonValue stringNode && | ||
| stringNode.TryGetValue<string>(out var stringValue) && | ||
| !string.IsNullOrWhiteSpace(stringValue)) | ||
| { | ||
| value = stringValue; | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private static bool TryGetJsonBoolean(IOpenApiExtension extension, out bool value) | ||
| { | ||
| value = false; | ||
|
|
||
| var node = extension switch | ||
| { | ||
| JsonValue jsonValue => jsonValue, | ||
| JsonNodeExtension jsonNodeExtension => jsonNodeExtension.Node, | ||
| _ => null, | ||
| }; | ||
|
|
||
| if (node is JsonValue booleanNode && | ||
| booleanNode.TryGetValue<bool>(out var booleanValue)) | ||
| { | ||
| value = booleanValue; | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| public static Dictionary<string, PropertyData> ComputeEnum( | ||
| this IList<JsonNode> @enum, | ||
|
|
@@ -994,4 +1191,4 @@ | |
| .Select(x => x.Value.OperationId!) | ||
| .ToArray(); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -94,7 +94,7 @@ public static MethodParameter FromSchemaContext(SchemaContext context) | |||||
| Style: parameter.Style, | ||||||
| Explode: parameter.Explode, | ||||||
| Settings: context.Settings, | ||||||
| IsDeprecated: context.Schema.Deprecated, | ||||||
| IsDeprecated: context.Schema.IsDeprecated(), | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Preserve parameter-level deprecation when building This currently reads only schema deprecation and can miss 💡 Proposed fix- IsDeprecated: context.Schema.IsDeprecated(),
+ IsDeprecated: parameter.Deprecated || context.Schema.IsDeprecated(),📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| DefaultValue: context.GetDefaultValue(), | ||||||
| Summary: context.Schema.GetSummary(), | ||||||
| Description: | ||||||
|
|
@@ -228,4 +228,4 @@ public string ArgumentName | |||||
| ProducesDeprecationWarning | ||||||
| ? "#pragma warning disable CS0618 // Type or member is obsolete" | ||||||
| : " "; | ||||||
| } | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix
generally-availablenormalization mismatch (currently treated as experimental).TryGetAvailabilitynormalizes toGenerallyAvailable, butNormalizeExperimentalStageonly suppressesGENERALLY-AVAILABLE(hyphenated). That returns a non-empty stage and can incorrectly mark GA operations as experimental.💡 Proposed fix
private static string NormalizeExperimentalStage(string? stage) { var normalized = stage?.Trim() ?? string.Empty; if (normalized.Length == 0) { return string.Empty; } return normalized.ToUpperInvariant() switch { "ALPHA" => "Alpha", "BETA" => "Beta", "EXPERIMENTAL" => "Experimental", - "GENERALLY-AVAILABLE" => string.Empty, + "GENERALLY-AVAILABLE" or "GENERALLYAVAILABLE" => string.Empty, "DEPRECATED" => string.Empty, _ => normalized, }; }Also applies to: 971-979, 990-996
🤖 Prompt for AI Agents