mirror of https://github.com/abpframework/abp
Merge pull request #3192 from abpframework/api-definition
Angular type script client proxy generatorpull/3193/head
commit
ced8c35b11
@ -0,0 +1,731 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Volo.Abp.Cli.Args;
|
||||
using Volo.Abp.Cli.ProjectBuilding;
|
||||
using Volo.Abp.Cli.ProjectBuilding.Building;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
namespace Volo.Abp.Cli.Commands
|
||||
{
|
||||
public class GenerateProxyCommand : IConsoleCommand, ITransientDependency
|
||||
{
|
||||
public static Dictionary<string, Dictionary<string, string>> propertyList = new Dictionary<string, Dictionary<string, string>>();
|
||||
public ILogger<GenerateProxyCommand> Logger { get; set; }
|
||||
|
||||
protected TemplateProjectBuilder TemplateProjectBuilder { get; }
|
||||
|
||||
public GenerateProxyCommand(TemplateProjectBuilder templateProjectBuilder)
|
||||
{
|
||||
TemplateProjectBuilder = templateProjectBuilder;
|
||||
|
||||
Logger = NullLogger<GenerateProxyCommand>.Instance;
|
||||
}
|
||||
|
||||
public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
|
||||
{
|
||||
var angularPath = $"angular.json";
|
||||
if (!File.Exists(angularPath))
|
||||
{
|
||||
throw new CliUsageException(
|
||||
"angular.json file not found. You must run this command in angular folder." +
|
||||
Environment.NewLine + Environment.NewLine +
|
||||
GetUsageInfo()
|
||||
);
|
||||
}
|
||||
|
||||
var module = commandLineArgs.Options.GetOrNull(Options.Module.Short, Options.Module.Long);
|
||||
module = module == null ? "app" : module.ToLower();
|
||||
|
||||
var apiUrl = commandLineArgs.Options.GetOrNull(Options.ApiUrl.Short, Options.ApiUrl.Long);
|
||||
if (string.IsNullOrWhiteSpace(apiUrl))
|
||||
{
|
||||
var environmentJson = File.ReadAllText("src/environments/environment.ts").Split("export const environment = ")[1].Replace(";", " ");
|
||||
var environment = JObject.Parse(environmentJson);
|
||||
apiUrl = environment["apis"]["default"]["url"].ToString();
|
||||
}
|
||||
apiUrl += "/api/abp/api-definition?IncludeTypes=true";
|
||||
|
||||
var uiFramework = GetUiFramework(commandLineArgs);
|
||||
|
||||
WebClient client = new WebClient();
|
||||
string json = client.DownloadString(apiUrl);
|
||||
//var sr = File.OpenText("api-definition.json");
|
||||
//var json = sr.ReadToEnd();
|
||||
|
||||
Logger.LogInformation("Downloading api definition...");
|
||||
Logger.LogInformation("Api Url: " + apiUrl);
|
||||
|
||||
var data = JObject.Parse(json);
|
||||
|
||||
Logger.LogInformation("Modules are combining");
|
||||
var moduleList = GetCombinedModules(data, module);
|
||||
|
||||
if (moduleList.Count < 1)
|
||||
{
|
||||
throw new CliUsageException(
|
||||
"Module can not find!" +
|
||||
Environment.NewLine + Environment.NewLine +
|
||||
GetUsageInfo()
|
||||
);
|
||||
}
|
||||
|
||||
Logger.LogInformation("Modules and types are creating");
|
||||
|
||||
foreach (var moduleItem in moduleList)
|
||||
{
|
||||
var moduleValue = JObject.Parse(moduleItem.Value);
|
||||
|
||||
var rootPath = moduleItem.Key;
|
||||
|
||||
Logger.LogInformation($"{rootPath} directory is creating");
|
||||
|
||||
Directory.CreateDirectory($"src/app/{rootPath}/shared/models");
|
||||
Directory.CreateDirectory($"src/app/{rootPath}/shared/services");
|
||||
|
||||
var serviceIndexList = new List<string>();
|
||||
var modelIndexList = new List<string>();
|
||||
|
||||
foreach (var controller in moduleValue.Root.ToList().Select(item => item.First))
|
||||
{
|
||||
var serviceFileText = new StringBuilder();
|
||||
|
||||
serviceFileText.AppendLine("[firstTypeList]");
|
||||
serviceFileText.AppendLine("import { Injectable } from '@angular/core';");
|
||||
serviceFileText.AppendLine("import { Observable } from 'rxjs';");
|
||||
serviceFileText.AppendLine("[secondTypeList]");
|
||||
serviceFileText.AppendLine("");
|
||||
serviceFileText.AppendLine("@Injectable({providedIn: 'root'})");
|
||||
serviceFileText.AppendLine("export class [controllerName]Service {");
|
||||
serviceFileText.AppendLine(" constructor(private restService: RestService) {}");
|
||||
serviceFileText.AppendLine("");
|
||||
|
||||
var firstTypeList = new List<string>();
|
||||
var secondTypeList = new List<string>();
|
||||
|
||||
var controllerName = (string)controller["controllerName"];
|
||||
var controllerServiceName = controllerName.PascalToKebabCase() + ".service.ts";
|
||||
|
||||
foreach (var actionItem in controller["actions"])
|
||||
{
|
||||
var action = actionItem.First;
|
||||
var actionName = (string)action["uniqueName"];
|
||||
|
||||
actionName = (char.ToLower(actionName[0]) + actionName.Substring(1)).Replace("Async", "").Replace("Controller", "");
|
||||
|
||||
var returnValueType = (string)action["returnValue"]["type"];
|
||||
|
||||
var parameters = action["parameters"];
|
||||
var parametersText = new StringBuilder();
|
||||
var parametersIndex = 0;
|
||||
var bodyExtra = "";
|
||||
var modelBindingExtra = "";
|
||||
var modelBindingExtraList = new List<string>();
|
||||
var parameterModel = new List<ParameterModel>();
|
||||
|
||||
foreach (var parameter in parameters.OrderBy(p => p["bindingSourceId"]))
|
||||
{
|
||||
var bindingSourceId = (string)parameter["bindingSourceId"];
|
||||
bindingSourceId = char.ToLower(bindingSourceId[0]) + bindingSourceId.Substring(1);
|
||||
|
||||
var name = (string)parameter["name"];
|
||||
var typeSimple = (string)parameter["typeSimple"];
|
||||
var typeArray = ((string)parameter["type"]).Split(".");
|
||||
var type = (typeArray[typeArray.Length - 1]).TrimEnd('>');
|
||||
var isOptional = (bool)parameter["isOptional"];
|
||||
var defaultValue = (string)parameter["defaultValue"];
|
||||
|
||||
var modelIndex = CreateType(data, (string)parameter["type"], rootPath, modelIndexList);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(modelIndex))
|
||||
{
|
||||
modelIndexList.Add(modelIndex);
|
||||
}
|
||||
|
||||
if (bindingSourceId == "body")
|
||||
{
|
||||
bodyExtra = ", body";
|
||||
parameterModel = AddParameter(bindingSourceId, type, isOptional, defaultValue, bindingSourceId, parameterModel);
|
||||
}
|
||||
else if (bindingSourceId == "path")
|
||||
{
|
||||
parameterModel = AddParameter(name, typeSimple, isOptional, defaultValue, bindingSourceId, parameterModel);
|
||||
}
|
||||
else if (bindingSourceId == "modelBinding")
|
||||
{
|
||||
var parameterNameOnMethod = (string)parameter["nameOnMethod"];
|
||||
|
||||
var parametersOnMethod = action["parametersOnMethod"];
|
||||
foreach (var parameterOnMethod in parametersOnMethod)
|
||||
{
|
||||
var parametersOnMethodName = (string)parameterOnMethod["name"];
|
||||
if (parametersOnMethodName == parameterNameOnMethod)
|
||||
{
|
||||
typeSimple = (string)parameterOnMethod["typeSimple"];
|
||||
typeArray = ((string)parameterOnMethod["type"]).Split(".");
|
||||
type = typeArray[typeArray.Length - 1];
|
||||
isOptional = (bool)parameterOnMethod["isOptional"];
|
||||
defaultValue = (string)parameterOnMethod["defaultValue"];
|
||||
|
||||
if (typeSimple == "string" || typeSimple == "boolean" || typeSimple == "number")
|
||||
{
|
||||
parameterModel = AddParameter(name, typeSimple, isOptional, defaultValue, bindingSourceId, parameterModel);
|
||||
}
|
||||
|
||||
modelIndex = CreateType(data, (string)parameterOnMethod["type"], rootPath, modelIndexList);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(modelIndex))
|
||||
{
|
||||
modelIndexList.Add(modelIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeSimple != "string" && typeSimple != "boolean" && typeSimple != "number")
|
||||
{
|
||||
parametersText.Append($"params = {{}} as {type}");
|
||||
modelBindingExtra = ", params";
|
||||
if (!string.IsNullOrWhiteSpace(modelIndex))
|
||||
{
|
||||
secondTypeList.Add(type);
|
||||
}
|
||||
else {
|
||||
firstTypeList.Add(type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parameterModel != null && parameterModel.Count > 0)
|
||||
{
|
||||
foreach (var parameterItem in parameterModel.OrderBy(p => p.DisplayOrder))
|
||||
{
|
||||
var parameterItemModelName = parameterItem.Type.PascalToKebabCase() + ".ts";
|
||||
var parameterItemModelPath = $"src/app/{rootPath}/shared/models/{parameterItemModelName}";
|
||||
if (parameterItem.BindingSourceId == "body" && !File.Exists(parameterItemModelPath))
|
||||
{
|
||||
parameterItem.Type = "any";
|
||||
}
|
||||
|
||||
parametersIndex++;
|
||||
|
||||
if (parametersIndex > 1)
|
||||
{
|
||||
parametersText.Append(", ");
|
||||
}
|
||||
|
||||
parametersText.Append(parameterItem.Name + (parameterItem.IsOptional ? "?" : "") + ": " + parameterItem.Type + (parameterItem.Value != null ? (" = " + (string.IsNullOrWhiteSpace(parameterItem.Value) ? "''" : parameterItem.Value)) : ""));
|
||||
|
||||
if (parameterItem.BindingSourceId == "modelBinding")
|
||||
{
|
||||
modelBindingExtraList.Add(parameterItem.Name);
|
||||
}
|
||||
else if (parameterItem.BindingSourceId == "body" && File.Exists(parameterItemModelPath))
|
||||
{
|
||||
secondTypeList.Add(parameterItem.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (returnValueType != null)
|
||||
{
|
||||
if (returnValueType.IndexOf('<') > -1)
|
||||
{
|
||||
var firstTypeArray = returnValueType.Split("<")[0].Split(".");
|
||||
var firstType = firstTypeArray[firstTypeArray.Length - 1];
|
||||
|
||||
var secondTypeArray = returnValueType.Split("<")[1].Split(".");
|
||||
var secondType = secondTypeArray[secondTypeArray.Length - 1].TrimEnd('>');
|
||||
|
||||
var secondTypeModelName = secondType.PascalToKebabCase() + ".ts";
|
||||
var secondTypeModelPath = $"src/app/{rootPath}/shared/models/{secondTypeModelName}";
|
||||
if (firstType == "List" && !File.Exists(secondTypeModelPath))
|
||||
{
|
||||
secondType = "any";
|
||||
}
|
||||
|
||||
serviceFileText.AppendLine(
|
||||
firstType == "List"
|
||||
? $" {actionName}({parametersText}): Observable<{secondType}[]> {{"
|
||||
: $" {actionName}({parametersText}): Observable<{firstType}<{secondType}>> {{");
|
||||
|
||||
if (firstType != "List")
|
||||
{
|
||||
firstTypeList.Add(firstType);
|
||||
}
|
||||
|
||||
if (secondType != "any")
|
||||
{
|
||||
secondTypeList.Add(secondType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var typeArray = returnValueType.Split(".");
|
||||
var type = typeArray[typeArray.Length - 1].TrimEnd('>');
|
||||
|
||||
type = type switch
|
||||
{
|
||||
"Void" => "void",
|
||||
"String" => "string",
|
||||
"IActionResult" => "void",
|
||||
"ActionResult" => "void",
|
||||
_ => type
|
||||
};
|
||||
|
||||
serviceFileText.AppendLine(
|
||||
$" {actionName}({parametersText}): Observable<{type}> {{");
|
||||
|
||||
if (type != "void" && type != "string")
|
||||
{
|
||||
secondTypeList.Add(type);
|
||||
}
|
||||
}
|
||||
|
||||
var modelIndex = CreateType(data, returnValueType, rootPath, modelIndexList);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(modelIndex))
|
||||
{
|
||||
modelIndexList.Add(modelIndex);
|
||||
}
|
||||
}
|
||||
|
||||
if (modelBindingExtraList != null && modelBindingExtraList.Count > 0)
|
||||
{
|
||||
modelBindingExtra = ", params: { " + string.Join(", ", modelBindingExtraList.ToArray()) + " }";
|
||||
}
|
||||
|
||||
var url = ((string)action["url"]).Replace("/{", "/${");
|
||||
var httpMethod = (string)action["httpMethod"];
|
||||
|
||||
serviceFileText.AppendLine(
|
||||
url.Contains("${")
|
||||
? $" return this.restService.request({{ url: `/{url}`, method: '{httpMethod}'{bodyExtra}{modelBindingExtra} }});"
|
||||
: $" return this.restService.request({{ url: '/{url}', method: '{httpMethod}'{bodyExtra}{modelBindingExtra} }});");
|
||||
|
||||
|
||||
serviceFileText.AppendLine(" }");
|
||||
}
|
||||
|
||||
serviceIndexList.Add(controllerServiceName.Replace(".ts", ""));
|
||||
|
||||
if (firstTypeList != null && firstTypeList.Count > 0)
|
||||
{
|
||||
var firstTypeListDistinct = ", " + string.Join(", ", firstTypeList.Where(p => p != "void").Distinct().ToArray());
|
||||
serviceFileText.Replace("[firstTypeList]",
|
||||
$"import {{ RestService {firstTypeListDistinct}}} from '@abp/ng.core';");
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceFileText.Replace("[firstTypeList]",
|
||||
$"import {{ RestService }} from '@abp/ng.core';");
|
||||
}
|
||||
|
||||
if (secondTypeList != null && secondTypeList.Count > 0)
|
||||
{
|
||||
var secondTypeListDistinct = string.Join(", ", secondTypeList.Where(p => p != "void").Distinct().ToArray());
|
||||
serviceFileText.Replace("[secondTypeList]",
|
||||
$"import {{{secondTypeListDistinct}}} from '../models';");
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceFileText.Replace("[secondTypeList]", "");
|
||||
}
|
||||
|
||||
serviceFileText.AppendLine("}");
|
||||
|
||||
serviceFileText.Replace("[controllerName]", controllerName);
|
||||
File.WriteAllText($"src/app/{rootPath}/shared/services/{controllerServiceName}", serviceFileText.ToString());
|
||||
}
|
||||
|
||||
var serviceIndexFileText = new StringBuilder();
|
||||
|
||||
foreach (var serviceIndexItem in serviceIndexList.Distinct())
|
||||
{
|
||||
serviceIndexFileText.AppendLine($"export * from './{serviceIndexItem}';");
|
||||
}
|
||||
|
||||
File.WriteAllText($"src/app/{rootPath}/shared/services/index.ts", serviceIndexFileText.ToString());
|
||||
|
||||
var modelIndexFileText = new StringBuilder();
|
||||
|
||||
foreach (var modelIndexItem in modelIndexList.Distinct())
|
||||
{
|
||||
modelIndexFileText.AppendLine($"export * from './{modelIndexItem}';");
|
||||
}
|
||||
|
||||
File.WriteAllText($"src/app/{rootPath}/shared/models/index.ts", modelIndexFileText.ToString());
|
||||
}
|
||||
|
||||
Logger.LogInformation("Completed!");
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetCombinedModules(JToken data, string module)
|
||||
{
|
||||
var moduleList = new Dictionary<string, string>();
|
||||
|
||||
foreach (var moduleItem in data["modules"])
|
||||
{
|
||||
var rootPath = ((string)moduleItem.First["rootPath"]).ToLower();
|
||||
|
||||
if (moduleList.Any(p => p.Key == rootPath))
|
||||
{
|
||||
var value = moduleList[rootPath];
|
||||
|
||||
moduleList[rootPath] = value.TrimEnd('}') + "," + moduleItem.First["controllers"].ToString().TrimStart('{');
|
||||
}
|
||||
else
|
||||
{
|
||||
moduleList.Add(rootPath, moduleItem.First["controllers"].ToString());
|
||||
}
|
||||
}
|
||||
|
||||
if (module != "all")
|
||||
{
|
||||
moduleList = moduleList.Where(p => p.Key.ToLower() == module).ToDictionary(p => p.Key, s => s.Value);
|
||||
}
|
||||
|
||||
return moduleList;
|
||||
}
|
||||
|
||||
private static string CreateType(JObject data, string returnValueType, string rootPath, List<string> modelIndexList)
|
||||
{
|
||||
var type = data["types"][returnValueType];
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (returnValueType.StartsWith("Volo.Abp.Application.Dtos")
|
||||
|| returnValueType.StartsWith("System.Collections")
|
||||
|| returnValueType == "System.String"
|
||||
|| returnValueType == "System.Void"
|
||||
|| returnValueType.Contains("System.Net.HttpStatusCode?")
|
||||
|| returnValueType.Contains("IActionResult")
|
||||
|| returnValueType.Contains("ActionResult")
|
||||
|| returnValueType.Contains("IStringValueType")
|
||||
|| returnValueType.Contains("IValueValidator")
|
||||
)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var typeNameSplit = returnValueType.Split(".");
|
||||
var typeName = typeNameSplit[typeNameSplit.Length - 1];
|
||||
|
||||
if (typeName.Contains("HttpStatusCode"))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
var typeModelName = typeName.Replace("<", "").Replace(">", "").Replace("?","").PascalToKebabCase() + ".ts";
|
||||
|
||||
var path = $"src/app/{rootPath}/shared/models/{typeModelName}";
|
||||
|
||||
var modelFileText = new StringBuilder();
|
||||
|
||||
var baseType = (string)type["baseType"];
|
||||
var extends = "";
|
||||
var customBaseTypeName = "";
|
||||
var baseTypeName = "";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(baseType))
|
||||
{
|
||||
var baseTypeSplit = baseType.Split(".");
|
||||
baseTypeName = baseTypeSplit[baseTypeSplit.Length - 1].Replace("<", "").Replace(">", "");
|
||||
var baseTypeKebabCase = "./" + baseTypeName.PascalToKebabCase();
|
||||
|
||||
if (baseType != "System.Enum")
|
||||
{
|
||||
if (baseType.Contains("Volo.Abp.Application.Dtos"))
|
||||
{
|
||||
baseTypeKebabCase = "@abp/ng.core";
|
||||
|
||||
baseTypeName = baseType.Split("Volo.Abp.Application.Dtos")[1].Split("<")[0].TrimStart('.');
|
||||
customBaseTypeName = baseType.Split("Volo.Abp.Application.Dtos")[1].Replace("System.Guid", "string").TrimStart('.');
|
||||
}
|
||||
|
||||
if (baseTypeName.Contains("guid") || baseTypeName.Contains("Guid"))
|
||||
{
|
||||
baseTypeName = "string";
|
||||
}
|
||||
|
||||
modelFileText.AppendLine($"import {{ {baseTypeName} }} from '{baseTypeKebabCase}';");
|
||||
extends = "extends " + (!string.IsNullOrWhiteSpace(customBaseTypeName) ? customBaseTypeName : baseTypeName);
|
||||
|
||||
var modelIndex = CreateType(data, baseType, rootPath, modelIndexList);
|
||||
if (!string.IsNullOrWhiteSpace(modelIndex))
|
||||
{
|
||||
modelIndexList.Add(modelIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (baseType == "System.Enum" && (bool)type["isEnum"])
|
||||
{
|
||||
modelFileText.AppendLine($"export enum {typeName} {{");
|
||||
|
||||
var enumNameList = type["enumNames"].ToArray();
|
||||
var enumValueList = type["enumValues"].ToArray();
|
||||
|
||||
for (var i = 0; i < enumNameList.Length; i++)
|
||||
{
|
||||
modelFileText.AppendLine($" {enumNameList[i]} = {enumValueList[i]},");
|
||||
}
|
||||
|
||||
modelFileText.AppendLine("}");
|
||||
}
|
||||
else
|
||||
{
|
||||
modelFileText.AppendLine("");
|
||||
modelFileText.AppendLine($"export class {typeName} {extends} {{");
|
||||
|
||||
foreach (var property in type["properties"])
|
||||
{
|
||||
var propertyName = (string)property["name"];
|
||||
propertyName = (char.ToLower(propertyName[0]) + propertyName.Substring(1));
|
||||
var typeSimple = (string)property["typeSimple"];
|
||||
|
||||
var modelIndex = CreateType(data, (string)property["type"], rootPath, modelIndexList);
|
||||
|
||||
if (typeSimple.IndexOf("[") > -1 && typeSimple.IndexOf("]") > -1)
|
||||
{
|
||||
typeSimple = typeSimple.Replace("[", "").Replace("]", "") + "[]";
|
||||
}
|
||||
|
||||
if (typeSimple.StartsWith("Volo.Abp"))
|
||||
{
|
||||
var typeSimpleSplit = typeSimple.Split(".");
|
||||
typeSimple = typeSimpleSplit[typeSimpleSplit.Length - 1];
|
||||
}
|
||||
|
||||
if (typeSimple.StartsWith("System.Object"))
|
||||
{
|
||||
typeSimple = typeSimple.Replace("System.Object", "object");
|
||||
}
|
||||
|
||||
if (typeSimple.Contains("?"))
|
||||
{
|
||||
typeSimple = typeSimple.Replace("?", "");
|
||||
propertyName += "?";
|
||||
}
|
||||
|
||||
if (typeSimple != "boolean"
|
||||
&& typeSimple != "string"
|
||||
&& typeSimple != "number"
|
||||
&& typeSimple != "boolean[]"
|
||||
&& typeSimple != "string[]"
|
||||
&& typeSimple != "number[]"
|
||||
)
|
||||
{
|
||||
var typeSimpleModelName = typeSimple.PascalToKebabCase() + ".ts";
|
||||
var modelPath = $"src/app/{rootPath}/shared/models/{typeSimpleModelName}";
|
||||
if (!File.Exists(modelPath))
|
||||
{
|
||||
typeSimple = "any" + (typeSimple.Contains("[]") ? "[]" : "");
|
||||
}
|
||||
}
|
||||
|
||||
if (propertyList.Any(p => p.Key == baseTypeName && p.Value.Any(q => q.Key == propertyName && q.Value == typeSimple)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(modelIndex))
|
||||
{
|
||||
var propertyTypeSplit = ((string)property["type"]).Split(".");
|
||||
var propertyType = propertyTypeSplit[propertyTypeSplit.Length - 1];
|
||||
modelFileText.Insert(0, "");
|
||||
modelFileText.Insert(0, $"import {{ {propertyType} }} from '../models';");
|
||||
modelFileText.Insert(0, "");
|
||||
modelIndexList.Add(modelIndex);
|
||||
}
|
||||
|
||||
if (propertyList.Any(p => p.Key == typeName && !p.Value.Any(q => q.Key == propertyName)))
|
||||
{
|
||||
propertyList[typeName].Add(propertyName, typeSimple);
|
||||
}
|
||||
else if (!propertyList.Any(p => p.Key == typeName))
|
||||
{
|
||||
var newProperty = new Dictionary<string, string>();
|
||||
newProperty.Add(propertyName, typeSimple);
|
||||
propertyList.Add(typeName, newProperty);
|
||||
}
|
||||
|
||||
modelFileText.AppendLine($" {propertyName}: {typeSimple};");
|
||||
}
|
||||
|
||||
modelFileText.AppendLine("");
|
||||
|
||||
modelFileText.AppendLine($" constructor(initialValues: Partial<{typeName}> = {{}}) {{");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(baseType))
|
||||
{
|
||||
modelFileText.AppendLine(" super(initialValues);");
|
||||
modelFileText.AppendLine(" }");
|
||||
}
|
||||
else
|
||||
{
|
||||
modelFileText.AppendLine(" if (initialValues) {");
|
||||
modelFileText.AppendLine(" for (const key in initialValues) {");
|
||||
modelFileText.AppendLine(" if (initialValues.hasOwnProperty(key)) {");
|
||||
modelFileText.AppendLine(" this[key] = initialValues[key];");
|
||||
|
||||
modelFileText.AppendLine(" }");
|
||||
modelFileText.AppendLine(" }");
|
||||
modelFileText.AppendLine(" }");
|
||||
modelFileText.AppendLine(" }");
|
||||
}
|
||||
|
||||
modelFileText.AppendLine("}");
|
||||
}
|
||||
|
||||
File.WriteAllText($"src/app/{rootPath}/shared/models/{typeModelName}", modelFileText.ToString());
|
||||
|
||||
return typeModelName.Replace(".ts", "");
|
||||
}
|
||||
|
||||
private static List<ParameterModel> AddParameter(string parameterName, string type, bool parameterIsOptional, string parameterDefaultValue, string bindingSourceId, List<ParameterModel> parameterModel)
|
||||
{
|
||||
if (parameterDefaultValue != "null")
|
||||
{
|
||||
parameterModel.Add(new ParameterModel
|
||||
{
|
||||
DisplayOrder = 3,
|
||||
Name = parameterName,
|
||||
Type = type,
|
||||
Value = parameterDefaultValue,
|
||||
BindingSourceId = bindingSourceId
|
||||
});
|
||||
}
|
||||
else if (parameterDefaultValue == "null" && parameterIsOptional)
|
||||
{
|
||||
parameterModel.Add(new ParameterModel
|
||||
{
|
||||
DisplayOrder = 2,
|
||||
Name = parameterName,
|
||||
IsOptional = true,
|
||||
Type = type,
|
||||
BindingSourceId = bindingSourceId
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
parameterModel.Add(new ParameterModel
|
||||
{
|
||||
DisplayOrder = 1,
|
||||
Name = parameterName,
|
||||
Type = type,
|
||||
BindingSourceId = bindingSourceId
|
||||
});
|
||||
}
|
||||
|
||||
return parameterModel;
|
||||
}
|
||||
|
||||
public string GetUsageInfo()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine("");
|
||||
sb.AppendLine("Usage:");
|
||||
sb.AppendLine("");
|
||||
sb.AppendLine(" abp generate-proxy [options]");
|
||||
sb.AppendLine("");
|
||||
sb.AppendLine("Options:");
|
||||
sb.AppendLine("");
|
||||
sb.AppendLine("-a|--apiUrl <api-url> (default: environment.ts>apis>default>url)");
|
||||
sb.AppendLine("-u|--ui <ui-framework> (default: angular)");
|
||||
sb.AppendLine("-m|--module <module> (default: app)");
|
||||
sb.AppendLine("");
|
||||
sb.AppendLine("Examples:");
|
||||
sb.AppendLine("");
|
||||
sb.AppendLine(" abp generate-proxy --apiUrl https://www.volosoft.com");
|
||||
sb.AppendLine("");
|
||||
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string GetShortDescription()
|
||||
{
|
||||
return "Generates typescript service proxies and DTOs";
|
||||
}
|
||||
|
||||
private UiFramework GetUiFramework(CommandLineArgs commandLineArgs)
|
||||
{
|
||||
var optionValue = commandLineArgs.Options.GetOrNull(Options.UiFramework.Short, Options.UiFramework.Long);
|
||||
switch (optionValue)
|
||||
{
|
||||
case "none":
|
||||
return UiFramework.None;
|
||||
case "mvc":
|
||||
return UiFramework.Mvc;
|
||||
case "angular":
|
||||
return UiFramework.Angular;
|
||||
default:
|
||||
return UiFramework.Angular;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Options
|
||||
{
|
||||
public static class Module
|
||||
{
|
||||
public const string Short = "m";
|
||||
public const string Long = "module";
|
||||
}
|
||||
|
||||
public static class ApiUrl
|
||||
{
|
||||
public const string Short = "a";
|
||||
public const string Long = "apiUrl";
|
||||
}
|
||||
|
||||
public static class UiFramework
|
||||
{
|
||||
public const string Short = "u";
|
||||
public const string Long = "ui";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static string PascalToKebabCase(this string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return Regex.Replace(
|
||||
value,
|
||||
"(?<!^)([A-Z][a-z]|(?<=[a-z])[A-Z])",
|
||||
"-$1",
|
||||
RegexOptions.Compiled)
|
||||
.Trim()
|
||||
.ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
public class ParameterModel
|
||||
{
|
||||
public int DisplayOrder { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
public string Type { get; set; }
|
||||
public bool IsOptional { get; set; }
|
||||
public string BindingSourceId { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Volo.Abp.Reflection;
|
||||
|
||||
namespace Volo.Abp.Http.Modeling
|
||||
{
|
||||
[Serializable]
|
||||
public class PropertyApiDescriptionModel
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
|
||||
public string TypeSimple { get; set; }
|
||||
|
||||
//TODO: Validation rules for this property
|
||||
public static PropertyApiDescriptionModel Create(PropertyInfo propertyInfo)
|
||||
{
|
||||
string typeName;
|
||||
string simpleTypeName;
|
||||
|
||||
if (TypeHelper.IsEnumerable(propertyInfo.PropertyType, out var itemType, includePrimitives: false))
|
||||
{
|
||||
typeName = $"[{ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(itemType)}]";
|
||||
simpleTypeName = $"[{ModelingTypeHelper.GetSimplifiedName(itemType)}]";
|
||||
}
|
||||
else if (TypeHelper.IsDictionary(propertyInfo.PropertyType, out var keyType, out var valueType))
|
||||
{
|
||||
typeName = $"{{{ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(keyType)}:{ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(valueType)}}}";
|
||||
simpleTypeName = $"{{{ModelingTypeHelper.GetSimplifiedName(keyType)}:{ModelingTypeHelper.GetSimplifiedName(valueType)}}}";
|
||||
}
|
||||
else
|
||||
{
|
||||
typeName = ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(propertyInfo.PropertyType);
|
||||
simpleTypeName = ModelingTypeHelper.GetSimplifiedName(propertyInfo.PropertyType);
|
||||
}
|
||||
|
||||
return new PropertyApiDescriptionModel
|
||||
{
|
||||
Name = propertyInfo.Name,
|
||||
Type = typeName,
|
||||
TypeSimple = simpleTypeName
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Volo.Abp.Http.Modeling
|
||||
{
|
||||
[Serializable]
|
||||
public class TypeApiDescriptionModel
|
||||
{
|
||||
public string BaseType { get; set; }
|
||||
|
||||
public bool IsEnum { get; set; }
|
||||
|
||||
public string[] EnumNames { get; set; }
|
||||
|
||||
public object[] EnumValues { get; set; }
|
||||
|
||||
public PropertyApiDescriptionModel[] Properties { get; set; }
|
||||
|
||||
private TypeApiDescriptionModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static TypeApiDescriptionModel Create(Type type)
|
||||
{
|
||||
var baseType = type.BaseType;
|
||||
if (baseType == typeof(object))
|
||||
{
|
||||
baseType = null;
|
||||
}
|
||||
|
||||
var typeModel = new TypeApiDescriptionModel
|
||||
{
|
||||
IsEnum = type.IsEnum,
|
||||
BaseType = baseType != null ? ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(baseType) : null
|
||||
};
|
||||
|
||||
if (typeModel.IsEnum)
|
||||
{
|
||||
typeModel.EnumNames = type.GetEnumNames();
|
||||
typeModel.EnumValues = type.GetEnumValues().Cast<object>().ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
typeModel.Properties = type
|
||||
.GetProperties()
|
||||
.Select(PropertyApiDescriptionModel.Create)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
return typeModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,124 @@
|
||||
export class ListResultDto<T> {
|
||||
items?: T[];
|
||||
|
||||
constructor(initialValues: Partial<ListResultDto<T>> = {}) {
|
||||
for (const key in initialValues) {
|
||||
if (initialValues.hasOwnProperty(key)) {
|
||||
this[key] = initialValues[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class PagedResultDto<T> extends ListResultDto<T> {
|
||||
totalCount?: number;
|
||||
|
||||
constructor(initialValues: Partial<PagedResultDto<T>> = {}) {
|
||||
super(initialValues);
|
||||
}
|
||||
}
|
||||
|
||||
export class LimitedResultRequestDto {
|
||||
maxResultCount = 10;
|
||||
|
||||
constructor(initialValues: Partial<LimitedResultRequestDto> = {}) {
|
||||
for (const key in initialValues) {
|
||||
if (initialValues.hasOwnProperty(key)) {
|
||||
this[key] = initialValues[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class PagedResultRequestDto extends LimitedResultRequestDto {
|
||||
skipCount?: number;
|
||||
|
||||
constructor(initialValues: Partial<PagedResultRequestDto> = {}) {
|
||||
super(initialValues);
|
||||
}
|
||||
}
|
||||
|
||||
export class PagedAndSortedResultRequestDto extends PagedResultRequestDto {
|
||||
sorting?: string;
|
||||
|
||||
constructor(initialValues: Partial<PagedAndSortedResultRequestDto> = {}) {
|
||||
super(initialValues);
|
||||
}
|
||||
}
|
||||
|
||||
export class EntityDto<TKey = string> {
|
||||
id?: TKey;
|
||||
|
||||
constructor(initialValues: Partial<EntityDto<TKey>> = {}) {
|
||||
for (const key in initialValues) {
|
||||
if (initialValues.hasOwnProperty(key)) {
|
||||
this[key] = initialValues[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CreationAuditedEntityDto<TPrimaryKey = string> extends EntityDto<TPrimaryKey> {
|
||||
creationTime?: string | Date;
|
||||
creatorId?: string;
|
||||
|
||||
constructor(initialValues: Partial<CreationAuditedEntityDto<TPrimaryKey>> = {}) {
|
||||
super(initialValues);
|
||||
}
|
||||
}
|
||||
|
||||
export class CreationAuditedEntityWithUserDto<
|
||||
TUserDto,
|
||||
TPrimaryKey = string
|
||||
> extends CreationAuditedEntityDto<TPrimaryKey> {
|
||||
creator?: TUserDto;
|
||||
|
||||
constructor(
|
||||
initialValues: Partial<CreationAuditedEntityWithUserDto<TUserDto, TPrimaryKey>> = {},
|
||||
) {
|
||||
super(initialValues);
|
||||
}
|
||||
}
|
||||
|
||||
export class AuditedEntityDto<TPrimaryKey = string> extends CreationAuditedEntityDto<TPrimaryKey> {
|
||||
lastModificationTime?: string | Date;
|
||||
lastModifierId?: string;
|
||||
|
||||
constructor(initialValues: Partial<AuditedEntityDto<TPrimaryKey>> = {}) {
|
||||
super(initialValues);
|
||||
}
|
||||
}
|
||||
|
||||
export class AuditedEntityWithUserDto<TUserDto, TPrimaryKey = string> extends AuditedEntityDto<
|
||||
TPrimaryKey
|
||||
> {
|
||||
creator?: TUserDto;
|
||||
lastModifier?: TUserDto;
|
||||
|
||||
constructor(initialValues: Partial<AuditedEntityWithUserDto<TUserDto, TPrimaryKey>> = {}) {
|
||||
super(initialValues);
|
||||
}
|
||||
}
|
||||
|
||||
export class FullAuditedEntityDto<TPrimaryKey = string> extends AuditedEntityDto<TPrimaryKey> {
|
||||
isDeleted?: boolean;
|
||||
deleterId?: string;
|
||||
deletionTime?: Date | string;
|
||||
|
||||
constructor(initialValues: Partial<FullAuditedEntityDto<TPrimaryKey>> = {}) {
|
||||
super(initialValues);
|
||||
}
|
||||
}
|
||||
|
||||
export class FullAuditedEntityWithUserDto<
|
||||
TUserDto,
|
||||
TPrimaryKey = string
|
||||
> extends FullAuditedEntityDto<TPrimaryKey> {
|
||||
creator?: TUserDto;
|
||||
lastModifier?: TUserDto;
|
||||
deleter?: TUserDto;
|
||||
|
||||
constructor(initialValues: Partial<FullAuditedEntityWithUserDto<TUserDto, TPrimaryKey>> = {}) {
|
||||
super(initialValues);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
export * from './application-configuration';
|
||||
export * from './common';
|
||||
export * from './config';
|
||||
export * from './dtos';
|
||||
export * from './profile';
|
||||
export * from './replaceable-components';
|
||||
export * from './replaceable-components';
|
||||
export * from './rest';
|
||||
export * from './session';
|
||||
|
||||
Loading…
Reference in new issue