Browse Source

Merge branch 'dev' of https://github.com/volosoft/abp into dev

pull/2225/head
Alper Ebicoglu 7 years ago
parent
commit
7e2076e6db
  1. 221
      docs/en/Data-Filtering.md
  2. 3
      docs/en/Modules/Index.md
  3. 37
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs
  4. 21
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCacheKeyNormalizeArgs.cs
  5. 33
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCacheKeyNormalizer.cs
  6. 7
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCacheKeyNormalizer.cs
  7. 15
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
  8. 21
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/NamespaceHelper.cs
  9. 9
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json
  10. 10
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pt-BR.json
  11. 12
      modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs
  12. 2
      npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html
  13. 4
      npm/ng-packs/packages/theme-shared/src/lib/contants/styles.ts
  14. 5
      npm/packs/client-generator/.prettierrc
  15. 2
      npm/packs/client-generator/lib/angular.d.ts
  16. 57
      npm/packs/client-generator/lib/angular.js
  17. 1
      npm/packs/client-generator/lib/cli.d.ts
  18. 102
      npm/packs/client-generator/lib/cli.js
  19. 2
      npm/packs/client-generator/lib/index.d.ts
  20. 24
      npm/packs/client-generator/lib/index.js
  21. 3
      npm/packs/client-generator/lib/templates/angular/service-templates.d.ts
  22. 15
      npm/packs/client-generator/lib/templates/angular/service-templates.js
  23. 14
      npm/packs/client-generator/lib/types/api-defination.d.ts
  24. 2
      npm/packs/client-generator/lib/types/api-defination.js
  25. 1
      npm/packs/client-generator/lib/utils/axios.d.ts
  26. 2119
      npm/packs/client-generator/lib/utils/axios.js
  27. 2
      npm/packs/client-generator/lib/utils/prompt.d.ts
  28. 56
      npm/packs/client-generator/lib/utils/prompt.js
  29. 6
      npm/packs/client-generator/nodemon.json
  30. 39
      npm/packs/client-generator/package.json
  31. 20
      npm/packs/client-generator/src/angular.ts
  32. 39
      npm/packs/client-generator/src/cli.ts
  33. 28
      npm/packs/client-generator/src/index.ts
  34. 18
      npm/packs/client-generator/src/templates/angular/service-templates.ts
  35. 14
      npm/packs/client-generator/src/types/api-defination.ts
  36. 2109
      npm/packs/client-generator/src/utils/axios.ts
  37. 13
      npm/packs/client-generator/src/utils/prompt.ts
  38. 14
      npm/packs/client-generator/tsconfig.json
  39. 1947
      npm/packs/client-generator/yarn.lock
  40. 4
      test-all.ps1

221
docs/en/Data-Filtering.md

