Skip to content

Update REST API specs to v7.4.0 #4117

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

Merged
merged 3 commits into from
Oct 8, 2019
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public static class CodeConfiguration
"data_frame.put_data_frame_transform.json",
"data_frame.start_data_frame_transform.json",
"data_frame.stop_data_frame_transform.json",
"data_frame.update_data_frame_transform.json",

"ml.evaluate_data_frame.json",
"ml.delete_data_frame_analytics.json",
Expand All @@ -41,7 +42,15 @@ public static class CodeConfiguration
// these APIs are new and need to be mapped
"ml.set_upgrade_mode.json",
"ml.find_file_structure.json",
"monitoring.bulk.json"
"monitoring.bulk.json",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somewhat related to this, I think we should start letting these generate methods on the low level client and only use this list to block high level generation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can follow up in a separate PR for this

"snapshot.cleanup_repository.json",
"ml.estimate_memory_usage.json",
"indices.clone.json",

"slm.delete_lifecycle.json",
"slm.execute_lifecycle.json",
"slm.get_lifecycle.json",
"slm.put_lifecycle.json",
Comment on lines +50 to +53
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are marked as stable, so should be implemented in the 7.4 release (OK to be ignored for now)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, they're stable but don't want to conflate generating implementations for them in this PR 🙂

};


Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ApiGenerator.Configuration.Overrides;
Expand All @@ -7,6 +8,7 @@
using ApiGenerator.Domain.Code.LowLevel;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;

namespace ApiGenerator.Domain.Specification
{
Expand All @@ -32,7 +34,7 @@ public class ApiEndpoint
public Stability Stability { get; set; }

[JsonProperty("documentation")]
public string OfficialDocumentationLink { get; set; }
public Documentation OfficialDocumentationLink { get; set; }

public UrlInformation Url { get; set; }

Expand All @@ -48,13 +50,13 @@ public class ApiEndpoint
CsharpNames = CsharpNames,
UrlParts = Url.Parts,
PartialParameters = Body == null ? Enumerable.Empty<QueryParameters>().ToList() : Url.Params.Values.Where(p=>p.RenderPartial && !p.Skip).ToList(),
OfficialDocumentationLink = OfficialDocumentationLink
OfficialDocumentationLink = OfficialDocumentationLink.Url
};

