diff --git a/npm/ng-packs/angular.json b/npm/ng-packs/angular.json index cee77b25bd..819ae99025 100644 --- a/npm/ng-packs/angular.json +++ b/npm/ng-packs/angular.json @@ -188,6 +188,39 @@ } } }, + "feature-management": { + "projectType": "library", + "root": "packages/feature-management", + "sourceRoot": "packages/feature-management/src", + "prefix": "abp", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "packages/feature-management/tsconfig.lib.json", + "project": "packages/feature-management/ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "packages/feature-management/src/test.ts", + "tsConfig": "packages/feature-management/tsconfig.spec.json", + "karmaConfig": "packages/feature-management/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "packages/feature-management/tsconfig.lib.json", + "packages/feature-management/tsconfig.spec.json" + ], + "exclude": ["**/node_modules/**"] + } + } + } + }, "tenant-management": { "projectType": "library", "root": "packages/tenant-management", diff --git a/npm/ng-packs/packages/feature-management/README.md b/npm/ng-packs/packages/feature-management/README.md new file mode 100644 index 0000000000..d040ec455a --- /dev/null +++ b/npm/ng-packs/packages/feature-management/README.md @@ -0,0 +1 @@ +

 @abp/ng.feature-management

diff --git a/npm/ng-packs/packages/feature-management/karma.conf.js b/npm/ng-packs/packages/feature-management/karma.conf.js new file mode 100644 index 0000000000..65f905a52e --- /dev/null +++ b/npm/ng-packs/packages/feature-management/karma.conf.js @@ -0,0 +1,32 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, '../../coverage/feature-management'), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/npm/ng-packs/packages/feature-management/ng-package.json b/npm/ng-packs/packages/feature-management/ng-package.json new file mode 100644 index 0000000000..0305bb9206 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/feature-management", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/packages/feature-management/package.json b/npm/ng-packs/packages/feature-management/package.json new file mode 100644 index 0000000000..f41fd28fa1 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/package.json @@ -0,0 +1,4 @@ +{ + "name": "@abp/ng.feature-management", + "version": "0.0.1" +} diff --git a/npm/ng-packs/packages/feature-management/src/lib/actions/feature-management.actions.ts b/npm/ng-packs/packages/feature-management/src/lib/actions/feature-management.actions.ts new file mode 100644 index 0000000000..dee5da664a --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/lib/actions/feature-management.actions.ts @@ -0,0 +1,11 @@ +import { FeatureManagement } from '../models'; + +export class GetFeatures { + static readonly type = '[FeatureManagement] Get Features'; + constructor(public payload: FeatureManagement.Provider) {} +} + +export class UpdateFeatures { + static readonly type = '[FeatureManagement] Update Features'; + constructor(public payload: FeatureManagement.Provider & FeatureManagement.Features) {} +} diff --git a/npm/ng-packs/packages/feature-management/src/lib/actions/index.ts b/npm/ng-packs/packages/feature-management/src/lib/actions/index.ts new file mode 100644 index 0000000000..66678fb322 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/lib/actions/index.ts @@ -0,0 +1 @@ +export * from './feature-management.actions'; diff --git a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html new file mode 100644 index 0000000000..2dad2dfd92 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html @@ -0,0 +1,32 @@ + + +

{{ 'AbpTenantManagement::Permission:ManageFeatures' | abpLocalization }}