@ -1,3 +1,222 @@
# Data Filtering
TODO
[Volo.Abp.Data](https://www.nuget.org/packages/Volo.Abp.Data) package defines services to automatically filter data on querying from a database.
## Pre-Defined Filters
ABP defines some filters out of the box.
### ISoftDelete
Used to mark an [entity](Entities.md) as deleted instead of actually deleting it. Implement the `ISoftDelete` interface to make your entity "soft delete".
Example:
````csharp
using System;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
namespace Acme.BookStore
{
public class Book : AggregateRoot<Guid>, ISoftDelete
{
public string Name { get; set; }
public bool IsDeleted { get; set; } //Defined by ISoftDelete
}
}
````
`ISoftDelete` defines the `IsDeleted` property. When you delete a book using [repositories](Repositories.md), ABP automatically sets `IsDeleted` to true and protects it from actual deletion (you can also manually set the `IsDeleted` property to true if you need). In addition, it **automatically filters deleted entities** when you query the database.
> `ISoftDelete` filter is enabled by default and you can not get deleted entities from database unless you explicitly disable it. See the `IDataFilter` service below.
### IMultiTenant
[Multi-tenancy](Multi-Tenancy.md) is an efficient way of creating SaaS applications. Once you create a multi-tenant application, you typically want to isolate data between tenants. Implement `IMultiTenant` interface to make your entity "multi-tenant aware".
Example:
````csharp
using System;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Acme.BookStore
{
public class Book : AggregateRoot<Guid>, ISoftDelete, IMultiTenant
{
public string Name { get; set; }
public bool IsDeleted { get; set; } //Defined by ISoftDelete
public Guid? TenantId { get; set; } //Defined by IMultiTenant
}
}
````
`IMultiTenant` interface defines the `TenantId` property which is then used to automatically filter the entities for the current tenant. See the [Multi-tenancy](Multi-Tenancy.md) document for more.
## IDataFilter Service: Enable/Disable Data Filters
You can control the filters using `IDataFilter` service.
Example:
````csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore
{
public class MyBookService : ITransientDependency
{
private readonly IDataFilter _dataFilter;
private readonly IRepository<Book, Guid> _bookRepository;
public MyBookService(
IDataFilter dataFilter,
IRepository<Book, Guid> bookRepository)
{
_dataFilter = dataFilter;
_bookRepository = bookRepository;
}
public async Task<List<Book>> GetAllBooksIncludingDeletedAsync()
{
//Temporary disable the ISoftDelete filter
using (_dataFilter.Disable<ISoftDelete>())
{
return await _bookRepository.GetListAsync();
}
}
}
}
````
* [Inject](Dependency-Injection.md) the `IDataFilter` service to your class.
* Use the `Disable` method within a `using` statement to create a code block where the `ISoftDelete` filter is disabled inside it (Always use it inside a `using` block to guarantee that the filter is reset to its previous state).
`IDataFilter.Enable` method can be used to enable a filter. `Enable` and `Disable` methods can be used in a nested way to define inner scopes.
## AbpDataFilterOptions
`AbpDataFilterOptions` can be used to [set options](Options.md) for the data filter system.
The example code below disables the `ISoftDelete` filter by default which will cause to include deleted entities when you query the database unless you explicitly enable the filter:
````csharp
Configure<AbpDataFilterOptions>(options =>
{
options.DefaultStates[typeof(ISoftDelete)] = new DataFilterState(isEnabled: false);
});
````
> Carefully change defaults for global filters, especially if you are using a pre-built module which might be developed assuming the soft delete filter is turned on by default. But you can do it for your own defined filters safely.
## Defining Custom Filters
Defining and implementing a new filter highly depends on the database provider. ABP implements all pre-defined filters for all database providers.
When you need it, start by defining an interface (like `ISoftDelete` and `IMultiTenant`) for your filter and implement it for your entities.
Example:
````csharp
public interface IIsActive
{
bool IsActive { get; }
}
````
Such an `IIsActive` interface can be used to filter active/passive data and can be easily implemented by any [entity](Entities.md):
````csharp
public class Book : AggregateRoot<Guid>, IIsActive
{
public string Name { get; set; }
public bool IsActive { get; set; } //Defined by IIsActive
}
````
### EntityFramework Core
ABP uses [EF Core's Global Query Filters](https://docs.microsoft.com/en-us/ef/core/querying/filters) system for the [EF Core Integration](Entity-Framework-Core.md). So, it is well integrated to EF Core and works as expected even if you directly work with `DbContext`.
Best way to implement a custom filter is to override `CreateFilterExpression` method for your `DbContext`. Example:
````csharp
protected bool IsActiveFilterEnabled => DataFilter?.IsEnabled<IIsActive>() ?? false;
protected override Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()
{
var expression = base.CreateFilterExpression<TEntity>();
if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity)))
{
Expression<Func<TEntity, bool>> isActiveFilter =
e => !IsActiveFilterEnabled || EF.Property<bool>(e, "IsActive");
expression = expression == null
? isActiveFilter
: CombineExpressions(expression, isActiveFilter);
}
return expression;
}
````
* Added a `IsActiveFilterEnabled` property to check if `IIsActive` is enabled or not. It internally uses the `IDataFilter` service introduced before.
* Overrided the `CreateFilterExpression` method, checked if given entity implements the `IIsActive` interface and combines the expressions if necessary.
### MongoDB
ABP implements data filters directly in the [repository](Repositories.md) level for the [MongoDB Integration](MongoDB.md). So, it works only if you use the repositories properly. Otherwise, you should manually filter the data.
Currently, the best way to implement a data filter for the MongoDB integration is to override the `AddGlobalFilters` in the repository derived from the `MongoDbRepository` class. Example:
````csharp
public class BookRepository : MongoDbRepository<BookStoreMongoDbContext, Book, Guid>
{
protected override void AddGlobalFilters(List<FilterDefinition<Book>> filters)
{
if (DataFilter.IsEnabled<IIsActive>())
{
filters.Add(Builders<Book>.Filter.Eq(e => ((IIsActive)e).IsActive, true));
}
}
}
````
This example implements it only for the `Book` entity. If you want to implement for all entities (those implement the `IIsActive` interface), create your own custom MongoDB repository base class and override the `AddGlobalFilters` as shown below:
````csharp
public abstract class MyMongoRepository<TMongoDbContext, TEntity, TKey> : MongoDbRepository<TMongoDbContext, TEntity, TKey>
where TMongoDbContext : IAbpMongoDbContext
where TEntity : class, IEntity<TKey>
{
protected MyMongoRepository(IMongoDbContextProvider<TMongoDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
protected override void AddGlobalFilters(List<FilterDefinition<TEntity>> filters)
{
if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity))
&& DataFilter.IsEnabled<IIsActive>())
{
filters.Add(Builders<TEntity>.Filter.Eq(e => ((IIsActive)e).IsActive, true));
}
}
}
````
> See "Set Default Repository Classes" section of the [MongoDb Integration document](MongoDB.md) to learn how to replace default repository base with your custom class.

