@ -1,16 +1,16 @@ |
|||
<Project> |
|||
<PropertyGroup> |
|||
<LangVersion>latest</LangVersion> |
|||
<Version>3.1.0</Version> |
|||
<NoWarn>$(NoWarn);CS1591</NoWarn> |
|||
<Version>3.2.0</Version> |
|||
<NoWarn>$(NoWarn);CS1591;CS0436</NoWarn> |
|||
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl> |
|||
<PackageProjectUrl>https://abp.io/</PackageProjectUrl> |
|||
<PackageLicenseFile>LICENSE.md</PackageLicenseFile> |
|||
<PackageLicenseExpression>LGPL-3.0-only</PackageLicenseExpression> |
|||
<RepositoryType>git</RepositoryType> |
|||
<RepositoryUrl>https://github.com/abpframework/abp/</RepositoryUrl> |
|||
<GenerateDocumentationFile>true</GenerateDocumentationFile> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.3" PrivateAssets="All" /> |
|||
<None Include="LICENSE.md" Pack="true" PackagePath=""/> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,62 @@ |
|||
# ABP Framework 3.1 Final Has Been Released |
|||
|
|||
It is exciting for us to announce that we've released the ABP Framework & ABP Commercial 3.1 today. |
|||
|
|||
Since all the new features are already explained in details with the [3.1 RC Announcement Post](https://blog.abp.io/abp/ABP-Framework-v3.1-RC-Has-Been-Released), I will not repeat all the details here. Please read [the RC post](https://blog.abp.io/abp/ABP-Framework-v3.1-RC-Has-Been-Released) for **new feature and changes** you may need to do for your solution while upgrading to the version 3.1. |
|||
|
|||
## Creating New Solutions |
|||
|
|||
You can create a new solution with the ABP Framework version 3.1 by either using the `abp new` command or using the **direct download** tab on the [get started page](https://abp.io/get-started). |
|||
|
|||
> See the [getting started document](https://docs.abp.io/en/abp/latest/Getting-Started) for details. |
|||
|
|||
## How to Upgrade an Existing Solution |
|||
|
|||
### Install/Update the ABP CLI |
|||
|
|||
First of all, install the ABP CLI or upgrade to the latest version. |
|||
|
|||
If you haven't installed yet: |
|||
|
|||
````bash |
|||
dotnet tool install -g Volo.Abp.Cli |
|||
```` |
|||
|
|||
To update an existing installation: |
|||
|
|||
```bash |
|||
dotnet tool update -g Volo.Abp.Cli |
|||
``` |
|||
|
|||
### ABP UPDATE Command |
|||
|
|||
[ABP CLI](https://docs.abp.io/en/abp/latest/CLI) provides a handy command to update all the ABP related NuGet and NPM packages in your solution with a single command: |
|||
|
|||
````bash |
|||
abp update |
|||
```` |
|||
|
|||
After the update command, check [the RC blog post](https://blog.abp.io/abp/ABP-Framework-v3.1-RC-Has-Been-Released) to learn if you need to make any changes in your solution. |
|||
|
|||
> You may want to see the new [upgrading document](https://docs.abp.io/en/abp/latest/Upgrading). |
|||
|
|||
## About the version 3.2 |
|||
|
|||
The planned schedule for the version 3.2 is like that; |
|||
|
|||
* **September 17, 2020**: 3.2.0-rc.1 (release candidate) |
|||
* **October 1, 2020**: 3.2.0 final (stable) |
|||
|
|||
You can check [the GitHub milestone](https://github.com/abpframework/abp/milestone/39) to see the features/issues we are working on. |
|||
|
|||
## ABP Community & Articles |
|||
|
|||
We had lunched the [ABP Community web site](https://community.abp.io/) a few weeks before. The core ABP team and the ABP community have started to create content for the community. |
|||
|
|||
Here, the last three articles from the ABP Community: |
|||
|
|||
* [ABP Suite: How to Add the User Entity as a Navigation Property of Another Entity](https://community.abp.io/articles/abp-suite-how-to-add-the-user-entity-as-a-navigation-property-of-another-entity-furp75ex) by [@ebicoglu](https://github.com/ebicoglu) |
|||
* [Reuse ABP vNext Modules to Quickly Implement Application Features](https://community.abp.io/articles/reuse-abp-vnext-modules-to-quickly-implement-application-features-tdtmwd9w) by [@gdlcf88](https://github.com/gdlcf88) |
|||
* [Using DevExtreme Components With the ABP Framework](https://community.abp.io/articles/using-devextreme-components-with-the-abp-framework-zb8z7yqv) by [@cotur](https://github.com/cotur). |
|||
|
|||
We are looking for your contributions; You can [submit your article](https://community.abp.io/articles/submit)! We will promote your article to the community. |
|||
|
Before Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 539 KiB |
|
After Width: | Height: | Size: 175 KiB |
@ -0,0 +1,167 @@ |
|||
# Distributed Event Bus Kafka Integration |
|||
|
|||
> This document explains **how to configure the [Kafka](https://kafka.apache.org/)** as the distributed event bus provider. See the [distributed event bus document](Distributed-Event-Bus.md) to learn how to use the distributed event bus system |
|||
|
|||
## Installation |
|||
|
|||
Use the ABP CLI to add [Volo.Abp.EventBus.Kafka](https://www.nuget.org/packages/Volo.Abp.EventBus.Kafka) NuGet package to your project: |
|||
|
|||
* Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before. |
|||
* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.EventBus.Kafka` package. |
|||
* Run `abp add-package Volo.Abp.EventBus.Kafka` command. |
|||
|
|||
If you want to do it manually, install the [Volo.Abp.EventBus.Kafka](https://www.nuget.org/packages/Volo.Abp.EventBus.Kafka) NuGet package to your project and add `[DependsOn(typeof(AbpEventBusKafkaModule))]` to the [ABP module](Module-Development-Basics.md) class inside your project. |
|||
|
|||
## Configuration |
|||
|
|||
You can configure using the standard [configuration system](Configuration.md), like using the `appsettings.json` file, or using the [options](Options.md) classes. |
|||
|
|||
### `appsettings.json` file configuration |
|||
|
|||
This is the simplest way to configure the Kafka settings. It is also very strong since you can use any other configuration source (like environment variables) that is [supported by the AspNet Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/). |
|||
|
|||
**Example: The minimal configuration to connect to a local kafka server with default configurations** |
|||
|
|||
````json |
|||
{ |
|||
"Kafka": { |
|||
"Connections": { |
|||
"Default": { |
|||
"BootstrapServers": "localhost:9092" |
|||
} |
|||
}, |
|||
"EventBus": { |
|||
"GroupId": "MyGroupId", |
|||
"TopicName": "MyTopicName" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* `MyGroupId` is the name of this application, which is used as the **GroupId** on the Kakfa. |
|||
* `MyTopicName` is the **topic name**. |
|||
|
|||
See [the Kafka document](https://docs.confluent.io/current/clients/confluent-kafka-dotnet/api/Confluent.Kafka.html) to understand these options better. |
|||
|
|||
#### Connections |
|||
|
|||
If you need to connect to another server than the localhost, you need to configure the connection properties. |
|||
|
|||
**Example: Specify the host name (as an IP address)** |
|||
|
|||
````json |
|||
{ |
|||
"Kafka": { |
|||
"Connections": { |
|||
"Default": { |
|||
"BootstrapServers": "123.123.123.123:9092" |
|||
} |
|||
}, |
|||
"EventBus": { |
|||
"GroupId": "MyGroupId", |
|||
"TopicName": "MyTopicName" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Defining multiple connections is allowed. In this case, you can specify the connection that is used for the event bus. |
|||
|
|||
**Example: Declare two connections and use one of them for the event bus** |
|||
|
|||
````json |
|||
{ |
|||
"Kafka": { |
|||
"Connections": { |
|||
"Default": { |
|||
"BootstrapServers": "123.123.123.123:9092" |
|||
}, |
|||
"SecondConnection": { |
|||
"BootstrapServers": "321.321.321.321:9092" |
|||
} |
|||
}, |
|||
"EventBus": { |
|||
"GroupId": "MyGroupId", |
|||
"TopicName": "MyTopicName", |
|||
"ConnectionName": "SecondConnection" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
This allows you to use multiple RabbitMQ server in your application, but select one of them for the event bus. |
|||
|
|||
You can use any of the [ClientConfig](https://docs.confluent.io/current/clients/confluent-kafka-dotnet/api/Confluent.Kafka.ClientConfig.html) properties as the connection properties. |
|||
|
|||
**Example: Specify the socket timeout** |
|||
|
|||
````json |
|||
{ |
|||
"Kafka": { |
|||
"Connections": { |
|||
"Default": { |
|||
"BootstrapServers": "123.123.123.123:9092", |
|||
"SocketTimeoutMs": 60000 |
|||
} |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
### The Options Classes |
|||
|
|||
`AbpRabbitMqOptions` and `AbpRabbitMqEventBusOptions` classes can be used to configure the connection strings and event bus options for the RabbitMQ. |
|||
|
|||
You can configure this options inside the `ConfigureServices` of your [module](Module-Development-Basics.md). |
|||
|
|||
**Example: Configure the connection** |
|||
|
|||
````csharp |
|||
Configure<AbpKafkaOptions>(options => |
|||
{ |
|||
options.Connections.Default.BootstrapServers = "123.123.123.123:9092"; |
|||
options.Connections.Default.SaslUsername = "user"; |
|||
options.Connections.Default.SaslPassword = "pwd"; |
|||
}); |
|||
```` |
|||
|
|||
**Example: Configure the consumer config** |
|||
|
|||
````csharp |
|||
Configure<AbpKafkaOptions>(options => |
|||
{ |
|||
options.ConfigureConsumer = config => |
|||
{ |
|||
config.GroupId = "MyGroupId"; |
|||
config.EnableAutoCommit = false; |
|||
}; |
|||
}); |
|||
```` |
|||
|
|||
**Example: Configure the producer config** |
|||
|
|||
````csharp |
|||
Configure<AbpKafkaOptions>(options => |
|||
{ |
|||
options.ConfigureProducer = config => |
|||
{ |
|||
config.MessageTimeoutMs = 6000; |
|||
config.Acks = Acks.All; |
|||
}; |
|||
}); |
|||
```` |
|||
|
|||
**Example: Configure the topic specification** |
|||
|
|||
````csharp |
|||
Configure<AbpKafkaOptions>(options => |
|||
{ |
|||
options.ConfigureTopic = specification => |
|||
{ |
|||
specification.ReplicationFactor = 3; |
|||
specification.NumPartitions = 3; |
|||
}; |
|||
}); |
|||
```` |
|||
|
|||
Using these options classes can be combined with the `appsettings.json` way. Configuring an option property in the code overrides the value in the configuration file. |
|||
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 12 KiB |
@ -0,0 +1,41 @@ |
|||
# Upgrading the ABP Framework |
|||
|
|||
This document explains how to upgrade your existing solution when a new ABP Framework version is published. |
|||
|
|||
## ABP UPDATE Command |
|||
|
|||
ABP Framework & module ecosystem consist of hundreds of NuGet and NPM packages. It would be tedious to manually update all these packages to upgrade your application. |
|||
|
|||
[ABP CLI](CLI.md) provides a handy command to update all the ABP related NuGet and NPM packages in your solution with a single command: |
|||
|
|||
````bash |
|||
abp update |
|||
```` |
|||
|
|||
Run this command in the terminal while you are in the root folder of your solution. |
|||
|
|||
> If your solution has the Angular UI, you probably have `aspnet-core` and `angular` folders in the solution. Run this command in the parent folder of these two folders. |
|||
|
|||
## The Blog Posts |
|||
|
|||
Sometimes we introduce new features/changes that requires to make changes in the startup template. We already implement the changes in the startup template for new applications. However, in some cases you need to manually make some minor changes in your solution. |
|||
|
|||
Whenever you upgrade your solution, it is strongly suggested to check the [ABP BLOG](https://blog.abp.io/?_ga=2.177248992.411298747.1597771169-1910388957.1594128976) to learn the new features and changes coming with the new version. We regularly publish posts and write these kind of changes. If the changes are not trivial, we also provide migration guides. |
|||
|
|||
## Semantic Versioning & Breaking Changes |
|||
|
|||
We are working hard to keep the semantic versioning rules, so you don't get breaking changes for minor (feature) versions like 3.1, 3.2, 3.3... |
|||
|
|||
However, there are some cases we may introduce breaking changes in feature versions too; |
|||
|
|||
* ABP has many integration packages and sometimes the integrated libraries/frameworks releases major versions and makes breaking changes. In such cases, we carefully check these changes and decide to upgrade the integration package or not. If the impact of the change is relatively small, we update the integration package and explain the change in the release blog post. In such a case, if you've used this integration package, you should follow the instructions explained in the blog post. If the change may break many applications and not easy to fix, we decide to wait this upgrade until the next major ABP Framework release. |
|||
* Sometimes we have to make breaking change to fix a major bug or usage problem. In this case, we think that developer already can't properly use that feature, so no problem to fix it with a breaking change. In such cases, the feature will generally be a rarely used feature. Again, we try to keep the impact minimum. |
|||
|
|||
## Preview Releases & Nightly Builds |
|||
|
|||
Preview releases and nightly builds can help you to try new features and adapt your solution earlier than a new stable release. |
|||
|
|||
* [Preview releases](Previews.md) are typically published ~2 weeks before a minor (feature) version (our minor version development cycle is about ~4 weeks). |
|||
* [Nightly builds](Nightly-Builds.md) are published in every night (except weekends) from the development branch. That means you can try the previous day's development. |
|||
|
|||
Refer to the their documents to learn details about these kind of releases. |
|||
@ -0,0 +1,163 @@ |
|||
# 分布式事件总线Kafka集成 |
|||
|
|||
> 本文解释了**如何配置[Kafka](https://kafka.apache.org/)**做为分布式总线提供程序. 参阅[分布式事件总线文档](Distributed-Event-Bus.md)了解如何使用分布式事件总线系统. |
|||
|
|||
## 安装 |
|||
|
|||
使用ABP CLI添加[Volo.Abp.EventBus.Kafka[Volo.Abp.EventBus.Kafka](https://www.nuget.org/packages/Volo.Abp.EventBus.Kafka)NuGet包到你的项目: |
|||
|
|||
* 安装[ABP CLI](https://docs.abp.io/en/abp/latest/CLI),如果你还没有安装. |
|||
* 在你想要安装 `Volo.Abp.EventBus.Kafka` 包的 `.csproj` 文件目录打开命令行(终端). |
|||
* 运行 `abp add-package Volo.Abp.EventBus.Kafka` 命令. |
|||
|
|||
如果你想要手动安装,安装[Volo.Abp.EventBus.Kafka](https://www.nuget.org/packages/Volo.Abp.EventBus.Kafka) NuGet 包到你的项目然后添加 `[DependsOn(typeof(AbpEventBusKafkaModule))]` 到你的项目[模块](Module-Development-Basics.md)类. |
|||
|
|||
## 配置 |
|||
|
|||
可以使用配置使用标准的[配置系统](Configuration.md),如 `appsettings.json` 文件,或[选项](Options.md)类. |
|||
|
|||
### `appsettings.json` 文件配置 |
|||
|
|||
这是配置Kafka设置最简单的方法. 它也非常强大,因为你可以使用[由AspNet Core支持的](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/)的任何其他配置源(如环境变量). |
|||
|
|||
**示例:最小化配置与默认配置连接到本地的Kafka服务器** |
|||
|
|||
|
|||
````json |
|||
{ |
|||
"Kafka": { |
|||
"EventBus": { |
|||
"GroupId": "MyGroupId", |
|||
"TopicName": "MyTopicName" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* `MyGroupId` 是应用程序的名称,用于Kafka的**GroupId**. |
|||
* `MyTopicName` 是**topic名称**. |
|||
|
|||
参阅[Kafka文档](https://docs.confluent.io/current/clients/confluent-kafka-dotnet/api/Confluent.Kafka.html)更好的了解这些选项. |
|||
|
|||
#### 连接 |
|||
|
|||
如果需要连接到本地主机以外的另一台服务器,需要配置连接属性. |
|||
|
|||
**示例: 指定主机名 (如IP地址)** |
|||
|
|||
````json |
|||
{ |
|||
"Kafka": { |
|||
"Connections": { |
|||
"Default": { |
|||
"BootstrapServers": "123.123.123.123:9092" |
|||
} |
|||
}, |
|||
"EventBus": { |
|||
"GroupId": "MyGroupId", |
|||
"TopicName": "MyTopicName" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
允许定义多个连接. 在这种情况下,你可以指定用于事件总线的连接. |
|||
|
|||
**示例: 声明两个连接并将其中一个用于事件总线** |
|||
|
|||
````json |
|||
{ |
|||
"Kafka": { |
|||
"Connections": { |
|||
"Default": { |
|||
"BootstrapServers": "123.123.123.123:9092" |
|||
}, |
|||
"SecondConnection": { |
|||
"BootstrapServers": "321.321.321.321:9092" |
|||
} |
|||
}, |
|||
"EventBus": { |
|||
"GroupId": "MyGroupId", |
|||
"TopicName": "MyTopicName", |
|||
"ConnectionName": "SecondConnection" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
这允许你可以在你的应用程序使用多个Kafka服务器,但将其中一个做为事件总线. |
|||
|
|||
你可以使用任何[ClientConfig](https://docs.confluent.io/current/clients/confluent-kafka-dotnet/api/Confluent.Kafka.ClientConfig.html)属性作为连接属性. |
|||
|
|||
**示例: 指定socket超时时间** |
|||
|
|||
````json |
|||
{ |
|||
"Kafka": { |
|||
"Connections": { |
|||
"Default": { |
|||
"BootstrapServers": "123.123.123.123:9092", |
|||
"SocketTimeoutMs": 60000 |
|||
} |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
### 选项类 |
|||
|
|||
`AbpKafkaOptions` 和 `AbpKafkaEventBusOptions` 类用于配置Kafka的连接字符串和事件总线选项. |
|||
|
|||
你可以在你的[模块](Module-Development-Basics.md)的 `ConfigureServices` 方法配置选项. |
|||
|
|||
**示例: 配置连接** |
|||
|
|||
````csharp |
|||
Configure<AbpKafkaOptions>(options => |
|||
{ |
|||
options.Connections.Default.BootstrapServers = "123.123.123.123:9092"; |
|||
options.Connections.Default.SaslUsername = "user"; |
|||
options.Connections.Default.SaslPassword = "pwd"; |
|||
}); |
|||
```` |
|||
|
|||
**示例: 配置 consumer config** |
|||
|
|||
````csharp |
|||
Configure<AbpKafkaOptions>(options => |
|||
{ |
|||
options.ConfigureConsumer = config => |
|||
{ |
|||
config.GroupId = "MyGroupId"; |
|||
config.EnableAutoCommit = false; |
|||
}; |
|||
}); |
|||
```` |
|||
|
|||
**示例: 配置 producer config** |
|||
|
|||
````csharp |
|||
Configure<AbpKafkaOptions>(options => |
|||
{ |
|||
options.ConfigureProducer = config => |
|||
{ |
|||
config.MessageTimeoutMs = 6000; |
|||
config.Acks = Acks.All; |
|||
}; |
|||
}); |
|||
```` |
|||
|
|||
**示例: 配置 topic specification** |
|||
|
|||
````csharp |
|||
Configure<AbpKafkaOptions>(options => |
|||
{ |
|||
options.ConfigureTopic = specification => |
|||
{ |
|||
specification.ReplicationFactor = 3; |
|||
specification.NumPartitions = 3; |
|||
}; |
|||
}); |
|||
```` |
|||
|
|||
使用这些选项类可以与 `appsettings.json` 组合在一起. 在代码中配置选项属性会覆盖配置文件中的值. |
|||
@ -0,0 +1,46 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using Microsoft.AspNetCore.Mvc.DataAnnotations; |
|||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; |
|||
using Microsoft.Extensions.Localization; |
|||
using Volo.Abp.Validation; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.DataAnnotations |
|||
{ |
|||
public class DynamicRangeAttributeAdapter : AttributeAdapterBase<DynamicRangeAttribute> |
|||
{ |
|||
private readonly string _max; |
|||
private readonly string _min; |
|||
|
|||
public DynamicRangeAttributeAdapter( |
|||
DynamicRangeAttribute attribute, |
|||
IStringLocalizer stringLocalizer) |
|||
: base(attribute, stringLocalizer) |
|||
{ |
|||
_min = Convert.ToString(Attribute.Minimum,CultureInfo.InvariantCulture); |
|||
_max = Convert.ToString(Attribute.Maximum,CultureInfo.InvariantCulture); |
|||
} |
|||
|
|||
public override void AddValidation(ClientModelValidationContext context) |
|||
{ |
|||
Check.NotNull(context, nameof(context)); |
|||
|
|||
MergeAttribute(context.Attributes, "data-val", "true"); |
|||
MergeAttribute(context.Attributes, "data-val-range", GetErrorMessage(context)); |
|||
MergeAttribute(context.Attributes, "data-val-range-min", _min); |
|||
MergeAttribute(context.Attributes, "data-val-range-max", _max); |
|||
} |
|||
|
|||
public override string GetErrorMessage(ModelValidationContextBase validationContext) |
|||
{ |
|||
Check.NotNull(validationContext, nameof(validationContext)); |
|||
|
|||
return GetErrorMessage( |
|||
validationContext.ModelMetadata, |
|||
validationContext.ModelMetadata.GetDisplayName(), |
|||
Attribute.Minimum, |
|||
Attribute.Maximum |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -1,140 +1,11 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Newtonsoft.Json.Linq; |
|||
using Volo.Abp.Cli.Args; |
|||
using Volo.Abp.Cli.Utils; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.Commands |
|||
{ |
|||
public class GenerateProxyCommand : IConsoleCommand, ITransientDependency |
|||
public class GenerateProxyCommand : ProxyCommandBase |
|||
{ |
|||
public Task ExecuteAsync(CommandLineArgs commandLineArgs) |
|||
{ |
|||
CheckAngularJsonFile(); |
|||
CheckNgSchematics(); |
|||
|
|||
var prompt = commandLineArgs.Options.ContainsKey("p") || commandLineArgs.Options.ContainsKey("prompt"); |
|||
var defaultValue = prompt ? null : "__default"; |
|||
|
|||
var module = commandLineArgs.Options.GetOrNull(Options.Module.Short, Options.Module.Long) ?? defaultValue; |
|||
var source = commandLineArgs.Options.GetOrNull(Options.Source.Short, Options.Source.Long) ?? defaultValue; |
|||
var target = commandLineArgs.Options.GetOrNull(Options.Target.Short, Options.Target.Long) ?? defaultValue; |
|||
|
|||
var commandBuilder = new StringBuilder("npx ng g @abp/ng.schematics:proxy"); |
|||
|
|||
if (module != null) |
|||
{ |
|||
commandBuilder.Append($" --module {module}"); |
|||
} |
|||
|
|||
if (source != null) |
|||
{ |
|||
commandBuilder.Append($" --source {source}"); |
|||
} |
|||
|
|||
if (target != null) |
|||
{ |
|||
commandBuilder.Append($" --target {target}"); |
|||
} |
|||
|
|||
CmdHelper.RunCmd(commandBuilder.ToString()); |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
private void CheckNgSchematics() |
|||
{ |
|||
var packageJsonPath = $"package.json"; |
|||
|
|||
if (!File.Exists(packageJsonPath)) |
|||
{ |
|||
throw new CliUsageException( |
|||
"package.json file not found" + |
|||
Environment.NewLine + |
|||
GetUsageInfo() |
|||
); |
|||
} |
|||
|
|||
var schematicsPackageNode = |
|||
(string) JObject.Parse(File.ReadAllText(packageJsonPath))["devDependencies"]?["@abp/ng.schematics"]; |
|||
|
|||
if (schematicsPackageNode == null) |
|||
{ |
|||
throw new CliUsageException( |
|||
"\"@abp/ng.schematics\" NPM package should be installed to the devDependencies before running this command!" + |
|||
Environment.NewLine + |
|||
GetUsageInfo() |
|||
); |
|||
} |
|||
} |
|||
|
|||
private 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." + |
|||
Environment.NewLine + Environment.NewLine + |
|||
GetUsageInfo() |
|||
); |
|||
} |
|||
} |
|||
|
|||
public string GetUsageInfo() |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
|
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Usage:"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine(" abp generate-proxy"); |
|||
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("-s|--source <source-name> (default: 'defaultProject') Angular project name to resolve the root namespace & API definition URL from."); |
|||
sb.AppendLine("-t|--target <target-name> (default: 'defaultProject') Angular project name to place generated code in."); |
|||
sb.AppendLine("-p|--prompt Asks the options from the command line prompt (for the missing options)"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public string GetShortDescription() |
|||
{ |
|||
return "Generates Angular service proxies and DTOs to consume HTTP APIs."; |
|||
} |
|||
|
|||
public static class Options |
|||
{ |
|||
public static class Module |
|||
{ |
|||
public const string Short = "m"; |
|||
public const string Long = "module"; |
|||
} |
|||
|
|||
public static class Source |
|||
{ |
|||
public const string Short = "s"; |
|||
public const string Long = "source"; |
|||
} |
|||
public const string Name = "generate-proxy"; |
|||
|
|||
public static class Target |
|||
{ |
|||
public const string Short = "t"; |
|||
public const string Long = "target"; |
|||
} |
|||
protected override string CommandName => Name; |
|||
|
|||
public static class Prompt |
|||
{ |
|||
public const string Short = "p"; |
|||
public const string Long = "prompt"; |
|||
} |
|||
} |
|||
protected override string SchematicsCommandName => "proxy-add"; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,157 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Newtonsoft.Json.Linq; |
|||
using Volo.Abp.Cli.Args; |
|||
using Volo.Abp.Cli.Utils; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.Commands |
|||
{ |
|||
public abstract class ProxyCommandBase : IConsoleCommand, ITransientDependency |
|||
{ |
|||
protected abstract string CommandName { get; } |
|||
|
|||
protected abstract string SchematicsCommandName { get; } |
|||
|
|||
public Task ExecuteAsync(CommandLineArgs commandLineArgs) |
|||
{ |
|||
CheckAngularJsonFile(); |
|||
CheckNgSchematics(); |
|||
|
|||
var prompt = commandLineArgs.Options.ContainsKey("p") || commandLineArgs.Options.ContainsKey("prompt"); |
|||
var defaultValue = prompt ? null : "__default"; |
|||
|
|||
var module = commandLineArgs.Options.GetOrNull(Options.Module.Short, Options.Module.Long) ?? defaultValue; |
|||
var apiName = commandLineArgs.Options.GetOrNull(Options.ApiName.Short, Options.ApiName.Long) ?? defaultValue; |
|||
var source = commandLineArgs.Options.GetOrNull(Options.Source.Short, Options.Source.Long) ?? defaultValue; |
|||
var target = commandLineArgs.Options.GetOrNull(Options.Target.Short, Options.Target.Long) ?? 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()); |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
private void CheckNgSchematics() |
|||
{ |
|||
var packageJsonPath = $"package.json"; |
|||
|
|||
if (!File.Exists(packageJsonPath)) |
|||
{ |
|||
throw new CliUsageException( |
|||
"package.json file not found" + |
|||
Environment.NewLine + |
|||
GetUsageInfo() |
|||
); |
|||
} |
|||
|
|||
var schematicsPackageNode = |
|||
(string) JObject.Parse(File.ReadAllText(packageJsonPath))["devDependencies"]?["@abp/ng.schematics"]; |
|||
|
|||
if (schematicsPackageNode == null) |
|||
{ |
|||
throw new CliUsageException( |
|||
"\"@abp/ng.schematics\" NPM package should be installed to the devDependencies before running this command!" + |
|||
Environment.NewLine + |
|||
GetUsageInfo() |
|||
); |
|||
} |
|||
} |
|||
|
|||
private 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." + |
|||
Environment.NewLine + Environment.NewLine + |
|||
GetUsageInfo() |
|||
); |
|||
} |
|||
} |
|||
|
|||
public string GetUsageInfo() |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
|
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Usage:"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine($" abp {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("-a|--api-name <module-name> (default: 'default') The name of the API endpoint defined in the /src/environments/environment.ts."); |
|||
sb.AppendLine("-s|--source <source-name> (default: 'defaultProject') Angular project name to resolve the root namespace & API definition URL from."); |
|||
sb.AppendLine("-t|--target <target-name> (default: 'defaultProject') Angular project name to place generated code in."); |
|||
sb.AppendLine("-p|--prompt Asks the options from the command line prompt (for the missing options)"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public string GetShortDescription() |
|||
{ |
|||
return "Generates Angular service proxies and DTOs to consume HTTP APIs."; |
|||
} |
|||
|
|||
public static class Options |
|||
{ |
|||
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 Target |
|||
{ |
|||
public const string Short = "t"; |
|||
public const string Long = "target"; |
|||
} |
|||
|
|||
public static class Prompt |
|||
{ |
|||
public const string Short = "p"; |
|||
public const string Long = "prompt"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
namespace Volo.Abp.Cli.Commands |
|||
{ |
|||
public class RemoveProxyCommand : ProxyCommandBase |
|||
{ |
|||
public const string Name = "remove-proxy"; |
|||
|
|||
protected override string CommandName => Name; |
|||
|
|||
protected override string SchematicsCommandName => "proxy-remove"; |
|||
} |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using System.Diagnostics; |
|||
using System.Reflection; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.Validation |
|||
{ |
|||
public class DynamicRangeAttribute : RangeAttribute |
|||
{ |
|||
private static readonly FieldInfo MaximumField; |
|||
private static readonly FieldInfo MinimumField; |
|||
|
|||
static DynamicRangeAttribute() |
|||
{ |
|||
MaximumField = typeof(RangeAttribute).GetField( |
|||
"<Maximum>k__BackingField", |
|||
BindingFlags.Instance | BindingFlags.NonPublic |
|||
); |
|||
Debug.Assert(MaximumField != null, nameof(MaximumField) + " != null"); |
|||
|
|||
MinimumField = typeof(RangeAttribute).GetField( |
|||
"<Minimum>k__BackingField", |
|||
BindingFlags.Instance | BindingFlags.NonPublic |
|||
); |
|||
Debug.Assert(MinimumField != null, nameof(MinimumField) + " != null"); |
|||
} |
|||
|
|||
/// <param name="sourceType">A type to get the values of the properties</param>
|
|||
/// <param name="operandType">The type of the range parameters. Must implement IComparable. <see cref="RangeAttribute.OperandType"/></param>
|
|||
/// <param name="minimumPropertyName">The name of the public static property for the <see cref="RangeAttribute.Minimum"/></param>
|
|||
/// <param name="maximumPropertyName">The name of the public static property for the <see cref="RangeAttribute.Maximum"/></param>
|
|||
public DynamicRangeAttribute( |
|||
[NotNull] Type sourceType, |
|||
[NotNull] Type operandType, |
|||
[CanBeNull] string minimumPropertyName, |
|||
[CanBeNull] string maximumPropertyName |
|||
) |
|||
: base(operandType, string.Empty, string.Empty) |
|||
{ |
|||
Check.NotNull(sourceType, nameof(sourceType)); |
|||
|
|||
if (minimumPropertyName != null) |
|||
{ |
|||
var minimumProperty = sourceType.GetProperty( |
|||
minimumPropertyName, |
|||
BindingFlags.Static | BindingFlags.Public |
|||
); |
|||
Debug.Assert(minimumProperty != null, nameof(minimumProperty) + " != null"); |
|||
MinimumField.SetValue(this, minimumProperty.GetValue(null)); |
|||
} |
|||
|
|||
if (maximumPropertyName != null) |
|||
{ |
|||
var maximumProperty = sourceType.GetProperty( |
|||
maximumPropertyName, |
|||
BindingFlags.Static | BindingFlags.Public |
|||
); |
|||
Debug.Assert(maximumProperty != null, nameof(maximumProperty) + " != null"); |
|||
MaximumField.SetValue(this, maximumProperty.GetValue(null)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
var abp = abp || {}; |
|||
(function () { |
|||
|
|||
if (!luxon) { |
|||
throw "abp/luxon library requires the luxon library included to the page!"; |
|||
} |
|||
|
|||
/* TIMING *************************************************/ |
|||
|
|||
abp.timing = abp.timing || {}; |
|||
|
|||
var setObjectValue = function (obj, property, value) { |
|||
if (typeof property === "string") { |
|||
property = property.split('.'); |
|||
} |
|||
|
|||
if (property.length > 1) { |
|||
var p = property.shift(); |
|||
setObjectValue(obj[p], property, value); |
|||
} else { |
|||
obj[property[0]] = value; |
|||
} |
|||
} |
|||
|
|||
var getObjectValue = function (obj, property) { |
|||
return property.split('.').reduce((a, v) => a[v], obj) |
|||
} |
|||
|
|||
abp.timing.convertFieldsToIsoDate = function (form, fields) { |
|||
for (var field of fields) { |
|||
var dateTime = luxon.DateTime |
|||
.fromFormat( |
|||
getObjectValue(form, field), |
|||
abp.localization.currentCulture.dateTimeFormat.shortDatePattern, |
|||
{locale: abp.localization.currentCulture.cultureName} |
|||
); |
|||
|
|||
if (!dateTime.invalid) { |
|||
setObjectValue(form, field, dateTime.toFormat("yyyy-MM-dd HH:mm:ss")) |
|||
} |
|||
} |
|||
|
|||
return form; |
|||
} |
|||
|
|||
})(jQuery); |
|||