Skip to content
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

Implement parsing OpenAPI spec in ApiGenerator #228

Merged
merged 1 commit into from
Jun 16, 2023
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
2 changes: 1 addition & 1 deletion build/scripts/Commandline.fs
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ Execution hints can be provided anywhere on the command line
| ["build"]
| ["clean"]
| ["benchmark"]
| ["codegen"; ]
| ["profile"] -> parsed
| "codegen" :: tail -> { parsed with RemainingArguments = tail }
| "rest-spec-tests" :: tail -> { parsed with RemainingArguments = tail }

| ["release"; version] -> { parsed with CommandArguments = SetVersion { Version = version; OutputLocation = None }; }
Expand Down
4 changes: 2 additions & 2 deletions build/scripts/ReposTooling.fs
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ module ReposTooling =

Shell.deleteDir tempDir

let GenerateApi () =
let GenerateApi args =
//TODO allow branch name to be passed for CI
let folder = Path.getDirectory (Paths.ProjFile "ApiGenerator")
let timeout = TimeSpan.FromMinutes(120.)
// Building to make sure XML docs files are there, faster then relying on the ApiGenerator to emit these
// from a compilation unit
Tooling.DotNet.ExecInWithTimeout folder ["run"; "-c"; " Release"; ] timeout |> ignore
Tooling.DotNet.ExecInWithTimeout folder (["run"; "-c"; " Release"; "--" ] @ args) timeout |> ignore

let RestSpecTests args =
let folder = Path.getDirectory (Paths.TestProjFile "Tests.YamlRunner")
Expand Down
3 changes: 2 additions & 1 deletion build/scripts/Targets.fs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ module Main =
command "cluster" [ "restore"; "full-build" ] <| fun _ ->
ReposTooling.LaunchCluster parsed

command "codegen" [ ] ReposTooling.GenerateApi
command "codegen" [ ] <| fun _ ->
ReposTooling.GenerateApi parsed.RemainingArguments

command "rest-spec-tests" [ ] <| fun _ ->
ReposTooling.RestSpecTests parsed.RemainingArguments
Expand Down
2 changes: 1 addition & 1 deletion src/ApiGenerator/ApiGenerator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NSwag.Core.Yaml" Version="13.19.0" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="CsQuery.Core" Version="2.0.1" />
<PackageReference Include="Spectre.Console" Version="0.47.0" />
<PackageReference Include="System.CommandLine.DragonFruit" Version="0.3.0-alpha.20371.2" />
<PackageReference Include="RazorLight" Version="2.1.0" />
Expand Down
6 changes: 0 additions & 6 deletions src/ApiGenerator/App.config

This file was deleted.

