Skip to content

Commit b17421d

Browse files
Fix the nuclia db generator
1 parent 643da51 commit b17421d

File tree

10 files changed

+827
-832
lines changed

10 files changed

+827
-832
lines changed

RestClient.Net.OpenApiGenerator/CodeGenerationHelpers.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@ public static string ToPascalCase(string text)
1919
return string.Join(string.Empty, parts.Select(p => char.ToUpperInvariant(p[0]) + p[1..]));
2020
}
2121

22+
/// <summary>Converts a string to camelCase.</summary>
23+
/// <param name="text">The text to convert.</param>
24+
/// <returns>The camelCase version of the text.</returns>
25+
public static string ToCamelCase(string text)
26+
{
27+
if (string.IsNullOrEmpty(text))
28+
{
29+
return text;
30+
}
31+
32+
var pascal = ToPascalCase(text);
33+
return char.ToLowerInvariant(pascal[0]) + pascal[1..];
34+
}
35+
2236
/// <summary>Indents text by the specified number of levels.</summary>
2337
/// <param name="text">The text to indent.</param>
2438
/// <param name="level">The indentation level (1 level = 4 spaces).</param>

RestClient.Net.OpenApiGenerator/ExtensionMethodGenerator.cs

Lines changed: 152 additions & 120 deletions
Large diffs are not rendered by default.

RestClient.Net.OpenApiGenerator/ModelGenerator.cs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ public static string GenerateModels(OpenApiDocument document, string @namespace)
1919
{
2020
if (schema.Value is OpenApiSchema schemaObj)
2121
{
22-
var model = GenerateModel(schema.Key, schemaObj);
22+
var className = CodeGenerationHelpers.ToPascalCase(schema.Key);
23+
var model = GenerateModel(className, schemaObj);
2324
models.Add(model);
2425
}
2526
}
@@ -45,22 +46,33 @@ private static string GenerateModel(string name, OpenApiSchema schema)
4546
{
4647
var propName = CodeGenerationHelpers.ToPascalCase(p.Key);
4748
var propType = MapOpenApiType(p.Value);
48-
var propDesc = (p.Value as OpenApiSchema)?.Description ?? propName;
49+
var propDesc = SanitizeDescription((p.Value as OpenApiSchema)?.Description ?? propName);
4950
return $" /// <summary>{propDesc}</summary>\n public {propType} {propName} {{ get; set; }}";
5051
})
5152
.ToList();
5253

5354
var propertiesCode = string.Join("\n\n", properties);
55+
var classDesc = SanitizeDescription(schema.Description ?? name);
5456

5557
return $$"""
56-
/// <summary>{{schema.Description ?? name}}</summary>
58+
/// <summary>{{classDesc}}</summary>
5759
public class {{name}}
5860
{
5961
{{propertiesCode}}
6062
}
6163
""";
6264
}
6365

66+
/// <summary>Sanitizes a description for use in XML comments.</summary>
67+
/// <param name="description">The description to sanitize.</param>
68+
/// <returns>A single-line description safe for XML comments.</returns>
69+
private static string SanitizeDescription(string description) =>
70+
description
71+
.Replace("\r\n", " ", StringComparison.Ordinal)
72+
.Replace("\n", " ", StringComparison.Ordinal)
73+
.Replace("\r", " ", StringComparison.Ordinal)
74+
.Trim();
75+
6476
/// <summary>Maps an OpenAPI schema to a C# type.</summary>
6577
/// <param name="schema">The OpenAPI schema.</param>
6678
/// <returns>The C# type name.</returns>
@@ -74,7 +86,9 @@ public static string MapOpenApiType(IOpenApiSchema? schema)
7486
// Check for schema reference first
7587
if (schema is OpenApiSchemaReference schemaRef)
7688
{
77-
return schemaRef.Reference.Id ?? "object";
89+
return schemaRef.Reference.Id != null
90+
? CodeGenerationHelpers.ToPascalCase(schemaRef.Reference.Id)
91+
: "object";
7892
}
7993

