Skip to content

Commit 04dc1d5

Browse files
authored
Feature/parent deprecation (elastic#3022)
* clean up global and local overrides concept. Removed .obsolete.json support now handled by * skip before rename * make sure all parent querystring parameters are marked as deprecated * remove parent field mapping * update generator for descriptor to include a newline after obsolete for descriptor parameters that are generated * Feature/base class codegen (elastic#3025) * moved requestparameter patching to its own thing, patch common query parameters seperately and started on generating the base classes for request OIS and fluent * cleaned up request generated and moved logic out of it * finished cleaning up request genererated cshtml * generate common query string params on base descriptor clas * generated descriptor arguments now adhere to csharp guidelines and are camel cased * centralized up how field types are calculated. RequestParameters rendered fields as IEnumerable<object> or object, since the highlevel generated code now sets the query strings directly this is no longer neccessary * Centralized how setters for object initializer classes are generated * requests were using DeprecatedInFavor of which was never set, they now use Obsolete * use expression getter and setters * make cat parameters more descriptive * documentation from spec now word wraps around 140 characters * moved generation code to seperate static class * remove nullable type builder as it the source csharp type itself is nullable now always * OCD over generated output spacing
1 parent 8b44c0e commit 04dc1d5

29 files changed

+7175
-15724
lines changed

src/CodeGeneration/ApiGenerator/ApiGenerator.cs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ private static KeyValuePair<string, ApiEndpoint> CreateApiEndpoint(string jsonFi
117117
PatchOfficialSpec(officialJsonSpec, jsonFile);
118118
var endpoint = officialJsonSpec.ToObject<Dictionary<string, ApiEndpoint>>().First();
119119
endpoint.Value.CsharpMethodName = CreateMethodName(endpoint.Key);
120-
AddObsoletes(jsonFile, endpoint.Value);
121120
return endpoint;
122121
}
123122

@@ -135,24 +134,12 @@ private static void PatchOfficialSpec(JObject original, string jsonFile)
135134
});
136135
}
137136

138-
private static void AddObsoletes(string jsonFile, ApiEndpoint endpoint)
139-
{
140-
var directory = Path.GetDirectoryName(jsonFile);
141-
var obsoleteFile = Path.Combine(directory, Path.GetFileNameWithoutExtension(jsonFile)) + ".obsolete.json";
142-
if (!File.Exists(obsoleteFile)) return;
143-
144-
var json = File.ReadAllText(obsoleteFile);
145-
var endpointOverride = JsonConvert.DeserializeObject<Dictionary<string, ApiEndpoint>>(json).First();
146-
endpoint.ObsoleteQueryParameters = endpointOverride.Value?.Url?.Params ?? new Dictionary<string, ApiQueryParameters>();
147-
endpoint.RemovedMethods = endpointOverride.Value?.RemovedMethods ?? new Dictionary<string, string>();
148-
}
149-
150137
private static Dictionary<string, ApiQueryParameters> CreateCommonApiQueryParameters(string jsonFile)
151138
{
152139
var json = File.ReadAllText(jsonFile);
153140
var jobject = JObject.Parse(json);
154141
var commonParameters = jobject.Property("params").Value.ToObject<Dictionary<string, ApiQueryParameters>>();
155-
return commonParameters;
142+
return ApiQueryParametersPatcher.Patch(commonParameters, null);
156143
}
157144

