mirror of https://github.com/abpframework/abp.git
694 changed files with 17664 additions and 5663 deletions
@ -1,15 +1,17 @@ |
|||
using System.Net.Http; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Http.Client.DynamicProxying; |
|||
using Volo.Abp.Http.Client.Proxying; |
|||
|
|||
namespace Volo.Abp.AspNetCore.TestBase.DynamicProxying |
|||
{ |
|||
[Dependency(ReplaceServices = true)] |
|||
public class AspNetCoreTestDynamicProxyHttpClientFactory : IDynamicProxyHttpClientFactory, ITransientDependency |
|||
public class AspNetCoreTestProxyHttpClientFactory : IProxyHttpClientFactory, ITransientDependency |
|||
{ |
|||
private readonly ITestServerAccessor _testServerAccessor; |
|||
|
|||
public AspNetCoreTestDynamicProxyHttpClientFactory( |
|||
public AspNetCoreTestProxyHttpClientFactory( |
|||
ITestServerAccessor testServerAccessor) |
|||
{ |
|||
_testServerAccessor = testServerAccessor; |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.AspNetCore.ExceptionHandling |
|||
{ |
|||
public class AbpAuthorizationExceptionHandlerOptions |
|||
{ |
|||
public string AuthenticationScheme { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Authentication; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Authorization; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.ExceptionHandling |
|||
{ |
|||
public class DefaultAbpAuthorizationExceptionHandler : IAbpAuthorizationExceptionHandler, ITransientDependency |
|||
{ |
|||
public virtual async Task HandleAsync(AbpAuthorizationException exception, HttpContext httpContext) |
|||
{ |
|||
var handlerOptions = httpContext.RequestServices.GetRequiredService<IOptions<AbpAuthorizationExceptionHandlerOptions>>().Value; |
|||
var isAuthenticated = httpContext.User.Identity?.IsAuthenticated ?? false; |
|||
var authenticationSchemeProvider = httpContext.RequestServices.GetRequiredService<IAuthenticationSchemeProvider>(); |
|||
|
|||
AuthenticationScheme scheme = null; |
|||
|
|||
if (!handlerOptions.AuthenticationScheme.IsNullOrWhiteSpace()) |
|||
{ |
|||
scheme = await authenticationSchemeProvider.GetSchemeAsync(handlerOptions.AuthenticationScheme); |
|||
if (scheme == null) |
|||
{ |
|||
throw new AbpException($"No authentication scheme named {handlerOptions.AuthenticationScheme} was found."); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (isAuthenticated) |
|||
{ |
|||
scheme = await authenticationSchemeProvider.GetDefaultForbidSchemeAsync(); |
|||
if (scheme == null) |
|||
{ |
|||
throw new AbpException($"There was no DefaultForbidScheme found."); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
scheme = await authenticationSchemeProvider.GetDefaultChallengeSchemeAsync(); |
|||
if (scheme == null) |
|||
{ |
|||
throw new AbpException($"There was no DefaultChallengeScheme found."); |
|||
} |
|||
} |
|||
} |
|||
|
|||
var handlers = httpContext.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>(); |
|||
var handler = await handlers.GetHandlerAsync(httpContext, scheme.Name); |
|||
if (handler == null) |
|||
{ |
|||
throw new AbpException($"No handler of {scheme.Name} was found."); |
|||
} |
|||
|
|||
if (isAuthenticated) |
|||
{ |
|||
await handler.ForbidAsync(null); |
|||
} |
|||
else |
|||
{ |
|||
await handler.ChallengeAsync(null); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Volo.Abp.Authorization; |
|||
|
|||
namespace Volo.Abp.AspNetCore.ExceptionHandling |
|||
{ |
|||
public interface IAbpAuthorizationExceptionHandler |
|||
{ |
|||
Task HandleAsync(AbpAuthorizationException exception, HttpContext httpContext); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,21 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.Auditing.Contracts</AssemblyName> |
|||
<PackageId>Volo.Abp.Auditing.Contracts</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class AbpAuditingContractsModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -1,23 +1,40 @@ |
|||
using Volo.Abp.Cli.Utils; |
|||
using System.Text; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Cli.ServiceProxying; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.Commands |
|||
{ |
|||
public class GenerateProxyCommand : ProxyCommandBase |
|||
public class GenerateProxyCommand : ProxyCommandBase<GenerateProxyCommand> |
|||
{ |
|||
public const string Name = "generate-proxy"; |
|||
|
|||
protected override string CommandName => Name; |
|||
|
|||
protected override string SchematicsCommandName => "proxy-add"; |
|||
public GenerateProxyCommand( |
|||
IOptions<AbpCliServiceProxyOptions> serviceProxyOptions, |
|||
IHybridServiceScopeFactory serviceScopeFactory) |
|||
: base(serviceProxyOptions, serviceScopeFactory) |
|||
{ |
|||
} |
|||
|
|||
public GenerateProxyCommand(CliService cliService, ICmdHelper cmdHelper) |
|||
: base(cliService, cmdHelper) |
|||
public override string GetUsageInfo() |
|||
{ |
|||
var sb = new StringBuilder(base.GetUsageInfo()); |
|||
|
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Examples:"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine(" abp generate-proxy -t ng"); |
|||
sb.AppendLine(" abp generate-proxy -t js -m identity -o Pages/Identity/client-proxies.js -url https://localhost:44302/"); |
|||
sb.AppendLine(" abp generate-proxy -t csharp --folder MyProxies/InnerFolder -url https://localhost:44302/"); |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public override string GetShortDescription() |
|||
{ |
|||
return "Generates Angular service proxies and DTOs to consume HTTP APIs."; |
|||
return "Generates client service proxies and DTOs to consume HTTP APIs."; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,25 +1,40 @@ |
|||
using Volo.Abp.Cli.Utils; |
|||
using System.Text; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Cli.ServiceProxying; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.Commands |
|||
{ |
|||
public class RemoveProxyCommand : ProxyCommandBase |
|||
public class RemoveProxyCommand : ProxyCommandBase<RemoveProxyCommand> |
|||
{ |
|||
public const string Name = "remove-proxy"; |
|||
|
|||
protected override string CommandName => Name; |
|||
|
|||
protected override string SchematicsCommandName => "proxy-remove"; |
|||
|
|||
public RemoveProxyCommand( |
|||
CliService cliService, |
|||
ICmdHelper cmdHelper) |
|||
: base(cliService, cmdHelper) |
|||
IOptions<AbpCliServiceProxyOptions> serviceProxyOptions, |
|||
IHybridServiceScopeFactory serviceScopeFactory) |
|||
: base(serviceProxyOptions, serviceScopeFactory) |
|||
{ |
|||
} |
|||
|
|||
public override string GetUsageInfo() |
|||
{ |
|||
var sb = new StringBuilder(base.GetUsageInfo()); |
|||
|
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Examples:"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine(" abp remove-proxy -t ng"); |
|||
sb.AppendLine(" abp remove-proxy -t js -m identity -o Pages/Identity/client-proxies.js"); |
|||
sb.AppendLine(" abp remove-proxy -t csharp --folder MyProxies/InnerFolder"); |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public override string GetShortDescription() |
|||
{ |
|||
return "Remove Angular service proxies and DTOs to consume HTTP APIs."; |
|||
return "Remove client service proxies and DTOs to consume HTTP APIs."; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,15 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Cli.ServiceProxying |
|||
{ |
|||
public class AbpCliServiceProxyOptions |
|||
{ |
|||
public IDictionary<string, Type> Generators { get; } |
|||
|
|||
public AbpCliServiceProxyOptions() |
|||
{ |
|||
Generators = new Dictionary<string, Type>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,115 @@ |
|||
using System.IO; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
using Newtonsoft.Json.Linq; |
|||
using NuGet.Versioning; |
|||
using Volo.Abp.Cli.Commands; |
|||
using Volo.Abp.Cli.Http; |
|||
using Volo.Abp.Cli.Utils; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace Volo.Abp.Cli.ServiceProxying.Angular |
|||
{ |
|||
public class AngularServiceProxyGenerator : ServiceProxyGeneratorBase<AngularServiceProxyGenerator> , ITransientDependency |
|||
{ |
|||
public const string Name = "NG"; |
|||
|
|||
private readonly CliService _cliService; |
|||
|
|||
public AngularServiceProxyGenerator( |
|||
CliHttpClientFactory cliHttpClientFactory, |
|||
IJsonSerializer jsonSerializer, |
|||
CliService cliService) : |
|||
base(cliHttpClientFactory, jsonSerializer) |
|||
{ |
|||
_cliService = cliService; |
|||
} |
|||
|
|||
public override async Task GenerateProxyAsync(GenerateProxyArgs args) |
|||
{ |
|||
CheckAngularJsonFile(); |
|||
await CheckNgSchematicsAsync(); |
|||
|
|||
var schematicsCommandName = args.CommandName == RemoveProxyCommand.Name ? "proxy-remove" : "proxy-add"; |
|||
var prompt = args.ExtraProperties.ContainsKey("p") || args.ExtraProperties.ContainsKey("prompt"); |
|||
var defaultValue = prompt ? null : "__default"; |
|||
|
|||
var module = args.Module ?? defaultValue; |
|||
var apiName = args.ApiName ?? defaultValue; |
|||
var source = args.Source ?? defaultValue; |
|||
var target = args.Target ?? defaultValue; |
|||
|
|||
var commandBuilder = new StringBuilder("npx ng g @abp/ng.schematics:" + schematicsCommandName); |
|||
|
|||
if (module != null) |
|||
{ |
|||
commandBuilder.Append($" --module {module}"); |
|||
} |
|||
|
|||
if (apiName != null) |
|||
{ |
|||
commandBuilder.Append($" --api-name {apiName}"); |
|||
} |
|||
|
|||
if (source != null) |
|||
{ |
|||
commandBuilder.Append($" --source {source}"); |
|||
} |
|||
|
|||
if (target != null) |
|||
{ |
|||
commandBuilder.Append($" --target {target}"); |
|||
} |
|||
|
|||
CmdHelper.RunCmd(commandBuilder.ToString()); |
|||
} |
|||
|
|||
private async Task CheckNgSchematicsAsync() |
|||
{ |
|||
var packageJsonPath = $"package.json"; |
|||
|
|||
if (!File.Exists(packageJsonPath)) |
|||
{ |
|||
throw new CliUsageException( |
|||
"package.json file not found" |
|||
); |
|||
} |
|||
|
|||
var schematicsVersion = |
|||
(string) JObject.Parse(File.ReadAllText(packageJsonPath))["devDependencies"]?["@abp/ng.schematics"]; |
|||
|
|||
if (schematicsVersion == null) |
|||
{ |
|||
throw new CliUsageException( |
|||
"\"@abp/ng.schematics\" NPM package should be installed to the devDependencies before running this command!" |
|||
); |
|||
} |
|||
|
|||
var parseError = SemanticVersion.TryParse(schematicsVersion.TrimStart('~', '^', 'v'), out var semanticSchematicsVersion); |
|||
if (parseError) |
|||
{ |
|||
Logger.LogWarning("Couldn't determinate version of \"@abp/ng.schematics\" package."); |
|||
return; |
|||
} |
|||
|
|||
var cliVersion = await _cliService.GetCurrentCliVersionAsync(typeof(CliService).Assembly); |
|||
if (semanticSchematicsVersion < cliVersion) |
|||
{ |
|||
Logger.LogWarning("\"@abp/ng.schematics\" version is lower than ABP Cli version."); |
|||
} |
|||
} |
|||
|
|||
private static void CheckAngularJsonFile() |
|||
{ |
|||
var angularPath = $"angular.json"; |
|||
if (!File.Exists(angularPath)) |
|||
{ |
|||
throw new CliUsageException( |
|||
"angular.json file not found. You must run this command in the angular folder." |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,400 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.Cli.Commands; |
|||
using Volo.Abp.Cli.Http; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Http.Modeling; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace Volo.Abp.Cli.ServiceProxying.CSharp |
|||
{ |
|||
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 const string ServicePostfix = "AppService"; |
|||
private const string DefaultNamespace = "ClientProxies"; |
|||
private const string Namespace = "<namespace>"; |
|||
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> : ClientProxyBase<<serviceInterface>>, <serviceInterface>" + |
|||
$"{Environment.NewLine} {{" + |
|||
$"{Environment.NewLine} <method placeholder>" + |
|||
$"{Environment.NewLine} }}" + |
|||
$"{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}}}" + |
|||
$"{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;" |
|||
}; |
|||
|
|||
public CSharpServiceProxyGenerator( |
|||
CliHttpClientFactory cliHttpClientFactory, |
|||
IJsonSerializer jsonSerializer) : |
|||
base(cliHttpClientFactory, jsonSerializer) |
|||
{ |
|||
} |
|||
|
|||
public override async Task GenerateProxyAsync(GenerateProxyArgs 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(GenerateProxyArgs 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(GenerateProxyArgs 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( |
|||
GenerateProxyArgs 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 rootNamespace = $"{GetTypeNamespace(controllerApiDescription.Type)}.{folder.Replace('/', '.')}"; |
|||
|
|||
var clientProxyBuilder = new StringBuilder(_clientProxyTemplate); |
|||
clientProxyBuilder.Replace(ClassName, clientProxyName); |
|||
clientProxyBuilder.Replace(Namespace, rootNamespace); |
|||
|
|||
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( |
|||
GenerateProxyArgs 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(usingNamespaceList, action.ReturnValue.Type); |
|||
|
|||
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(usingNamespaceList, parameter.Type)} {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(usingNamespaceList, parameter.Type)} {parameter.Name}, <args>"); |
|||
} |
|||
|
|||
methodBuilder.Replace("<args>", string.Empty); |
|||
methodBuilder.Replace(", )", ")"); |
|||
|
|||
methodBuilder.AppendLine(" {"); |
|||
|
|||
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>", $"{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 serviceInterface.Type.EndsWith(ServicePostfix); |
|||
} |
|||
|
|||
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(List<string> usingNamespaceList, string typeName) |
|||
{ |
|||
var filter = new []{"<", ",", ">"}; |
|||
var stringBuilder = new StringBuilder(); |
|||
var typeNames = typeName.Split('.'); |
|||
|
|||
if (typeNames.All(x => !filter.Any(x.Contains))) |
|||
{ |
|||
AddUsingNamespace(usingNamespaceList, typeName); |
|||
return NormalizeTypeName(typeNames.Last()); |
|||
} |
|||
|
|||
var fullName = string.Empty; |
|||
|
|||
foreach (var item in typeNames) |
|||
{ |
|||
if (filter.Any(x => item.Contains(x))) |
|||
{ |
|||
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, "*HttpApi.Client.csproj"); |
|||
if (!projectFiles.Any()) |
|||
{ |
|||
throw new CliUsageException( |
|||
"No project file found in the directory. The working directory must have a HttpApi.Client project file."); |
|||
} |
|||
} |
|||
|
|||
private void CheckFolder(string folder) |
|||
{ |
|||
if (!folder.IsNullOrWhiteSpace() && Path.HasExtension(folder)) |
|||
{ |
|||
throw new CliUsageException("Option folder should be a directory."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.Cli.ServiceProxying |
|||
{ |
|||
public class GenerateProxyArgs |
|||
{ |
|||
[NotNull] |
|||
public string CommandName { get; } |
|||
|
|||
[NotNull] |
|||
public string WorkDirectory { get; } |
|||
|
|||
public string Module { get; } |
|||
|
|||
public string Url { get; } |
|||
|
|||
public string Output { get; } |
|||
|
|||
public string Target { get; } |
|||
|
|||
public string ApiName { get; } |
|||
|
|||
public string Source { get; } |
|||
|
|||
public string Folder { get; } |
|||
|
|||
[NotNull] |
|||
public Dictionary<string, string> ExtraProperties { get; set; } |
|||
|
|||
public GenerateProxyArgs( |
|||
[NotNull] string commandName, |
|||
[NotNull] string workDirectory, |
|||
string module, |
|||
string url, |
|||
string output, |
|||
string target, |
|||
string apiName, |
|||
string source, |
|||
string folder, |
|||
Dictionary<string, string> extraProperties = null) |
|||
{ |
|||
CommandName = Check.NotNullOrWhiteSpace(commandName, nameof(commandName)); |
|||
WorkDirectory = Check.NotNullOrWhiteSpace(workDirectory, nameof(workDirectory)); |
|||
Module = module; |
|||
Url = url; |
|||
Output = output; |
|||
Target = target; |
|||
ApiName = apiName; |
|||
Source = source; |
|||
Folder = folder; |
|||
ExtraProperties = extraProperties ?? new Dictionary<string, string>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.Cli.ServiceProxying |
|||
{ |
|||
public interface IServiceProxyGenerator |
|||
{ |
|||
Task GenerateProxyAsync(GenerateProxyArgs args); |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.Cli.Commands; |
|||
using Volo.Abp.Cli.Http; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Http.ProxyScripting.Generators.JQuery; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace Volo.Abp.Cli.ServiceProxying.JavaScript |
|||
{ |
|||
public class JavaScriptServiceProxyGenerator : ServiceProxyGeneratorBase<JavaScriptServiceProxyGenerator>, ITransientDependency |
|||
{ |
|||
public const string Name = "JS"; |
|||
private const string EventTriggerScript = "abp.event.trigger('abp.serviceProxyScriptInitialized');"; |
|||
private const string DefaultOutput = "wwwroot/client-proxies"; |
|||
|
|||
private readonly JQueryProxyScriptGenerator _jQueryProxyScriptGenerator; |
|||
|
|||
public JavaScriptServiceProxyGenerator( |
|||
CliHttpClientFactory cliHttpClientFactory, |
|||
IJsonSerializer jsonSerializer, |
|||
JQueryProxyScriptGenerator jQueryProxyScriptGenerator) : |
|||
base(cliHttpClientFactory, jsonSerializer) |
|||
{ |
|||
_jQueryProxyScriptGenerator = jQueryProxyScriptGenerator; |
|||
} |
|||
|
|||
public override async Task GenerateProxyAsync(GenerateProxyArgs args) |
|||
{ |
|||
CheckWorkDirectory(args.WorkDirectory); |
|||
|
|||
var output = Path.Combine(args.WorkDirectory, DefaultOutput, $"{args.Module}-proxy.js"); |
|||
if (!args.Output.IsNullOrWhiteSpace()) |
|||
{ |
|||
output = args.Output.EndsWith(".js") ? Path.Combine(args.WorkDirectory, args.Output) : Path.Combine(args.WorkDirectory, Path.GetDirectoryName(args.Output), $"{args.Module}-proxy.js"); |
|||
} |
|||
|
|||
if (args.CommandName == RemoveProxyCommand.Name) |
|||
{ |
|||
RemoveProxy(args, output); |
|||
return; |
|||
} |
|||
|
|||
var applicationApiDescriptionModel = await GetApplicationApiDescriptionModelAsync(args); |
|||
var script = RemoveInitializedEventTrigger(_jQueryProxyScriptGenerator.CreateScript(applicationApiDescriptionModel)); |
|||
|
|||
Directory.CreateDirectory(Path.GetDirectoryName(output)); |
|||
|
|||
using (var writer = new StreamWriter(output)) |
|||
{ |
|||
await writer.WriteAsync(script); |
|||
} |
|||
|
|||
Logger.LogInformation($"Create {GetLoggerOutputPath(output, args.WorkDirectory)}"); |
|||
} |
|||
|
|||
private void RemoveProxy(GenerateProxyArgs args, string filePath) |
|||
{ |
|||
if (File.Exists(filePath)) |
|||
{ |
|||
File.Delete(filePath); |
|||
} |
|||
|
|||
Logger.LogInformation($"Delete {GetLoggerOutputPath(filePath, args.WorkDirectory)}"); |
|||
} |
|||
|
|||
private static 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 found in the directory. The working directory must have a Web project file."); |
|||
} |
|||
} |
|||
|
|||
private static string RemoveInitializedEventTrigger(string script) |
|||
{ |
|||
return script.Replace(EventTriggerScript, string.Empty); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Volo.Abp.Cli.Http; |
|||
using Volo.Abp.Http.Modeling; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace Volo.Abp.Cli.ServiceProxying |
|||
{ |
|||
public abstract class ServiceProxyGeneratorBase<T> : IServiceProxyGenerator where T: IServiceProxyGenerator |
|||
{ |
|||
public IJsonSerializer JsonSerializer { get; } |
|||
|
|||
public CliHttpClientFactory CliHttpClientFactory { get; } |
|||
|
|||
public ILogger<T> Logger { get; set; } |
|||
|
|||
protected ServiceProxyGeneratorBase(CliHttpClientFactory cliHttpClientFactory, IJsonSerializer jsonSerializer) |
|||
{ |
|||
CliHttpClientFactory = cliHttpClientFactory; |
|||
JsonSerializer = jsonSerializer; |
|||
Logger = NullLogger<T>.Instance; |
|||
} |
|||
|
|||
public abstract Task GenerateProxyAsync(GenerateProxyArgs args); |
|||
|
|||
protected virtual async Task<ApplicationApiDescriptionModel> GetApplicationApiDescriptionModelAsync(GenerateProxyArgs args) |
|||
{ |
|||
Check.NotNull(args.Url, nameof(args.Url)); |
|||
|
|||
var client = CliHttpClientFactory.CreateClient(); |
|||
|
|||
var apiDefinitionResult = await client.GetStringAsync(CliUrls.GetApiDefinitionUrl(args.Url)); |
|||
var apiDefinition = JsonSerializer.Deserialize<ApplicationApiDescriptionModel>(apiDefinitionResult); |
|||
|
|||
var moduleDefinition = apiDefinition.Modules.FirstOrDefault(x => string.Equals(x.Key, args.Module, StringComparison.CurrentCultureIgnoreCase)).Value; |
|||
if (moduleDefinition == null) |
|||
{ |
|||
throw new CliUsageException($"Module name: {args.Module} is invalid"); |
|||
} |
|||
|
|||
var apiDescriptionModel = ApplicationApiDescriptionModel.Create(); |
|||
apiDescriptionModel.AddModule(moduleDefinition); |
|||
|
|||
return apiDescriptionModel; |
|||
} |
|||
|
|||
protected string GetLoggerOutputPath(string path, string workDirectory) |
|||
{ |
|||
return path.Replace(workDirectory, string.Empty).TrimStart(Path.DirectorySeparatorChar); |
|||
} |
|||
} |
|||
} |
|||
@ -1,4 +1,4 @@ |
|||
namespace Volo.Abp //TODO: Change namespace to Volo.Abp.Data
|
|||
namespace Volo.Abp |
|||
{ |
|||
/// <summary>
|
|||
/// Used to standardize soft deleting entities.
|
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue