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

Support deprecation and added versions for API methods in ApiGenerator #402

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
1 change: 1 addition & 0 deletions src/ApiGenerator/ApiGenerator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<PackageReference Include="Glob" Version="1.1.9" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NSwag.Core.Yaml" Version="13.20.0" />
<PackageReference Include="SemanticVersioning" Version="2.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="Spectre.Console" Version="0.47.0" />
<PackageReference Include="System.CommandLine.DragonFruit" Version="0.3.0-alpha.20371.2" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,36 @@
using System.Linq;
using ApiGenerator.Configuration;
using ApiGenerator.Domain.Specification;
using SemanticVersioning;

namespace ApiGenerator.Domain.Code.HighLevel.Methods
namespace ApiGenerator.Domain.Code.HighLevel.Methods
{
public class BoundFluentMethod : FluentSyntaxBase
{
public BoundFluentMethod(CsharpNames names, IReadOnlyCollection<UrlPart> parts, bool selectorIsOptional, string link, string summary)
: base(names, parts, selectorIsOptional, link, summary) { }
public BoundFluentMethod(CsharpNames names, IReadOnlyCollection<UrlPart> parts, bool selectorIsOptional, string link, string summary, Deprecation deprecated, Version versionAdded)
: base(names, parts, selectorIsOptional, link, summary, deprecated, versionAdded) { }

private string DescriptorTypeParams => string.Join(", ", CsharpNames.DescriptorGenerics
.Select(e => CsharpNames.DescriptorBoundDocumentGeneric));

private string RequestTypeParams => string.Join(", ", CsharpNames.SplitGeneric(CsharpNames.GenericsDeclaredOnRequest)
.Select(e => CsharpNames.DescriptorBoundDocumentGeneric));

private string SelectorReturn => string.IsNullOrWhiteSpace(CsharpNames.GenericsDeclaredOnRequest)
|| !CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName)
? CsharpNames.RequestInterfaceName
: $"{CsharpNames.RequestInterfaceName}<{RequestTypeParams}>";

public override string DescriptorName => $"{CsharpNames.DescriptorName}<{DescriptorTypeParams}>";
public override string GenericWhereClause => $"where {CsharpNames.DescriptorBoundDocumentGeneric} : class";
public override string MethodGenerics => $"<{CsharpNames.DescriptorBoundDocumentGeneric}>";
public override string RequestMethodGenerics => !string.IsNullOrWhiteSpace(RequestTypeParams)

public override string RequestMethodGenerics => !string.IsNullOrWhiteSpace(RequestTypeParams)
? $"<{RequestTypeParams}>"
: base.RequestMethodGenerics;

public override string Selector => $"Func<{DescriptorName}, {SelectorReturn}>";


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@
using System.Collections.Generic;
using System.Linq;
using ApiGenerator.Domain.Specification;
using SemanticVersioning;

namespace ApiGenerator.Domain.Code.HighLevel.Methods
namespace ApiGenerator.Domain.Code.HighLevel.Methods
{
public class FluentMethod : FluentSyntaxBase
{
public FluentMethod(CsharpNames names, IReadOnlyCollection<UrlPart> parts, bool selectorIsOptional, string link, string summary)
: base(names, parts, selectorIsOptional, link, summary) { }
public FluentMethod(CsharpNames names, IReadOnlyCollection<UrlPart> parts, bool selectorIsOptional, string link, string summary, Deprecation deprecated, Version versionAdded)
: base(names, parts, selectorIsOptional, link, summary, deprecated, versionAdded) { }

public override string GenericWhereClause =>
string.Join(" ", CsharpNames.HighLevelDescriptorMethodGenerics
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@
using System.Linq;
using ApiGenerator.Configuration;
using ApiGenerator.Domain.Specification;
using SemanticVersioning;

namespace ApiGenerator.Domain.Code.HighLevel.Methods
namespace ApiGenerator.Domain.Code.HighLevel.Methods
{
public abstract class FluentSyntaxBase : MethodSyntaxBase
{
private readonly bool _selectorIsOptional;

protected FluentSyntaxBase(CsharpNames names, IReadOnlyCollection<UrlPart> parts, bool selectorIsOptional, string link, string summary)
: base(names, link, summary) =>
protected FluentSyntaxBase(CsharpNames names, IReadOnlyCollection<UrlPart> parts, bool selectorIsOptional, string link, string summary, Deprecation deprecated, Version versionAdded)
: base(names, link, summary, deprecated, versionAdded) =>
(UrlParts, _selectorIsOptional) = (CreateDescriptorArgs(parts), selectorIsOptional);

private IReadOnlyCollection<UrlPart> UrlParts { get; }
Expand All @@ -61,7 +62,7 @@ protected FluentSyntaxBase(CsharpNames names, IReadOnlyCollection<UrlPart> parts
CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName)
? CsharpNames.GenericsDeclaredOnRequest
: DescriptorGenerics;

public virtual string RequestMethodGenerics =>
CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName)
? CsharpNames.GenericsDeclaredOnRequest
Expand All @@ -74,11 +75,11 @@ protected FluentSyntaxBase(CsharpNames names, IReadOnlyCollection<UrlPart> parts
private List<UrlPart> CreateDescriptorArgs(IReadOnlyCollection<UrlPart> parts)
{
var requiredParts = parts.Where(p => p.Required).ToList();

//Many api's return ALOT of information by default e.g get_alias or get_mapping
//the client methods that take a descriptor default to forcing a choice on the user.
//except for cat api's where the amount of information returned is manageable

var willInferFromDocument = CsharpNames.GenericsDeclaredOnDescriptor?.Contains("Document") ?? false;
if (!requiredParts.Any() && CsharpNames.Namespace != "Cat")
{
Expand Down Expand Up @@ -113,15 +114,15 @@ private List<UrlPart> CreateDescriptorArgs(IReadOnlyCollection<UrlPart> parts)
}

private bool IsDocumentRequest => CodeConfiguration.DocumentRequests.Contains(CsharpNames.RequestInterfaceName);
private string GenericFirstArgument =>
private string GenericFirstArgument =>
CsharpNames.GenericsDeclaredOnDescriptor.Replace("<", "").Replace(">", "").Split(",").First().Trim();

public string DescriptorArguments()
{
string codeArgs;
if (CodeConfiguration.DescriptorConstructors.TryGetValue(CsharpNames.DescriptorName, out codeArgs))
codeArgs += ",";

if (!UrlParts.Any()) return codeArgs;

string Optional(UrlPart p) => !p.Required && SelectorIsOptional ? " = null" : string.Empty;
Expand All @@ -136,17 +137,17 @@ public string SelectorArguments()
codeArgs = string.Join(", ", codeArgs.Split(',').Select(a=>a.Split(' ').Last()));
return codeArgs;
}

var parts = UrlParts.Where(p => p.Required).ToList();
if (!parts.Any()) return null;

string ToArg(UrlPart p)
{
if (IsDocumentRequest) return "documentWithId: document";

if (p.HighLevelTypeName.StartsWith("DocumentPath"))
return "documentWithId: id?.Document, index: id?.Self?.Index, id: id?.Self?.Id";


return $"{p.Name.ToCamelCase()}: {p.Name.ToCamelCase()}";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@

using System.Linq;
using ApiGenerator.Configuration;
using ApiGenerator.Domain.Specification;
using SemanticVersioning;

namespace ApiGenerator.Domain.Code.HighLevel.Methods
{
public class InitializerMethod : MethodSyntaxBase
{
public InitializerMethod(CsharpNames names, string link, string summary) : base(names, link, summary) { }
public InitializerMethod(CsharpNames names, string link, string summary, Deprecation deprecated, Version versionAdded) : base(names, link, summary, deprecated, versionAdded) { }

public string MethodName => CsharpNames.MethodName;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,24 @@
* under the License.
*/

using ApiGenerator.Domain.Specification;
using SemanticVersioning;

namespace ApiGenerator.Domain.Code.HighLevel.Methods
{
public abstract class MethodSyntaxBase
{
protected MethodSyntaxBase(CsharpNames names, string link, string summary) =>
(CsharpNames, DocumentationLink, XmlDocSummary) = (names, link, summary);
protected MethodSyntaxBase(CsharpNames names, string link, string summary, Deprecation deprecated, Version versionAdded) =>
(CsharpNames, DocumentationLink, XmlDocSummary, Deprecated, VersionAdded) = (names, link, summary, deprecated, versionAdded);

public string DocumentationLink { get; }

public string XmlDocSummary { get; }

public Deprecation Deprecated { get; }

public Version VersionAdded { get; set; }

protected CsharpNames CsharpNames { get; }

public bool InterfaceResponse => ResponseName.StartsWith("ISearchResponse<");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ string generic
Parameterless = true,
Generated = $"protected {typeName}() : base()",
Description =
$"///<summary>Used for serialization purposes, making sure we have a parameterless constructor</summary>{Indent}[SerializationConstructor]",
$"/// <summary>Used for serialization purposes, making sure we have a parameterless constructor</summary>{Indent}[SerializationConstructor]",
});
return constructors;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
using System.Linq;
using ApiGenerator.Domain.Specification;

namespace ApiGenerator.Domain.Code.HighLevel.Requests
namespace ApiGenerator.Domain.Code.HighLevel.Requests
{
public class DescriptorPartialImplementation
{
Expand All @@ -41,7 +41,7 @@ public class DescriptorPartialImplementation
public IReadOnlyCollection<UrlPath> Paths { get; set; }
public IReadOnlyCollection<QueryParameters> Params { get; set; }
public bool HasBody { get; set; }

public IEnumerable<FluentRouteSetter> GetFluentRouteSetters()
{
var setters = new List<FluentRouteSetter>();
Expand All @@ -67,26 +67,26 @@ public IEnumerable<FluentRouteSetter> GetFluentRouteSetters()

var code =
$"public {returnType} {p.InterfaceName}({p.HighLevelTypeName} {paramName}) => Assign({paramName}, (a,v)=>a.RouteValues.{routeSetter}(\"{p.Name}\", {routeValue}));";
var xmlDoc = $"///<summary>{p.Description}</summary>";
var xmlDoc = $"/// <summary>{p.Description}</summary>";
setters.Add(new FluentRouteSetter { Code = code, XmlDoc = xmlDoc });
if (paramName == "index")
{
code = $"public {returnType} {p.InterfaceName}<TOther>() where TOther : class ";
code += $"=> Assign(typeof(TOther), (a,v)=>a.RouteValues.{routeSetter}(\"{p.Name}\", ({p.HighLevelTypeName})v));";
xmlDoc = $"///<summary>a shortcut into calling {p.InterfaceName}(typeof(TOther))</summary>";
xmlDoc = $"/// <summary>a shortcut into calling {p.InterfaceName}(typeof(TOther))</summary>";
setters.Add(new FluentRouteSetter { Code = code, XmlDoc = xmlDoc });
}
if (paramName == "index" && p.Type == "list")
{
code = $"public {returnType} AllIndices() => Index(Indices.All);";
xmlDoc = $"///<summary>A shortcut into calling Index(Indices.All)</summary>";
xmlDoc = $"/// <summary>A shortcut into calling Index(Indices.All)</summary>";
setters.Add(new FluentRouteSetter { Code = code, XmlDoc = xmlDoc });
}
if (paramName == "fields" && p.Type == "list")
{
code = $"public {returnType} Fields<T>(params Expression<Func<T, object>>[] fields) ";
code += $"=> Assign(fields, (a,v)=>a.RouteValues.{routeSetter}(\"fields\", (Fields)v));";
xmlDoc = $"///<summary>{p.Description}</summary>";
xmlDoc = $"/// <summary>{p.Description}</summary>";
setters.Add(new FluentRouteSetter { Code = code, XmlDoc = xmlDoc });
}
}
Expand Down
29 changes: 13 additions & 16 deletions src/ApiGenerator/Domain/Code/LowLevel/LowLevelClientMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
using System.Linq;
using System.Text.RegularExpressions;
using ApiGenerator.Domain.Specification;
using SemanticVersioning;

namespace ApiGenerator.Domain.Code.LowLevel
{
Expand All @@ -44,35 +45,31 @@ public class LowLevelClientMethod
public string PerPathMethodName { get; set; }
public string HttpMethod { get; set; }

public DeprecatedPath DeprecatedPath { get; set; }
public Deprecation Deprecation { get; set; }
public UrlInformation Url { get; set; }
public bool HasBody { get; set; }
public IEnumerable<UrlPart> Parts { get; set; }
public string Path { get; set; }

public Version VersionAdded { get; set; }

public string UrlInCode
{
get
{
string Evaluator(Match m)
{

var arg = m.Groups[^1].Value.ToCamelCase();
return $"{{{arg}:{arg}}}";
}

var url = Path.TrimStart('/');
var options = Url.OriginalParts?.Select(p => p.Key) ?? Enumerable.Empty<string>();
var url = Path.TrimStart('/');
var options = Url.AllPaths.SelectMany(p => p.Parts).Select(p => p.Name).Distinct();

var pattern = string.Join("|", options);
var urlCode = $"\"{url}\"";
if (Path.Contains("{"))
{
var patchedUrl = Regex.Replace(url, "{(" + pattern + ")}", Evaluator);
urlCode = $"Url($\"{patchedUrl}\")";
}
return urlCode;
if (!Path.Contains('{')) return urlCode;

var patchedUrl = Regex.Replace(url, "{(" + pattern + ")}", m =>
{
var arg = m.Groups[^1].Value.ToCamelCase();
return $"{{{arg}:{arg}}}";
});
return $"Url($\"{patchedUrl}\")";
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/ApiGenerator/Domain/RestApiSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public IEnumerable<EnumDescription> EnumsInTheSpec
var urlParameterEnums = Endpoints
.Values
.SelectMany(e => e.Url.Params.Values)
.Where(p => p.Options != null && p.Options.Any())
.Where(p => !p.Skip && p.Options != null && p.Options.Any())
.Select(p => new EnumDescription
{
Name = p.ClsName,
Expand Down
Loading
Loading