Bumped package version to 1.1.9. Added documentation and cleaned up code. Going to merge into Master.

This commit is contained in:
MattMo 2023-02-05 12:28:37 -08:00
parent 1475159f1c
commit 42c4682c89
5 changed files with 119 additions and 17 deletions

View File

@ -8,6 +8,7 @@ namespace MontoyaTech.Rest.Net.Example
{ {
using System; using System;
using System.Net.Http; using System.Net.Http;
using Newtonsoft.Json;
public class Client public class Client
{ {
@ -46,7 +47,7 @@ namespace MontoyaTech.Rest.Net.Example
var response = this.Client.HttpClient.Send(message); var response = this.Client.HttpClient.Send(message);
if (response.StatusCode == System.Net.HttpStatusCode.OK) if (response.StatusCode == System.Net.HttpStatusCode.OK)
return Newtonsoft.Json.JsonConvert.DeserializeObject<string>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult()); return JsonConvert.DeserializeObject<string>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult());
else else
throw new Exception("Unexpected Http Response StatusCode:" + response.StatusCode); throw new Exception("Unexpected Http Response StatusCode:" + response.StatusCode);
} }
@ -58,7 +59,7 @@ namespace MontoyaTech.Rest.Net.Example
var response = this.Client.HttpClient.Send(message); var response = this.Client.HttpClient.Send(message);
if (response.StatusCode == System.Net.HttpStatusCode.OK) if (response.StatusCode == System.Net.HttpStatusCode.OK)
return Newtonsoft.Json.JsonConvert.DeserializeObject<string>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult()); return JsonConvert.DeserializeObject<string>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult());
else else
throw new Exception("Unexpected Http Response StatusCode:" + response.StatusCode); throw new Exception("Unexpected Http Response StatusCode:" + response.StatusCode);
} }
@ -80,7 +81,7 @@ namespace MontoyaTech.Rest.Net.Example
var response = this.Client.HttpClient.Send(message); var response = this.Client.HttpClient.Send(message);
if (response.StatusCode == System.Net.HttpStatusCode.OK) if (response.StatusCode == System.Net.HttpStatusCode.OK)
return Newtonsoft.Json.JsonConvert.DeserializeObject<bool>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult()); return JsonConvert.DeserializeObject<bool>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult());
else else
throw new Exception("Unexpected Http Response StatusCode:" + response.StatusCode); throw new Exception("Unexpected Http Response StatusCode:" + response.StatusCode);
} }
@ -89,7 +90,7 @@ namespace MontoyaTech.Rest.Net.Example
{ {
var message = new HttpRequestMessage(HttpMethod.Post, $"{this.Client.BaseUrl}/auth/signup"); var message = new HttpRequestMessage(HttpMethod.Post, $"{this.Client.BaseUrl}/auth/signup");
message.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(request)); message.Content = new StringContent(JsonConvert.SerializeObject(request));
var response = this.Client.HttpClient.Send(message); var response = this.Client.HttpClient.Send(message);
@ -104,7 +105,7 @@ namespace MontoyaTech.Rest.Net.Example
var response = this.Client.HttpClient.Send(message); var response = this.Client.HttpClient.Send(message);
if (response.StatusCode == System.Net.HttpStatusCode.OK) if (response.StatusCode == System.Net.HttpStatusCode.OK)
return Newtonsoft.Json.JsonConvert.DeserializeObject<User>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult()); return JsonConvert.DeserializeObject<User>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult());
else else
throw new Exception("Unexpected Http Response StatusCode:" + response.StatusCode); throw new Exception("Unexpected Http Response StatusCode:" + response.StatusCode);
} }

View File

@ -37,7 +37,7 @@ namespace MontoyaTech.Rest.Net.Example
new Route(HttpRequestMethod.Get, "/auth/", Json) new Route(HttpRequestMethod.Get, "/auth/", Json)
); );
string code = CodeGenerator.GenerateCSharpClient(listener.Routes); string code = ClientCodeGenerator.GenerateCSharpClient(listener.Routes);
Console.WriteLine(code); Console.WriteLine(code);

View File