public RequestPartialImplementation RequestPartialImplementation => new RequestPartialImplementation
{
CsharpNames = CsharpNames,
OfficialDocumentationLink = OfficialDocumentationLink,
OfficialDocumentationLink = OfficialDocumentationLink.Url,
Stability = Stability,
Paths = Url.Paths,
Parts = Url.Parts,
Expand All @@ -67,7 +69,7 @@ public class ApiEndpoint
public DescriptorPartialImplementation DescriptorPartialImplementation => new DescriptorPartialImplementation
{
CsharpNames = CsharpNames,
OfficialDocumentationLink = OfficialDocumentationLink,
OfficialDocumentationLink = OfficialDocumentationLink.Url,
Constructors = Constructor.DescriptorConstructors(CsharpNames, Url).ToList(),
Paths = Url.Paths,
Parts = Url.Parts,
Expand All @@ -78,7 +80,7 @@ public class ApiEndpoint
public RequestParameterImplementation RequestParameterImplementation => new RequestParameterImplementation
{
CsharpNames = CsharpNames,
OfficialDocumentationLink = OfficialDocumentationLink,
OfficialDocumentationLink = OfficialDocumentationLink.Url,
Params = Url.Params.Values.Where(p=>!p.Skip).ToList(),
HttpMethod = PreferredHttpMethod
};
Expand All @@ -101,16 +103,16 @@ public string PreferredHttpMethod
CsharpNames = CsharpNames,
Fluent = new FluentMethod(CsharpNames, Url.Parts,
selectorIsOptional: Body == null || !Body.Required || HttpMethods.Contains("GET"),
link: OfficialDocumentationLink,
link: OfficialDocumentationLink.Url,
summary: HighLevelMethodXmlDocDescription
),
FluentBound = !CsharpNames.DescriptorBindsOverMultipleDocuments ? null : new BoundFluentMethod(CsharpNames, Url.Parts,
selectorIsOptional: Body == null || !Body.Required || HttpMethods.Contains("GET"),
link: OfficialDocumentationLink,
link: OfficialDocumentationLink.Url,
summary: HighLevelMethodXmlDocDescription
),
Initializer = new InitializerMethod(CsharpNames,
link: OfficialDocumentationLink,
link: OfficialDocumentationLink.Url,
summary: HighLevelMethodXmlDocDescription
)
};
Expand Down Expand Up @@ -145,7 +147,7 @@ public IReadOnlyCollection<LowLevelClientMethod> LowLevelClientMethods
CsharpNames = CsharpNames,
PerPathMethodName = methodName,
HttpMethod = httpMethod,
OfficialDocumentationLink = OfficialDocumentationLink,
OfficialDocumentationLink = OfficialDocumentationLink.Url,
Stability = Stability,
DeprecatedPath = path.Deprecation,
Path = path.Path,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace ApiGenerator.Domain.Specification
{
[JsonConverter(typeof(DocumentationConverter))]
public class Documentation
{
public string Description { get; set; }
public string Url { get; set; }
}

public class DocumentationConverter : JsonConverter
{
public override bool CanWrite { get; } = false;

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotSupportedException();

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{

var documentation = new Documentation();

if (reader.TokenType == JsonToken.String)
{
documentation.Url = (string)reader.Value;
return documentation;
}

while (reader.Read())
{
if (reader.TokenType == JsonToken.EndObject)
break;

var prop = (string)reader.Value;
switch (prop)
{
case "url":
documentation.Url = reader.ReadAsString();
break;
case "description":
documentation.Description = reader.ReadAsString();
break;
default:
throw new Exception($"Property '{prop}' unexpected in documentation object");
}
}
return documentation;
}

public override bool CanConvert(Type objectType) => true;
}
}
85 changes: 79 additions & 6 deletions src/CodeGeneration/ApiGenerator/Generator/ApiEndpointFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,37 @@
using ApiGenerator.Domain;
using ApiGenerator.Domain.Code;
using ApiGenerator.Domain.Specification;
using Microsoft.Extensions.DependencyModel;
using Newtonsoft.Json.Linq;

namespace ApiGenerator.Generator
namespace ApiGenerator.Generator
{
public static class ApiEndpointFactory
{
public static ApiEndpoint FromFile(string jsonFile)
{
var officialJsonSpec = JObject.Parse(File.ReadAllText(jsonFile));
TransformNewSpecStructureToOld(officialJsonSpec);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++

PatchOfficialSpec(officialJsonSpec, jsonFile);
var (name, endpoint) = officialJsonSpec.ToObject<Dictionary<string, ApiEndpoint>>().First();

endpoint.FileName = Path.GetFileName(jsonFile);
endpoint.Name = name;
var tokens = name.Split(".");

endpoint.MethodName = tokens.Last();
if (tokens.Length > 1)
endpoint.Namespace = tokens[0];
//todo side effect
endpoint.CsharpNames = new CsharpNames(name, endpoint.MethodName, endpoint.Namespace);

LoadOverridesOnEndpoint(endpoint);
PatchRequestParameters(endpoint);

EnforceRequiredOnParts(jsonFile, endpoint.Url);
return endpoint;
}

/// <summary>
/// This makes sure required is configured correctly by inspecting the paths.
/// Will emit a warning if the spec file got this wrong
Expand Down Expand Up @@ -70,7 +72,7 @@ private static void PatchRequestParameters(ApiEndpoint endpoint)
var newParams = ApiQueryParametersPatcher.Patch(endpoint.Name, endpoint.Url.Params, endpoint.Overrides);
endpoint.Url.Params = newParams;
}

/// <summary>
/// Finds a patch file in patches and union merges this with the official spec.
/// This allows us to check in tweaks should breaking changes occur in the spec before we catch them
Expand Down Expand Up @@ -102,5 +104,76 @@ void ReplaceOptions(string path)
ReplaceOptions("*.url.parts.metric.options");
ReplaceOptions("*.url.parts.index_metric.options");
}

/// <summary>
/// Changes the structure of new REST API spec in 7.4.0 to one that matches prior spec structure.
/// </summary>
private static void TransformNewSpecStructureToOld(JObject original)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the right approach on our 7.x branch, in master we might need to look in to moving our in memory representation over as well. This can be forward ported to master in the mean time.

Love the pragmatic solution though 👍

{
var name = (JProperty)original.First;
var spec = (JObject)name.Value;

// old spec structure, nothing to change
if (spec.ContainsKey("methods"))
return;

var methods = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
JObject parts = null;
var paths = new List<string>();
var deprecatedPaths = new List<JObject>();

foreach (var path in spec["url"]["paths"].Cast<JObject>())
{
if (path.ContainsKey("deprecated"))
{
var deprecated = new JObject
{
["version"] = path["deprecated"]["version"].Value<string>(),
["path"] = path["path"].Value<string>(),
["description"] = path["deprecated"]["description"].Value<string>()
};

deprecatedPaths.Add(deprecated);
}
else
paths.Add(path["path"].Value<string>());

if (path.ContainsKey("parts"))
{
if (parts == null)
parts = path["parts"].Value<JObject>();
else
parts.Merge(path["parts"].Value<JObject>(), new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Union
});
}

foreach (var method in path["methods"].Cast<JValue>())
methods.Add(method.Value<string>());
}



var newUrl = new JObject
{
["paths"] = new JArray(paths.ToArray()),
};

if (spec.ContainsKey("params"))
{
newUrl["params"] = spec["params"];
spec.Remove("params");
}

if (parts != null)
newUrl["parts"] = parts;

if (deprecatedPaths.Any())
newUrl["deprecated_paths"] = new JArray(deprecatedPaths.ToArray());

spec["url"] = newUrl;
spec["methods"] = new JArray(methods.ToArray());
}
}
}
Loading