3
docs/en/Modules/Index.md

@ -19,7 +19,8 @@ There are some **free and open source** application modules developed and mainta
* **Identity**: Used to manage roles, users and their permissions.
* **Identity Server**: Integrates to IdentityServer4.
* **Permission Management**: Used to persist permissions.
* **[Setting Management](Setting-Management.md)**: Used to persist and manage the [settings](../Settings.md).
* [**Setting Management**](Setting-Management.md): Used to persist
and manage the [settings](../Settings.md).
* **Tenant Management**: Used to manage tenants for a [multi-tenant](../Multi-Tenancy.md) application.
* **Users**: Used to abstract users, so other modules can depend on this instead of the Identity module.

37
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs

@ -23,12 +23,12 @@ namespace Volo.Abp.Caching
IDistributedCache cache,
ICancellationTokenProvider cancellationTokenProvider,
IDistributedCacheSerializer serializer,
ICurrentTenant currentTenant) : base(
IDistributedCacheKeyNormalizer keyNormalizer) : base(
distributedCacheOption: distributedCacheOption,
cache: cache,
cancellationTokenProvider: cancellationTokenProvider,
serializer: serializer,
currentTenant: currentTenant)
keyNormalizer: keyNormalizer)
{
}
@ -54,7 +54,7 @@ namespace Volo.Abp.Caching
protected IDistributedCacheSerializer Serializer { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IDistributedCacheKeyNormalizer KeyNormalizer { get; }
protected SemaphoreSlim SyncSemaphore { get; }
@ -67,29 +67,29 @@ namespace Volo.Abp.Caching
IDistributedCache cache,
ICancellationTokenProvider cancellationTokenProvider,
IDistributedCacheSerializer serializer,
ICurrentTenant currentTenant)
IDistributedCacheKeyNormalizer keyNormalizer)
{
_distributedCacheOption = distributedCacheOption.Value;
Cache = cache;
CancellationTokenProvider = cancellationTokenProvider;
Logger = NullLogger<DistributedCache<TCacheItem, TCacheKey>>.Instance;
Serializer = serializer;
CurrentTenant = currentTenant;
KeyNormalizer = keyNormalizer;
SyncSemaphore = new SemaphoreSlim(1, 1);
SetDefaultOptions();
}
protected virtual string NormalizeKey(TCacheKey key)
{
var normalizedKey = "c:" + CacheName + ",k:" + _distributedCacheOption.KeyPrefix + key.ToString();
if (!IgnoreMultiTenancy && CurrentTenant.Id.HasValue)
{
normalizedKey = "t:" + CurrentTenant.Id.Value + "," + normalizedKey;
}
return normalizedKey;
return KeyNormalizer.NormalizeKey(
new DistributedCacheKeyNormalizeArgs(
key.ToString(),
CacheName,
IgnoreMultiTenancy
)
);
}
protected virtual DistributedCacheEntryOptions GetDefaultCacheEntryOptions()
@ -102,6 +102,7 @@ namespace Volo.Abp.Caching
return options;
}
}
return _distributedCacheOption.GlobalCacheEntryOptions;
}
@ -115,6 +116,7 @@ namespace Volo.Abp.Caching
//Configure default cache entry options
DefaultCacheOptions = GetDefaultCacheEntryOptions();
}
/// <summary>
/// Gets a cache item with the given key. If no cache item is found for the given key then returns null.
/// </summary>
@ -193,6 +195,7 @@ namespace Volo.Abp.Caching
return Serializer.Deserialize<TCacheItem>(cachedBytes);
}
/// <summary>
/// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item
/// provided by <paramref name="factory" /> delegate and returns the provided cache item.
@ -228,6 +231,7 @@ namespace Volo.Abp.Caching
return value;
}
/// <summary>
/// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item
/// provided by <paramref name="factory" /> delegate and returns the provided cache item.
@ -266,6 +270,7 @@ namespace Volo.Abp.Caching
return value;
}
/// <summary>
/// Sets the cache item value for the provided key.
/// </summary>
@ -300,6 +305,7 @@ namespace Volo.Abp.Caching
throw;
}
}
/// <summary>
/// Sets the cache item value for the provided key.
/// </summary>
@ -338,6 +344,7 @@ namespace Volo.Abp.Caching
throw;
}
}
/// <summary>
/// Refreshes the cache value of the given key, and resets its sliding expiration timeout.
/// </summary>
@ -393,6 +400,7 @@ namespace Volo.Abp.Caching
throw;
}
}
/// <summary>
/// Removes the cache item for given key from cache.
/// </summary>
@ -418,6 +426,7 @@ namespace Volo.Abp.Caching
throw;
}
}
/// <summary>
/// Removes the cache item for given key from cache.
/// </summary>
@ -447,7 +456,5 @@ namespace Volo.Abp.Caching
throw;
}
}
}
}