158145
private static string CreateMethodName(string apiEndpointKey)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using CsQuery.ExtensionMethods.Internal;
5+
6+
namespace ApiGenerator.Domain
7+
{
8+
public static class CodeGenerator
9+
{
10+
public static string PropertyGenerator(string type, string name, string key, string setter) =>
11+
$"public {type} {name} {{ get => Q<{type}>(\"{key}\"); set => Q(\"{key}\", {setter}); }}";
12+
13+
public static string Property(string type, string name, string key, string setter, string obsolete, params string[] doc)
14+
{
15+
var components = new List<string>();
16+
foreach (var d in RenderDocumentation(doc)) A(d);
17+
if (!string.IsNullOrWhiteSpace(obsolete)) A($"[Obsolete(\"Scheduled to be removed in 7.0, {obsolete}\")]");
18+
19+
A(PropertyGenerator(type, name, key, setter));
20+
return string.Join("\r\n\t\t", components);
21+
22+
void A(string s) => components.Add(s);
23+
}
24+
25+
public static string Constructor(Constructor c)
26+
{
27+
var components = new List<string>();
28+
if (!c.Description.IsNullOrEmpty()) A(c.Description);
29+
var generated = c.Generated;
30+
if (c.Body.IsNullOrEmpty()) generated += "{}";
31+
A(generated);
32+
if (!c.Body.IsNullOrEmpty()) A(c.Body);
33+
if (!c.AdditionalCode.IsNullOrEmpty()) A(c.AdditionalCode);
34+
return string.Join("\r\n\t\t", components);
35+
void A(string s) => components.Add(s);
36+
}
37+
38+
39+
40+
private static IEnumerable<string> RenderDocumentation(params string[] doc)
41+
{
42+
doc = (doc?.SelectMany(WrapDocumentation) ?? Enumerable.Empty<string>()).ToArray();
43+
switch (doc.Length)
44+
{
45+
case 0: yield break;
46+
case 1:
47+
yield return ($"///<summary>{doc[0]}</summary>");
48+
yield break;
49+
default:
50+
yield return "///<summary>";
51+
foreach (var d in doc) yield return $"/// {d}";
52+
yield return "///</summary>";
53+
yield break;
54+
}
55+
}
56+
private static string[] WrapDocumentation(string documentation)
57+
{
58+
const int max = 140;
59+
var lines = documentation.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
60+
var charCount = 0;
61+
return lines.GroupBy(Wrap).Select(l => string.Join(" ", l.ToArray())).ToArray();
62+
int Wrap(string w)
63+
{
64+
var increase = (charCount % max + w.Length + 1 >= max ? max - (charCount % max) : 0);
65+
return (charCount += increase + w.Length + 1) / max;
66+
}
67+
}
68+
}
69+
}

src/CodeGeneration/ApiGenerator/Domain/ApiEndpoint.cs

Lines changed: 3 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ public class ApiEndpoint
6464
public IDictionary<string, string> RemovedMethods { get; set; } = new Dictionary<string, string>();
6565
public ApiUrl Url { get; set; }
6666
public ApiBody Body { get; set; }
67-
public IDictionary<string, ApiQueryParameters> ObsoleteQueryParameters { get; set; }
6867

6968
public string PascalCase(string s)
7069
{
@@ -260,78 +259,11 @@ private void PatchEndpoint()
260259
}
261260
}
262261

262+
263263
private void PatchRequestParameters(IEndpointOverrides overrides)
264264
{
265-
if (this.Url.Params == null) return;
266-
foreach (var param in RestApiSpec.CommonApiQueryParameters)
267-
{
268-
if (!this.Url.Params.ContainsKey(param.Key))
269-
this.Url.Params.Add(param.Key, param.Value);
270-
}
271-
272-
if (this.ObsoleteQueryParameters != null)
273-
{
274-
foreach (var param in this.ObsoleteQueryParameters)
275-
{
276-
if (!this.Url.Params.ContainsKey(param.Key))
277-
this.Url.Params.Add(param.Key, param.Value);
278-
}
279-
}
280-
var declaredKeys = this.Url.Params.Select(p => p.Value.OriginalQueryStringParamName ?? p.Key).ToList();
281-
IEnumerable<string> skipList = new List<string>();
282-
IDictionary<string, string> queryStringParamsRenameList = new Dictionary<string, string>();
283-
284-
if (overrides != null)
285-
{
286-
var name = overrides.GetType().Name;
287-
skipList = overrides.SkipQueryStringParams ?? skipList;
288-
queryStringParamsRenameList = overrides.RenameQueryStringParams ?? queryStringParamsRenameList;
289-
foreach (var p in skipList.Except(declaredKeys))
290-
ApiGenerator.Warnings.Add($"On {name} skip key '{p}' is not found in spec");
291-
foreach (var p in queryStringParamsRenameList.Keys.Except(declaredKeys))
292-
ApiGenerator.Warnings.Add($"On {name} rename key '{p}' is not found in spec");
293-
}
294-
295-
var globalQueryStringRenames = new Dictionary<string, string>
296-
{
297-
{"_source", "source_enabled"},
298-
{"_source_include", "source_include"},
299-
{"_source_exclude", "source_exclude"},
300-
{"q", "query_on_query_string"},
301-
{"docvalue_fields", "doc_value_fields"},
302-
};
303-
304-
foreach (var kv in globalQueryStringRenames)
305-
if (!queryStringParamsRenameList.ContainsKey(kv.Key))
306-
queryStringParamsRenameList[kv.Key] = kv.Value;
307-
308-
var globalOverrides = new GlobalOverrides();
309-
var patchedParams = new Dictionary<string, ApiQueryParameters>();
310-
foreach (var kv in this.Url.Params)
311-
{
312-
if (kv.Value.OriginalQueryStringParamName.IsNullOrEmpty())
313-
kv.Value.OriginalQueryStringParamName = kv.Key;
314-
315-
if (skipList.Contains(kv.Key)) continue;
316-
if (globalOverrides.RenderPartial.Contains(kv.Key))
317-
kv.Value.RenderPartial = true;
318-
319-
if (!queryStringParamsRenameList.TryGetValue(kv.Key, out var newName))
320-
{
321-
patchedParams.Add(kv.Key, kv.Value);
322-
continue;
323-
}
324-
325-
if (globalOverrides.RenderPartial.Contains(newName))
326-
kv.Value.RenderPartial = true;
327-
328-
//make sure source_enabled takes a boolean only
329-
if (newName == "source_enabled") kv.Value.Type = "boolean";
330-
331-
patchedParams.Add(newName, kv.Value);
332-
}
333-
334-
this.Url.Params = patchedParams;
265+
var newParams = ApiQueryParametersPatcher.Patch(this.Url.Params, overrides);
266+
this.Url.Params = newParams;
335267
}
336268

