committed by
GitHub
29 changed files with 1081 additions and 314 deletions
@ -0,0 +1,146 @@ |
|||
using LINGYUN.Abp.Cli.ServiceProxying; |
|||
using LINGYUN.Abp.Cli.ServiceProxying.CSharp; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System.IO; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Cli.Args; |
|||
using Volo.Abp.Cli.Commands; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
using VoloGenerateProxyArgs = Volo.Abp.Cli.ServiceProxying.GenerateProxyArgs; |
|||
|
|||
namespace LINGYUN.Abp.Cli.Commands; |
|||
|
|||
public class GenerateProxyCommand : IConsoleCommand, ITransientDependency |
|||
{ |
|||
public const string Name = "generate-proxy"; |
|||
|
|||
protected string CommandName => Name; |
|||
|
|||
protected IServiceScopeFactory ServiceScopeFactory { get; } |
|||
|
|||
public GenerateProxyCommand( |
|||
IServiceScopeFactory serviceScopeFactory) |
|||
{ |
|||
ServiceScopeFactory = serviceScopeFactory; |
|||
} |
|||
|
|||
public async Task ExecuteAsync(CommandLineArgs commandLineArgs) |
|||
{ |
|||
using (var scope = ServiceScopeFactory.CreateScope()) |
|||
{ |
|||
var serviceProxyGenerator = scope.ServiceProvider.GetRequiredService<CSharpServiceProxyGenerator>(); |
|||
|
|||
await serviceProxyGenerator.GenerateProxyAsync(BuildArgs(commandLineArgs)); |
|||
} |
|||
} |
|||
|
|||
private VoloGenerateProxyArgs BuildArgs(CommandLineArgs commandLineArgs) |
|||
{ |
|||
var provider = commandLineArgs.Options.GetOrNull(Options.Provider.Short, Options.Provider.Long); |
|||
var url = commandLineArgs.Options.GetOrNull(Options.Url.Short, Options.Url.Long); |
|||
var target = commandLineArgs.Options.GetOrNull(Options.Target.Long); |
|||
var module = commandLineArgs.Options.GetOrNull(Options.Module.Short, Options.Module.Long) ?? "app"; |
|||
var output = commandLineArgs.Options.GetOrNull(Options.Output.Short, Options.Output.Long); |
|||
var apiName = commandLineArgs.Options.GetOrNull(Options.ApiName.Short, Options.ApiName.Long); |
|||
var source = commandLineArgs.Options.GetOrNull(Options.Source.Short, Options.Source.Long); |
|||
var workDirectory = commandLineArgs.Options.GetOrNull(Options.WorkDirectory.Short, Options.WorkDirectory.Long) ?? Directory.GetCurrentDirectory(); |
|||
var folder = commandLineArgs.Options.GetOrNull(Options.Folder.Long); |
|||
|
|||
return new GenerateProxyArgs(CommandName, workDirectory, module, url, output, target, apiName, source, folder, provider, commandLineArgs.Options); |
|||
} |
|||
|
|||
public string GetUsageInfo() |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
|
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Usage:"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine($" labp {CommandName}"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Options:"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("-m|--module <module-name> (default: 'app') The name of the backend module you wish to generate proxies for."); |
|||
sb.AppendLine("-wd|--working-directory <directory-path> Execution directory."); |
|||
sb.AppendLine("-u|--url <url> API definition URL from."); |
|||
sb.AppendLine("-p|--provider <client-proxy-provider> The client proxy provider(http, dapr)."); |
|||
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); |
|||
|
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Examples:"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine(" labp generate-proxy"); |
|||
sb.AppendLine(" labp generate-proxy -p dapr"); |
|||
sb.AppendLine(" labp generate-proxy -m identity -o Pages/Identity/client-proxies.js -url https://localhost:44302/"); |
|||
sb.AppendLine(" labp generate-proxy --folder MyProxies/InnerFolder -url https://localhost:44302/"); |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public string GetShortDescription() |
|||
{ |
|||
return "Generates client service proxies and DTOs to consume HTTP APIs."; |
|||
} |
|||
|
|||
public static class Options |
|||
{ |
|||
public static class Provider |
|||
{ |
|||
public const string Short = "p"; |
|||
public const string Long = "provider"; |
|||
} |
|||
|
|||
public static class Module |
|||
{ |
|||
public const string Short = "m"; |
|||
public const string Long = "module"; |
|||
} |
|||
|
|||
public static class ApiName |
|||
{ |
|||
public const string Short = "a"; |
|||
public const string Long = "api-name"; |
|||
} |
|||
|
|||
public static class Source |
|||
{ |
|||
public const string Short = "s"; |
|||
public const string Long = "source"; |
|||
} |
|||
public static class Output |
|||
{ |
|||
public const string Short = "o"; |
|||
public const string Long = "output"; |
|||
} |
|||
|
|||
public static class Target |
|||
{ |
|||
public const string Long = "target"; |
|||
} |
|||
|
|||
public static class Prompt |
|||
{ |
|||
public const string Short = "p"; |
|||
public const string Long = "prompt"; |
|||
} |
|||
|
|||
public static class Folder |
|||
{ |
|||
public const string Long = "folder"; |
|||
} |
|||
|
|||
public static class Url |
|||
{ |
|||
public const string Short = "u"; |
|||
public const string Long = "url"; |
|||
} |
|||
|
|||
public static class WorkDirectory |
|||
{ |
|||
public const string Short = "wd"; |
|||
public const string Long = "working-directory"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,422 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Cli; |
|||
using Volo.Abp.Cli.Commands; |
|||
using Volo.Abp.Cli.Http; |
|||
using Volo.Abp.Cli.ServiceProxying; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Http.Modeling; |
|||
using Volo.Abp.Json; |
|||
|
|||
using VoloGenerateProxyArgs = Volo.Abp.Cli.ServiceProxying.GenerateProxyArgs; |
|||
|
|||
namespace LINGYUN.Abp.Cli.ServiceProxying.CSharp; |
|||
|
|||
[Dependency(ReplaceServices = true)] |
|||
[ExposeServices(typeof(IServiceProxyGenerator), typeof(CSharpServiceProxyGenerator))] |
|||
public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServiceProxyGenerator>, ITransientDependency |
|||
{ |
|||
public const string Name = "CSHARP"; |
|||
|
|||
private const string UsingPlaceholder = "<using placeholder>"; |
|||
private const string MethodPlaceholder = "<method placeholder>"; |
|||
private const string ClassName = "<className>"; |
|||
private const string ServiceInterface = "<serviceInterface>"; |
|||
private readonly static string[] ServicePostfixes = { "AppService", "ApplicationService" , "Service"}; |
|||
private const string DefaultNamespace = "ClientProxies"; |
|||
private const string Namespace = "<namespace>"; |
|||
private const string DefaultProvider = "ClientProxyBase"; |
|||
private const string Provider = "<provider>"; |
|||
private const string AppServicePrefix = "Volo.Abp.Application.Services"; |
|||
private readonly string _clientProxyGeneratedTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" + |
|||
$"{Environment.NewLine}<using placeholder>" + |
|||
$"{Environment.NewLine}" + |
|||
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" + |
|||
$"{Environment.NewLine}namespace <namespace>;" + |
|||
$"{Environment.NewLine}" + |
|||
$"{Environment.NewLine}[Dependency(ReplaceServices = true)]" + |
|||
$"{Environment.NewLine}[ExposeServices(typeof(<serviceInterface>), typeof(<className>))]" + |
|||
$"{Environment.NewLine}public partial class <className> : <provider><<serviceInterface>>, <serviceInterface>" + |
|||
$"{Environment.NewLine}{{" + |
|||
$"{Environment.NewLine} <method placeholder>" + |
|||
$"{Environment.NewLine}}}" + |
|||
$"{Environment.NewLine}"; |
|||
private readonly string _clientProxyTemplate = "// This file is part of <className>, you can customize it here" + |
|||
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" + |
|||
$"{Environment.NewLine}namespace <namespace>;" + |
|||
$"{Environment.NewLine}" + |
|||
$"{Environment.NewLine}public partial class <className>" + |
|||
$"{Environment.NewLine}{{" + |
|||
$"{Environment.NewLine}}}" + |
|||
$"{Environment.NewLine}"; |
|||
private readonly List<string> _usingNamespaceList = new() |
|||
{ |
|||
"using System;", |
|||
"using System.Threading.Tasks;", |
|||
"using Volo.Abp.Application.Dtos;", |
|||
"using Volo.Abp.Http.Client;", |
|||
"using Volo.Abp.Http.Modeling;", |
|||
"using Volo.Abp.DependencyInjection;", |
|||
"using Volo.Abp.Http.Client.ClientProxying;", |
|||
"using LINGYUN.Abp.Dapr;", |
|||
"using LINGYUN.Abp.Dapr.Client;", |
|||
"using LINGYUN.Abp.Dapr.Client.ClientProxying;" |
|||
}; |
|||
|
|||
public CSharpServiceProxyGenerator( |
|||
CliHttpClientFactory cliHttpClientFactory, |
|||
IJsonSerializer jsonSerializer) : |
|||
base(cliHttpClientFactory, jsonSerializer) |
|||
{ |
|||
} |
|||
|
|||
public async override Task GenerateProxyAsync(VoloGenerateProxyArgs args) |
|||
{ |
|||
CheckWorkDirectory(args.WorkDirectory); |
|||
CheckFolder(args.Folder); |
|||
|
|||
if (args.CommandName == RemoveProxyCommand.Name) |
|||
{ |
|||
RemoveClientProxyFile(args); |
|||
return; |
|||
} |
|||
|
|||
var applicationApiDescriptionModel = await GetApplicationApiDescriptionModelAsync(args); |
|||
|
|||
foreach (var controller in applicationApiDescriptionModel.Modules.Values.SelectMany(x => x.Controllers)) |
|||
{ |
|||
if (ShouldGenerateProxy(controller.Value)) |
|||
{ |
|||
await GenerateClientProxyFileAsync(args, controller.Value); |
|||
} |
|||
} |
|||
|
|||
await CreateGenerateProxyJsonFile(args, applicationApiDescriptionModel); |
|||
} |
|||
|
|||
private async Task CreateGenerateProxyJsonFile(VoloGenerateProxyArgs args, ApplicationApiDescriptionModel applicationApiDescriptionModel) |
|||
{ |
|||
var folder = args.Folder.IsNullOrWhiteSpace() ? DefaultNamespace : args.Folder; |
|||
var filePath = Path.Combine(args.WorkDirectory, folder, $"{args.Module}-generate-proxy.json"); |
|||
|
|||
using (var writer = new StreamWriter(filePath)) |
|||
{ |
|||
await writer.WriteAsync(JsonSerializer.Serialize(applicationApiDescriptionModel, indented: true)); |
|||
} |
|||
} |
|||
|
|||
private void RemoveClientProxyFile(VoloGenerateProxyArgs args) |
|||
{ |
|||
var folder = args.Folder.IsNullOrWhiteSpace() ? DefaultNamespace : args.Folder; |
|||
var folderPath = Path.Combine(args.WorkDirectory, folder); |
|||
|
|||
if (Directory.Exists(folderPath)) |
|||
{ |
|||
Directory.Delete(folderPath, true); |
|||
} |
|||
|
|||
Logger.LogInformation($"Delete {GetLoggerOutputPath(folderPath, args.WorkDirectory)}"); |
|||
} |
|||
|
|||
private async Task GenerateClientProxyFileAsync( |
|||
VoloGenerateProxyArgs args, |
|||
ControllerApiDescriptionModel controllerApiDescription) |
|||
{ |
|||
var folder = args.Folder.IsNullOrWhiteSpace() ? DefaultNamespace : args.Folder; |
|||
|
|||
var appServiceTypeFullName = controllerApiDescription.Interfaces.Last().Type; |
|||
var appServiceTypeName = appServiceTypeFullName.Split('.').Last(); |
|||
var clientProxyName = $"{controllerApiDescription.ControllerName}ClientProxy"; |
|||
var clientProvider = args.As<GenerateProxyArgs>().Provider; |
|||
var rootNamespace = $"{GetTypeNamespace(controllerApiDescription.Type)}.{folder.Replace('/', '.')}"; |
|||
var clientProxyBuilder = new StringBuilder(_clientProxyTemplate); |
|||
clientProxyBuilder.Replace(ClassName, clientProxyName); |
|||
clientProxyBuilder.Replace(Namespace, rootNamespace); |
|||
clientProxyBuilder.Replace(Provider, clientProvider); |
|||
|
|||
var filePath = Path.Combine(args.WorkDirectory, folder, $"{clientProxyName}.cs"); |
|||
Directory.CreateDirectory(Path.GetDirectoryName(filePath)); |
|||
|
|||
if (!File.Exists(filePath)) |
|||
{ |
|||
using (var writer = new StreamWriter(filePath)) |
|||
{ |
|||
await writer.WriteAsync(clientProxyBuilder.ToString()); |
|||
} |
|||
|
|||
Logger.LogInformation($"Create {GetLoggerOutputPath(filePath, args.WorkDirectory)}"); |
|||
} |
|||
|
|||
await GenerateClientProxyGeneratedFileAsync( |
|||
args, |
|||
controllerApiDescription, |
|||
clientProxyName, |
|||
appServiceTypeName, |
|||
appServiceTypeFullName, |
|||
rootNamespace, |
|||
folder); |
|||
} |
|||
|
|||
private async Task GenerateClientProxyGeneratedFileAsync( |
|||
VoloGenerateProxyArgs args, |
|||
ControllerApiDescriptionModel controllerApiDescription, |
|||
string clientProxyName, |
|||
string appServiceTypeName, |
|||
string appServiceTypeFullName, |
|||
string rootNamespace, |
|||
string folder) |
|||
{ |
|||
var clientProxyBuilder = new StringBuilder(_clientProxyGeneratedTemplate); |
|||
|
|||
var usingNamespaceList = new List<string>(_usingNamespaceList) |
|||
{ |
|||
$"using {GetTypeNamespace(appServiceTypeFullName)};" |
|||
}; |
|||
|
|||
clientProxyBuilder.Replace(ClassName, clientProxyName); |
|||
clientProxyBuilder.Replace(Namespace, rootNamespace); |
|||
clientProxyBuilder.Replace(ServiceInterface, appServiceTypeName); |
|||
|
|||
foreach (var action in controllerApiDescription.Actions.Values) |
|||
{ |
|||
if (!ShouldGenerateMethod(appServiceTypeFullName, action)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
GenerateMethod(action, clientProxyBuilder, usingNamespaceList); |
|||
} |
|||
|
|||
foreach (var usingNamespace in usingNamespaceList) |
|||
{ |
|||
clientProxyBuilder.Replace($"{UsingPlaceholder}", $"{usingNamespace}{Environment.NewLine}{UsingPlaceholder}"); |
|||
} |
|||
|
|||
clientProxyBuilder.Replace($"{Environment.NewLine}{UsingPlaceholder}", string.Empty); |
|||
clientProxyBuilder.Replace($"{Environment.NewLine}{Environment.NewLine} {MethodPlaceholder}", string.Empty); |
|||
|
|||
var filePath = Path.Combine(args.WorkDirectory, folder, $"{clientProxyName}.Generated.cs"); |
|||
|
|||
using (var writer = new StreamWriter(filePath)) |
|||
{ |
|||
await writer.WriteAsync(clientProxyBuilder.ToString()); |
|||
Logger.LogInformation($"Create {GetLoggerOutputPath(filePath, args.WorkDirectory)}"); |
|||
} |
|||
} |
|||
|
|||
private void GenerateMethod( |
|||
ActionApiDescriptionModel action, |
|||
StringBuilder clientProxyBuilder, |
|||
List<string> usingNamespaceList) |
|||
{ |
|||
var methodBuilder = new StringBuilder(); |
|||
|
|||
var returnTypeName = GetRealTypeName(action.ReturnValue.Type, usingNamespaceList); |
|||
|
|||
if (!action.Name.EndsWith("Async")) |
|||
{ |
|||
GenerateSynchronizationMethod(action, returnTypeName, methodBuilder, usingNamespaceList); |
|||
clientProxyBuilder.Replace(MethodPlaceholder, $"{methodBuilder}{Environment.NewLine} {MethodPlaceholder}"); |
|||
return; |
|||
} |
|||
|
|||
GenerateAsynchronousMethod(action, returnTypeName, methodBuilder, usingNamespaceList); |
|||
clientProxyBuilder.Replace(MethodPlaceholder, $"{methodBuilder}{Environment.NewLine} {MethodPlaceholder}"); |
|||
} |
|||
|
|||
private void GenerateSynchronizationMethod(ActionApiDescriptionModel action, string returnTypeName, StringBuilder methodBuilder, List<string> usingNamespaceList) |
|||
{ |
|||
methodBuilder.AppendLine($"public virtual {returnTypeName} {action.Name}(<args>)"); |
|||
|
|||
foreach (var parameter in action.Parameters.GroupBy(x => x.Name).Select(x => x.First())) |
|||
{ |
|||
methodBuilder.Replace("<args>", $"{GetRealTypeName(parameter.Type, usingNamespaceList)} {parameter.Name}, <args>"); |
|||
} |
|||
|
|||
methodBuilder.Replace("<args>", string.Empty); |
|||
methodBuilder.Replace(", )", ")"); |
|||
|
|||
methodBuilder.AppendLine(" {"); |
|||
methodBuilder.AppendLine(" //Client Proxy does not support the synchronization method, you should always use asynchronous methods as a best practice"); |
|||
methodBuilder.AppendLine(" throw new System.NotImplementedException(); "); |
|||
methodBuilder.AppendLine(" }"); |
|||
} |
|||
|
|||
private void GenerateAsynchronousMethod( |
|||
ActionApiDescriptionModel action, |
|||
string returnTypeName, |
|||
StringBuilder methodBuilder, |
|||
List<string> usingNamespaceList) |
|||
{ |
|||
var returnSign = returnTypeName == "void" ? "Task" : $"Task<{returnTypeName}>"; |
|||
|
|||
methodBuilder.AppendLine($"public virtual async {returnSign} {action.Name}(<args>)"); |
|||
|
|||
foreach (var parameter in action.ParametersOnMethod) |
|||
{ |
|||
methodBuilder.Replace("<args>", $"{GetRealTypeName(parameter.Type, usingNamespaceList)} {parameter.Name}, <args>"); |
|||
} |
|||
|
|||
methodBuilder.Replace("<args>", string.Empty); |
|||
methodBuilder.Replace(", )", ")"); |
|||
|
|||
methodBuilder.AppendLine(" {"); |
|||
|
|||
var argsTemplate = "new ClientProxyRequestTypeValue" + |
|||
$"{Environment.NewLine} {{<args>" + |
|||
$"{Environment.NewLine} }}"; |
|||
|
|||
var args = action.ParametersOnMethod.Any() ? argsTemplate : string.Empty; |
|||
|
|||
if (returnTypeName == "void") |
|||
{ |
|||
methodBuilder.AppendLine($" await RequestAsync(nameof({action.Name}), {args});"); |
|||
} |
|||
else |
|||
{ |
|||
methodBuilder.AppendLine($" return await RequestAsync<{returnTypeName}>(nameof({action.Name}), {args});"); |
|||
} |
|||
|
|||
foreach (var parameter in action.ParametersOnMethod) |
|||
{ |
|||
methodBuilder.Replace("<args>", $"{Environment.NewLine} {{ typeof({GetRealTypeName(parameter.Type)}), {parameter.Name} }},<args>"); |
|||
} |
|||
|
|||
methodBuilder.Replace(",<args>", string.Empty); |
|||
methodBuilder.Replace(", )", ")"); |
|||
methodBuilder.AppendLine(" }"); |
|||
} |
|||
|
|||
private bool ShouldGenerateProxy(ControllerApiDescriptionModel controllerApiDescription) |
|||
{ |
|||
if (!controllerApiDescription.Interfaces.Any()) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
var serviceInterface = controllerApiDescription.Interfaces.Last(); |
|||
return ServicePostfixes.Any(x => serviceInterface.Type.EndsWith(x)); |
|||
} |
|||
|
|||
private bool ShouldGenerateMethod(string appServiceTypeName, ActionApiDescriptionModel action) |
|||
{ |
|||
return action.ImplementFrom.StartsWith(AppServicePrefix) || action.ImplementFrom.StartsWith(appServiceTypeName); |
|||
} |
|||
|
|||
private string GetTypeNamespace(string typeFullName) |
|||
{ |
|||
return typeFullName.Substring(0, typeFullName.LastIndexOf('.')); |
|||
} |
|||
|
|||
private string GetRealTypeName(string typeName, List<string> usingNamespaceList = null) |
|||
{ |
|||
var filter = new[] { "<", ",", ">" }; |
|||
var stringBuilder = new StringBuilder(); |
|||
var typeNames = typeName.Split('.'); |
|||
|
|||
if (typeNames.All(x => !filter.Any(x.Contains))) |
|||
{ |
|||
if (usingNamespaceList != null) |
|||
{ |
|||
AddUsingNamespace(usingNamespaceList, typeName); |
|||
} |
|||
|
|||
return NormalizeTypeName(typeNames.Last()); |
|||
} |
|||
|
|||
var fullName = string.Empty; |
|||
|
|||
foreach (var item in typeNames) |
|||
{ |
|||
if (filter.Any(x => item.Contains(x))) |
|||
{ |
|||
if (usingNamespaceList != null) |
|||
{ |
|||
AddUsingNamespace(usingNamespaceList, $"{fullName}.{item}".TrimStart('.')); |
|||
} |
|||
|
|||
fullName = string.Empty; |
|||
|
|||
if (item.Contains('<') || item.Contains(',')) |
|||
{ |
|||
stringBuilder.Append(item.Substring(0, item.IndexOf(item.Contains('<') ? '<' : ',') + 1)); |
|||
fullName = item.Substring(item.IndexOf(item.Contains('<') ? '<' : ',') + 1); |
|||
} |
|||
else |
|||
{ |
|||
stringBuilder.Append(item); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
fullName = $"{fullName}.{item}"; |
|||
} |
|||
} |
|||
|
|||
return stringBuilder.ToString(); |
|||
} |
|||
|
|||
private void AddUsingNamespace(List<string> usingNamespaceList, string typeName) |
|||
{ |
|||
var rootNamespace = $"using {GetTypeNamespace(typeName)};"; |
|||
if (usingNamespaceList.Contains(rootNamespace)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
usingNamespaceList.Add(rootNamespace); |
|||
} |
|||
|
|||
private string NormalizeTypeName(string typeName) |
|||
{ |
|||
var nullable = string.Empty; |
|||
if (typeName.EndsWith("?")) |
|||
{ |
|||
typeName = typeName.TrimEnd('?'); |
|||
nullable = "?"; |
|||
} |
|||
|
|||
typeName = typeName switch |
|||
{ |
|||
"Void" => "void", |
|||
"Boolean" => "bool", |
|||
"String" => "string", |
|||
"Int32" => "int", |
|||
"Int64" => "long", |
|||
"Double" => "double", |
|||
"Object" => "object", |
|||
"Byte" => "byte", |
|||
"Char" => "char", |
|||
_ => typeName |
|||
}; |
|||
|
|||
return $"{typeName}{nullable}"; |
|||
} |
|||
|
|||
private void CheckWorkDirectory(string directory) |
|||
{ |
|||
if (!Directory.Exists(directory)) |
|||
{ |
|||
throw new CliUsageException("Specified directory does not exist."); |
|||
} |
|||
|
|||
var projectFiles = Directory.GetFiles(directory, "*.csproj"); |
|||
if (!projectFiles.Any()) |
|||
{ |
|||
throw new CliUsageException("No project file(csproj) found in the directory."); |
|||
} |
|||
} |
|||
|
|||
private void CheckFolder(string folder) |
|||
{ |
|||
if (!folder.IsNullOrWhiteSpace() && Path.HasExtension(folder)) |
|||
{ |
|||
throw new CliUsageException("Option folder should be a directory."); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
|
|||
namespace LINGYUN.Abp.Cli.ServiceProxying; |
|||
|
|||
public class GenerateProxyArgs : Volo.Abp.Cli.ServiceProxying.GenerateProxyArgs |
|||
{ |
|||
public string Provider { get; } |
|||
|
|||
public GenerateProxyArgs( |
|||
[NotNull] string commandName, |
|||
[NotNull] string workDirectory, |
|||
string module, |
|||
string url, |
|||
string output, |
|||
string target, |
|||
string apiName, |
|||
string source, |
|||
string folder, |
|||
string provider, |
|||
Dictionary<string, string> extraProperties = null) |
|||
: base(commandName, workDirectory, module, url, output, target, apiName, source, folder, extraProperties) |
|||
{ |
|||
Provider = provider; |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Http.Client.Wrapper; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpHttpClientModule), |
|||
typeof(AbpWrapperModule))] |
|||
public class AbpHttpClientWrapperModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
PreConfigure<AbpHttpClientBuilderOptions>(options => |
|||
{ |
|||
options.ProxyClientBuildActions.Add( |
|||
(_, builder) => |
|||
{ |
|||
builder.ConfigureHttpClient((provider, client) => |
|||
{ |
|||
var wrapperOptions = provider.GetRequiredService<IOptions<AbpWrapperOptions>>(); |
|||
var wrapperHeader = wrapperOptions.Value.IsEnabled |
|||
? AbpHttpWrapConsts.AbpWrapResult |
|||
: AbpHttpWrapConsts.AbpDontWrapResult; |
|||
|
|||
client.DefaultRequestHeaders.TryAddWithoutValidation(wrapperHeader, "true"); |
|||
}); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
@ -1,13 +0,0 @@ |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.HttpClient.Wrapper |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpHttpClientModule), |
|||
typeof(AbpWrapperModule))] |
|||
public class AbpHttpClientWrapperModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -1,153 +0,0 @@ |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Content; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Http; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Http.Client.Authentication; |
|||
using Volo.Abp.Http.Client.ClientProxying; |
|||
using Volo.Abp.Http.Client.DynamicProxying; |
|||
|
|||
namespace LINGYUN.Abp.HttpClient.Wrapper |
|||
{ |
|||
[Dependency(ReplaceServices = true)] |
|||
[ExposeServices(typeof(DynamicHttpProxyInterceptorClientProxy<>))] |
|||
public class DynamicHttpProxyInterceptorWrapClientProxy<TService> |
|||
: DynamicHttpProxyInterceptorClientProxy<TService>, ITransientDependency |
|||
{ |
|||
protected IOptions<AbpWrapperOptions> WrapperOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpWrapperOptions>>(); |
|||
|
|||
protected override async Task<T> RequestAsync<T>(ClientProxyRequestContext requestContext) |
|||
{ |
|||
var response = await RequestAndGetResponseAsync(requestContext); |
|||
|
|||
var responseContent = response.Content; |
|||
|
|||
if (typeof(T) == typeof(IRemoteStreamContent) || |
|||
typeof(T) == typeof(RemoteStreamContent)) |
|||
{ |
|||
/* returning a class that holds a reference to response |
|||
* content just to be sure that GC does not dispose of |
|||
* it before we finish doing our work with the stream */ |
|||
return (T)(object)new RemoteStreamContent( |
|||
await responseContent.ReadAsStreamAsync(), |
|||
responseContent.Headers?.ContentDisposition?.FileNameStar ?? |
|||
RemoveQuotes(responseContent.Headers?.ContentDisposition?.FileName).ToString(), |
|||
responseContent.Headers?.ContentType?.ToString(), |
|||
responseContent.Headers?.ContentLength); |
|||
} |
|||
|
|||
var stringContent = await responseContent.ReadAsStringAsync(); |
|||
|
|||
if (stringContent.IsNullOrWhiteSpace()) |
|||
{ |
|||
return default; |
|||
} |
|||
|
|||
// 对于包装后的结果需要处理
|
|||
if (response.Headers.Contains(AbpHttpWrapConsts.AbpWrapResult)) |
|||
{ |
|||
var wrapResult = JsonSerializer.Deserialize<WrapResult<T>>(stringContent); |
|||
|
|||
ThrowExceptionForResponse(wrapResult); |
|||
|
|||
if (typeof(T) == typeof(string)) |
|||
{ |
|||
return (T)(object)wrapResult.Result; |
|||
} |
|||
|
|||
return wrapResult.Result; |
|||
} |
|||
|
|||
if (typeof(T) == typeof(string)) |
|||
{ |
|||
return (T)(object)stringContent; |
|||
} |
|||
|
|||
if (stringContent.IsNullOrWhiteSpace()) |
|||
{ |
|||
return default; |
|||
} |
|||
|
|||
return JsonSerializer.Deserialize<T>(stringContent); |
|||
} |
|||
|
|||
public override async Task<HttpContent> CallRequestAsync(ClientProxyRequestContext requestContext) |
|||
{ |
|||
var response = await RequestAndGetResponseAsync(requestContext); |
|||
// 对于包装后的结果需要处理
|
|||
if (response.Headers.Contains(AbpHttpWrapConsts.AbpWrapResult)) |
|||
{ |
|||
var stringContent = await response.Content.ReadAsStringAsync(); |
|||
var wrapResult = JsonSerializer.Deserialize<WrapResult>(stringContent); |
|||
|
|||
ThrowExceptionForResponse(wrapResult); |
|||
} |
|||
|
|||
return response.Content; |
|||
} |
|||
|
|||
protected virtual void ThrowExceptionForResponse<T>(WrapResult<T> wrapResult) |
|||
{ |
|||
if (!string.Equals(wrapResult.Code, WrapperOptions.Value.CodeWithSuccess)) |
|||
{ |
|||
var errorInfo = new RemoteServiceErrorInfo( |
|||
wrapResult.Message, |
|||
wrapResult.Details, |
|||
wrapResult.Code); |
|||
throw new AbpRemoteCallException(errorInfo) |
|||
{ |
|||
HttpStatusCode = (int)WrapperOptions.Value.HttpStatusCode |
|||
}; |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task<HttpResponseMessage> RequestAndGetResponseAsync(ClientProxyRequestContext requestContext) |
|||
{ |
|||
var clientConfig = ClientOptions.Value.HttpClientProxies.GetOrDefault(requestContext.ServiceType) ?? throw new AbpException($"Could not get HttpClientProxyConfig for {requestContext.ServiceType.FullName}."); |
|||
var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(clientConfig.RemoteServiceName); |
|||
|
|||
var client = HttpClientFactory.Create(clientConfig.RemoteServiceName); |
|||
|
|||
var apiVersion = await GetApiVersionInfoAsync(requestContext); |
|||
var url = remoteServiceConfig.BaseUrl.EnsureEndsWith('/') + await GetUrlWithParametersAsync(requestContext, apiVersion); |
|||
|
|||
var requestMessage = new HttpRequestMessage(requestContext.Action.GetHttpMethod(), url) |
|||
{ |
|||
Content = await ClientProxyRequestPayloadBuilder.BuildContentAsync(requestContext.Action, requestContext.Arguments, JsonSerializer, apiVersion) |
|||
}; |
|||
|
|||
AddHeaders(requestContext.Arguments, requestContext.Action, requestMessage, apiVersion); |
|||
|
|||
if (requestContext.Action.AllowAnonymous != true) |
|||
{ |
|||
await ClientAuthenticator.Authenticate( |
|||
new RemoteServiceHttpClientAuthenticateContext( |
|||
client, |
|||
requestMessage, |
|||
remoteServiceConfig, |
|||
clientConfig.RemoteServiceName |
|||
) |
|||
); |
|||
} |
|||
|
|||
var response = await client.SendAsync( |
|||
requestMessage, |
|||
HttpCompletionOption.ResponseHeadersRead /*this will buffer only the headers, the content will be used as a stream*/, |
|||
GetCancellationToken(requestContext.Arguments) |
|||
); |
|||
|
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
await ThrowExceptionForResponseAsync(response); |
|||
} |
|||
|
|||
return response; |
|||
} |
|||
} |
|||
} |
|||
@ -1,10 +1,11 @@ |
|||
using System; |
|||
using LINGYUN.Abp.Dapr.Client.DynamicProxying; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.Client.DynamicProxying |
|||
namespace LINGYUN.Abp.Dapr.Client.ClientProxying |
|||
{ |
|||
public class AbpDaprClientProxyOptions |
|||
{ |
|||
@ -0,0 +1,124 @@ |
|||
using Dapr.Client; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Content; |
|||
using Volo.Abp.Http.Client.Authentication; |
|||
using Volo.Abp.Http.Client.ClientProxying; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.Client.ClientProxying |
|||
{ |
|||
public abstract class DaprClientProxyBase<TService> : ClientProxyBase<TService> |
|||
{ |
|||
protected IOptions<AbpDaprClientProxyOptions> DaprClientProxyOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpDaprClientProxyOptions>>(); |
|||
protected IDaprClientFactory DaprClientFactory => LazyServiceProvider.LazyGetRequiredService<IDaprClientFactory>(); |
|||
|
|||
protected async override Task<T> RequestAsync<T>(ClientProxyRequestContext requestContext) |
|||
{ |
|||
var response = await MakeRequestAsync(requestContext); |
|||
|
|||
var responseContent = response.Content; |
|||
|
|||
if (typeof(T) == typeof(IRemoteStreamContent) || |
|||
typeof(T) == typeof(RemoteStreamContent)) |
|||
{ |
|||
/* returning a class that holds a reference to response |
|||
* content just to be sure that GC does not dispose of |
|||
* it before we finish doing our work with the stream */ |
|||
return (T)(object)new RemoteStreamContent( |
|||
await responseContent.ReadAsStreamAsync(), |
|||
responseContent.Headers?.ContentDisposition?.FileNameStar ?? |
|||
RemoveQuotes(responseContent.Headers?.ContentDisposition?.FileName).ToString(), |
|||
responseContent.Headers?.ContentType?.ToString(), |
|||
responseContent.Headers?.ContentLength); |
|||
} |
|||
|
|||
var stringContent = await DaprClientProxyOptions |
|||
.Value |
|||
.ProxyResponseContent(response, LazyServiceProvider); |
|||
|
|||
if (stringContent.IsNullOrWhiteSpace()) |
|||
{ |
|||
return default; |
|||
} |
|||
|
|||
if (typeof(T) == typeof(string)) |
|||
{ |
|||
return (T)(object)stringContent; |
|||
} |
|||
|
|||
return JsonSerializer.Deserialize<T>(stringContent); |
|||
} |
|||
|
|||
protected async override Task<string> GetConfiguredApiVersionAsync(ClientProxyRequestContext requestContext) |
|||
{ |
|||
var clientConfig = DaprClientProxyOptions.Value.DaprClientProxies.GetOrDefault(requestContext.ServiceType) |
|||
?? throw new AbpException($"Could not get DynamicDaprClientProxyConfig for {requestContext.ServiceType.FullName}."); |
|||
var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(clientConfig.RemoteServiceName); |
|||
|
|||
return remoteServiceConfig?.Version; |
|||
} |
|||
|
|||
private async Task<HttpResponseMessage> MakeRequestAsync(ClientProxyRequestContext requestContext) |
|||
{ |
|||
var clientConfig = DaprClientProxyOptions.Value.DaprClientProxies.GetOrDefault(requestContext.ServiceType) ?? throw new AbpException($"Could not get DaprClientProxyConfig for {requestContext.ServiceType.FullName}."); |
|||
var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(clientConfig.RemoteServiceName); |
|||
|
|||
var appId = remoteServiceConfig.GetAppId(); |
|||
var apiVersion = await GetApiVersionInfoAsync(requestContext); |
|||
var methodName = await GetUrlWithParametersAsync(requestContext, apiVersion); |
|||
// See: https://docs.dapr.io/reference/api/service_invocation_api/#examples
|
|||
var daprClient = DaprClientFactory.CreateClient(clientConfig.RemoteServiceName); |
|||
var requestMessage = daprClient.CreateInvokeMethodRequest( |
|||
requestContext.Action.GetHttpMethod(), |
|||
appId, |
|||
methodName); |
|||
requestMessage.Content = await ClientProxyRequestPayloadBuilder.BuildContentAsync( |
|||
requestContext.Action, |
|||
requestContext.Arguments, |
|||
JsonSerializer, |
|||
apiVersion); |
|||
|
|||
AddHeaders(requestContext.Arguments, requestContext.Action, requestMessage, apiVersion); |
|||
|
|||
if (requestContext.Action.AllowAnonymous != true) |
|||
{ |
|||
var httpClient = HttpClientFactory.Create(AbpDaprClientModule.DaprHttpClient); |
|||
|
|||
await ClientAuthenticator.Authenticate( |
|||
new RemoteServiceHttpClientAuthenticateContext( |
|||
httpClient, |
|||
requestMessage, |
|||
remoteServiceConfig, |
|||
clientConfig.RemoteServiceName |
|||
) |
|||
); |
|||
|
|||
// 其他库可能将授权标头写入到HttpClient中
|
|||
if (requestMessage.Headers.Authorization == null && |
|||
httpClient.DefaultRequestHeaders.Authorization != null) |
|||
{ |
|||
requestMessage.Headers.Authorization = httpClient.DefaultRequestHeaders.Authorization; |
|||
} |
|||
} |
|||
|
|||
// 增加一个可配置的请求消息
|
|||
foreach (var clientRequestAction in DaprClientProxyOptions.Value.ProxyRequestActions) |
|||
{ |
|||
clientRequestAction(appId, requestMessage); |
|||
} |
|||
|
|||
var response = await daprClient.InvokeMethodWithResponseAsync(requestMessage, GetCancellationToken(requestContext.Arguments)); |
|||
|
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
await ThrowExceptionForResponseAsync(response); |
|||
} |
|||
|
|||
return response; |
|||
} |
|||
} |
|||
} |
|||
@ -1,134 +1,20 @@ |
|||
using Dapr.Client; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using LINGYUN.Abp.Dapr.Client.ClientProxying; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Content; |
|||
using Volo.Abp.Http.Client.Authentication; |
|||
using Volo.Abp.Http.Client.ClientProxying; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.Client.DynamicProxying |
|||
{ |
|||
public class DynamicDaprProxyInterceptorClientProxy<TService> : ClientProxyBase<TService> |
|||
public class DynamicDaprProxyInterceptorClientProxy<TService> : DaprClientProxyBase<TService> |
|||
{ |
|||
protected IOptions<AbpDaprClientProxyOptions> DaprClientProxyOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpDaprClientProxyOptions>>(); |
|||
protected IDaprClientFactory DaprClientFactory => LazyServiceProvider.LazyGetRequiredService<IDaprClientFactory>(); |
|||
|
|||
public virtual async Task<T> CallRequestAsync<T>(ClientProxyRequestContext requestContext) |
|||
public async virtual Task<T> CallRequestAsync<T>(ClientProxyRequestContext requestContext) |
|||
{ |
|||
return await RequestAsync<T>(requestContext); |
|||
} |
|||
|
|||
public virtual async Task<HttpContent> CallRequestAsync(ClientProxyRequestContext requestContext) |
|||
public async virtual Task<HttpContent> CallRequestAsync(ClientProxyRequestContext requestContext) |
|||
{ |
|||
return await RequestAsync(requestContext); |
|||
} |
|||
|
|||
protected override async Task<T> RequestAsync<T>(ClientProxyRequestContext requestContext) |
|||
{ |
|||
var response = await MakeRequestAsync(requestContext); |
|||
|
|||
var responseContent = response.Content; |
|||
|
|||
if (typeof(T) == typeof(IRemoteStreamContent) || |
|||
typeof(T) == typeof(RemoteStreamContent)) |
|||
{ |
|||
/* returning a class that holds a reference to response |
|||
* content just to be sure that GC does not dispose of |
|||
* it before we finish doing our work with the stream */ |
|||
return (T)(object)new RemoteStreamContent( |
|||
await responseContent.ReadAsStreamAsync(), |
|||
responseContent.Headers?.ContentDisposition?.FileNameStar ?? |
|||
RemoveQuotes(responseContent.Headers?.ContentDisposition?.FileName).ToString(), |
|||
responseContent.Headers?.ContentType?.ToString(), |
|||
responseContent.Headers?.ContentLength); |
|||
} |
|||
|
|||
var stringContent = await DaprClientProxyOptions |
|||
.Value |
|||
.ProxyResponseContent(response, LazyServiceProvider); |
|||
|
|||
if (stringContent.IsNullOrWhiteSpace()) |
|||
{ |
|||
return default; |
|||
} |
|||
|
|||
if (typeof(T) == typeof(string)) |
|||
{ |
|||
return (T)(object)stringContent; |
|||
} |
|||
|
|||
return JsonSerializer.Deserialize<T>(stringContent); |
|||
} |
|||
|
|||
protected override async Task<string> GetConfiguredApiVersionAsync(ClientProxyRequestContext requestContext) |
|||
{ |
|||
var clientConfig = DaprClientProxyOptions.Value.DaprClientProxies.GetOrDefault(requestContext.ServiceType) |
|||
?? throw new AbpException($"Could not get DynamicDaprClientProxyConfig for {requestContext.ServiceType.FullName}."); |
|||
var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(clientConfig.RemoteServiceName); |
|||
|
|||
return remoteServiceConfig?.Version; |
|||
} |
|||
|
|||
private async Task<HttpResponseMessage> MakeRequestAsync(ClientProxyRequestContext requestContext) |
|||
{ |
|||
var clientConfig = DaprClientProxyOptions.Value.DaprClientProxies.GetOrDefault(requestContext.ServiceType) ?? throw new AbpException($"Could not get DaprClientProxyConfig for {requestContext.ServiceType.FullName}."); |
|||
var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(clientConfig.RemoteServiceName); |
|||
|
|||
var appId = remoteServiceConfig.GetAppId(); |
|||
var apiVersion = await GetApiVersionInfoAsync(requestContext); |
|||
var methodName = await GetUrlWithParametersAsync(requestContext, apiVersion); |
|||
// See: https://docs.dapr.io/reference/api/service_invocation_api/#examples
|
|||
var daprClient = DaprClientFactory.CreateClient(clientConfig.RemoteServiceName); |
|||
var requestMessage = daprClient.CreateInvokeMethodRequest( |
|||
requestContext.Action.GetHttpMethod(), |
|||
appId, |
|||
methodName); |
|||
requestMessage.Content = await ClientProxyRequestPayloadBuilder.BuildContentAsync( |
|||
requestContext.Action, |
|||
requestContext.Arguments, |
|||
JsonSerializer, |
|||
apiVersion); |
|||
|
|||
AddHeaders(requestContext.Arguments, requestContext.Action, requestMessage, apiVersion); |
|||
|
|||
if (requestContext.Action.AllowAnonymous != true) |
|||
{ |
|||
var httpClient = HttpClientFactory.Create(AbpDaprClientModule.DaprHttpClient); |
|||
|
|||
await ClientAuthenticator.Authenticate( |
|||
new RemoteServiceHttpClientAuthenticateContext( |
|||
httpClient, |
|||
requestMessage, |
|||
remoteServiceConfig, |
|||
clientConfig.RemoteServiceName |
|||
) |
|||
); |
|||
|
|||
// 其他库可能将授权标头写入到HttpClient中
|
|||
if (requestMessage.Headers.Authorization == null && |
|||
httpClient.DefaultRequestHeaders.Authorization != null) |
|||
{ |
|||
requestMessage.Headers.Authorization = httpClient.DefaultRequestHeaders.Authorization; |
|||
} |
|||
} |
|||
|
|||
// 增加一个可配置的请求消息
|
|||
foreach (var clientRequestAction in DaprClientProxyOptions.Value.ProxyRequestActions) |
|||
{ |
|||
clientRequestAction(appId, requestMessage); |
|||
} |
|||
|
|||
var response = await daprClient.InvokeMethodWithResponseAsync(requestMessage, GetCancellationToken(requestContext.Arguments)); |
|||
|
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
await ThrowExceptionForResponseAsync(response); |
|||
} |
|||
|
|||
return response; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,45 @@ |
|||
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Http.Modeling; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Http.Client.ClientProxying; |
|||
using LINGYUN.Abp.Dapr.ServiceInvocation; |
|||
using LINGYUN.Abp.Dapr; |
|||
using System.Linq; |
|||
using Volo.Abp; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Reflection; |
|||
using System.Net.Http; |
|||
using LINGYUN.Abp.Dapr.Client.ClientProxying; |
|||
|
|||
// ReSharper disable once CheckNamespace
|
|||
namespace LINGYUN.Abp.Dapr.ServiceInvocation.ClientProxies; |
|||
|
|||
[Dependency(ReplaceServices = true)] |
|||
[ExposeServices(typeof(ITestAppService), typeof(TestClientProxy))] |
|||
public partial class TestClientProxy : DaprClientProxyBase<ITestAppService>, ITestAppService |
|||
{ |
|||
public virtual async Task<ListResultDto<NameValue>> GetAsync() |
|||
{ |
|||
return await RequestAsync<ListResultDto<NameValue>>(nameof(GetAsync)); |
|||
} |
|||
|
|||
public virtual async Task<NameValue> UpdateAsync(int inctement) |
|||
{ |
|||
return await RequestAsync<NameValue>(nameof(UpdateAsync), new ClientProxyRequestTypeValue |
|||
{ |
|||
{ typeof(int), inctement } |
|||
}); |
|||
} |
|||
|
|||
public virtual async Task<TestNeedWrapObject> GetWrapedAsync(string name) |
|||
{ |
|||
return await RequestAsync<TestNeedWrapObject>(nameof(GetWrapedAsync), new ClientProxyRequestTypeValue |
|||
{ |
|||
{ typeof(string), name } |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
// This file is part of TestClientProxy, you can customize it here
|
|||
// ReSharper disable once CheckNamespace
|
|||
namespace LINGYUN.Abp.Dapr.ServiceInvocation.ClientProxies; |
|||
|
|||
public partial class TestClientProxy |
|||
{ |
|||
} |
|||
@ -0,0 +1,114 @@ |
|||
{ |
|||
"modules": { |
|||
"app": { |
|||
"rootPath": "app", |
|||
"remoteServiceName": "TestDapr", |
|||
"controllers": { |
|||
"LINGYUN.Abp.Dapr.ServiceInvocation.TestAppService": { |
|||
"controllerName": "Test", |
|||
"controllerGroupName": "Test", |
|||
"isRemoteService": true, |
|||
"apiVersion": null, |
|||
"type": "LINGYUN.Abp.Dapr.ServiceInvocation.TestAppService", |
|||
"interfaces": [ |
|||
{ |
|||
"type": "LINGYUN.Abp.Dapr.ServiceInvocation.ITestAppService" |
|||
} |
|||
], |
|||
"actions": { |
|||
"GetAsync": { |
|||
"uniqueName": "GetAsync", |
|||
"name": "GetAsync", |
|||
"httpMethod": "GET", |
|||
"url": "api/dapr/test", |
|||
"supportedVersions": [], |
|||
"parametersOnMethod": [], |
|||
"parameters": [], |
|||
"returnValue": { |
|||
"type": "Volo.Abp.Application.Dtos.ListResultDto<LINGYUN.Abp.Dapr.NameValue>", |
|||
"typeSimple": "Volo.Abp.Application.Dtos.ListResultDto<LINGYUN.Abp.Dapr.NameValue>" |
|||
}, |
|||
"allowAnonymous": null, |
|||
"implementFrom": "LINGYUN.Abp.Dapr.ServiceInvocation.ITestAppService" |
|||
}, |
|||
"UpdateAsyncByInctement": { |
|||
"uniqueName": "UpdateAsyncByInctement", |
|||
"name": "UpdateAsync", |
|||
"httpMethod": "PUT", |
|||
"url": "api/dapr/test", |
|||
"supportedVersions": [], |
|||
"parametersOnMethod": [ |
|||
{ |
|||
"name": "inctement", |
|||
"typeAsString": "System.Int32, System.Private.CoreLib", |
|||
"type": "System.Int32", |
|||
"typeSimple": "number", |
|||
"isOptional": false, |
|||
"defaultValue": null |
|||
} |
|||
], |
|||
"parameters": [ |
|||
{ |
|||
"nameOnMethod": "inctement", |
|||
"name": "inctement", |
|||
"jsonName": null, |
|||
"type": "System.Int32", |
|||
"typeSimple": "number", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": null, |
|||
"bindingSourceId": "ModelBinding", |
|||
"descriptorName": "" |
|||
} |
|||
], |
|||
"returnValue": { |
|||
"type": "LINGYUN.Abp.Dapr.NameValue", |
|||
"typeSimple": "LINGYUN.Abp.Dapr.NameValue" |
|||
}, |
|||
"allowAnonymous": null, |
|||
"implementFrom": "LINGYUN.Abp.Dapr.ServiceInvocation.ITestAppService" |
|||
}, |
|||
"GetWrapedAsyncByName": { |
|||
"uniqueName": "GetWrapedAsyncByName", |
|||
"name": "GetWrapedAsync", |
|||
"httpMethod": "GET", |
|||
"url": "api/dapr/test/{name}", |
|||
"supportedVersions": [], |
|||
"parametersOnMethod": [ |
|||
{ |
|||
"name": "name", |
|||
"typeAsString": "System.String, System.Private.CoreLib", |
|||
"type": "System.String", |
|||
"typeSimple": "string", |
|||
"isOptional": false, |
|||
"defaultValue": null |
|||
} |
|||
], |
|||
"parameters": [ |
|||
{ |
|||
"nameOnMethod": "name", |
|||
"name": "name", |
|||
"jsonName": null, |
|||
"type": "System.String", |
|||
"typeSimple": "string", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": [], |
|||
"bindingSourceId": "Path", |
|||
"descriptorName": "" |
|||
} |
|||
], |
|||
"returnValue": { |
|||
"type": "LINGYUN.Abp.Dapr.TestNeedWrapObject", |
|||
"typeSimple": "LINGYUN.Abp.Dapr.TestNeedWrapObject" |
|||
}, |
|||
"allowAnonymous": null, |
|||
"implementFrom": "LINGYUN.Abp.Dapr.ServiceInvocation.ITestAppService" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"types": {} |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
using LINGYUN.Abp.Dapr.ServiceInvocation; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Xunit; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.Client.Tests |
|||
{ |
|||
public class TestAppServiceStaticProxyTests : AbpDaptClientTestBase |
|||
{ |
|||
private readonly ITestAppService _service; |
|||
|
|||
public TestAppServiceStaticProxyTests() |
|||
{ |
|||
_service = GetRequiredService<ITestAppService>(); |
|||
} |
|||
|
|||
protected override void BeforeAddApplication(IServiceCollection services) |
|||
{ |
|||
services.AddStaticDaprClientProxies( |
|||
typeof(AbpDaprTestModule).Assembly, |
|||
"TestDapr"); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Get_Result_Items_Count_Should_5() |
|||
{ |
|||
var result = await _service.GetAsync(); |
|||
|
|||
result.Items.Count.ShouldBe(5); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Get_Wraped_Object() |
|||
{ |
|||
var result = await _service.GetWrapedAsync("Test"); |
|||
|
|||
result.Name.ShouldBe("Test"); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_Result_Value_Should_Value_Updated_1() |
|||
{ |
|||
var result = await _service.UpdateAsync(1); |
|||
|
|||
result.Value.ShouldBe("value:updated:1"); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue