mirror of https://github.com/abpframework/abp.git
52 changed files with 5548 additions and 3 deletions
@ -0,0 +1,36 @@ |
|||
{ |
|||
"extends": ["../../.eslintrc.json"], |
|||
"ignorePatterns": ["!**/*"], |
|||
"overrides": [ |
|||
{ |
|||
"files": ["*.ts"], |
|||
"extends": [ |
|||
"plugin:@nrwl/nx/angular", |
|||
"plugin:@angular-eslint/template/process-inline-templates" |
|||
], |
|||
"rules": { |
|||
"@angular-eslint/directive-selector": [ |
|||
"error", |
|||
{ |
|||
"type": "attribute", |
|||
"prefix": "abp", |
|||
"style": "camelCase" |
|||
} |
|||
], |
|||
"@angular-eslint/component-selector": [ |
|||
"error", |
|||
{ |
|||
"type": "element", |
|||
"prefix": "abp", |
|||
"style": "kebab-case" |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"files": ["*.html"], |
|||
"extends": ["plugin:@nrwl/nx/angular-template"], |
|||
"rules": {} |
|||
} |
|||
] |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<h1> @abp/ng.tenant-management </h1> |
|||
|
|||
[docs.abp.io](https://docs.abp.io) |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", |
|||
"dest": "../../../dist/packages/tenant-management/config", |
|||
"lib": { |
|||
"entryFile": "src/public-api.ts" |
|||
} |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
export * from './policy-names'; |
|||
export * from './route-names'; |
|||
@ -0,0 +1,4 @@ |
|||
export const enum eTenantManagementPolicyNames { |
|||
TenantManagement = 'AbpTenantManagement.Tenants', |
|||
Tenants = 'AbpTenantManagement.Tenants', |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
export const enum eTenantManagementRouteNames { |
|||
TenantManagement = 'AbpTenantManagement::Menu:TenantManagement', |
|||
Tenants = 'AbpTenantManagement::Tenants', |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './route.provider'; |
|||
@ -0,0 +1,32 @@ |
|||
import { eLayoutType, RoutesService } from '@abp/ng.core'; |
|||
import { eThemeSharedRouteNames } from '@abp/ng.theme.shared'; |
|||
import { APP_INITIALIZER } from '@angular/core'; |
|||
import { eTenantManagementPolicyNames } from '../enums/policy-names'; |
|||
import { eTenantManagementRouteNames } from '../enums/route-names'; |
|||
|
|||
export const TENANT_MANAGEMENT_ROUTE_PROVIDERS = [ |
|||
{ provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true }, |
|||
]; |
|||
|
|||
export function configureRoutes(routes: RoutesService) { |
|||
return () => { |
|||
routes.add([ |
|||
{ |
|||
path: undefined, |
|||
name: eTenantManagementRouteNames.TenantManagement, |
|||
parentName: eThemeSharedRouteNames.Administration, |
|||
requiredPolicy: eTenantManagementPolicyNames.TenantManagement, |
|||
layout: eLayoutType.application, |
|||
iconClass: 'fa fa-users', |
|||
order: 2, |
|||
}, |
|||
{ |
|||
path: '/tenant-management/tenants', |
|||
name: eTenantManagementRouteNames.Tenants, |
|||
parentName: eTenantManagementRouteNames.TenantManagement, |
|||
requiredPolicy: eTenantManagementPolicyNames.Tenants, |
|||
order: 1, |
|||
}, |
|||
]); |
|||
}; |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
export * from './enums'; |
|||
export * from './providers'; |
|||
export * from './tenant-management-config.module'; |
|||
@ -0,0 +1,12 @@ |
|||
import { ModuleWithProviders, NgModule } from '@angular/core'; |
|||
import { TENANT_MANAGEMENT_ROUTE_PROVIDERS } from './providers/route.provider'; |
|||
|
|||
@NgModule() |
|||
export class TenantManagementConfigModule { |
|||
static forRoot(): ModuleWithProviders<TenantManagementConfigModule> { |
|||
return { |
|||
ngModule: TenantManagementConfigModule, |
|||
providers: [TENANT_MANAGEMENT_ROUTE_PROVIDERS], |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
module.exports = { |
|||
displayName: 'tenant-management', |
|||
preset: '../../jest.preset.js', |
|||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], |
|||
globals: { |
|||
'ts-jest': { |
|||
tsconfig: '<rootDir>/tsconfig.spec.json', |
|||
stringifyContentPathRegex: '\\.(html|svg)$', |
|||
}, |
|||
}, |
|||
coverageDirectory: '../../coverage/packages/tenant-management', |
|||
transform: { |
|||
'^.+\\.(ts|js|html)$': 'jest-preset-angular', |
|||
}, |
|||
snapshotSerializers: [ |
|||
'jest-preset-angular/build/serializers/no-ng-attributes', |
|||
'jest-preset-angular/build/serializers/ng-snapshot', |
|||
'jest-preset-angular/build/serializers/html-comment', |
|||
], |
|||
}; |
|||
@ -0,0 +1,11 @@ |
|||
{ |
|||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json", |
|||
"dest": "../../dist/packages/tenant-management", |
|||
"lib": { |
|||
"entryFile": "src/public-api.ts" |
|||
}, |
|||
"allowedNonPeerDependencies": [ |
|||
"@abp/ng.theme.shared", |
|||
"@abp/ng.feature-management" |
|||
] |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
{ |
|||
"name": "@abp/ng.tenant-management", |
|||
"version": "4.4.0", |
|||
"homepage": "https://abp.io", |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "https://github.com/abpframework/abp.git" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/ng.feature-management": "~4.4.0", |
|||
"@abp/ng.theme.shared": "~4.4.0", |
|||
"tslib": "^2.0.0" |
|||
}, |
|||
"publishConfig": { |
|||
"access": "public" |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './tenant-management.actions'; |
|||
@ -0,0 +1,26 @@ |
|||
import { GetTenantsInput, TenantCreateDto, TenantUpdateDto } from '../proxy/models'; |
|||
|
|||
export class GetTenants { |
|||
static readonly type = '[TenantManagement] Get Tenant'; |
|||
constructor(public payload?: GetTenantsInput) {} |
|||
} |
|||
|
|||
export class GetTenantById { |
|||
static readonly type = '[TenantManagement] Get Tenant By Id'; |
|||
constructor(public payload: string) {} |
|||
} |
|||
|
|||
export class CreateTenant { |
|||
static readonly type = '[TenantManagement] Create Tenant'; |
|||
constructor(public payload: TenantCreateDto) {} |
|||
} |
|||
|
|||
export class UpdateTenant { |
|||
static readonly type = '[TenantManagement] Update Tenant'; |
|||
constructor(public payload: TenantUpdateDto & { id: string }) {} |
|||
} |
|||
|
|||
export class DeleteTenant { |
|||
static readonly type = '[TenantManagement] Delete Tenant'; |
|||
constructor(public payload: string) {} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './tenants/tenants.component'; |
|||
@ -0,0 +1,72 @@ |
|||
<div id="wrapper" class="card"> |
|||
<div class="card-header"> |
|||
<div class="row"> |
|||
<div class="col col-md-6"> |
|||
<h5 class="card-title">{{ 'AbpTenantManagement::Tenants' | abpLocalization }}</h5> |
|||
</div> |
|||
<div class="text-right col col-md-6"> |
|||
<abp-page-toolbar [record]="data$ | async"></abp-page-toolbar> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="card-body"> |
|||
<div id="data-tables-table-filter" class="data-tables-filter"> |
|||
<div class="input-group"> |
|||
<input |
|||
type="search" |
|||
class="form-control" |
|||
[placeholder]="'AbpUi::PagerSearch' | abpLocalization" |
|||
[(ngModel)]="list.filter" |
|||
/> |
|||
</div> |
|||
</div> |
|||
|
|||
<abp-extensible-table |
|||
[data]="data$ | async" |
|||
[recordsTotal]="totalCount$ | async" |
|||
[list]="list" |
|||
></abp-extensible-table> |
|||
</div> |
|||
</div> |
|||
|
|||
<abp-modal size="md" [(visible)]="isModalVisible" [busy]="modalBusy"> |
|||
<ng-template #abpHeader> |
|||
<h3>{{ selectedModalContent.title | abpLocalization }}</h3> |
|||
</ng-template> |
|||
|
|||
<ng-template #abpBody> |
|||
<ng-container *ngTemplateOutlet="selectedModalContent?.template"></ng-container> |
|||
</ng-template> |
|||
|
|||
<ng-template #abpFooter> |
|||
<button abpClose type="button" class="btn btn-secondary"> |
|||
{{ 'AbpTenantManagement::Cancel' | abpLocalization }} |
|||
</button> |
|||
<abp-button iconClass="fa fa-check" (click)="save()" [disabled]="isDisabledSaveButton">{{ |
|||
'AbpTenantManagement::Save' | abpLocalization |
|||
}}</abp-button> |
|||
</ng-template> |
|||
</abp-modal> |
|||
|
|||
<ng-template #tenantModalTemplate> |
|||
<form [formGroup]="tenantForm" (ngSubmit)="save()" validateOnSubmit> |
|||
<abp-extensible-form [selectedRecord]="selected"></abp-extensible-form> |
|||
</form> |
|||
</ng-template> |
|||
|
|||
<abp-feature-management |
|||
*abpReplaceableTemplate="{ |
|||
inputs: { |
|||
providerName: { value: 'T' }, |
|||
providerKey: { value: providerKey }, |
|||
visible: { value: visibleFeatures, twoWay: true } |
|||
}, |
|||
outputs: { visibleChange: onVisibleFeaturesChange }, |
|||
componentKey: featureManagementKey |
|||
}" |
|||
[(visible)]="visibleFeatures" |
|||
providerName="T" |
|||
[providerKey]="providerKey" |
|||
> |
|||
</abp-feature-management> |
|||
@ -0,0 +1,259 @@ |
|||
import { ListService, PagedResultDto } from '@abp/ng.core'; |
|||
import { eFeatureManagementComponents } from '@abp/ng.feature-management'; |
|||
import { Confirmation, ConfirmationService, getPasswordValidators } from '@abp/ng.theme.shared'; |
|||
import { Component, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { Select, Store } from '@ngxs/store'; |
|||
import { Observable } from 'rxjs'; |
|||
import { finalize, pluck, switchMap, take } from 'rxjs/operators'; |
|||
import { |
|||
CreateTenant, |
|||
DeleteTenant, |
|||
GetTenantById, |
|||
GetTenants, |
|||
UpdateTenant, |
|||
} from '../../actions/tenant-management.actions'; |
|||
import { GetTenantsInput, TenantDto } from '../../proxy/models'; |
|||
import { TenantService } from '../../proxy/tenant.service'; |
|||
import { TenantManagementState } from '../../states/tenant-management.state'; |
|||
import { |
|||
EXTENSIONS_IDENTIFIER, |
|||
FormPropData, |
|||
generateFormFromProps, |
|||
} from '@abp/ng.theme.shared/extensions'; |
|||
import { eTenantManagementComponents } from '../../enums/components'; |
|||
|
|||
interface SelectedModalContent { |
|||
type: 'saveConnStr' | 'saveTenant'; |
|||
title: string; |
|||
template: TemplateRef<any>; |
|||
} |
|||
|
|||
@Component({ |
|||
selector: 'abp-tenants', |
|||
templateUrl: './tenants.component.html', |
|||
providers: [ |
|||
ListService, |
|||
{ |
|||
provide: EXTENSIONS_IDENTIFIER, |
|||
useValue: eTenantManagementComponents.Tenants, |
|||
}, |
|||
], |
|||
}) |
|||
export class TenantsComponent implements OnInit { |
|||
@Select(TenantManagementState.get) |
|||
data$: Observable<PagedResultDto<TenantDto>>; |
|||
|
|||
@Select(TenantManagementState.getTenantsTotalCount) |
|||
totalCount$: Observable<number>; |
|||
|
|||
selected: TenantDto; |
|||
|
|||
tenantForm: FormGroup; |
|||
|
|||
defaultConnectionStringForm: FormGroup; |
|||
|
|||
defaultConnectionString: string; |
|||
|
|||
isModalVisible: boolean; |
|||
|
|||
selectedModalContent = {} as SelectedModalContent; |
|||
|
|||
visibleFeatures = false; |
|||
|
|||
providerKey: string; |
|||
|
|||
_useSharedDatabase: boolean; |
|||
|
|||
modalBusy = false; |
|||
|
|||
featureManagementKey = eFeatureManagementComponents.FeatureManagement; |
|||
|
|||
get hasSelectedTenant(): boolean { |
|||
return Boolean(this.selected.id); |
|||
} |
|||
|
|||
get useSharedDatabase(): boolean { |
|||
return this.defaultConnectionStringForm.get('useSharedDatabase').value; |
|||
} |
|||
|
|||
get connectionString(): string { |
|||
return this.defaultConnectionStringForm.get('defaultConnectionString').value; |
|||
} |
|||
|
|||
@ViewChild('tenantModalTemplate') |
|||
tenantModalTemplate: TemplateRef<any>; |
|||
|
|||
get isDisabledSaveButton(): boolean { |
|||
if (!this.selectedModalContent) return false; |
|||
|
|||
if ( |
|||
this.selectedModalContent.type === 'saveConnStr' && |
|||
this.defaultConnectionStringForm && |
|||
this.defaultConnectionStringForm.invalid |
|||
) { |
|||
return true; |
|||
} else if ( |
|||
this.selectedModalContent.type === 'saveTenant' && |
|||
this.tenantForm && |
|||
this.tenantForm.invalid |
|||
) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
onVisibleFeaturesChange = (value: boolean) => { |
|||
this.visibleFeatures = value; |
|||
}; |
|||
|
|||
constructor( |
|||
public readonly list: ListService<GetTenantsInput>, |
|||
private injector: Injector, |
|||
private confirmationService: ConfirmationService, |
|||
private tenantService: TenantService, |
|||
private fb: FormBuilder, |
|||
private store: Store, |
|||
) {} |
|||
|
|||
ngOnInit() { |
|||
this.hookToQuery(); |
|||
} |
|||
|
|||
private createTenantForm() { |
|||
const data = new FormPropData(this.injector, this.selected); |
|||
this.tenantForm = generateFormFromProps(data); |
|||
} |
|||
|
|||
private createDefaultConnectionStringForm() { |
|||
this.defaultConnectionStringForm = this.fb.group({ |
|||
useSharedDatabase: this._useSharedDatabase, |
|||
defaultConnectionString: [this.defaultConnectionString || ''], |
|||
}); |
|||
} |
|||
|
|||
openModal(title: string, template: TemplateRef<any>, type: 'saveConnStr' | 'saveTenant') { |
|||
this.selectedModalContent = { |
|||
title, |
|||
template, |
|||
type, |
|||
}; |
|||
|
|||
this.isModalVisible = true; |
|||
} |
|||
|
|||
addTenant() { |
|||
this.selected = {} as TenantDto; |
|||
this.createTenantForm(); |
|||
this.openModal('AbpTenantManagement::NewTenant', this.tenantModalTemplate, 'saveTenant'); |
|||
} |
|||
|
|||
editTenant(id: string) { |
|||
this.store |
|||
.dispatch(new GetTenantById(id)) |
|||
.pipe(pluck('TenantManagementState', 'selectedItem')) |
|||
.subscribe(selected => { |
|||
this.selected = selected; |
|||
this.createTenantForm(); |
|||
this.openModal('AbpTenantManagement::Edit', this.tenantModalTemplate, 'saveTenant'); |
|||
}); |
|||
} |
|||
|
|||
save() { |
|||
const { type } = this.selectedModalContent; |
|||
if (!type) return; |
|||
if (type === 'saveTenant') this.saveTenant(); |
|||
else if (type === 'saveConnStr') this.saveConnectionString(); |
|||
} |
|||
|
|||
saveConnectionString() { |
|||
if (this.modalBusy) return; |
|||
|
|||
this.modalBusy = true; |
|||
if (this.useSharedDatabase || (!this.useSharedDatabase && !this.connectionString)) { |
|||
this.tenantService |
|||
.deleteDefaultConnectionString(this.selected.id) |
|||
.pipe( |
|||
take(1), |
|||
finalize(() => (this.modalBusy = false)), |
|||
) |
|||
.subscribe(() => { |
|||
this.isModalVisible = false; |
|||
}); |
|||
} else { |
|||
this.tenantService |
|||
.updateDefaultConnectionString(this.selected.id, this.connectionString) |
|||
.pipe( |
|||
take(1), |
|||
finalize(() => (this.modalBusy = false)), |
|||
) |
|||
.subscribe(() => { |
|||
this.isModalVisible = false; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
saveTenant() { |
|||
if (!this.tenantForm.valid || this.modalBusy) return; |
|||
this.modalBusy = true; |
|||
|
|||
this.store |
|||
.dispatch( |
|||
this.selected.id |
|||
? new UpdateTenant({ ...this.selected, ...this.tenantForm.value, id: this.selected.id }) |
|||
: new CreateTenant(this.tenantForm.value), |
|||
) |
|||
.pipe(finalize(() => (this.modalBusy = false))) |
|||
.subscribe(() => { |
|||
this.isModalVisible = false; |
|||
this.list.get(); |
|||
}); |
|||
} |
|||
|
|||
delete(id: string, name: string) { |
|||
this.confirmationService |
|||
.warn( |
|||
'AbpTenantManagement::TenantDeletionConfirmationMessage', |
|||
'AbpTenantManagement::AreYouSure', |
|||
{ |
|||
messageLocalizationParams: [name], |
|||
}, |
|||
) |
|||
.subscribe((status: Confirmation.Status) => { |
|||
if (status === Confirmation.Status.confirm) { |
|||
this.store.dispatch(new DeleteTenant(id)).subscribe(() => this.list.get()); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
hookToQuery() { |
|||
this.list.hookToQuery(query => this.store.dispatch(new GetTenants(query))).subscribe(); |
|||
} |
|||
|
|||
onSharedDatabaseChange(value: boolean) { |
|||
if (!value) { |
|||
setTimeout(() => { |
|||
const defaultConnectionString = document.getElementById( |
|||
'defaultConnectionString', |
|||
) as HTMLInputElement; |
|||
if (defaultConnectionString) { |
|||
defaultConnectionString.focus(); |
|||
} |
|||
}, 0); |
|||
} |
|||
} |
|||
|
|||
openFeaturesModal(providerKey: string) { |
|||
this.providerKey = providerKey; |
|||
setTimeout(() => { |
|||
this.visibleFeatures = true; |
|||
}, 0); |
|||
} |
|||
|
|||
sort(data) { |
|||
const { prop, dir } = data.sorts[0]; |
|||
this.list.sortKey = prop; |
|||
this.list.sortOrder = dir; |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
import { EntityAction } from '@abp/ng.theme.shared/extensions'; |
|||
import { TenantsComponent } from '../components/tenants/tenants.component'; |
|||
import { TenantDto } from '../proxy/models'; |
|||
|
|||
export const DEFAULT_TENANTS_ENTITY_ACTIONS = EntityAction.createMany<TenantDto>([ |
|||
{ |
|||
text: 'AbpTenantManagement::Edit', |
|||
action: data => { |
|||
const component = data.getInjected(TenantsComponent); |
|||
component.editTenant(data.record.id); |
|||
}, |
|||
permission: 'AbpTenantManagement.Tenants.Update', |
|||
}, |
|||
{ |
|||
text: 'AbpTenantManagement::Permission:ManageFeatures', |
|||
action: data => { |
|||
const component = data.getInjected(TenantsComponent); |
|||
component.openFeaturesModal(data.record.id); |
|||
}, |
|||
permission: 'AbpTenantManagement.Tenants.ManageFeatures', |
|||
}, |
|||
{ |
|||
text: 'AbpTenantManagement::Delete', |
|||
action: data => { |
|||
const component = data.getInjected(TenantsComponent); |
|||
component.delete(data.record.id, data.record.name); |
|||
}, |
|||
permission: 'AbpTenantManagement.Tenants.Delete', |
|||
}, |
|||
]); |
|||
@ -0,0 +1,11 @@ |
|||
import { EntityProp, ePropType } from '@abp/ng.theme.shared/extensions'; |
|||
import { TenantDto } from '../proxy/models'; |
|||
|
|||
export const DEFAULT_TENANTS_ENTITY_PROPS = EntityProp.createMany<TenantDto>([ |
|||
{ |
|||
type: ePropType.String, |
|||
name: 'name', |
|||
displayName: 'AbpTenantManagement::TenantName', |
|||
sortable: true, |
|||
}, |
|||
]); |
|||
@ -0,0 +1,33 @@ |
|||
import { getPasswordValidators } from '@abp/ng.theme.shared'; |
|||
import { ePropType, FormProp } from '@abp/ng.theme.shared/extensions'; |
|||
import { Validators } from '@angular/forms'; |
|||
import { TenantCreateDto, TenantUpdateDto } from '../proxy/models'; |
|||
|
|||
export const DEFAULT_TENANTS_CREATE_FORM_PROPS = FormProp.createMany< |
|||
TenantCreateDto | TenantUpdateDto |
|||
>([ |
|||
{ |
|||
type: ePropType.String, |
|||
name: 'name', |
|||
id: 'name', |
|||
displayName: 'AbpTenantManagement::TenantName', |
|||
validators: () => [Validators.required, Validators.maxLength(256)], |
|||
}, |
|||
{ |
|||
type: ePropType.Email, |
|||
name: 'adminEmailAddress', |
|||
displayName: 'AbpTenantManagement::DisplayName:AdminEmailAddress', |
|||
id: 'admin-email-address', |
|||
validators: () => [Validators.required, Validators.maxLength(256), Validators.email], |
|||
}, |
|||
{ |
|||
type: ePropType.Password, |
|||
name: 'adminPassword', |
|||
displayName: 'AbpTenantManagement::DisplayName:AdminPassword', |
|||
id: 'admin-password', |
|||
autocomplete: 'new-password', |
|||
validators: data => [Validators.required, ...getPasswordValidators({ get: data.getInjected })], |
|||
}, |
|||
]); |
|||
|
|||
export const DEFAULT_TENANTS_EDIT_FORM_PROPS = DEFAULT_TENANTS_CREATE_FORM_PROPS.slice(0, 1); |
|||
@ -0,0 +1,24 @@ |
|||
import { ToolbarAction } from '@abp/ng.theme.shared/extensions'; |
|||
import { TenantsComponent } from '../components/tenants/tenants.component'; |
|||
import { TenantDto } from '../proxy/models'; |
|||
|
|||
export const DEFAULT_TENANTS_TOOLBAR_ACTIONS = ToolbarAction.createMany<TenantDto[]>([ |
|||
{ |
|||
text: 'AbpTenantManagement::ManageHostFeatures', |
|||
action: data => { |
|||
const component = data.getInjected(TenantsComponent); |
|||
component.openFeaturesModal(null); |
|||
}, |
|||
permission: 'FeatureManagement.ManageHostFeatures', |
|||
icon: 'fa fa-cog', |
|||
}, |
|||
{ |
|||
text: 'AbpTenantManagement::NewTenant', |
|||
action: data => { |
|||
const component = data.getInjected(TenantsComponent); |
|||
component.addTenant(); |
|||
}, |
|||
permission: 'AbpTenantManagement.Tenants.Create', |
|||
icon: 'fa fa-plus', |
|||
}, |
|||
]); |
|||
@ -0,0 +1,3 @@ |
|||
export const enum eTenantManagementComponents { |
|||
Tenants = 'TenantManagement.TenantsComponent', |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './components'; |
|||
@ -0,0 +1,90 @@ |
|||
import { ConfigStateService } from '@abp/ng.core'; |
|||
import { |
|||
ExtensionsService, |
|||
getObjectExtensionEntitiesFromStore, |
|||
mapEntitiesToContributors, |
|||
mergeWithDefaultActions, |
|||
mergeWithDefaultProps, |
|||
} from '@abp/ng.theme.shared/extensions'; |
|||
import { Injectable, Injector } from '@angular/core'; |
|||
import { CanActivate } from '@angular/router'; |
|||
import { Observable } from 'rxjs'; |
|||
import { map, mapTo, tap } from 'rxjs/operators'; |
|||
import { eTenantManagementComponents } from '../enums/components'; |
|||
import { |
|||
TenantManagementCreateFormPropContributors, |
|||
TenantManagementEditFormPropContributors, |
|||
TenantManagementEntityActionContributors, |
|||
TenantManagementEntityPropContributors, |
|||
TenantManagementToolbarActionContributors, |
|||
} from '../models/config-options'; |
|||
import { |
|||
DEFAULT_TENANT_MANAGEMENT_CREATE_FORM_PROPS, |
|||
DEFAULT_TENANT_MANAGEMENT_EDIT_FORM_PROPS, |
|||
DEFAULT_TENANT_MANAGEMENT_ENTITY_ACTIONS, |
|||
DEFAULT_TENANT_MANAGEMENT_ENTITY_PROPS, |
|||
DEFAULT_TENANT_MANAGEMENT_TOOLBAR_ACTIONS, |
|||
TENANT_MANAGEMENT_CREATE_FORM_PROP_CONTRIBUTORS, |
|||
TENANT_MANAGEMENT_EDIT_FORM_PROP_CONTRIBUTORS, |
|||
TENANT_MANAGEMENT_ENTITY_ACTION_CONTRIBUTORS, |
|||
TENANT_MANAGEMENT_ENTITY_PROP_CONTRIBUTORS, |
|||
TENANT_MANAGEMENT_TOOLBAR_ACTION_CONTRIBUTORS, |
|||
} from '../tokens/extensions.token'; |
|||
|
|||
@Injectable() |
|||
export class TenantManagementExtensionsGuard implements CanActivate { |
|||
constructor(private injector: Injector) {} |
|||
|
|||
canActivate(): Observable<boolean> { |
|||
const extensions: ExtensionsService = this.injector.get(ExtensionsService); |
|||
const actionContributors: TenantManagementEntityActionContributors = |
|||
this.injector.get(TENANT_MANAGEMENT_ENTITY_ACTION_CONTRIBUTORS, null) || {}; |
|||
const toolbarContributors: TenantManagementToolbarActionContributors = |
|||
this.injector.get(TENANT_MANAGEMENT_TOOLBAR_ACTION_CONTRIBUTORS, null) || {}; |
|||
const propContributors: TenantManagementEntityPropContributors = |
|||
this.injector.get(TENANT_MANAGEMENT_ENTITY_PROP_CONTRIBUTORS, null) || {}; |
|||
const createFormContributors: TenantManagementCreateFormPropContributors = |
|||
this.injector.get(TENANT_MANAGEMENT_CREATE_FORM_PROP_CONTRIBUTORS, null) || {}; |
|||
const editFormContributors: TenantManagementEditFormPropContributors = |
|||
this.injector.get(TENANT_MANAGEMENT_EDIT_FORM_PROP_CONTRIBUTORS, null) || {}; |
|||
|
|||
const configState = this.injector.get(ConfigStateService); |
|||
return getObjectExtensionEntitiesFromStore(configState, 'TenantManagement').pipe( |
|||
map(entities => ({ |
|||
[eTenantManagementComponents.Tenants]: entities.Tenant, |
|||
})), |
|||
mapEntitiesToContributors(configState, 'TenantManagement'), |
|||
tap(objectExtensionContributors => { |
|||
mergeWithDefaultActions( |
|||
extensions.entityActions, |
|||
DEFAULT_TENANT_MANAGEMENT_ENTITY_ACTIONS, |
|||
actionContributors, |
|||
); |
|||
mergeWithDefaultActions( |
|||
extensions.toolbarActions, |
|||
DEFAULT_TENANT_MANAGEMENT_TOOLBAR_ACTIONS, |
|||
toolbarContributors, |
|||
); |
|||
mergeWithDefaultProps( |
|||
extensions.entityProps, |
|||
DEFAULT_TENANT_MANAGEMENT_ENTITY_PROPS, |
|||
objectExtensionContributors.prop, |
|||
propContributors, |
|||
); |
|||
mergeWithDefaultProps( |
|||
extensions.createFormProps, |
|||
DEFAULT_TENANT_MANAGEMENT_CREATE_FORM_PROPS, |
|||
objectExtensionContributors.createForm, |
|||
createFormContributors, |
|||
); |
|||
mergeWithDefaultProps( |
|||
extensions.editFormProps, |
|||
DEFAULT_TENANT_MANAGEMENT_EDIT_FORM_PROPS, |
|||
objectExtensionContributors.editForm, |
|||
editFormContributors, |
|||
); |
|||
}), |
|||
mapTo(true), |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './extensions.guard'; |
|||
@ -0,0 +1,37 @@ |
|||
import { |
|||
CreateFormPropContributorCallback, |
|||
EditFormPropContributorCallback, |
|||
EntityActionContributorCallback, |
|||
EntityPropContributorCallback, |
|||
ToolbarActionContributorCallback, |
|||
} from '@abp/ng.theme.shared/extensions'; |
|||
import { eTenantManagementComponents } from '../enums/components'; |
|||
import { TenantCreateDto, TenantDto, TenantUpdateDto } from '../proxy/models'; |
|||
|
|||
export type TenantManagementEntityActionContributors = Partial<{ |
|||
[eTenantManagementComponents.Tenants]: EntityActionContributorCallback<TenantDto>[]; |
|||
}>; |
|||
|
|||
export type TenantManagementToolbarActionContributors = Partial<{ |
|||
[eTenantManagementComponents.Tenants]: ToolbarActionContributorCallback<TenantDto[]>[]; |
|||
}>; |
|||
|
|||
export type TenantManagementEntityPropContributors = Partial<{ |
|||
[eTenantManagementComponents.Tenants]: EntityPropContributorCallback<TenantDto>[]; |
|||
}>; |
|||
|
|||
export type TenantManagementCreateFormPropContributors = Partial<{ |
|||
[eTenantManagementComponents.Tenants]: CreateFormPropContributorCallback<TenantCreateDto>[]; |
|||
}>; |
|||
|
|||
export type TenantManagementEditFormPropContributors = Partial<{ |
|||
[eTenantManagementComponents.Tenants]: EditFormPropContributorCallback<TenantUpdateDto>[]; |
|||
}>; |
|||
|
|||
export interface TenantManagementConfigOptions { |
|||
entityActionContributors?: TenantManagementEntityActionContributors; |
|||
toolbarActionContributors?: TenantManagementToolbarActionContributors; |
|||
entityPropContributors?: TenantManagementEntityPropContributors; |
|||
createFormPropContributors?: TenantManagementCreateFormPropContributors; |
|||
editFormPropContributors?: TenantManagementEditFormPropContributors; |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
export * from './tenant-management'; |
|||
export * from './config-options'; |
|||
@ -0,0 +1,9 @@ |
|||
import { PagedResultDto } from '@abp/ng.core'; |
|||
import { TenantDto } from '../proxy/models'; |
|||
|
|||
export namespace TenantManagement { |
|||
export interface State { |
|||
result: PagedResultDto<TenantDto>; |
|||
selectedItem: TenantDto; |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
# Proxy Generation Output |
|||
|
|||
This directory includes the output of the latest proxy generation. |
|||
The files and folders in it will be overwritten when proxy generation is run again. |
|||
Therefore, please do not place your own content in this folder. |
|||
|
|||
In addition, `generate-proxy.json` works like a lock file. |
|||
It includes information used by the proxy generator, so please do not delete or modify it. |
|||
|
|||
Finally, the name of the files and folders should not be changed for two reasons: |
|||
- Proxy generator will keep creating them at those paths and you will have multiple copies of the same content. |
|||
- ABP Suite generates files which include imports from this folder. |
|||
|
|||
> **Important Notice:** If you are building a module and are planning to publish to npm, |
|||
> some of the generated proxies are likely to be exported from public-api.ts file. In such a case, |
|||
> please make sure you export files directly and not from barrel exports. In other words, |
|||
> do not include index.ts exports in your public-api.ts exports. |
|||
File diff suppressed because it is too large
@ -0,0 +1,2 @@ |
|||
export * from './models'; |
|||
export * from './tenant.service'; |
|||
@ -0,0 +1,22 @@ |
|||
import type { ExtensibleEntityDto, ExtensibleObject, PagedAndSortedResultRequestDto } from '@abp/ng.core'; |
|||
|
|||
export interface GetTenantsInput extends PagedAndSortedResultRequestDto { |
|||
filter: string; |
|||
} |
|||
|
|||
export interface TenantCreateDto extends TenantCreateOrUpdateDtoBase { |
|||
adminEmailAddress: string; |
|||
adminPassword: string; |
|||
} |
|||
|
|||
export interface TenantCreateOrUpdateDtoBase extends ExtensibleObject { |
|||
name: string; |
|||
} |
|||
|
|||
export interface TenantDto extends ExtensibleEntityDto<string> { |
|||
name: string; |
|||
} |
|||
|
|||
// tslint:disable-next-line: no-empty-interface
|
|||
export interface TenantUpdateDto extends TenantCreateOrUpdateDtoBase { |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
import type { GetTenantsInput, TenantCreateDto, TenantDto, TenantUpdateDto } from './models'; |
|||
import { RestService } from '@abp/ng.core'; |
|||
import type { PagedResultDto } from '@abp/ng.core'; |
|||
import { Injectable } from '@angular/core'; |
|||
|
|||
@Injectable({ |
|||
providedIn: 'root', |
|||
}) |
|||
export class TenantService { |
|||
apiName = 'AbpTenantManagement'; |
|||
|
|||
create = (input: TenantCreateDto) => |
|||
this.restService.request<any, TenantDto>({ |
|||
method: 'POST', |
|||
url: '/api/multi-tenancy/tenants', |
|||
body: input, |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
delete = (id: string) => |
|||
this.restService.request<any, void>({ |
|||
method: 'DELETE', |
|||
url: `/api/multi-tenancy/tenants/${id}`, |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
deleteDefaultConnectionString = (id: string) => |
|||
this.restService.request<any, void>({ |
|||
method: 'DELETE', |
|||
url: `/api/multi-tenancy/tenants/${id}/default-connection-string`, |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
get = (id: string) => |
|||
this.restService.request<any, TenantDto>({ |
|||
method: 'GET', |
|||
url: `/api/multi-tenancy/tenants/${id}`, |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
getDefaultConnectionString = (id: string) => |
|||
this.restService.request<any, string>({ |
|||
method: 'GET', |
|||
responseType: 'text', |
|||
url: `/api/multi-tenancy/tenants/${id}/default-connection-string`, |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
getList = (input: GetTenantsInput) => |
|||
this.restService.request<any, PagedResultDto<TenantDto>>({ |
|||
method: 'GET', |
|||
url: '/api/multi-tenancy/tenants', |
|||
params: { filter: input.filter, sorting: input.sorting, skipCount: input.skipCount, maxResultCount: input.maxResultCount }, |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
update = (id: string, input: TenantUpdateDto) => |
|||
this.restService.request<any, TenantDto>({ |
|||
method: 'PUT', |
|||
url: `/api/multi-tenancy/tenants/${id}`, |
|||
body: input, |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
updateDefaultConnectionString = (id: string, defaultConnectionString: string) => |
|||
this.restService.request<any, void>({ |
|||
method: 'PUT', |
|||
url: `/api/multi-tenancy/tenants/${id}/default-connection-string`, |
|||
params: { defaultConnectionString }, |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
constructor(private restService: RestService) {} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './tenant-management-state.service'; |
|||
@ -0,0 +1,41 @@ |
|||
import { Injectable } from '@angular/core'; |
|||
import { Store } from '@ngxs/store'; |
|||
import { TenantManagementState } from '../states/tenant-management.state'; |
|||
import { ABP } from '@abp/ng.core'; |
|||
import { GetTenants, GetTenantById, CreateTenant, UpdateTenant, DeleteTenant } from '../actions'; |
|||
import { TenantManagement } from '../models'; |
|||
|
|||
@Injectable({ |
|||
providedIn: 'root', |
|||
}) |
|||
export class TenantManagementStateService { |
|||
constructor(private store: Store) {} |
|||
|
|||
get() { |
|||
return this.store.selectSnapshot(TenantManagementState.get); |
|||
} |
|||
|
|||
getTenantsTotalCount() { |
|||
return this.store.selectSnapshot(TenantManagementState.getTenantsTotalCount); |
|||
} |
|||
|
|||
dispatchGetTenants(...args: ConstructorParameters<typeof GetTenants>) { |
|||
return this.store.dispatch(new GetTenants(...args)); |
|||
} |
|||
|
|||
dispatchGetTenantById(...args: ConstructorParameters<typeof GetTenantById>) { |
|||
return this.store.dispatch(new GetTenantById(...args)); |
|||
} |
|||
|
|||
dispatchCreateTenant(...args: ConstructorParameters<typeof CreateTenant>) { |
|||
return this.store.dispatch(new CreateTenant(...args)); |
|||
} |
|||
|
|||
dispatchUpdateTenant(...args: ConstructorParameters<typeof UpdateTenant>) { |
|||
return this.store.dispatch(new UpdateTenant(...args)); |
|||
} |
|||
|
|||
dispatchDeleteTenant(...args: ConstructorParameters<typeof DeleteTenant>) { |
|||
return this.store.dispatch(new DeleteTenant(...args)); |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './tenant-management.state'; |
|||
@ -0,0 +1,70 @@ |
|||
import { ABP, PagedResultDto } from '@abp/ng.core'; |
|||
import { Action, Selector, State, StateContext } from '@ngxs/store'; |
|||
import { tap } from 'rxjs/operators'; |
|||
import { |
|||
CreateTenant, |
|||
DeleteTenant, |
|||
GetTenantById, |
|||
GetTenants, |
|||
UpdateTenant, |
|||
} from '../actions/tenant-management.actions'; |
|||
import { TenantManagement } from '../models/tenant-management'; |
|||
import { Injectable } from '@angular/core'; |
|||
import { TenantService } from '../proxy/tenant.service'; |
|||
import { TenantDto } from '../proxy/models'; |
|||
|
|||
@State<TenantManagement.State>({ |
|||
name: 'TenantManagementState', |
|||
defaults: { result: {}, selectedItem: {} } as TenantManagement.State, |
|||
}) |
|||
@Injectable() |
|||
export class TenantManagementState { |
|||
@Selector() |
|||
static get({ result }: TenantManagement.State): TenantDto[] { |
|||
return result.items || []; |
|||
} |
|||
|
|||
@Selector() |
|||
static getTenantsTotalCount({ result }: TenantManagement.State): number { |
|||
return result.totalCount; |
|||
} |
|||
|
|||
constructor(private service: TenantService) {} |
|||
|
|||
@Action(GetTenants) |
|||
get({ patchState }: StateContext<TenantManagement.State>, { payload }: GetTenants) { |
|||
return this.service.getList(payload).pipe( |
|||
tap(result => |
|||
patchState({ |
|||
result, |
|||
}), |
|||
), |
|||
); |
|||
} |
|||
|
|||
@Action(GetTenantById) |
|||
getById({ patchState }: StateContext<TenantManagement.State>, { payload }: GetTenantById) { |
|||
return this.service.get(payload).pipe( |
|||
tap(selectedItem => |
|||
patchState({ |
|||
selectedItem, |
|||
}), |
|||
), |
|||
); |
|||
} |
|||
|
|||
@Action(DeleteTenant) |
|||
delete(_, { payload }: DeleteTenant) { |
|||
return this.service.delete(payload); |
|||
} |
|||
|
|||
@Action(CreateTenant) |
|||
add(_, { payload }: CreateTenant) { |
|||
return this.service.create(payload); |
|||
} |
|||
|
|||
@Action(UpdateTenant) |
|||
update({ getState }: StateContext<TenantManagement.State>, { payload }: UpdateTenant) { |
|||
return this.service.update(payload.id, { ...getState().selectedItem, ...payload }); |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
import { |
|||
AuthGuard, |
|||
DynamicLayoutComponent, |
|||
PermissionGuard, |
|||
ReplaceableComponents, |
|||
ReplaceableRouteContainerComponent, |
|||
} from '@abp/ng.core'; |
|||
import { NgModule } from '@angular/core'; |
|||
import { RouterModule, Routes } from '@angular/router'; |
|||
import { TenantsComponent } from './components/tenants/tenants.component'; |
|||
import { eTenantManagementComponents } from './enums/components'; |
|||
import { TenantManagementExtensionsGuard } from './guards'; |
|||
|
|||
const routes: Routes = [ |
|||
{ path: '', redirectTo: 'tenants', pathMatch: 'full' }, |
|||
{ |
|||
path: '', |
|||
component: DynamicLayoutComponent, |
|||
canActivate: [AuthGuard, PermissionGuard, TenantManagementExtensionsGuard], |
|||
children: [ |
|||
{ |
|||
path: 'tenants', |
|||
component: ReplaceableRouteContainerComponent, |
|||
data: { |
|||
requiredPolicy: 'AbpTenantManagement.Tenants', |
|||
replaceableComponent: { |
|||
key: eTenantManagementComponents.Tenants, |
|||
defaultComponent: TenantsComponent, |
|||
} as ReplaceableComponents.RouteData<TenantsComponent>, |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
]; |
|||
|
|||
@NgModule({ |
|||
imports: [RouterModule.forChild(routes)], |
|||
exports: [RouterModule], |
|||
}) |
|||
export class TenantManagementRoutingModule {} |
|||
@ -0,0 +1,73 @@ |
|||
import { CoreModule, LazyModuleFactory } from '@abp/ng.core'; |
|||
import { FeatureManagementModule } from '@abp/ng.feature-management'; |
|||
import { ThemeSharedModule } from '@abp/ng.theme.shared'; |
|||
import { UiExtensionsModule } from '@abp/ng.theme.shared/extensions'; |
|||
import { ModuleWithProviders, NgModule, NgModuleFactory } from '@angular/core'; |
|||
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; |
|||
import { NgxValidateCoreModule } from '@ngx-validate/core'; |
|||
import { NgxsModule } from '@ngxs/store'; |
|||
import { TenantsComponent } from './components/tenants/tenants.component'; |
|||
import { TenantManagementExtensionsGuard } from './guards/extensions.guard'; |
|||
import { TenantManagementConfigOptions } from './models/config-options'; |
|||
import { TenantManagementState } from './states/tenant-management.state'; |
|||
import { TenantManagementRoutingModule } from './tenant-management-routing.module'; |
|||
import { |
|||
TENANT_MANAGEMENT_CREATE_FORM_PROP_CONTRIBUTORS, |
|||
TENANT_MANAGEMENT_EDIT_FORM_PROP_CONTRIBUTORS, |
|||
TENANT_MANAGEMENT_ENTITY_ACTION_CONTRIBUTORS, |
|||
TENANT_MANAGEMENT_ENTITY_PROP_CONTRIBUTORS, |
|||
TENANT_MANAGEMENT_TOOLBAR_ACTION_CONTRIBUTORS, |
|||
} from './tokens/extensions.token'; |
|||
|
|||
@NgModule({ |
|||
declarations: [TenantsComponent], |
|||
exports: [TenantsComponent], |
|||
imports: [ |
|||
TenantManagementRoutingModule, |
|||
NgxsModule.forFeature([TenantManagementState]), |
|||
NgxValidateCoreModule, |
|||
CoreModule, |
|||
ThemeSharedModule, |
|||
NgbDropdownModule, |
|||
FeatureManagementModule, |
|||
UiExtensionsModule, |
|||
], |
|||
}) |
|||
export class TenantManagementModule { |
|||
static forChild( |
|||
options: TenantManagementConfigOptions = {}, |
|||
): ModuleWithProviders<TenantManagementModule> { |
|||
return { |
|||
ngModule: TenantManagementModule, |
|||
providers: [ |
|||
{ |
|||
provide: TENANT_MANAGEMENT_ENTITY_ACTION_CONTRIBUTORS, |
|||
useValue: options.entityActionContributors, |
|||
}, |
|||
{ |
|||
provide: TENANT_MANAGEMENT_TOOLBAR_ACTION_CONTRIBUTORS, |
|||
useValue: options.toolbarActionContributors, |
|||
}, |
|||
{ |
|||
provide: TENANT_MANAGEMENT_ENTITY_PROP_CONTRIBUTORS, |
|||
useValue: options.entityPropContributors, |
|||
}, |
|||
{ |
|||
provide: TENANT_MANAGEMENT_CREATE_FORM_PROP_CONTRIBUTORS, |
|||
useValue: options.createFormPropContributors, |
|||
}, |
|||
{ |
|||
provide: TENANT_MANAGEMENT_EDIT_FORM_PROP_CONTRIBUTORS, |
|||
useValue: options.editFormPropContributors, |
|||
}, |
|||
TenantManagementExtensionsGuard, |
|||
], |
|||
}; |
|||
} |
|||
|
|||
static forLazy( |
|||
options: TenantManagementConfigOptions = {}, |
|||
): NgModuleFactory<TenantManagementModule> { |
|||
return new LazyModuleFactory(TenantManagementModule.forChild(options)); |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest'; |
|||
import { TenantManagementStateService } from '../services/tenant-management-state.service'; |
|||
import { TenantManagementState } from '../states/tenant-management.state'; |
|||
import { Store } from '@ngxs/store'; |
|||
import * as TenantManagementActions from '../actions'; |
|||
|
|||
describe('TenantManagementStateService', () => { |
|||
let service: TenantManagementStateService; |
|||
let spectator: SpectatorService<TenantManagementStateService>; |
|||
let store: SpyObject<Store>; |
|||
|
|||
const createService = createServiceFactory({ |
|||
service: TenantManagementStateService, |
|||
mocks: [Store], |
|||
}); |
|||
beforeEach(() => { |
|||
spectator = createService(); |
|||
service = spectator.service; |
|||
store = spectator.inject(Store); |
|||
}); |
|||
|
|||
test('should have the all TenantManagementState static methods', () => { |
|||
const reg = /(?<=static )(.*)(?=\()/gm; |
|||
TenantManagementState.toString() |
|||
.match(reg) |
|||
.forEach(fnName => { |
|||
expect(service[fnName]).toBeTruthy(); |
|||
|
|||
const spy = jest.spyOn(store, 'selectSnapshot'); |
|||
spy.mockClear(); |
|||
|
|||
const isDynamicSelector = TenantManagementState[fnName].name !== 'memoized'; |
|||
|
|||
if (isDynamicSelector) { |
|||
TenantManagementState[fnName] = jest.fn((...args) => args); |
|||
service[fnName]('test', 0, {}); |
|||
expect(TenantManagementState[fnName]).toHaveBeenCalledWith('test', 0, {}); |
|||
} else { |
|||
service[fnName](); |
|||
expect(spy).toHaveBeenCalledWith(TenantManagementState[fnName]); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
test('should have a dispatch method for every TenantManagementState action', () => { |
|||
const reg = /(?<=dispatch)(\w+)(?=\()/gm; |
|||
TenantManagementStateService.toString() |
|||
.match(reg) |
|||
.forEach(fnName => { |
|||
expect(TenantManagementActions[fnName]).toBeTruthy(); |
|||
|
|||
const spy = jest.spyOn(store, 'dispatch'); |
|||
spy.mockClear(); |
|||
|
|||
const params = Array.from(new Array(TenantManagementActions[fnName].length)); |
|||
|
|||
service[`dispatch${fnName}`](...params); |
|||
expect(spy).toHaveBeenCalledWith(new TenantManagementActions[fnName](...params)); |
|||
}); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,74 @@ |
|||
import { |
|||
CreateFormPropContributorCallback, |
|||
EditFormPropContributorCallback, |
|||
EntityActionContributorCallback, |
|||
EntityPropContributorCallback, |
|||
ToolbarActionContributorCallback, |
|||
} from '@abp/ng.theme.shared/extensions'; |
|||
import { InjectionToken } from '@angular/core'; |
|||
import { DEFAULT_TENANTS_ENTITY_ACTIONS } from '../defaults/default-tenants-entity-actions'; |
|||
import { DEFAULT_TENANTS_ENTITY_PROPS } from '../defaults/default-tenants-entity-props'; |
|||
import { |
|||
DEFAULT_TENANTS_CREATE_FORM_PROPS, |
|||
DEFAULT_TENANTS_EDIT_FORM_PROPS, |
|||
} from '../defaults/default-tenants-form-props'; |
|||
import { DEFAULT_TENANTS_TOOLBAR_ACTIONS } from '../defaults/default-tenants-toolbar-actions'; |
|||
import { eTenantManagementComponents } from '../enums/components'; |
|||
import { TenantCreateDto, TenantDto, TenantUpdateDto } from '../proxy/models'; |
|||
|
|||
export const DEFAULT_TENANT_MANAGEMENT_ENTITY_ACTIONS = { |
|||
[eTenantManagementComponents.Tenants]: DEFAULT_TENANTS_ENTITY_ACTIONS, |
|||
}; |
|||
|
|||
export const DEFAULT_TENANT_MANAGEMENT_TOOLBAR_ACTIONS = { |
|||
[eTenantManagementComponents.Tenants]: DEFAULT_TENANTS_TOOLBAR_ACTIONS, |
|||
}; |
|||
|
|||
export const DEFAULT_TENANT_MANAGEMENT_ENTITY_PROPS = { |
|||
[eTenantManagementComponents.Tenants]: DEFAULT_TENANTS_ENTITY_PROPS, |
|||
}; |
|||
|
|||
export const DEFAULT_TENANT_MANAGEMENT_CREATE_FORM_PROPS = { |
|||
[eTenantManagementComponents.Tenants]: DEFAULT_TENANTS_CREATE_FORM_PROPS, |
|||
}; |
|||
|
|||
export const DEFAULT_TENANT_MANAGEMENT_EDIT_FORM_PROPS = { |
|||
[eTenantManagementComponents.Tenants]: DEFAULT_TENANTS_EDIT_FORM_PROPS, |
|||
}; |
|||
|
|||
export const TENANT_MANAGEMENT_ENTITY_ACTION_CONTRIBUTORS = new InjectionToken<EntityActionContributors>( |
|||
'TENANT_MANAGEMENT_ENTITY_ACTION_CONTRIBUTORS', |
|||
); |
|||
|
|||
export const TENANT_MANAGEMENT_TOOLBAR_ACTION_CONTRIBUTORS = new InjectionToken<ToolbarActionContributors>( |
|||
'TENANT_MANAGEMENT_TOOLBAR_ACTION_CONTRIBUTORS', |
|||
); |
|||
|
|||
export const TENANT_MANAGEMENT_ENTITY_PROP_CONTRIBUTORS = new InjectionToken<EntityPropContributors>( |
|||
'TENANT_MANAGEMENT_ENTITY_PROP_CONTRIBUTORS', |
|||
); |
|||
|
|||
export const TENANT_MANAGEMENT_CREATE_FORM_PROP_CONTRIBUTORS = new InjectionToken<CreateFormPropContributors>( |
|||
'TENANT_MANAGEMENT_CREATE_FORM_PROP_CONTRIBUTORS', |
|||
); |
|||
|
|||
export const TENANT_MANAGEMENT_EDIT_FORM_PROP_CONTRIBUTORS = new InjectionToken<EditFormPropContributors>( |
|||
'TENANT_MANAGEMENT_EDIT_FORM_PROP_CONTRIBUTORS', |
|||
); |
|||
|
|||
// Fix for TS4023 -> https://github.com/microsoft/TypeScript/issues/9944#issuecomment-254693497
|
|||
type EntityActionContributors = Partial<{ |
|||
[eTenantManagementComponents.Tenants]: EntityActionContributorCallback<TenantDto>[]; |
|||
}>; |
|||
type ToolbarActionContributors = Partial<{ |
|||
[eTenantManagementComponents.Tenants]: ToolbarActionContributorCallback<TenantDto[]>[]; |
|||
}>; |
|||
type EntityPropContributors = Partial<{ |
|||
[eTenantManagementComponents.Tenants]: EntityPropContributorCallback<TenantDto>[]; |
|||
}>; |
|||
type CreateFormPropContributors = Partial<{ |
|||
[eTenantManagementComponents.Tenants]: CreateFormPropContributorCallback<TenantCreateDto>[]; |
|||
}>; |
|||
type EditFormPropContributors = Partial<{ |
|||
[eTenantManagementComponents.Tenants]: EditFormPropContributorCallback<TenantUpdateDto>[]; |
|||
}>; |
|||
@ -0,0 +1 @@ |
|||
export * from './extensions.token'; |
|||
@ -0,0 +1,10 @@ |
|||
export * from './lib/actions'; |
|||
export * from './lib/components'; |
|||
export * from './lib/enums'; |
|||
export * from './lib/guards'; |
|||
export * from './lib/models'; |
|||
export * from './lib/proxy'; |
|||
export * from './lib/services'; |
|||
export * from './lib/states'; |
|||
export * from './lib/tenant-management.module'; |
|||
export * from './lib/tokens'; |
|||
@ -0,0 +1 @@ |
|||
import 'jest-preset-angular/setup-jest'; |
|||
@ -0,0 +1,13 @@ |
|||
{ |
|||
"extends": "../../tsconfig.base.json", |
|||
"files": [], |
|||
"include": [], |
|||
"references": [ |
|||
{ |
|||
"path": "./tsconfig.lib.json" |
|||
}, |
|||
{ |
|||
"path": "./tsconfig.spec.json" |
|||
} |
|||
] |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
{ |
|||
"extends": "./tsconfig.json", |
|||
"compilerOptions": { |
|||
"outDir": "../../dist/out-tsc", |
|||
"target": "es2015", |
|||
"declaration": true, |
|||
"declarationMap": true, |
|||
"inlineSources": true, |
|||
"types": [], |
|||
"lib": ["dom", "es2018"] |
|||
}, |
|||
"exclude": ["src/test-setup.ts", "**/*.spec.ts"], |
|||
"include": ["**/*.ts"] |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
/* To learn more about this file see: https://angular.io/config/tsconfig. */ |
|||
{ |
|||
"extends": "./tsconfig.lib.json", |
|||
"compilerOptions": { |
|||
"declarationMap": false |
|||
}, |
|||
"angularCompilerOptions": { |
|||
"compilationMode": "partial", |
|||
"enableIvy": false |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
{ |
|||
"extends": "./tsconfig.json", |
|||
"compilerOptions": { |
|||
"outDir": "../../dist/out-tsc", |
|||
"module": "commonjs", |
|||
"types": ["jest", "node"] |
|||
}, |
|||
"files": ["src/test-setup.ts"], |
|||
"include": ["**/*.spec.ts", "**/*.d.ts"] |
|||
} |
|||
Loading…
Reference in new issue