Browse Source

fix: update extension types and tests

pull/6350/head
Arman Ozak 6 years ago
parent
commit
aa48838cf3
  1. 146
      npm/ng-packs/packages/theme-shared/extensions/src/lib/models/internal/object-extensions.ts
  2. 10
      npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/enum.util.ts
  3. 5
      npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/localization.util.ts
  4. 27
      npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/state.util.ts
  5. 4
      npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/validation.util.ts
  6. 69
      npm/ng-packs/packages/theme-shared/extensions/src/tests/enum.util.spec.ts
  7. 120
      npm/ng-packs/packages/theme-shared/extensions/src/tests/state.util.spec.ts
  8. 2
      npm/ng-packs/packages/theme-shared/extensions/src/tests/validation.util.spec.ts

146
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<string, ExtensionPropertyDto>;
configuration: Record<string, object>;
}
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<string, any>; // UI, was Record<string, object>
}
export interface ExtensionPropertyDto {
type?: string;
typeSimple?: ePropType; // UI, was string
displayName: LocalizableStringDto;
api: ExtensionPropertyApiDto;
ui: ExtensionPropertyUiDto;
attributes: ExtensionPropertyAttributeDto[];
configuration: Record<string, any>; // UI, was Record<string, object>;
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<string, EntityExtensionDto>;
configuration: Record<string, object>;
}
export interface ObjectExtensionsDto {
modules: Record<string, ModuleExtensionDto>;
enums: Record<string, ExtensionEnumDto>;
}
export interface PropContributors<T = any> {
prop: PropContributorCallbacks<EntityPropList<T>>;
createForm: PropContributorCallbacks<FormPropList<T>>;
editForm: PropContributorCallbacks<FormPropList<T>>;
}
/**
* @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<string, ModuleExtensionDto>. To be deleted in v4.2.
*/
export type Modules = Configuration<Module>;
/**
* @deprecated Use ModuleExtensionDto. To be deleted in v4.2.
*/
export interface Module {
configuration: Configuration;
entities: Entities;
}
/**
* @deprecated Use Record<string, EntityExtensionDto>. To be deleted in v4.2.
*/
export type Entities = Configuration<Entity>;
/**
* @deprecated Use EntityExtensionDto. To be deleted in v4.2.
*/
export interface Entity {
configuration: Configuration;
properties: Properties;
}
/**
* @deprecated Use Record<string, ExtensionPropertyDto>. To be deleted in v4.2.
*/
export type Properties = Configuration<Property>;
/**
* @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<T = any> = Record<string, T>;
/**
* @deprecated Use Record<string, ExtensionEnumDto>. To be deleted in v4.2.
*/
export type Enums = Record<string, Enum>;
/**
* @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;

10
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<T = any>(
enumType: string,
lookupEnum: ObjectExtensions.Enum,
lookupEnum: ObjectExtensions.ExtensionEnumDto,
propName: string,
): PropCallback<T, Observable<string>> {
return data => {
@ -32,7 +32,7 @@ export function createEnumValueResolver<T = any>(
export function createEnumOptions<T = any>(
enumType: string,
lookupEnum: ObjectExtensions.Enum,
lookupEnum: ObjectExtensions.ExtensionEnumDto,
): PropCallback<T, Observable<ABP.Option<any>[]>> {
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);

5
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],

27
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<ObjectExtensions.Item> {
): Observable<ObjectExtensions.ObjectExtensionsDto> {
return configState.getOne$('objectExtensions');
}
@ -25,9 +25,9 @@ function selectLocalization(
function selectEnums(
configState: ConfigStateService,
): Observable<Record<string, ObjectExtensions.Enum>> {
): Observable<Record<string, ObjectExtensions.ExtensionEnumDto>> {
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<string, ObjectExtensions.ExtensionEnumDto>),
),
);
}
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<ObjectExtensions.Entities>(Boolean),
filter<Entities>(Boolean),
take(1),
);
}
@ -68,12 +69,12 @@ export function mapEntitiesToContributors<T = any>(
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<T = any>(
function createPropertiesToContributorsMapper<T = any>(
generateDisplayName: DisplayNameGeneratorFn,
resource: string,
enums: Record<string, ObjectExtensions.Enum>,
enums: Record<string, ObjectExtensions.ExtensionEnumDto>,
) {
return (
properties: Record<string, ObjectExtensions.Property>,
properties: Record<string, ObjectExtensions.ExtensionPropertyDto>,
contributors: ObjectExtensions.PropContributors<T>,
key: string,
) => {
@ -169,7 +170,7 @@ function createPropertiesToContributorsMapper<T = any>(
};
}
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<typeof createDisplayNameLocalizationPipeKeyGenerator>;
type ModuleKey = keyof ObjectExtensions.Modules;
type Entities = Record<string, ObjectExtensions.EntityExtensionDto>;

4
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 => {

69
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<R = any> extends PropData<R> {
}
}
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);
}

120
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<Store>;
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<string, ObjectExtensions.EntityExtensionDto> {
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;
};
}

2
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);
});

Loading…
Cancel
Save