42 changes: 3 additions & 39 deletions src/ApiGenerator/Configuration/CodeConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,37 +36,7 @@ namespace ApiGenerator.Configuration
{
public static class CodeConfiguration
{
/// <summary> These APIs are not implemented yet in the low and high level client</summary>
public static string[] IgnoredApis { get; } =
{
// To be removed
"indices.upgrade.json",
"indices.get_upgrade.json",
};

private static string[] IgnoredApisHighLevel { get; } =
{
"indices.delete_index_template.json",
"indices.exists_index_template.json",
"indices.get_index_template.json",
"indices.put_index_template.json",
"indices.simulate_index_template.json",
"indices.simulate_template.json",

"get_script_context.json", // 7.7 experimental
"get_script_languages.json", // 7.7 experimental

"indices.exist_type.json", // already removed on client

"rank_eval.json", // 7.7 experimental
"scripts_painless_context.json", // 7.7 experimental
"cluster.delete_component_template.json", // 7.8 experimental
"cluster.get_component_template.json", // 7.8 experimental
"cluster.put_component_template.json", // 7.8 experimental
"cluster.exists_component_template.json", // 7.8 experimental
};

/// <summary>
/// <summary>
/// Map API default names for API's we are only supporting on the low level client first
/// </summary>
private static readonly Dictionary<string, string> LowLevelApiNameMapping = new Dictionary<string, string>
Expand All @@ -92,18 +62,12 @@ public static class CodeConfiguration
public static readonly HashSet<string> EnableHighLevelCodeGen = new HashSet<string>();

public static bool IsNewHighLevelApi(string apiFileName) =>
// if its explicitly ignored we know about it.
!IgnoredApis.Contains(apiFileName)
&& !IgnoredApisHighLevel.Contains(apiFileName)
// no requests with [MapsApi("filename.json")] found
&& !HighLevelApiNameMapping.ContainsKey(apiFileName.Replace(".json", ""));
!HighLevelApiNameMapping.ContainsKey(apiFileName.Replace(".json", ""));

public static bool IgnoreHighLevelApi(string apiFileName)
{
//explicitly ignored
if (IgnoredApis.Contains(apiFileName) || IgnoredApisHighLevel.Contains(apiFileName)) return true;

//always generate already mapped requests
//always generate already mapped requests

if (HighLevelApiNameMapping.ContainsKey(apiFileName.Replace(".json", ""))) return false;

Expand Down
4 changes: 2 additions & 2 deletions src/ApiGenerator/Configuration/GeneratorLocations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ namespace ApiGenerator.Configuration
public static class GeneratorLocations
{
// @formatter:off — disable formatter after this line
public static string OpenApiSpecFile { get; } = $@"{Root}OpenSearch.openapi.json";

public static string OpenSearchNetFolder { get; } = $@"{Root}../../src/OpenSearch.Net/";
public static string LastDownloadedRef { get; } = Path.Combine(Root, "last_downloaded_version.txt");

public static string OpenSearchClientFolder { get; } = $@"{Root}../../src/OpenSearch.Client/";
public static string RestSpecificationFolder { get; } = $@"{Root}RestSpecification/";
// @formatter:on — enable formatter after this line

public static string HighLevel(params string[] paths) => OpenSearchClientFolder + string.Join("/", paths);
Expand Down
30 changes: 10 additions & 20 deletions src/ApiGenerator/Domain/ApiQueryParametersPatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ public static class ApiQueryParametersPatcher
public static SortedDictionary<string, QueryParameters> Patch(
string endpointName,
IDictionary<string, QueryParameters> source,
IEndpointOverrides overrides,
bool checkCommon = true
IEndpointOverrides overrides
)
{
if (source == null) return null;
Expand All @@ -54,32 +53,23 @@ public static SortedDictionary<string, QueryParameters> Patch(
var obsoleteLookup = CreateObsoleteLookup(globalOverrides, overrides, declaredKeys);

var patchedParams = new SortedDictionary<string, QueryParameters>();
var name = overrides?.GetType().Name ?? endpointName ?? "unknown";
foreach (var kv in source)
foreach (var (queryStringKey, value) in source)
{
var queryStringKey = kv.Key;
kv.Value.QueryStringKey = queryStringKey;
value.QueryStringKey = queryStringKey;

if (checkCommon && RestApiSpec.CommonApiQueryParameters.Keys.Contains(queryStringKey))
{
Generator.ApiGenerator.Warnings.Add($"key '{queryStringKey}' in {name} is already declared in _common.json");
continue;
}
if (!renameLookup.TryGetValue(queryStringKey, out var preferredName)) preferredName = queryStringKey;
value.ClsName = CreateCSharpName(preferredName, endpointName);

if (!renameLookup.TryGetValue(queryStringKey, out var preferredName)) preferredName = kv.Key;
kv.Value.ClsName = CreateCSharpName(preferredName, endpointName);
if (skipList.Contains(queryStringKey)) value.Skip = true;

if (skipList.Contains(queryStringKey)) kv.Value.Skip = true;
if (partialList.Contains(queryStringKey)) value.RenderPartial = true;

if (partialList.Contains(queryStringKey)) kv.Value.RenderPartial = true;

if (obsoleteLookup.TryGetValue(queryStringKey, out var obsolete)) kv.Value.Obsolete = obsolete;
if (obsoleteLookup.TryGetValue(queryStringKey, out var obsolete)) value.Obsolete = obsolete;

//make sure source_enabled takes a boolean only
if (preferredName == "source_enabled") kv.Value.Type = "boolean";

if (preferredName == "source_enabled") value.Type = "boolean";

patchedParams[preferredName] = kv.Value;
patchedParams[preferredName] = value;
}

return patchedParams;
Expand Down
3 changes: 1 addition & 2 deletions src/ApiGenerator/Domain/Code/CsharpNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
using System.Linq;
using ApiGenerator.Configuration;
using ApiGenerator.Generator;
using CsQuery.ExtensionMethods.Internal;

namespace ApiGenerator.Domain.Code
{
Expand Down Expand Up @@ -73,7 +72,7 @@ string Replace(string original, string ns, string find, string replace, string[]
public string RestSpecName { get; }

/// <summary>
/// The pascal cased method name as loaded by <see cref="ApiEndpointFactory.FromFile"/>
/// The pascal cased method name as loaded by <see cref="ApiEndpointFactory.From"/>
/// <pre>Uses <see cref="CodeConfiguration.ApiNameMapping"/> mapping of request implementations in the OSC code base</pre>
/// </summary>
public string MethodName { get; }
Expand Down
19 changes: 3 additions & 16 deletions src/ApiGenerator/Domain/RestApiSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,15 @@ public class EnumDescription

public class RestApiSpec
{
public string Commit { get; set; }

public static SortedDictionary<string, QueryParameters> CommonApiQueryParameters { get; set; }

public IDictionary<string, ApiEndpoint> Endpoints { get; set; }
public IDictionary<string, ApiEndpoint> Endpoints { get; set; }

public ImmutableSortedDictionary<string, ReadOnlyCollection<ApiEndpoint>> EndpointsPerNamespaceLowLevel =>
Endpoints.Values.GroupBy(e=>e.CsharpNames.Namespace)
.ToImmutableSortedDictionary(kv => kv.Key, kv => kv.ToList().AsReadOnly());

public ImmutableSortedDictionary<string, ReadOnlyCollection<ApiEndpoint>> EndpointsPerNamespaceHighLevel =>
Endpoints.Values
.Where(v => !CodeConfiguration.IgnoreHighLevelApi(v.FileName))
.Where(v => !CodeConfiguration.IgnoreHighLevelApi(v.Name))
.GroupBy(e => e.CsharpNames.Namespace)
.ToImmutableSortedDictionary(kv => kv.Key, kv => kv.ToList().AsReadOnly());

Expand Down Expand Up @@ -111,16 +107,7 @@ from part in e.Url.Parts
.DistinctBy(e => e.Name)
.ToList();

//TODO can be removed in 8.x
var versionType = _enumDescriptions.FirstOrDefault(f => f.Name == "VersionType");
if (versionType != null)
{
var options = new List<string>(versionType.Options);
options.Add("force");
versionType.Options = options;
}

return _enumDescriptions;
return _enumDescriptions;
}
}
}
Expand Down
5 changes: 1 addition & 4 deletions src/ApiGenerator/Domain/Specification/ApiEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,7 @@ namespace ApiGenerator.Domain.Specification
{
public class ApiEndpoint
{
/// <summary> The filename of the spec describing the api endpoint </summary>
public string FileName { get; set; }

/// <summary> The original name as declared in the spec </summary>
/// <summary> The original name as declared in the spec </summary>
public string Name { get; set; }

/// <summary> The original namespace as declared in the spec </summary>
Expand Down
4 changes: 2 additions & 2 deletions src/ApiGenerator/Domain/Specification/UrlInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public class UrlInformation
{
public IDictionary<string, QueryParameters> Params { get; set; } = new SortedDictionary<string, QueryParameters>();

[JsonProperty("paths")]
private IReadOnlyCollection<string> OriginalPaths { get; set; }
[JsonProperty("paths")]
public IList<string> OriginalPaths { get; set; } = new List<string>();

[JsonProperty("parts")]
public IDictionary<string, UrlPart> OriginalParts { get; set; }
Expand Down
8 changes: 5 additions & 3 deletions src/ApiGenerator/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using CsQuery.ExtensionMethods.Internal;

namespace ApiGenerator
{
Expand Down Expand Up @@ -64,10 +63,13 @@ public static string ToCamelCase(this string s)
var pascal = s.ToPascalCase(true);
if (pascal.Length <= 1) return pascal;

return pascal[0].ToLower() + pascal.Substring(1);
return char.ToLower(pascal[0]) + pascal.Substring(1);
}

public static string SplitPascalCase(this string s) =>
Regex.Replace(s, "([A-Z]+[a-z]*)", " $1").Trim();
}

public static bool IsNullOrEmpty(this string s) =>
string.IsNullOrEmpty(s);
}
}
Loading
Loading