337269
private static string RenameMetricUrlPathParam(string path)
Lines changed: 95 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,130 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Text;
5+
using CsQuery.ExtensionMethods.Internal;
46

57
namespace ApiGenerator.Domain
68
{
79
public class ApiQueryParameters
810
{
9-
public string OriginalQueryStringParamName { get; set; }
10-
public string DeprecatedInFavorOf { get; set; }
11-
public string Type { get; set; }
12-
public string Description { get; set; }
11+
public string QueryStringKey { get; set; }
12+
13+
public bool RenderPartial { get; set; }
14+
15+
public string ClsName { get; set; }
16+
1317
public string Obsolete { get; set; }
18+
1419
public IEnumerable<string> Options { get; set; }
1520

16-
public string CsharpType(string paramName)
21+
public string Type { get; set; }
22+
23+
private static readonly string[] FieldsParams = {"fields", "_source_include", "_source_exclude"};
24+
public string TypeHighLevel
1725
{
18-
switch (this.Type)
26+
get
1927
{
20-
case "boolean":
21-
return "bool";
22-
case "list":
23-
return "params string[]";
24-
case "integer":
25-
return "int";
26-
case "number":
27-
return new [] {"boost", "percen", "score"}.Any(s=>paramName.ToLowerInvariant().Contains(s))
28-
? "double"
29-
: "long";
30-
case "duration":
31-
case "time":
32-
return "TimeSpan";
33-
case "text":
34-
case "":
35-
case null:
36-
return "string";
37-
case "date":
38-
return "DateTimeOffset";
39-
case "enum":
40-
return paramName.ToPascalCase();
41-
default:
42-
return this.Type;
28+
if (this.QueryStringKey == "routing") return "Routing";
29+
var isFields = FieldsParams.Contains(this.QueryStringKey) || this.QueryStringKey.EndsWith("_fields");
30+
31+
var csharpType = this.TypeLowLevel;
32+
switch (csharpType)
33+
{
34+
case "TimeSpan": return "Time";
35+
}
36+
37+
switch (this.Type)
38+
{
39+
case "list" when isFields:
40+
case "string" when isFields: return "Fields";
41+
case "string" when this.QueryStringKey.Contains("field"): return "Field";
42+
default:
43+
return csharpType;
44+
}
4345
}
4446
}
4547

46-
public IEnumerable<string> HighLevelTypeDescription(string paramName)
48+
public string ClsArgumentName => this.ClsName.ToCamelCase();
49+
public string DescriptorArgumentType => this.Type == "list" && this.TypeHighLevel.EndsWith("[]") ? "params " + this.TypeHighLevel : TypeHighLevel;
50+
public string SetterHighLevel
4751
{
48-
switch (paramName)
52+
get
4953
{
50-
case "routing":
51-
yield return "A document is routed to a particular shard in an index using the following formula";
52-
yield return "<para> shard_num = hash(_routing) % num_primary_shards</para>";
53-
yield return "<para>Elasticsearch will use the document id if not provided. </para>";
54-
yield return "<para>For requests that are constructed from/for a document NEST will automatically infer the routing key";
55-
yield return "if that document has a <see cref=\"Nest.JoinField\" /> or a routing mapping on for its type exists on <see cref=\"Nest.ConnectionSettings\" /></para> ";
56-
yield break;
57-
case "source_enabled":
58-
yield return "Whether the _source should be included in the response.";
59-
yield break;
60-
default:
61-
yield return this.Description;
62-
yield break;
54+
var setter = "value";
55+
if (this.TypeHighLevel == "Time") setter += ".ToString()";
56+
return setter;
6357
}
6458
}
65-
66-
public string HighLevelType(string paramName)
59+
public string SetterLowLevel
6760
{
68-
if (paramName == "routing") return "Routing";
69-
var o = OriginalQueryStringParamName;
70-
var isFields = (o.Contains("fields") || o.Contains("source_include") || o.Contains("source_exclude"));
71-
72-
var csharpType = this.CsharpType(paramName);
73-
switch (csharpType)
61+
get
7462
{
75-
case "TimeSpan": return "Time";
63+
var setter = "value";
64+
if (this.TypeLowLevel == "TimeSpan") setter += ".ToTimeUnit()";
65+
return setter;
7666
}
77-
switch (this.Type)
78-
{
67+
}
68+
69+
public bool IsFieldsParam => this.TypeHighLevel == "Fields";
70+
public bool IsFieldParam => this.TypeHighLevel == "Field";
7971

80-
case "list" when isFields:
81-
case "string" when isFields: return "Fields";
82-
case "string" when o.Contains("field"): return "Field";
83-
default:
84-
return NullableCsharpType(csharpType);
72+
public string TypeLowLevel
73+
{
74+
get
75+
{
76+
switch (this.Type)
77+
{
78+
case "boolean": return "bool?";
79+
case "list": return "string[]";
80+
case "integer": return "int?";
81+
case "date": return "DateTimeOffset";
82+
case "enum": return this.ClsName;
83+
case "number":
84+
return new[] {"boost", "percen", "score"}.Any(s => this.QueryStringKey.ToLowerInvariant().Contains(s))
85+
? "double?"
86+
: "long?";
87+
case "duration":
88+
case "time":
89+
return "TimeSpan";
90+
case "text":
91+
case "":
92+
case null:
93+
return "string";
94+
default:
95+
return this.Type;
96+
}
8597
}
8698
}
87-
private static string NullableCsharpType(string fieldType)
99+
100+
public string Description { get; set; }
101+
public IEnumerable<string> DescriptionHighLevel
88102
{
89-
switch (fieldType)
103+
get
90104
{
91-
case "bool": return "bool?";
92-
case "integer": return "int?";
93-
case "double": return "double?";
94-
case "long": return "long?";
95-
default:
96-
return fieldType;
105+
switch (this.QueryStringKey)
106+
{
107+
case "routing":
108+
yield return "A document is routed to a particular shard in an index using the following formula";
109+
yield return "<para> shard_num = hash(_routing) % num_primary_shards</para>";
110+
yield return "<para>Elasticsearch will use the document id if not provided. </para>";
111+
yield return "<para>For requests that are constructed from/for a document NEST will automatically infer the routing key";
112+
yield return
113+
"if that document has a <see cref=\"Nest.JoinField\" /> or a routing mapping on for its type exists on <see cref=\"Nest.ConnectionSettings\" /></para> ";
114+
yield break;
115+
case "_source":
116+
yield return "Whether the _source should be included in the response.";
117+
yield break;
118+
default:
119+
yield return this.Description;
120+
yield break;
121+
}
97122
}
98123
}
99124

100-
public Func<string, string, string, string, string> Generator { get; set; } =
101-
(fieldType, mm, original, setter) =>
102-
$"public {NullableCsharpType(fieldType)} {mm} {{ get {{ return Q<{NullableCsharpType(fieldType)}>(\"{original}\"); }} set {{ Q(\"{original}\", {setter}); }} }}";
125+
public string InitializerGenerator(string type, string name, string key, string setter, params string[] doc) =>
126+
CodeGenerator.Property(type, name, key, setter, this.Obsolete, doc);
103127

104128
public Func<string, string, string, string, string> FluentGenerator { get; set; }
105-
106-
public bool RenderPartial { get; set; }
107129
}
108130
}

0 commit comments

Comments
 (0)