diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/internal/object-extensions.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/internal/object-extensions.ts index 6a44e4e231..ab075737bc 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/internal/object-extensions.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/internal/object-extensions.ts @@ -3,37 +3,150 @@ import { EntityPropList } from '../entity-props'; import { FormPropList } from '../form-props'; import { PropContributorCallbacks } from '../props'; +export interface EntityExtensionDto { + properties: Record; + configuration: Record; +} + +export interface ExtensionEnumDto { + fields: ExtensionEnumFieldDto[]; + localizationResource?: string; + transformed?: any; // UI +} + +export interface ExtensionEnumFieldDto { + name?: string; + value: any; // UI, was object +} + +export interface ExtensionPropertyApiCreateDto { + isAvailable: boolean; +} + +export interface ExtensionPropertyApiDto { + onGet: ExtensionPropertyApiGetDto; + onCreate: ExtensionPropertyApiCreateDto; + onUpdate: ExtensionPropertyApiUpdateDto; +} + +export interface ExtensionPropertyApiGetDto { + isAvailable: boolean; +} + +export interface ExtensionPropertyApiUpdateDto { + isAvailable: boolean; +} + +export interface ExtensionPropertyAttributeDto { + typeSimple?: string; + config: Record; // UI, was Record +} + +export interface ExtensionPropertyDto { + type?: string; + typeSimple?: ePropType; // UI, was string + displayName: LocalizableStringDto; + api: ExtensionPropertyApiDto; + ui: ExtensionPropertyUiDto; + attributes: ExtensionPropertyAttributeDto[]; + configuration: Record; // UI, was Record; + defaultValue: any; // UI, was object +} + +export interface ExtensionPropertyUiDto { + onTable: ExtensionPropertyUiTableDto; + onCreateForm: ExtensionPropertyUiFormDto; + onEditForm: ExtensionPropertyUiFormDto; + lookup: ExtensionPropertyUiLookupDto; +} + +export interface ExtensionPropertyUiFormDto { + isVisible: boolean; +} + +export interface ExtensionPropertyUiLookupDto { + url?: string; + resultListPropertyName?: string; + displayPropertyName?: string; + valuePropertyName?: string; + filterParamName?: string; +} + +export interface ExtensionPropertyUiTableDto { + isSortable?: boolean; // UI + isVisible: boolean; +} + +export interface LocalizableStringDto { + name?: string; + resource?: string; +} + +export interface ModuleExtensionDto { + entities: Record; + configuration: Record; +} + +export interface ObjectExtensionsDto { + modules: Record; + enums: Record; +} + export interface PropContributors { prop: PropContributorCallbacks>; createForm: PropContributorCallbacks>; editForm: PropContributorCallbacks>; } +/** + * @deprecated To be deleted in v4.2. + */ export interface Config { objectExtensions: Item; } +/** + * @deprecated Use ObjectExtensionsDto. To be deleted in v4.2. + */ export interface Item { modules: Modules; enums: Enums; } +/** + * @deprecated Use Record. To be deleted in v4.2. + */ export type Modules = Configuration; +/** + * @deprecated Use ModuleExtensionDto. To be deleted in v4.2. + */ export interface Module { configuration: Configuration; entities: Entities; } +/** + * @deprecated Use Record. To be deleted in v4.2. + */ export type Entities = Configuration; +/** + * @deprecated Use EntityExtensionDto. To be deleted in v4.2. + */ export interface Entity { configuration: Configuration; properties: Properties; } +/** + * @deprecated Use Record. To be deleted in v4.2. + */ export type Properties = Configuration; +/** + * @deprecated Use ExtensionPropertyDto. To be deleted in v4.2. + */ export interface Property { type: string; typeSimple: ePropType; @@ -45,51 +158,84 @@ export interface Property { defaultValue?: any; } +/** + * @deprecated Use LocalizableStringDto. To be deleted in v4.2. + */ export interface DisplayName { name: string; resource: string; } +/** + * @deprecated Use ExtensionPropertyApiDto. To be deleted in v4.2. + */ export interface Api { onGet: ApiConfig; onCreate: ApiConfig; onUpdate: ApiConfig; } +/** + * @deprecated Use ExtensionPropertyApiCreateDto. To be deleted in v4.2. + */ export interface ApiConfig { isAvailable: boolean; } +/** + * @deprecated Use ExtensionPropertyUiDto. To be deleted in v4.2. + */ export interface Ui { onTable: UiPropConfig; onCreateForm: UiFormConfig; onEditForm: UiFormConfig; } +/** + * @deprecated Use ExtensionPropertyUiTableDto. To be deleted in v4.2. + */ export interface UiPropConfig { isSortable: boolean; isVisible: boolean; } +/** + * @deprecated Use ExtensionPropertyUiFormDto. To be deleted in v4.2. + */ export interface UiFormConfig { isVisible: boolean; } +/** + * @deprecated Use ExtensionPropertyAttributeDto. To be deleted in v4.2. + */ export interface Attribute { typeSimple: string; config: Configuration; } +/** + * @deprecated To be deleted in v4.2. + */ export type Configuration = Record; +/** + * @deprecated Use Record. To be deleted in v4.2. + */ export type Enums = Record; +/** + * @deprecated Use ExtensionEnumDto. To be deleted in v4.2. + */ export interface Enum { fields: EnumMember[]; localizationResource?: string; transformed?: any; } +/** + * @deprecated Use ExtensionEnumFieldDto. To be deleted in v4.2. + */ export interface EnumMember { name: string; value: any; diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/enum.util.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/enum.util.ts index 6e9985d0f6..bb8da3694f 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/enum.util.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/enum.util.ts @@ -5,11 +5,11 @@ import { EXTRA_PROPERTIES_KEY } from '../constants/extra-properties'; import { ObjectExtensions } from '../models/object-extensions'; import { PropCallback } from '../models/props'; -export function createEnum(members: ObjectExtensions.EnumMember[]) { +export function createEnum(members: ObjectExtensions.ExtensionEnumFieldDto[]) { const enumObject: any = {}; members.forEach(({ name, value }) => { - enumObject[(enumObject[name] = value)] = name; + enumObject[(enumObject[name] = value as any)] = name; }); return enumObject; @@ -17,7 +17,7 @@ export function createEnum(members: ObjectExtensions.EnumMember[]) { export function createEnumValueResolver( enumType: string, - lookupEnum: ObjectExtensions.Enum, + lookupEnum: ObjectExtensions.ExtensionEnumDto, propName: string, ): PropCallback> { return data => { @@ -32,7 +32,7 @@ export function createEnumValueResolver( export function createEnumOptions( enumType: string, - lookupEnum: ObjectExtensions.Enum, + lookupEnum: ObjectExtensions.ExtensionEnumDto, ): PropCallback[]>> { return data => { const l10n = data.getInjected(LocalizationService); @@ -55,7 +55,7 @@ function createLocalizationStream(l10n: LocalizationService, mapTarget: any) { function createEnumLocalizer( l10n: LocalizationService, enumType: string, - lookupEnum: ObjectExtensions.Enum, + lookupEnum: ObjectExtensions.ExtensionEnumDto, ): (key: string) => string { const resource = lookupEnum.localizationResource; const shortType = getShortEnumType(enumType); diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/localization.util.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/localization.util.ts index 72fed93dea..8cd4694d3b 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/localization.util.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/localization.util.ts @@ -6,7 +6,10 @@ export function createDisplayNameLocalizationPipeKeyGenerator( ) { const generateLocalizationPipeKey = createLocalizationPipeKeyGenerator(localization); - return (displayName: ObjectExtensions.DisplayName, fallback: ObjectExtensions.DisplayName) => { + return ( + displayName: ObjectExtensions.LocalizableStringDto, + fallback: ObjectExtensions.LocalizableStringDto, + ) => { if (displayName && displayName.name) return generateLocalizationPipeKey( [displayName.resource], diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/state.util.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/state.util.ts index 47f80c3fa1..6c122721e3 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/state.util.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/state.util.ts @@ -13,7 +13,7 @@ import { getValidatorsFromProperty } from './validation.util'; function selectObjectExtensions( configState: ConfigStateService, -): Observable { +): Observable { return configState.getOne$('objectExtensions'); } @@ -25,9 +25,9 @@ function selectLocalization( function selectEnums( configState: ConfigStateService, -): Observable> { +): Observable> { return selectObjectExtensions(configState).pipe( - map((extensions: ObjectExtensions.Item) => + map((extensions: ObjectExtensions.ObjectExtensionsDto) => Object.keys(extensions.enums).reduce((acc, key) => { const { fields, localizationResource } = extensions.enums[key]; acc[key] = { @@ -36,23 +36,24 @@ function selectEnums( transformed: createEnum(fields), }; return acc; - }, {} as ObjectExtensions.Enums), + }, {} as Record), ), ); } export function getObjectExtensionEntitiesFromStore( configState: ConfigStateService, - moduleKey: ModuleKey, + moduleKey: string, ) { return selectObjectExtensions(configState).pipe( map(extensions => { if (!extensions) return null; - return (extensions.modules[moduleKey] || ({} as ObjectExtensions.Module)).entities; + return (extensions.modules[moduleKey] || ({} as ObjectExtensions.ModuleExtensionDto)) + .entities; }), map(entities => (isUndefined(entities) ? {} : entities)), - filter(Boolean), + filter(Boolean), take(1), ); } @@ -68,12 +69,12 @@ export function mapEntitiesToContributors( const generateDisplayName = createDisplayNameLocalizationPipeKeyGenerator(localization); return Object.keys(entities).reduce( - (acc, key: keyof ObjectExtensions.Entities) => { + (acc, key: keyof Entities) => { acc.prop[key] = []; acc.createForm[key] = []; acc.editForm[key] = []; - const entity: ObjectExtensions.Entity = entities[key]; + const entity: ObjectExtensions.EntityExtensionDto = entities[key]; if (!entity) return acc; const properties = entity.properties; @@ -103,10 +104,10 @@ export function mapEntitiesToContributors( function createPropertiesToContributorsMapper( generateDisplayName: DisplayNameGeneratorFn, resource: string, - enums: Record, + enums: Record, ) { return ( - properties: Record, + properties: Record, contributors: ObjectExtensions.PropContributors, key: string, ) => { @@ -169,7 +170,7 @@ function createPropertiesToContributorsMapper( }; } -function getTypeFromProperty(property: ObjectExtensions.Property): ePropType { +function getTypeFromProperty(property: ObjectExtensions.ExtensionPropertyDto): ePropType { return (property.typeSimple.replace(/\?$/, '') as string) as ePropType; } @@ -178,4 +179,4 @@ function isUndefined(obj: any): obj is undefined { } type DisplayNameGeneratorFn = ReturnType; -type ModuleKey = keyof ObjectExtensions.Modules; +type Entities = Record; diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/validation.util.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/validation.util.ts index 26d546fbd3..d87c6c1260 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/validation.util.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/validation.util.ts @@ -2,7 +2,9 @@ import { AbpValidators } from '@abp/ng.core'; import { ValidatorFn } from '@angular/forms'; import { ObjectExtensions } from '../models/object-extensions'; -export function getValidatorsFromProperty(property: ObjectExtensions.Property): ValidatorFn[] { +export function getValidatorsFromProperty( + property: ObjectExtensions.ExtensionPropertyDto, +): ValidatorFn[] { const validators: ValidatorFn[] = []; property.attributes.forEach(attr => { diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/tests/enum.util.spec.ts b/npm/ng-packs/packages/theme-shared/extensions/src/tests/enum.util.spec.ts index 12f520b12a..cb4b6debfb 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/tests/enum.util.spec.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/tests/enum.util.spec.ts @@ -1,6 +1,5 @@ -import { LocalizationService } from '@abp/ng.core'; -import { Store } from '@ngxs/store'; -import { BehaviorSubject, Subject } from 'rxjs'; +import { ConfigStateService, LocalizationService } from '@abp/ng.core'; +import { BehaviorSubject } from 'rxjs'; import { take } from 'rxjs/operators'; import { PropData } from '../lib/models/props'; import { createEnum, createEnumOptions, createEnumValueResolver } from '../lib/utils/enum.util'; @@ -25,6 +24,19 @@ class MockPropData extends PropData { } } +const mockL10n = { + values: { + Default: { + 'Enum:MyEnum.foo': 'Foo', + 'MyEnum.bar': 'Bar', + baz: 'Baz', + }, + }, + defaultResourceName: 'Default', + currentCulture: null, + languages: [], +}; + describe('Enum Utils', () => { describe('#createEnum', () => { const enumFromFields = createEnum(fields); @@ -43,27 +55,6 @@ describe('Enum Utils', () => { }); describe('#createEnumValueResolver', () => { - const service = new LocalizationService( - mockSessionState, - ({ - selectSnapshot: () => ({ - values: { - Default: { - 'Enum:MyEnum.foo': 'Foo', - 'MyEnum.bar': 'Bar', - baz: 'Baz', - }, - }, - defaultResourceName: 'Default', - currentCulture: null, - languages: [], - }), - } as unknown) as Store, - null, - null, - null, - ); - test.each` value | expected ${1} | ${'Foo'} @@ -72,6 +63,7 @@ describe('Enum Utils', () => { `( 'should create a resolver that returns observable $expected when enum value is $value', async ({ value, expected }) => { + const service = createMockLocalizationService(); const valueResolver = createEnumValueResolver( 'MyCompanyName.MyProjectName.MyEnum', { @@ -92,28 +84,8 @@ describe('Enum Utils', () => { }); describe('#createEnumOptions', () => { - const service = new LocalizationService( - mockSessionState, - ({ - selectSnapshot: () => ({ - values: { - Default: { - 'Enum:MyEnum.foo': 'Foo', - 'MyEnum.bar': 'Bar', - baz: 'Baz', - }, - }, - defaultResourceName: 'Default', - currentCulture: null, - languages: [], - }), - } as unknown) as Store, - null, - null, - null, - ); - it('should create a generator that returns observable options from enums', async () => { + const service = createMockLocalizationService(); const options = createEnumOptions('MyCompanyName.MyProjectName.MyEnum', { fields, localizationResource: null, @@ -133,3 +105,10 @@ describe('Enum Utils', () => { }); }); }); + +function createMockLocalizationService() { + const configState = new ConfigStateService(); + configState.setState({ localization: mockL10n } as any); + + return new LocalizationService(mockSessionState, null, null, null, null, configState, null); +} diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/tests/state.util.spec.ts b/npm/ng-packs/packages/theme-shared/extensions/src/tests/state.util.spec.ts index 86060b8597..34acb30667 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/tests/state.util.spec.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/tests/state.util.spec.ts @@ -1,8 +1,4 @@ -import { ApplicationConfiguration, ConfigState } from '@abp/ng.core'; -import { HttpClient } from '@angular/common/http'; -import { SpectatorService } from '@ngneat/spectator'; -import { createServiceFactory } from '@ngneat/spectator/jest'; -import { NgxsModule, Store } from '@ngxs/store'; +import { ConfigStateService } from '@abp/ng.core'; import { of } from 'rxjs'; import { take } from 'rxjs/operators'; import { ePropType } from '../lib/enums/props.enum'; @@ -13,40 +9,30 @@ import { getObjectExtensionEntitiesFromStore, mapEntitiesToContributors, } from '../lib/utils/state.util'; -import { OAuthService } from 'angular-oauth2-oidc'; -describe('State Utils', () => { - let spectator: SpectatorService; - let store: Store; - - const createService = createServiceFactory({ - service: Store, - imports: [NgxsModule.forRoot([ConfigState])], - mocks: [HttpClient, OAuthService], - }); - - beforeEach(() => { - spectator = createService(); - store = spectator.service; - store.reset(createMockState()); - }); +const configState = new ConfigStateService(); +configState.setState(createMockState() as any); +describe('State Utils', () => { describe('#getObjectExtensionEntitiesFromStore', () => { it('should return observable entities of an existing module', async () => { - const entities = await getObjectExtensionEntitiesFromStore(store, 'Identity').toPromise(); + const entities = await getObjectExtensionEntitiesFromStore( + configState, + 'Identity', + ).toPromise(); expect('Role' in entities).toBe(true); }); it('should return observable empty object if module does not exist', async () => { - const entities = await getObjectExtensionEntitiesFromStore(store, 'Saas').toPromise(); + const entities = await getObjectExtensionEntitiesFromStore(configState, 'Saas').toPromise(); expect(entities).toEqual({}); }); it('should not emit when object extensions do not exist', done => { - store.reset({ ConfigState: {} }); + const emptyConfigState = new ConfigStateService(); const emit = jest.fn(); - getObjectExtensionEntitiesFromStore(store, 'Identity').subscribe(emit); + getObjectExtensionEntitiesFromStore(emptyConfigState, 'Identity').subscribe(emit); setTimeout(() => { expect(emit).not.toHaveBeenCalled(); @@ -58,7 +44,7 @@ describe('State Utils', () => { describe('#mapEntitiesToContributors', () => { it('should return contributors from given entities', async () => { const contributors = await of(createMockEntities()) - .pipe(mapEntitiesToContributors(store, 'AbpIdentity'), take(1)) + .pipe(mapEntitiesToContributors(configState, 'AbpIdentity'), take(1)) .toPromise(); const propList = new EntityPropList(); @@ -86,50 +72,48 @@ describe('State Utils', () => { }); }); -function createMockState(): MockState { +function createMockState() { return { - ConfigState: { - objectExtensions: { - modules: { - Identity: { - entities: createMockEntities(), - configuration: null, - }, - }, - enums: { - 'MyCompanyName.MyProjectName.MyEnum': { - fields: [ - { - name: 'MyEnumValue0', - value: 0, - }, - { - name: 'MyEnumValue1', - value: 1, - }, - { - name: 'MyEnumValue2', - value: 2, - }, - ], - localizationResource: null, - }, + objectExtensions: { + modules: { + Identity: { + entities: createMockEntities(), + configuration: null, }, }, - localization: { - values: { - Default: {}, - AbpIdentity: {}, + enums: { + 'MyCompanyName.MyProjectName.MyEnum': { + fields: [ + { + name: 'MyEnumValue0', + value: 0, + }, + { + name: 'MyEnumValue1', + value: 1, + }, + { + name: 'MyEnumValue2', + value: 2, + }, + ], + localizationResource: null, }, - defaultResourceName: 'Default', - currentCulture: null, - languages: [], }, }, + localization: { + values: { + Default: {}, + AbpIdentity: {}, + }, + defaultResourceName: 'Default', + currentCulture: null, + languages: [], + }, }; } -function createMockEntities(): ObjectExtensions.Entities { +function createMockEntities(): Record { return { Role: { properties: { @@ -159,6 +143,7 @@ function createMockEntities(): ObjectExtensions.Entities { onEditForm: { isVisible: true, }, + lookup: {}, }, attributes: [ { @@ -174,6 +159,7 @@ function createMockEntities(): ObjectExtensions.Entities { }, ], configuration: {}, + defaultValue: null, }, IsHero: { type: 'System.Boolean', @@ -201,9 +187,11 @@ function createMockEntities(): ObjectExtensions.Entities { onEditForm: { isVisible: true, }, + lookup: {}, }, attributes: [], configuration: {}, + defaultValue: null, }, AsOf: { type: 'System.Date', @@ -234,9 +222,11 @@ function createMockEntities(): ObjectExtensions.Entities { onEditForm: { isVisible: false, }, + lookup: {}, }, attributes: [], configuration: {}, + defaultValue: null, }, MyEnum: { type: 'MyCompanyName.MyProjectName.MyEnum', @@ -264,6 +254,7 @@ function createMockEntities(): ObjectExtensions.Entities { onEditForm: { isVisible: false, }, + lookup: {}, }, attributes: [ { @@ -294,10 +285,3 @@ function createMockEntities(): ObjectExtensions.Entities { ClaimType: null, }; } - -interface MockState { - ConfigState: { - localization: ApplicationConfiguration.Localization; - objectExtensions: ObjectExtensions.Item; - }; -} diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/tests/validation.util.spec.ts b/npm/ng-packs/packages/theme-shared/extensions/src/tests/validation.util.spec.ts index b26bd9e1b0..abf4a6c486 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/tests/validation.util.spec.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/tests/validation.util.spec.ts @@ -12,7 +12,7 @@ describe('Validation Utils', () => { config: {}, }, ], - } as ObjectExtensions.Property; + } as ObjectExtensions.ExtensionPropertyDto; expect(getValidatorsFromProperty(property)[0]).toBe(Validators.email); });