21
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCacheKeyNormalizeArgs.cs

@ -0,0 +1,21 @@
namespace Volo.Abp.Caching
{
public class DistributedCacheKeyNormalizeArgs
{
public string Key { get; }
public string CacheName { get; }
public bool IgnoreMultiTenancy { get; }
public DistributedCacheKeyNormalizeArgs(
string key,
string cacheName,
bool ignoreMultiTenancy)
{
Key = key;
CacheName = cacheName;
IgnoreMultiTenancy = ignoreMultiTenancy;
}
}
}

33
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCacheKeyNormalizer.cs

@ -0,0 +1,33 @@
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Caching
{
public class DistributedCacheKeyNormalizer : IDistributedCacheKeyNormalizer, ITransientDependency
{
protected ICurrentTenant CurrentTenant { get; }
protected AbpDistributedCacheOptions DistributedCacheOptions { get; }
public DistributedCacheKeyNormalizer(
ICurrentTenant currentTenant,
IOptions<AbpDistributedCacheOptions> distributedCacheOptions)
{
CurrentTenant = currentTenant;
DistributedCacheOptions = distributedCacheOptions.Value;
}
public virtual string NormalizeKey(DistributedCacheKeyNormalizeArgs args)
{
var normalizedKey = $"c:{args.CacheName},k:{DistributedCacheOptions.KeyPrefix}{args.Key}";
if (!args.IgnoreMultiTenancy && CurrentTenant.Id.HasValue)
{
normalizedKey = $"t:{CurrentTenant.Id.Value},{normalizedKey}";
}
return normalizedKey;
}
}
}

7
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCacheKeyNormalizer.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.Caching
{
public interface IDistributedCacheKeyNormalizer
{
string NormalizeKey(DistributedCacheKeyNormalizeArgs args);
}
}

15
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs

@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.Commands
@ -28,7 +29,9 @@ namespace Volo.Abp.Cli.Commands
public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
if (commandLineArgs.Target == null)
var projectName = NamespaceHelper.NormalizeNamespace(commandLineArgs.Target);
if (projectName == null)
{
throw new CliUsageException(
"Project name is missing!" +
@ -36,9 +39,9 @@ namespace Volo.Abp.Cli.Commands
GetUsageInfo()
);
}
Logger.LogInformation("Creating your project...");
Logger.LogInformation("Project name: " + commandLineArgs.Target);
Logger.LogInformation("Project name: " + projectName);
var template = commandLineArgs.Options.GetOrNull(Options.Template.Short, Options.Template.Long);
if (template != null)
@ -73,7 +76,7 @@ namespace Volo.Abp.Cli.Commands
var outputFolder = commandLineArgs.Options.GetOrNull(Options.OutputFolder.Short, Options.OutputFolder.Long);
outputFolder = Path.Combine(outputFolder != null ? Path.GetFullPath(outputFolder) : Directory.GetCurrentDirectory(),
SolutionName.Parse(commandLineArgs.Target).FullName);
SolutionName.Parse(projectName).FullName);
if (!Directory.Exists(outputFolder))
{
@ -86,7 +89,7 @@ namespace Volo.Abp.Cli.Commands
var result = await TemplateProjectBuilder.BuildAsync(
new ProjectBuildArgs(
SolutionName.Parse(commandLineArgs.Target),
SolutionName.Parse(projectName),
template,
version,
databaseProvider,
@ -129,7 +132,7 @@ namespace Volo.Abp.Cli.Commands
}
}
Logger.LogInformation($"'{commandLineArgs.Target}' has been successfully created to '{outputFolder}'");
Logger.LogInformation($"'{projectName}' has been successfully created to '{outputFolder}'");
}
public string GetUsageInfo()