@ -15,8 +15,13 @@ namespace MontoyaTech.Rest.Net
/// A class that can take a set of routes and generate code /// A class that can take a set of routes and generate code
/// for a client that can be used to interact with them. /// for a client that can be used to interact with them.
/// </summary> /// </summary>
public class CodeGenerator public class ClientCodeGenerator
{ {
/// <summary>
/// Returns whether or not a given type belongs to DotNet.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static bool IsTypeDotNet(Type type) private static bool IsTypeDotNet(Type type)
{ {
if (type.Assembly.GetName().Name == "System.Private.CoreLib") if (type.Assembly.GetName().Name == "System.Private.CoreLib")
@ -25,6 +30,11 @@ namespace MontoyaTech.Rest.Net
return false; return false;
} }
/// <summary>
/// Finds all the dependencies for a given type and returns them.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static List<Type> FindTypeDependencies(Type type) private static List<Type> FindTypeDependencies(Type type)
{ {
var dependencies = new HashSet<Type>(); var dependencies = new HashSet<Type>();
@ -85,6 +95,11 @@ namespace MontoyaTech.Rest.Net
return dependencies.ToList(); return dependencies.ToList();
} }
/// <summary>
/// Finds all the types that a given route depends on to function.
/// </summary>
/// <param name="route"></param>
/// <returns></returns>
private static List<Type> FindRouteDependencies(Route route) private static List<Type> FindRouteDependencies(Route route)
{ {
var dependencies = new HashSet<Type>(); var dependencies = new HashSet<Type>();
@ -133,6 +148,11 @@ namespace MontoyaTech.Rest.Net
return dependencies.ToList(); return dependencies.ToList();
} }
/// <summary>
/// Finds all the types that a given set of routes depend on to function.
/// </summary>
/// <param name="routes"></param>
/// <returns></returns>
private static List<Type> FindRoutesDependencies(List<Route> routes) private static List<Type> FindRoutesDependencies(List<Route> routes)
{ {
var dependencies = new HashSet<Type>(); var dependencies = new HashSet<Type>();
@ -150,6 +170,11 @@ namespace MontoyaTech.Rest.Net
return dependencies.ToList(); return dependencies.ToList();
} }
/// <summary>
/// Returns the fully resolve name for a given type.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static string GetTypeFullyResolvedName(Type type) private static string GetTypeFullyResolvedName(Type type)
{ {
if (IsTypeDotNet(type)) if (IsTypeDotNet(type))
@ -252,6 +277,11 @@ namespace MontoyaTech.Rest.Net
} }
} }
/// <summary>
/// Finds all the route groupings from a set of routes and returns those groups.
/// </summary>
/// <param name="routes"></param>
/// <returns></returns>
private static Dictionary<string, List<Route>> FindRouteGroups(List<Route> routes) private static Dictionary<string, List<Route>> FindRouteGroups(List<Route> routes)
{ {
var groups = new Dictionary<string, List<Route>>(); var groups = new Dictionary<string, List<Route>>();
@ -278,7 +308,24 @@ namespace MontoyaTech.Rest.Net
return groups; return groups;
} }
public static string GenerateCSharpClient(List<Route> routes) /// <summary>
/// Generates a CSharpClient from a RouteListener.
/// </summary>
/// <param name="listener"></param>
/// <param name="name">The name of the Client class, default is Client.</param>
/// <returns>The generated C# code.</returns>
public static string GenerateCSharpClient(RouteListener listener, string name = "Client")
{
return GenerateCSharpClient(listener.Routes, name);
}
/// <summary>
/// Generates a CSharpClient from a given set of Routes.
/// </summary>
/// <param name="routes"></param>
/// <param name="name">The name of the Client class, default is Client.</param>
/// <returns></returns>
public static string GenerateCSharpClient(List<Route> routes, string name = "Client")
{ {
var includedTypes = FindRoutesDependencies(routes); var includedTypes = FindRoutesDependencies(routes);
@ -288,8 +335,9 @@ namespace MontoyaTech.Rest.Net
writer.WriteLine("using System;"); writer.WriteLine("using System;");
writer.WriteLine("using System.Net.Http;"); writer.WriteLine("using System.Net.Http;");
writer.WriteLine("using Newtonsoft.Json;");
writer.WriteBreak().WriteLine("public class Client").WriteLine("{").Indent(); writer.WriteBreak().WriteLine($"public class {name}").WriteLine("{").Indent();
writer.WriteBreak().WriteLine("public string BaseUrl;"); writer.WriteBreak().WriteLine("public string BaseUrl;");
@ -326,12 +374,22 @@ namespace MontoyaTech.Rest.Net
return writer.ToString(); return writer.ToString();
} }
/// <summary>
/// Generates C# for a set of included types.
/// </summary>
/// <param name="types"></param>
/// <param name="writer"></param>
private static void GenerateCSharpIncludedTypes(List<Type> types, CodeWriter writer) private static void GenerateCSharpIncludedTypes(List<Type> types, CodeWriter writer)
{ {
foreach (var type in types) foreach (var type in types)
GenerateCSharpIncludedType(type, writer); GenerateCSharpIncludedType(type, writer);
} }
/// <summary>
/// Generates C# for a given included type.
/// </summary>
/// <param name="type"></param>
/// <param name="writer"></param>
private static void GenerateCSharpIncludedType(Type type, CodeWriter writer) private static void GenerateCSharpIncludedType(Type type, CodeWriter writer)
{ {
writer.WriteBreak(); writer.WriteBreak();
@ -355,6 +413,11 @@ namespace MontoyaTech.Rest.Net
writer.Outdent().WriteLine("}"); writer.Outdent().WriteLine("}");
} }
/// <summary>
/// Generates C# for a field inside an included type.
/// </summary>
/// <param name="field"></param>
/// <param name="writer"></param>
private static void GenerateCSharpIncludedField(FieldInfo field, CodeWriter writer) private static void GenerateCSharpIncludedField(FieldInfo field, CodeWriter writer)
{ {
writer.WriteBreak(); writer.WriteBreak();
@ -362,6 +425,11 @@ namespace MontoyaTech.Rest.Net
writer.WriteLine($"public {GetTypeFullyResolvedName(field.FieldType)} {field.Name};"); writer.WriteLine($"public {GetTypeFullyResolvedName(field.FieldType)} {field.Name};");
} }
/// <summary>
/// Generates C# for a property inside an included type.
/// </summary>
/// <param name="property"></param>
/// <param name="writer"></param>
private static void GenerateCSharpIncludedProperty(PropertyInfo property, CodeWriter writer) private static void GenerateCSharpIncludedProperty(PropertyInfo property, CodeWriter writer)
{ {
writer.WriteBreak(); writer.WriteBreak();
@ -369,12 +437,23 @@ namespace MontoyaTech.Rest.Net
writer.WriteLine($"public {GetTypeFullyResolvedName(property.PropertyType)} {property.Name} {{ get; set; }}"); writer.WriteLine($"public {GetTypeFullyResolvedName(property.PropertyType)} {property.Name} {{ get; set; }}");
} }
/// <summary>
/// Generates C# for a set of route groups.
/// </summary>
/// <param name="groups"></param>
/// <param name="writer"></param>
private static void GenerateCSharpRouteGroups(Dictionary<string, List<Route>> groups, CodeWriter writer) private static void GenerateCSharpRouteGroups(Dictionary<string, List<Route>> groups, CodeWriter writer)
{ {
foreach (var group in groups) foreach (var group in groups)
GenerateCSharpRouteGroup(group.Key, group.Value, writer); GenerateCSharpRouteGroup(group.Key, group.Value, writer);
} }
/// <summary>
/// Generates C# for a given route group.
/// </summary>
/// <param name="name"></param>
/// <param name="routes"></param>
/// <param name="writer"></param>
private static void GenerateCSharpRouteGroup(string name, List<Route> routes, CodeWriter writer) private static void GenerateCSharpRouteGroup(string name, List<Route> routes, CodeWriter writer)
{ {
writer.WriteBreak(); writer.WriteBreak();
@ -399,6 +478,12 @@ namespace MontoyaTech.Rest.Net
writer.Outdent().WriteLine("}"); writer.Outdent().WriteLine("}");
} }
/// <summary>
/// Generates a C# function for a given route.
/// </summary>
/// <param name="route"></param>
/// <param name="writer"></param>
/// <exception cref="NotSupportedException"></exception>
private static void GenerateCSharpRouteFunction(Route route, CodeWriter writer) private static void GenerateCSharpRouteFunction(Route route, CodeWriter writer)
{ {
writer.WriteBreak(); writer.WriteBreak();
@ -411,9 +496,10 @@ namespace MontoyaTech.Rest.Net
var routeResponse = methodInfo.GetCustomAttribute<RouteResponse>(); var routeResponse = methodInfo.GetCustomAttribute<RouteResponse>();
//Construct the routes request function //Generate the route function header
writer.Write($"public {(routeResponse == null ? "void" : GetTypeFullyResolvedName(routeResponse.ResponseType))} {(routeName == null ? methodInfo.Name : routeName.Name)}("); writer.Write($"public {(routeResponse == null ? "void" : GetTypeFullyResolvedName(routeResponse.ResponseType))} {(routeName == null ? methodInfo.Name : routeName.Name)}(");
//Generate the functions parameters
var parameters = methodInfo.GetParameters(); var parameters = methodInfo.GetParameters();
if (parameters != null) if (parameters != null)
@ -482,9 +568,7 @@ namespace MontoyaTech.Rest.Net
//Add the request content if any. //Add the request content if any.
if (routeRequest != null) if (routeRequest != null)
{ writer.WriteBreak().WriteLine("message.Content = new StringContent(JsonConvert.SerializeObject(request));");
writer.WriteBreak().WriteLine("message.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(request));");
}
//Generate the response code //Generate the response code
writer.WriteBreak().WriteLine("var response = this.Client.HttpClient.Send(message);"); writer.WriteBreak().WriteLine("var response = this.Client.HttpClient.Send(message);");
@ -493,7 +577,7 @@ namespace MontoyaTech.Rest.Net
if (routeResponse != null) if (routeResponse != null)
{ {
writer.WriteBreak().WriteLine("if (response.StatusCode == System.Net.HttpStatusCode.OK)").Indent(); writer.WriteBreak().WriteLine("if (response.StatusCode == System.Net.HttpStatusCode.OK)").Indent();
writer.WriteLine($"return Newtonsoft.Json.JsonConvert.DeserializeObject<{GetTypeFullyResolvedName(routeResponse.ResponseType)}>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult());"); writer.WriteLine($"return JsonConvert.DeserializeObject<{GetTypeFullyResolvedName(routeResponse.ResponseType)}>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult());");
writer.Outdent().WriteLine("else").Indent(); writer.Outdent().WriteLine("else").Indent();
writer.WriteLine(@"throw new Exception(""Unexpected Http Response StatusCode:"" + response.StatusCode);").Outdent(); writer.WriteLine(@"throw new Exception(""Unexpected Http Response StatusCode:"" + response.StatusCode);").Outdent();
} }
@ -503,12 +587,19 @@ namespace MontoyaTech.Rest.Net
writer.WriteLine(@"throw new Exception(""Unexpected Http Response StatusCode:"" + response.StatusCode);").Outdent(); writer.WriteLine(@"throw new Exception(""Unexpected Http Response StatusCode:"" + response.StatusCode);").Outdent();
} }
//Close off the route function.
writer.Outdent().WriteLine("}"); writer.Outdent().WriteLine("}");
} }
public static string GenerateJavascriptClient(IList<Route> routes) /// <summary>
/// Generates a Javascript client from a given set of routes.
/// </summary>
/// <param name="routes"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public static string GenerateJavascriptClient(List<Route> routes)
{ {
return null; throw new NotImplementedException();
} }
} }
} }