+
+ + +
+
+
{{ feature.name }}
+
+ +
+
+ +
+
+
+
+ + + + + {{ 'AbpFeatureManagement::Save' | abpLocalization }} + + +
diff --git a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts new file mode 100644 index 0000000000..38219b2b08 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts @@ -0,0 +1,97 @@ +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; +import { Select, Store } from '@ngxs/store'; +import { Observable } from 'rxjs'; +import { GetFeatures, UpdateFeatures } from '../../actions'; +import { FeatureManagement } from '../../models/feature-management'; +import { FeatureManagementState } from '../../states'; +import { FormGroup, FormControl } from '@angular/forms'; +import { pluck, tap } from 'rxjs/operators'; + +@Component({ + selector: 'abp-feature-management', + templateUrl: './feature-management.component.html', +}) +export class FeatureManagementComponent { + @Input() + providerKey: string; + + @Input() + providerName: string; + + protected _visible; + + @Input() + get visible(): boolean { + return this._visible; + } + + set visible(value: boolean) { + this._visible = value; + this.visibleChange.emit(value); + + if (value) this.openModal(); + } + + @Output() + visibleChange = new EventEmitter(); + + @Select(FeatureManagementState.getFeatures) + features$: Observable; + + modalBusy: boolean = false; + + form: FormGroup; + + constructor(private store: Store) {} + + openModal() { + if (!this.providerKey || !this.providerName) { + throw new Error('Provider Key and Provider Name are required.'); + } + + this.getFeatures(); + } + + getFeatures() { + this.store + .dispatch(new GetFeatures({ providerKey: this.providerKey, providerName: this.providerName })) + .pipe(pluck('FeatureManagementState', 'features')) + .subscribe(features => { + this.buildForm(features); + }); + } + + buildForm(features) { + const formGroupObj = {}; + + for (let i = 0; i < features.length; i++) { + formGroupObj[i] = new FormControl(features[i].value === 'false' ? null : features[i].value); + } + + this.form = new FormGroup(formGroupObj); + } + + save() { + this.modalBusy = true; + + let features = this.store.selectSnapshot(FeatureManagementState.getFeatures); + + features = features.map((feature, i) => ({ + name: feature.name, + value: !this.form.value[i] || this.form.value[i] === 'false' ? null : this.form.value[i], + })); + + this.store + .dispatch( + new UpdateFeatures({ + providerKey: this.providerKey, + providerName: this.providerName, + features, + }), + ) + .subscribe(() => { + this.modalBusy = false; + this.visible = false; + }); + } +} diff --git a/npm/ng-packs/packages/feature-management/src/lib/components/index.ts b/npm/ng-packs/packages/feature-management/src/lib/components/index.ts new file mode 100644 index 0000000000..64f8bd0fc5 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/lib/components/index.ts @@ -0,0 +1 @@ +export * from './feature-management/feature-management.component'; diff --git a/npm/ng-packs/packages/feature-management/src/lib/feature-management.module.ts b/npm/ng-packs/packages/feature-management/src/lib/feature-management.module.ts new file mode 100644 index 0000000000..ee3eca6d42 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/lib/feature-management.module.ts @@ -0,0 +1,13 @@ +import { CoreModule } from '@abp/ng.core'; +import { ThemeSharedModule } from '@abp/ng.theme.shared'; +import { NgModule } from '@angular/core'; +import { FeatureManagementComponent } from './components/feature-management/feature-management.component'; +import { NgxsModule } from '@ngxs/store'; +import { FeatureManagementState } from './states/feature-management.state'; + +@NgModule({ + declarations: [FeatureManagementComponent], + imports: [CoreModule, ThemeSharedModule, NgxsModule.forFeature([FeatureManagementState])], + exports: [FeatureManagementComponent], +}) +export class FeatureManagementModule {} diff --git a/npm/ng-packs/packages/feature-management/src/lib/models/feature-management.ts b/npm/ng-packs/packages/feature-management/src/lib/models/feature-management.ts new file mode 100644 index 0000000000..05a0a28c99 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/lib/models/feature-management.ts @@ -0,0 +1,29 @@ +export namespace FeatureManagement { + export interface State { + features: Feature[]; + } + + export interface ValueType { + name: string; + properties: object; + validator: object; + } + + export interface Feature { + name: string; + value: string; + description?: string; + valueType?: ValueType; + depth?: number; + parentName?: string; + } + + export interface Features { + features: Feature[]; + } + + export interface Provider { + providerName: string; + providerKey: string; + } +} diff --git a/npm/ng-packs/packages/feature-management/src/lib/models/index.ts b/npm/ng-packs/packages/feature-management/src/lib/models/index.ts new file mode 100644 index 0000000000..d6ed522ae0 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/lib/models/index.ts @@ -0,0 +1 @@ +export * from './feature-management'; diff --git a/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts b/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts new file mode 100644 index 0000000000..5f6c0eed6e --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@angular/core'; +import { RestService, Rest } from '@abp/ng.core'; +import { Store } from '@ngxs/store'; +import { Observable } from 'rxjs'; +import { FeatureManagement } from '../models'; + +@Injectable({ + providedIn: 'root', +}) +export class FeatureManagementService { + constructor(private rest: RestService, private store: Store) {} + + getFeatures(params: FeatureManagement.Provider): Observable { + const request: Rest.Request = { + method: 'GET', + url: '/api/abp/features', + params, + }; + return this.rest.request(request); + } + + updateFeatures({ + features, + providerKey, + providerName, + }: FeatureManagement.Provider & FeatureManagement.Features): Observable { + const request: Rest.Request = { + method: 'PUT', + url: '/api/abp/features', + body: { features }, + params: { providerKey, providerName }, + }; + return this.rest.request(request); + } +} diff --git a/npm/ng-packs/packages/feature-management/src/lib/services/index.ts b/npm/ng-packs/packages/feature-management/src/lib/services/index.ts new file mode 100644 index 0000000000..10fceceafe --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/lib/services/index.ts @@ -0,0 +1 @@ +export * from './feature-management.service'; diff --git a/npm/ng-packs/packages/feature-management/src/lib/states/feature-management.state.ts b/npm/ng-packs/packages/feature-management/src/lib/states/feature-management.state.ts new file mode 100644 index 0000000000..a02e075bdb --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/lib/states/feature-management.state.ts @@ -0,0 +1,34 @@ +import { Action, Selector, State, StateContext } from '@ngxs/store'; +import { tap } from 'rxjs/operators'; +import { GetFeatures, UpdateFeatures } from '../actions/feature-management.actions'; +import { FeatureManagement } from '../models/feature-management'; +import { FeatureManagementService } from '../services/feature-management.service'; + +@State({ + name: 'FeatureManagementState', + defaults: { features: {} } as FeatureManagement.State, +}) +export class FeatureManagementState { + @Selector() + static getFeatures({ features }: FeatureManagement.State) { + return features; + } + + constructor(private featureManagementService: FeatureManagementService) {} + + @Action(GetFeatures) + getFeatures({ patchState }: StateContext, { payload }: GetFeatures) { + return this.featureManagementService.getFeatures(payload).pipe( + tap(({ features }) => + patchState({ + features, + }), + ), + ); + } + + @Action(UpdateFeatures) + updateFeatures(_, { payload }: UpdateFeatures) { + return this.featureManagementService.updateFeatures(payload); + } +} diff --git a/npm/ng-packs/packages/feature-management/src/lib/states/index.ts b/npm/ng-packs/packages/feature-management/src/lib/states/index.ts new file mode 100644 index 0000000000..5c32d87889 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/lib/states/index.ts @@ -0,0 +1 @@ +export * from "./feature-management.state"; diff --git a/npm/ng-packs/packages/feature-management/src/public-api.ts b/npm/ng-packs/packages/feature-management/src/public-api.ts new file mode 100644 index 0000000000..047ac00914 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/public-api.ts @@ -0,0 +1,2 @@ +export * from './lib/feature-management.module'; +export * from './lib/components'; diff --git a/npm/ng-packs/packages/feature-management/src/test.ts b/npm/ng-packs/packages/feature-management/src/test.ts new file mode 100644 index 0000000000..978c64fb83 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/test.ts @@ -0,0 +1,21 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone'; +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/npm/ng-packs/packages/feature-management/tsconfig.lib.json b/npm/ng-packs/packages/feature-management/tsconfig.lib.json new file mode 100644 index 0000000000..bd23948e59 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/tsconfig.lib.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "skipTemplateCodegen": true, + "strictMetadataEmit": true, + "fullTemplateTypeCheck": true, + "strictInjectionParameters": true, + "enableResourceInlining": true + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/npm/ng-packs/packages/feature-management/tsconfig.spec.json b/npm/ng-packs/packages/feature-management/tsconfig.spec.json new file mode 100644 index 0000000000..16da33db07 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/npm/ng-packs/packages/feature-management/tslint.json b/npm/ng-packs/packages/feature-management/tslint.json new file mode 100644 index 0000000000..8c3919ea62 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "abp", + "camelCase" + ], + "component-selector": [ + true, + "element", + "abp", + "kebab-case" + ] + } +} diff --git a/npm/ng-packs/packages/tenant-management/ng-package.json b/npm/ng-packs/packages/tenant-management/ng-package.json index e506119f69..07fbab773b 100644 --- a/npm/ng-packs/packages/tenant-management/ng-package.json +++ b/npm/ng-packs/packages/tenant-management/ng-package.json @@ -4,5 +4,5 @@ "lib": { "entryFile": "src/public-api.ts" }, - "whitelistedNonPeerDependencies": ["@abp/ng.theme.shared"] + "whitelistedNonPeerDependencies": ["@abp/ng.theme.shared", "@abp/ng.feature-management"] } diff --git a/npm/ng-packs/packages/tenant-management/package.json b/npm/ng-packs/packages/tenant-management/package.json index 73347fccd4..0e415b4e80 100644 --- a/npm/ng-packs/packages/tenant-management/package.json +++ b/npm/ng-packs/packages/tenant-management/package.json @@ -2,7 +2,8 @@ "name": "@abp/ng.tenant-management", "version": "0.8.3", "dependencies": { - "@abp/ng.theme.shared": "^0.8.3" + "@abp/ng.theme.shared": "^0.8.3", + "@abp/ng.feature-management": "^0.8.4" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html b/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html index 9972e10220..cd1abf487d 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html +++ b/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html @@ -71,7 +71,14 @@ ngbDropdownItem (click)="onEditConnectionString(data.id)" > - {{ 'AbpTenantManagement::ConnectionStrings' | abpLocalization }} + {{ 'AbpTenantManagement::Permission:ManageConnectionStrings' | abpLocalization }} + +