8094
if (schema is not OpenApiSchema schemaObj)
@@ -86,7 +100,7 @@ public static string MapOpenApiType(IOpenApiSchema? schema)
86100
if (schemaObj.Type == JsonSchemaType.Array)
87101
{
88102
return schemaObj.Items is OpenApiSchemaReference itemsRef
89-
? $"List<{itemsRef.Reference.Id ?? "object"}>"
103+
? $"List<{(itemsRef.Reference.Id != null ? CodeGenerationHelpers.ToPascalCase(itemsRef.Reference.Id) : "object")}>"
90104
: schemaObj.Items is OpenApiSchema items
91105
? items.Type switch
92106
{

RestClient.Net.OpenApiGenerator/OpenApiCodeGenerator.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,6 @@ public static Outcome.Result<GeneratorResult, string> Generate(
4545

4646
var readResult = OpenApiDocument.Parse(openApiContent, settings: settings);
4747

48-
if (readResult.Diagnostic?.Errors.Count > 0)
49-
{
50-
var errors = string.Join(", ", readResult.Diagnostic.Errors.Select(e => e.Message));
51-
return new GeneratorError($"Error parsing OpenAPI: {errors}");
52-
}
53-
5448
if (readResult.Document == null)
5549
{
5650
return new GeneratorError("Error parsing OpenAPI: Document is null");

RestClient.Net.OpenApiGenerator/UrlParser.cs

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
using System.Text.RegularExpressions;
12
using Microsoft.OpenApi;
23
using Outcome;
34

45
namespace RestClient.Net.OpenApiGenerator;
56

67
/// <summary>Parses base URLs and paths from OpenAPI documents.</summary>
7-
internal static class UrlParser
8+
internal static partial class UrlParser
89
{
10+
[GeneratedRegex(@"\{[^}]+\}")]
11+
private static partial Regex TemplateVariableRegex();
12+
913
/// <summary>Gets the base URL and path from an OpenAPI document.</summary>
1014
/// <param name="document">The OpenAPI document.</param>
1115
/// <param name="baseUrlOverride">Optional base URL override.</param>
@@ -41,13 +45,28 @@ internal static class UrlParser
4145
);
4246
}
4347

48+
// Handle URLs with template variables (e.g., https://{region}.example.com)
49+
var urlForParsing = fullUrl;
50+
var hasTemplateVariables = fullUrl.Contains('{', StringComparison.Ordinal);
51+
if (hasTemplateVariables)
52+
{
53+
// Replace template variables with placeholder for parsing
54+
urlForParsing = TemplateVariableRegex().Replace(fullUrl, "placeholder");
55+
}
56+
4457
// Parse the URL to separate base URL (protocol + host) from base path
45-
if (!Uri.TryCreate(fullUrl, UriKind.Absolute, out var uri))
58+
if (!Uri.TryCreate(urlForParsing, UriKind.Absolute, out var uri))
4659
{
47-
return Result<(string, string), string>.Failure(
48-
$"Server URL '{fullUrl}' is not a valid absolute URL. "
49-
+ "URL must include protocol and host (e.g., https://api.example.com)."
50-
);
60+
// If URL is invalid but override is provided, use override
61+
return !string.IsNullOrWhiteSpace(baseUrlOverride)
62+
? new Result<(string, string), string>.Ok<(string, string), string>(
63+
(baseUrlOverride!, string.Empty)
64+
)
65+
: Result<(string, string), string>.Failure(
66+
$"Server URL '{fullUrl}' is not a valid absolute URL. "
67+
+ "URL must include protocol and host (e.g., https://api.example.com), "
68+
+ "or you must provide a baseUrlOverride parameter when calling Generate()."
69+
);
5170
}
5271

5372
// Check if it's a valid http/https URL (not file://)
@@ -59,10 +78,24 @@ internal static class UrlParser
5978
);
6079
}
6180

81+
// If there are template variables and no override, use the full URL as base
82+
if (hasTemplateVariables && string.IsNullOrWhiteSpace(baseUrlOverride))
83+
{
84+
// Extract path from parsed URL, but use original fullUrl for base
85+
var basePath = uri.AbsolutePath.TrimEnd('/');
86+
return new Result<(string, string), string>.Ok<(string, string), string>(
87+
(
88+
fullUrl.Replace(uri.AbsolutePath, string.Empty, StringComparison.Ordinal)
89+
.TrimEnd('/'),
90+
basePath
91+
)
92+
);
93+
}
94+
6295
var baseUrl = baseUrlOverride ?? $"{uri.Scheme}://{uri.Authority}";
63-
var basePath = uri.AbsolutePath.TrimEnd('/');
96+
var basePath2 = uri.AbsolutePath.TrimEnd('/');
6497
return new Result<(string, string), string>.Ok<(string, string), string>(
65-
(baseUrl, basePath)
98+
(baseUrl, basePath2)
6699
);
67100
}
68101
}

Samples/NucliaDbClient/Generated/GlobalUsings.g.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
using ErrorKnowledgeBoxSynonyms = Outcome.Result<NucliaDB.Generated.KnowledgeBoxSynonyms, Outcome.HttpError<string>>.Error<NucliaDB.Generated.KnowledgeBoxSynonyms, Outcome.HttpError<string>>;
3030
using OkLabelSet = Outcome.Result<NucliaDB.Generated.LabelSet, Outcome.HttpError<string>>.Ok<NucliaDB.Generated.LabelSet, Outcome.HttpError<string>>;
3131
using ErrorLabelSet = Outcome.Result<NucliaDB.Generated.LabelSet, Outcome.HttpError<string>>.Error<NucliaDB.Generated.LabelSet, Outcome.HttpError<string>>;
32-
using Oknucliadb_models__resource__Resource = Outcome.Result<NucliaDB.Generated.nucliadb_models__resource__Resource, Outcome.HttpError<string>>.Ok<NucliaDB.Generated.nucliadb_models__resource__Resource, Outcome.HttpError<string>>;
33-
using Errornucliadb_models__resource__Resource = Outcome.Result<NucliaDB.Generated.nucliadb_models__resource__Resource, Outcome.HttpError<string>>.Error<NucliaDB.Generated.nucliadb_models__resource__Resource, Outcome.HttpError<string>>;
32+
using OkNucliadbModelsResourceResource = Outcome.Result<NucliaDB.Generated.NucliadbModelsResourceResource, Outcome.HttpError<string>>.Ok<NucliaDB.Generated.NucliadbModelsResourceResource, Outcome.HttpError<string>>;
33+
using ErrorNucliadbModelsResourceResource = Outcome.Result<NucliaDB.Generated.NucliadbModelsResourceResource, Outcome.HttpError<string>>.Error<NucliaDB.Generated.NucliadbModelsResourceResource, Outcome.HttpError<string>>;
3434
using Okobject = Outcome.Result<System.Object, Outcome.HttpError<string>>.Ok<System.Object, Outcome.HttpError<string>>;
3535
using Errorobject = Outcome.Result<System.Object, Outcome.HttpError<string>>.Error<System.Object, Outcome.HttpError<string>>;
3636
using OkRequestsResults = Outcome.Result<NucliaDB.Generated.RequestsResults, Outcome.HttpError<string>>.Ok<NucliaDB.Generated.RequestsResults, Outcome.HttpError<string>>;

0 commit comments

Comments
 (0)