View File

@ -17,7 +17,7 @@
<AssemblyName>MontoyaTech.Rest.Net</AssemblyName> <AssemblyName>MontoyaTech.Rest.Net</AssemblyName>
<RootNamespace>MontoyaTech.Rest.Net</RootNamespace> <RootNamespace>MontoyaTech.Rest.Net</RootNamespace>
<GenerateDocumentationFile>True</GenerateDocumentationFile> <GenerateDocumentationFile>True</GenerateDocumentationFile>
<Version>1.1.8</Version> <Version>1.1.9</Version>
<PackageReleaseNotes></PackageReleaseNotes> <PackageReleaseNotes></PackageReleaseNotes>
<PackageIcon>Logo_Symbol_Black_Outline.png</PackageIcon> <PackageIcon>Logo_Symbol_Black_Outline.png</PackageIcon>
</PropertyGroup> </PropertyGroup>

View File

@ -219,5 +219,15 @@ namespace MontoyaTech.Rest.Net
if (!Thread.Yield()) if (!Thread.Yield())
Thread.Sleep(1000); Thread.Sleep(1000);
} }
/// <summary>
/// Generates a C# client from this Route Listener and returns the code.
/// </summary>
/// <param name="name">The name of the Client. Default is Client.</param>
/// <returns></returns>
public string GenerateCSharpClient(string name = "Client")
{
return ClientCodeGenerator.GenerateCSharpClient(this, name);
}
} }
} }