21
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/NamespaceHelper.cs

@ -0,0 +1,21 @@
using System.Text.RegularExpressions;
using JetBrains.Annotations;
namespace Volo.Abp.Cli.Utils
{
public static class NamespaceHelper
{
public static string NormalizeNamespace([CanBeNull] string value)
{
if (string.IsNullOrEmpty(value))
{
return value;
}
value = value.Trim();
value = Regex.Replace(value, @"(((?<=\.)|^)((?=\d)|\.)|[^\w\.])|(\.$)", "_");
return value;
}
}
}

9
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json

@ -46,6 +46,13 @@
"DatatableActionDropdownDefaultText": "Ações",
"ChangePassword": "Alterar Senha",
"PersonalInfo": "Perfil",
"AreYouSureYouWantToCancelEditingWarningMessage": "Há alterações não salvas."
"AreYouSureYouWantToCancelEditingWarningMessage": "Há alterações não salvas.",
"UnhandledException": "Exceção não tratada!",
"401Message": "Autenticação inválida",
"403Message": "Não autorizado",
"404Message": "Página não encontrada",
"500Message": "Erro interno do servidor",
"GoHomePage": "Voltar à página principal",
"GoBack": "Voltar"
}
}

10
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pt-BR.json

@ -3,7 +3,7 @@
"texts": {
"UserName": "Usuário",
"EmailAddress": "E-mail",
"UserNameOrEmailAddress": "Utilize seu nome de usuário ou e-mail",
"UserNameOrEmailAddress": "Usuário ou e-mail",
"Password": "Senha",
"RememberMe": "Lembrar",
"UseAnotherServiceToLogin": "Usar outro serviço para entrar",
@ -13,10 +13,12 @@
"SelfRegistrationDisabledMessage": "Não é permitido que você crie uma nova conta neste site. Contate um administrador para que ele crie uma conta para você.",
"Login": "Entrar",
"Cancel": "Cancelar",
"Register": "Registrar-se",
"InvalidLoginRequest": "Requisição de acesso inválida!",
"Register": "Registre-se",
"AreYouANewUser": "Você é um novo usuário?",
"AlreadyRegistered": "Já registrado?",
"InvalidLoginRequest": "Requisição de acesso inválido!",
"ThereAreNoLoginSchemesConfiguredForThisClient": "Não existe um esquema de acesso para este usuário",
"LogInUsingYourProviderAccount": "Acessse utilizando sua conta {0}",
"LogInUsingYourProviderAccount": "Acesse utilizando sua conta {0}",
"DisplayName:CurrentPassword": "Senha atual",
"DisplayName:NewPassword": "Nova Senha",
"DisplayName:NewPasswordConfirm": "Confirmação de senha",

12
modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs

@ -127,8 +127,16 @@ namespace Volo.Abp.Identity
private async Task UpdateUserByInput(IdentityUser user, IdentityUserCreateOrUpdateDtoBase input)
{
(await _userManager.SetEmailAsync(user, input.Email)).CheckErrors();
(await _userManager.SetPhoneNumberAsync(user, input.PhoneNumber)).CheckErrors();
if (!string.Equals(user.Email, input.Email, StringComparison.InvariantCultureIgnoreCase))
{
(await _userManager.SetEmailAsync(user, input.Email)).CheckErrors();
}
if (!string.Equals(user.PhoneNumber, input.PhoneNumber, StringComparison.InvariantCultureIgnoreCase))
{
(await _userManager.SetPhoneNumberAsync(user, input.PhoneNumber)).CheckErrors();
}
(await _userManager.SetTwoFactorEnabledAsync(user, input.TwoFactorEnabled)).CheckErrors();
(await _userManager.SetLockoutEnabledAsync(user, input.LockoutEnabled)).CheckErrors();

2
npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html

@ -158,7 +158,7 @@
</ng-template>
<ng-template #language>
<li class="nav-item">
<li *ngIf="(dropdownLanguages$ | async)?.length > 1" class="nav-item">
<div class="dropdown" ngbDropdown #languageDropdown="ngbDropdown" display="static">
<a
ngbDropdownToggle

4
npm/ng-packs/packages/theme-shared/src/lib/contants/styles.ts

@ -184,6 +184,10 @@ export default `
color: #FFF !important;
}
.custom-checkbox > label {
cursor: pointer;
}
/* <animations */
.fade-in-top {

5
npm/packs/client-generator/.prettierrc

@ -0,0 +1,5 @@
{
"trailingComma": "all",
"printWidth": 120,
"singleQuote": true
}

2
npm/packs/client-generator/lib/angular.d.ts

@ -0,0 +1,2 @@
import { APIDefination } from './types/api-defination';
export declare function angular(data: APIDefination.Response, selectedModules: string[]): Promise<void>;

57
npm/packs/client-generator/lib/angular.js

@ -0,0 +1,57 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var service_templates_1 = require("./templates/angular/service-templates");
function angular(data, selectedModules) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
selectedModules.forEach(function (module) {
var element = data.modules[module];
var contents = [];
(Object.keys(element.controllers) || []).forEach(function (key) {
console.warn(element.controllers[key].controllerName);
contents.push('');
});
var service = service_templates_1.ServiceTemplates.classTemplate(element.rootPath, contents);
console.log(service);
});
return [2 /*return*/];
});
});
}
exports.angular = angular;

1
npm/packs/client-generator/lib/cli.d.ts

@ -0,0 +1 @@
export declare function cli(program: any): Promise<void>;

102
npm/packs/client-generator/lib/cli.js

@ -0,0 +1,102 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var prompt_1 = require("./utils/prompt");
var axios_1 = require("./utils/axios");
var ora = require("ora");
var angular_1 = require("./angular");
var chalk_1 = __importDefault(require("chalk"));
function cli(program) {
return __awaiter(this, void 0, void 0, function () {
var _a, loading, data, selection, modules, _b;
var _this = this;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
if (!(program.ui !== 'angular')) return [3 /*break*/, 2];
_a = program;
return [4 /*yield*/, prompt_1.uiSelection(['Angular'])];
case 1:
_a.ui = (_c.sent()).toLowerCase();
_c.label = 2;
case 2:
loading = ora('Waiting for the API response... \n');
loading.start();
return [4 /*yield*/, axios_1.axiosInstance.get('a')];
case 3:
data = (_c.sent());
loading.stop();
selection = function (modules) { return __awaiter(_this, void 0, void 0, function () {
var selectedModules;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, prompt_1.moduleSelection(modules)];
case 1:
selectedModules = (_a.sent());
if (!!selectedModules.length) return [3 /*break*/, 3];
console.log(chalk_1.default.red('Please select module(s)'));
return [4 /*yield*/, selection(modules)];
case 2: return [2 /*return*/, _a.sent()];
case 3: return [2 /*return*/, selectedModules];
}
});
}); };
return [4 /*yield*/, selection(Object.keys(data.modules))];
case 4:
modules = _c.sent();
_b = program.ui;
switch (_b) {
case 'angular': return [3 /*break*/, 5];
}
return [3 /*break*/, 7];
case 5: return [4 /*yield*/, angular_1.angular(data, modules)];
case 6:
_c.sent();
return [3 /*break*/, 8];
case 7:
process.exit(1);
_c.label = 8;
case 8: return [2 /*return*/];
}
});
});
}
exports.cli = cli;

2
npm/packs/client-generator/lib/index.d.ts

@ -0,0 +1,2 @@
#!/usr/bin/env node
export {};

24
npm/packs/client-generator/lib/index.js

@ -0,0 +1,24 @@
#!/usr/bin/env node
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var chalk_1 = __importDefault(require("chalk"));
var commander_1 = __importDefault(require("commander"));
var cli_1 = require("./cli");
var clear = require('clear');
var figlet = require('figlet');
clear();
console.log(chalk_1.default.red(figlet.textSync('ABP', { horizontalLayout: 'full' })));
commander_1.default
.version('0.0.1')
.description('ABP Client Generator')
.option('-u, --ui <type>', 'UI option (Angular)')
.parse(process.argv);
if (!process.argv.slice(2).length || !commander_1.default.ui || typeof commander_1.default.ui !== 'string') {
commander_1.default.outputHelp();
process.exit(1);
}
commander_1.default.ui = commander_1.default.ui.toLowerCase();
cli_1.cli(commander_1.default);

3
npm/packs/client-generator/lib/templates/angular/service-templates.d.ts

@ -0,0 +1,3 @@
export declare namespace ServiceTemplates {
function classTemplate(name: string, content: string[]): string;
}

15
npm/packs/client-generator/lib/templates/angular/service-templates.js

@ -0,0 +1,15 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var change_case_1 = __importDefault(require("change-case"));
var ServiceTemplates;
(function (ServiceTemplates) {
function classTemplate(name, content) {
return "import { RestService } from '@abp/ng.core';\nimport { Injectable } from '@angular/core';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class " + change_case_1.default.pascalCase(name) + "Service {\n\n constructor(private restService: RestService) { }\n\n " + content.forEach(function (element) {
element + '\n\n';
}) + "\n}";
}
ServiceTemplates.classTemplate = classTemplate;
})(ServiceTemplates = exports.ServiceTemplates || (exports.ServiceTemplates = {}));

14
npm/packs/client-generator/lib/types/api-defination.d.ts

@ -0,0 +1,14 @@
export declare namespace APIDefination {
interface Response {
modules: Modules;
}
interface Modules {
[key: string]: Module;
}
interface Module {
rootPath: string;
controllers: {
[key: string]: any;
};
}
}

2
npm/packs/client-generator/lib/types/api-defination.js

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

1
npm/packs/client-generator/lib/utils/axios.d.ts

@ -0,0 +1 @@
export declare const axiosInstance: import("axios").AxiosInstance;

2119
npm/packs/client-generator/lib/utils/axios.js

File diff suppressed because it is too large

2
npm/packs/client-generator/lib/utils/prompt.d.ts

@ -0,0 +1,2 @@
export declare const uiSelection: (uiArray: string[]) => Promise<any>;
export declare const moduleSelection: (modules: string[]) => Promise<any>;

56
npm/packs/client-generator/lib/utils/prompt.js

@ -0,0 +1,56 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var inquirer_1 = __importDefault(require("inquirer"));
exports.uiSelection = function (uiArray) { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, inquirer_1.default
.prompt({ type: 'list', name: 'Please select an UI', choices: uiArray })
.then(function (res) { return res['Please select an UI']; })];
});
}); };
exports.moduleSelection = function (modules) { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, inquirer_1.default
.prompt({ type: 'checkbox', name: 'Please select module(s)', choices: modules })
.then(function (res) { return res['Please select module(s)']; })];
});
}); };

6
npm/packs/client-generator/nodemon.json

@ -0,0 +1,6 @@
{
"watch": ["src"],
"ext": "ts",
"ignore": ["src/**/*.spec.ts"],
"exec": "ts-node ./src/index.ts"
}

39
npm/packs/client-generator/package.json

@ -0,0 +1,39 @@
{
"name": "@abp/client-generator",
"version": "0.0.1",
"description": "",
"main": "./lib/index.js",
"bin": {
"abp-type": "./lib/index.js"
},
"scripts": {
"start": "ts-node src/index.ts",
"start:watch": "nodemon",
"create": "npm run build && npm run test",
"build": "tsc -p .",
"test": "jest",
"prepublish": "yarn build"
},
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^0.19.0",
"chalk": "^3.0.0",
"change-case": "^3.1.0",
"clear": "^0.1.0",
"commander": "^4.0.1",
"figlet": "^1.2.4",
"fs-extra": "^8.1.0",
"inquirer": "^7.0.0",
"ora": "^4.0.3",
"path": "^0.12.7"
},
"devDependencies": {
"@types/fs-extra": "^8.0.1",
"@types/inquirer": "^6.5.0",
"@types/node": "^12.12.8",
"nodemon": "^1.19.4",
"ts-node": "^8.5.2",
"typescript": "^3.7.2"
}
}

20
npm/packs/client-generator/src/angular.ts

@ -0,0 +1,20 @@
import { APIDefination } from './types/api-defination';
import { ServiceTemplates } from './templates/angular/service-templates';
import changeCase from 'change-case';
import fse from 'fs-extra';
export async function angular(data: APIDefination.Response, selectedModules: string[]) {
selectedModules.forEach(async module => {
const element = data.modules[module];
let contents = [] as string[];
(Object.keys(element.controllers) || []).forEach(key => {
console.warn(element.controllers[key].controllerName);
contents.push(' ');
});
const service = ServiceTemplates.classTemplate(element.rootPath, contents);
await fse.writeFile(`${changeCase.kebabCase(element.rootPath + '-service')}.ts`, service);
console.log(service);
});
}

39
npm/packs/client-generator/src/cli.ts

@ -0,0 +1,39 @@
import { uiSelection, moduleSelection } from './utils/prompt';
import { axiosInstance } from './utils/axios';
import ora = require('ora');
import { angular } from './angular';
import chalk from 'chalk';
export async function cli(program: any) {
if (program.ui !== 'angular') {
program.ui = ((await uiSelection(['Angular'])) as string).toLowerCase();
}
const loading = ora('Waiting for the API response... \n');
loading.start();
const data = (await axiosInstance.get('a')) as any;
loading.stop();
const selection = async (modules: string[]): Promise<string[]> => {
const selectedModules = (await moduleSelection(modules)) as string[];
if (!selectedModules.length) {
console.log(chalk.red('Please select module(s)'));
return await selection(modules);
}
return selectedModules;
};
// const modules = await selection(Object.keys(data.modules));
const modules = ['multi-tenancy'];
switch (program.ui) {
case 'angular':
await angular(data, modules);
break;
default:
process.exit(1);
}
}

28
npm/packs/client-generator/src/index.ts

@ -0,0 +1,28 @@
#!/usr/bin/env node
import chalk from 'chalk';
import program from 'commander';
import ora from 'ora';
import { axiosInstance } from './utils/axios';
import { moduleSelection, uiSelection } from './utils/prompt';
import { cli } from './cli';
const clear = require('clear');
const figlet = require('figlet');
clear();
console.log(chalk.red(figlet.textSync('ABP', { horizontalLayout: 'full' })));
program
.version('0.0.1')
.description('ABP Client Generator')
.option('-u, --ui <type>', 'UI option (Angular)')
.parse(process.argv);
if (!process.argv.slice(2).length || !program.ui || typeof program.ui !== 'string') {
program.outputHelp();
process.exit(1);
}
program.ui = program.ui.toLowerCase();
cli(program);

18
npm/packs/client-generator/src/templates/angular/service-templates.ts

@ -0,0 +1,18 @@
import changeCase from 'change-case';
export namespace ServiceTemplates {
export function classTemplate(name: string, contents: string[]) {
return `import { RestService } from '@abp/ng.core';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class ${changeCase.pascalCase(name)}Service {
constructor(private restService: RestService) { }
${contents[0]}
}`;
}
}

14
npm/packs/client-generator/src/types/api-defination.ts

@ -0,0 +1,14 @@
export namespace APIDefination {
export interface Response {
modules: Modules;
}
export interface Modules {
[key: string]: Module;
}
export interface Module {
rootPath: string;
controllers: { [key: string]: any };
}
}

2109
npm/packs/client-generator/src/utils/axios.ts

File diff suppressed because it is too large

13
npm/packs/client-generator/src/utils/prompt.ts

@ -0,0 +1,13 @@
import inquirer from 'inquirer';
export const uiSelection = async (uiArray: string[]) => {
return inquirer
.prompt({ type: 'list', name: 'Please select an UI', choices: uiArray })
.then(res => res['Please select an UI']);
};
export const moduleSelection = async (modules: string[]) => {
return inquirer
.prompt({ type: 'checkbox', name: 'Please select module(s)', choices: modules })
.then(res => res['Please select module(s)']);
};

14
npm/packs/client-generator/tsconfig.json

@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es6", "es2015", "dom"],
"declaration": true,
"outDir": "lib",
"rootDir": "src",
"strict": true,
"types": ["node"],
"esModuleInterop": true,
"resolveJsonModule": true,
},
}

1947
npm/packs/client-generator/yarn.lock

File diff suppressed because it is too large

4
test-all.ps1

@ -23,12 +23,12 @@ $solutionPaths = (
"templates/app/aspnet-core"
)
# Build all solutions
# Test all solutions
foreach ($solutionPath in $solutionPaths) {
$solutionAbsPath = (Join-Path $rootFolder $solutionPath)
Set-Location $solutionAbsPath
dotnet test
dotnet test --no-build --no-restore
if (-Not $?) {
Write-Host ("Test failed for the solution: " + $solutionPath)
Set-Location $rootFolder

Loading…
Cancel
Save