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 System.Net.Http; |
||||
using Volo.Abp.DependencyInjection; |
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Http.Client; |
||||
using Volo.Abp.Http.Client.DynamicProxying; |
using Volo.Abp.Http.Client.DynamicProxying; |
||||
|
using Volo.Abp.Http.Client.Proxying; |
||||
|
|
||||
namespace Volo.Abp.AspNetCore.TestBase.DynamicProxying |
namespace Volo.Abp.AspNetCore.TestBase.DynamicProxying |
||||
{ |
{ |
||||
[Dependency(ReplaceServices = true)] |
[Dependency(ReplaceServices = true)] |
||||
public class AspNetCoreTestDynamicProxyHttpClientFactory : IDynamicProxyHttpClientFactory, ITransientDependency |
public class AspNetCoreTestProxyHttpClientFactory : IProxyHttpClientFactory, ITransientDependency |
||||
{ |
{ |
||||
private readonly ITestServerAccessor _testServerAccessor; |
private readonly ITestServerAccessor _testServerAccessor; |
||||
|
|
||||
public AspNetCoreTestDynamicProxyHttpClientFactory( |
public AspNetCoreTestProxyHttpClientFactory( |
||||
ITestServerAccessor testServerAccessor) |
ITestServerAccessor testServerAccessor) |
||||
{ |
{ |
||||
_testServerAccessor = 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 |
namespace Volo.Abp.Cli.Commands |
||||
{ |
{ |
||||
public class GenerateProxyCommand : ProxyCommandBase |
public class GenerateProxyCommand : ProxyCommandBase<GenerateProxyCommand> |
||||
{ |
{ |
||||
public const string Name = "generate-proxy"; |
public const string Name = "generate-proxy"; |
||||
|
|
||||
protected override string CommandName => Name; |
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) |
public override string GetUsageInfo() |
||||
: base(cliService, cmdHelper) |
|
||||
{ |
{ |
||||
|
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() |
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 |
namespace Volo.Abp.Cli.Commands |
||||
{ |
{ |
||||
public class RemoveProxyCommand : ProxyCommandBase |
public class RemoveProxyCommand : ProxyCommandBase<RemoveProxyCommand> |
||||
{ |
{ |
||||
public const string Name = "remove-proxy"; |
public const string Name = "remove-proxy"; |
||||
|
|
||||
protected override string CommandName => Name; |
protected override string CommandName => Name; |
||||
|
|
||||
protected override string SchematicsCommandName => "proxy-remove"; |
|
||||
|
|
||||
public RemoveProxyCommand( |
public RemoveProxyCommand( |
||||
CliService cliService, |
IOptions<AbpCliServiceProxyOptions> serviceProxyOptions, |
||||
ICmdHelper cmdHelper) |
IHybridServiceScopeFactory serviceScopeFactory) |
||||
: base(cliService, cmdHelper) |
: 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() |
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>
|
/// <summary>
|
||||
/// Used to standardize soft deleting entities.
|
/// Used to standardize soft deleting entities.
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue