mirror of https://github.com/abpframework/abp.git
90 changed files with 6309 additions and 917 deletions
|
After Width: | Height: | Size: 311 KiB |
|
After Width: | Height: | Size: 256 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 294 KiB |
@ -0,0 +1,19 @@ |
|||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Options; |
|||
|
|||
namespace Microsoft.Extensions.DependencyInjection |
|||
{ |
|||
public static class ServiceCollectionDynamicOptionsManagerExtensions |
|||
{ |
|||
public static IServiceCollection AddAbpDynamicOptions<TOptions, TManager>(this IServiceCollection services) |
|||
where TOptions : class |
|||
where TManager : AbpDynamicOptionsManager<TOptions> |
|||
{ |
|||
services.Replace(ServiceDescriptor.Scoped(typeof(IOptions<TOptions>), typeof(TManager))); |
|||
services.Replace(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<TOptions>), typeof(TManager))); |
|||
|
|||
return services; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Options; |
|||
|
|||
namespace Microsoft.Extensions.Options |
|||
{ |
|||
public static class OptionsAbpDynamicOptionsManagerExtensions |
|||
{ |
|||
public static Task SetAsync<T>(this IOptions<T> options) |
|||
where T : class |
|||
{ |
|||
return options.ToDynamicOptions().SetAsync(); |
|||
} |
|||
|
|||
public static Task SetAsync<T>(this IOptions<T> options, string name) |
|||
where T : class |
|||
{ |
|||
return options.ToDynamicOptions().SetAsync(name); |
|||
} |
|||
|
|||
private static AbpDynamicOptionsManager<T> ToDynamicOptions<T>(this IOptions<T> options) |
|||
where T : class |
|||
{ |
|||
if (options is AbpDynamicOptionsManager<T> dynamicOptionsManager) |
|||
{ |
|||
return dynamicOptionsManager; |
|||
} |
|||
|
|||
throw new AbpException($"Options must be derived from the {typeof(AbpDynamicOptionsManager<>).FullName}!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Options; |
|||
|
|||
namespace Volo.Abp.Options |
|||
{ |
|||
public abstract class AbpDynamicOptionsManager<T> : OptionsManager<T> |
|||
where T : class |
|||
{ |
|||
protected AbpDynamicOptionsManager(IOptionsFactory<T> factory) |
|||
: base(factory) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public Task SetAsync() => SetAsync(Microsoft.Extensions.Options.Options.DefaultName); |
|||
|
|||
public virtual Task SetAsync(string name) |
|||
{ |
|||
return OverrideOptionsAsync(base.Get(name)); |
|||
} |
|||
|
|||
protected abstract Task OverrideOptionsAsync(T options); |
|||
} |
|||
} |
|||
@ -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,3 @@ |
|||
import * as Pages from './pages'; |
|||
import * as Volo from './volo'; |
|||
export { Pages, Volo }; |
|||
@ -0,0 +1,2 @@ |
|||
import * as MultiTenancy from './multi-tenancy'; |
|||
export { MultiTenancy }; |
|||
@ -0,0 +1,26 @@ |
|||
import { RestService } from '@abp/ng.core'; |
|||
import { Injectable } from '@angular/core'; |
|||
import type { FindTenantResultDto } from '../../../volo/abp/asp-net-core/mvc/multi-tenancy/models'; |
|||
|
|||
@Injectable({ |
|||
providedIn: 'root', |
|||
}) |
|||
export class AbpTenantService { |
|||
apiName = 'abp'; |
|||
|
|||
findTenantById = (id: string) => |
|||
this.restService.request<any, FindTenantResultDto>({ |
|||
method: 'GET', |
|||
url: `/api/abp/multi-tenancy/tenants/by-id/${id}`, |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
findTenantByName = (name: string) => |
|||
this.restService.request<any, FindTenantResultDto>({ |
|||
method: 'GET', |
|||
url: `/api/abp/multi-tenancy/tenants/by-name/${name}`, |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
constructor(private restService: RestService) {} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './abp-tenant.service'; |
|||
@ -0,0 +1,2 @@ |
|||
import * as Abp from './abp'; |
|||
export { Abp }; |
|||
@ -0,0 +1,2 @@ |
|||
import * as Mvc from './mvc'; |
|||
export { Mvc }; |
|||
@ -0,0 +1,20 @@ |
|||
import { RestService } from '@abp/ng.core'; |
|||
import { Injectable } from '@angular/core'; |
|||
import type { ApplicationApiDescriptionModel, ApplicationApiDescriptionModelRequestDto } from '../../../http/modeling/models'; |
|||
|
|||
@Injectable({ |
|||
providedIn: 'root', |
|||
}) |
|||
export class AbpApiDefinitionService { |
|||
apiName = 'abp'; |
|||
|
|||
getByModel = (model: ApplicationApiDescriptionModelRequestDto) => |
|||
this.restService.request<any, ApplicationApiDescriptionModel>({ |
|||
method: 'GET', |
|||
url: '/api/abp/api-definition', |
|||
params: { includeTypes: model.includeTypes }, |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
constructor(private restService: RestService) {} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './abp-api-definition.service'; |
|||
@ -0,0 +1,19 @@ |
|||
import type { ApplicationConfigurationDto } from './models'; |
|||
import { RestService } from '@abp/ng.core'; |
|||
import { Injectable } from '@angular/core'; |
|||
|
|||
@Injectable({ |
|||
providedIn: 'root', |
|||
}) |
|||
export class AbpApplicationConfigurationService { |
|||
apiName = 'abp'; |
|||
|
|||
get = () => |
|||
this.restService.request<any, ApplicationConfigurationDto>({ |
|||
method: 'GET', |
|||
url: '/api/abp/application-configuration', |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
constructor(private restService: RestService) {} |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
import * as ObjectExtending from './object-extending'; |
|||
export * from './abp-application-configuration.service'; |
|||
export * from './models'; |
|||
export { ObjectExtending }; |
|||
@ -0,0 +1,96 @@ |
|||
import type { CurrentTenantDto, MultiTenancyInfoDto } from '../multi-tenancy/models'; |
|||
import type { ObjectExtensionsDto } from './object-extending/models'; |
|||
import type { LanguageInfo } from '../../../localization/models'; |
|||
import type { NameValue } from '../../../models'; |
|||
|
|||
export interface ApplicationAuthConfigurationDto { |
|||
policies: Record<string, boolean>; |
|||
grantedPolicies: Record<string, boolean>; |
|||
} |
|||
|
|||
export interface ApplicationConfigurationDto { |
|||
localization: ApplicationLocalizationConfigurationDto; |
|||
auth: ApplicationAuthConfigurationDto; |
|||
setting: ApplicationSettingConfigurationDto; |
|||
currentUser: CurrentUserDto; |
|||
features: ApplicationFeatureConfigurationDto; |
|||
multiTenancy: MultiTenancyInfoDto; |
|||
currentTenant: CurrentTenantDto; |
|||
timing: TimingDto; |
|||
clock: ClockDto; |
|||
objectExtensions: ObjectExtensionsDto; |
|||
} |
|||
|
|||
export interface ApplicationFeatureConfigurationDto { |
|||
values: Record<string, string>; |
|||
} |
|||
|
|||
export interface ApplicationLocalizationConfigurationDto { |
|||
values: Record<string, Dictionary<string, string>>; |
|||
languages: LanguageInfo[]; |
|||
currentCulture: CurrentCultureDto; |
|||
defaultResourceName?: string; |
|||
languagesMap: Record<string, NameValue[]>; |
|||
languageFilesMap: Record<string, NameValue[]>; |
|||
} |
|||
|
|||
export interface ApplicationSettingConfigurationDto { |
|||
values: Record<string, string>; |
|||
} |
|||
|
|||
export interface ClockDto { |
|||
kind?: string; |
|||
} |
|||
|
|||
export interface CurrentCultureDto { |
|||
displayName?: string; |
|||
englishName?: string; |
|||
threeLetterIsoLanguageName?: string; |
|||
twoLetterIsoLanguageName?: string; |
|||
isRightToLeft: boolean; |
|||
cultureName?: string; |
|||
name?: string; |
|||
nativeName?: string; |
|||
dateTimeFormat: DateTimeFormatDto; |
|||
} |
|||
|
|||
export interface CurrentUserDto { |
|||
isAuthenticated: boolean; |
|||
id?: string; |
|||
tenantId?: string; |
|||
userName?: string; |
|||
name?: string; |
|||
surName?: string; |
|||
email?: string; |
|||
emailVerified: boolean; |
|||
phoneNumber?: string; |
|||
phoneNumberVerified: boolean; |
|||
roles: string[]; |
|||
} |
|||
|
|||
export interface DateTimeFormatDto { |
|||
calendarAlgorithmType?: string; |
|||
dateTimeFormatLong?: string; |
|||
shortDatePattern?: string; |
|||
fullDateTimePattern?: string; |
|||
dateSeparator?: string; |
|||
shortTimePattern?: string; |
|||
longTimePattern?: string; |
|||
} |
|||
|
|||
export interface IanaTimeZone { |
|||
timeZoneName?: string; |
|||
} |
|||
|
|||
export interface TimeZone { |
|||
iana: IanaTimeZone; |
|||
windows: WindowsTimeZone; |
|||
} |
|||
|
|||
export interface TimingDto { |
|||
timeZone: TimeZone; |
|||
} |
|||
|
|||
export interface WindowsTimeZone { |
|||
timeZoneId?: string; |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './models'; |
|||
@ -0,0 +1,87 @@ |
|||
|
|||
export interface EntityExtensionDto { |
|||
properties: Record<string, ExtensionPropertyDto>; |
|||
configuration: Record<string, object>; |
|||
} |
|||
|
|||
export interface ExtensionEnumDto { |
|||
fields: ExtensionEnumFieldDto[]; |
|||
localizationResource?: string; |
|||
} |
|||
|
|||
export interface ExtensionEnumFieldDto { |
|||
name?: string; |
|||
value: 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, object>; |
|||
} |
|||
|
|||
export interface ExtensionPropertyDto { |
|||
type?: string; |
|||
typeSimple?: string; |
|||
displayName: LocalizableStringDto; |
|||
api: ExtensionPropertyApiDto; |
|||
ui: ExtensionPropertyUiDto; |
|||
attributes: ExtensionPropertyAttributeDto[]; |
|||
configuration: Record<string, object>; |
|||
defaultValue: 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 { |
|||
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>; |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
import * as ApiExploring from './api-exploring'; |
|||
import * as ApplicationConfigurations from './application-configurations'; |
|||
import * as MultiTenancy from './multi-tenancy'; |
|||
export { ApiExploring, ApplicationConfigurations, MultiTenancy }; |
|||
@ -0,0 +1 @@ |
|||
export * from './models'; |
|||
@ -0,0 +1,16 @@ |
|||
|
|||
export interface FindTenantResultDto { |
|||
success: boolean; |
|||
tenantId?: string; |
|||
name?: string; |
|||
} |
|||
|
|||
export interface CurrentTenantDto { |
|||
id?: string; |
|||
name?: string; |
|||
isAvailable: boolean; |
|||
} |
|||
|
|||
export interface MultiTenancyInfoDto { |
|||
isEnabled: boolean; |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
import * as Modeling from './modeling'; |
|||
export { Modeling }; |
|||
@ -0,0 +1 @@ |
|||
export * from './models'; |
|||
@ -0,0 +1,79 @@ |
|||
|
|||
export interface ActionApiDescriptionModel { |
|||
uniqueName?: string; |
|||
name?: string; |
|||
httpMethod?: string; |
|||
url?: string; |
|||
supportedVersions: string[]; |
|||
parametersOnMethod: MethodParameterApiDescriptionModel[]; |
|||
parameters: ParameterApiDescriptionModel[]; |
|||
returnValue: ReturnValueApiDescriptionModel; |
|||
} |
|||
|
|||
export interface ApplicationApiDescriptionModel { |
|||
modules: Record<string, ModuleApiDescriptionModel>; |
|||
types: Record<string, TypeApiDescriptionModel>; |
|||
} |
|||
|
|||
export interface ApplicationApiDescriptionModelRequestDto { |
|||
includeTypes: boolean; |
|||
} |
|||
|
|||
export interface ControllerApiDescriptionModel { |
|||
controllerName?: string; |
|||
type?: string; |
|||
interfaces: ControllerInterfaceApiDescriptionModel[]; |
|||
actions: Record<string, ActionApiDescriptionModel>; |
|||
} |
|||
|
|||
export interface ControllerInterfaceApiDescriptionModel { |
|||
type?: string; |
|||
} |
|||
|
|||
export interface MethodParameterApiDescriptionModel { |
|||
name?: string; |
|||
typeAsString?: string; |
|||
type?: string; |
|||
typeSimple?: string; |
|||
isOptional: boolean; |
|||
defaultValue: object; |
|||
} |
|||
|
|||
export interface ModuleApiDescriptionModel { |
|||
rootPath?: string; |
|||
remoteServiceName?: string; |
|||
controllers: Record<string, ControllerApiDescriptionModel>; |
|||
} |
|||
|
|||
export interface ParameterApiDescriptionModel { |
|||
nameOnMethod?: string; |
|||
name?: string; |
|||
type?: string; |
|||
typeSimple?: string; |
|||
isOptional: boolean; |
|||
defaultValue: object; |
|||
constraintTypes: string[]; |
|||
bindingSourceId?: string; |
|||
descriptorName?: string; |
|||
} |
|||
|
|||
export interface PropertyApiDescriptionModel { |
|||
name?: string; |
|||
type?: string; |
|||
typeSimple?: string; |
|||
isRequired: boolean; |
|||
} |
|||
|
|||
export interface ReturnValueApiDescriptionModel { |
|||
type?: string; |
|||
typeSimple?: string; |
|||
} |
|||
|
|||
export interface TypeApiDescriptionModel { |
|||
baseType?: string; |
|||
isEnum: boolean; |
|||
enumNames: string[]; |
|||
enumValues: object[]; |
|||
genericArguments: string[]; |
|||
properties: PropertyApiDescriptionModel[]; |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
import * as AspNetCore from './asp-net-core'; |
|||
import * as Http from './http'; |
|||
import * as Localization from './localization'; |
|||
export * from './models'; |
|||
export { AspNetCore, Http, Localization }; |
|||
@ -0,0 +1 @@ |
|||
export * from './models'; |
|||
@ -0,0 +1,7 @@ |
|||
|
|||
export interface LanguageInfo { |
|||
cultureName?: string; |
|||
uiCultureName?: string; |
|||
displayName?: string; |
|||
flagIcon?: string; |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
|
|||
export interface NameValue<T = string> { |
|||
name: string; |
|||
value: T; |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
import * as Abp from './abp'; |
|||
export { Abp }; |
|||
@ -1,21 +1,27 @@ |
|||
import { createHttpFactory, HttpMethod, SpectatorHttp } from '@ngneat/spectator/jest'; |
|||
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; |
|||
import { of } from 'rxjs'; |
|||
import { ApplicationConfigurationService, RestService } from '../services'; |
|||
import { Store } from '@ngxs/store'; |
|||
import { CORE_OPTIONS } from '../tokens'; |
|||
|
|||
describe('ApplicationConfigurationService', () => { |
|||
let spectator: SpectatorHttp<ApplicationConfigurationService>; |
|||
const createHttp = createHttpFactory({ |
|||
dataService: ApplicationConfigurationService, |
|||
providers: [RestService, { provide: CORE_OPTIONS, useValue: { environment: {} } }], |
|||
mocks: [Store], |
|||
let spectator: SpectatorService<ApplicationConfigurationService>; |
|||
const createService = createServiceFactory({ |
|||
service: ApplicationConfigurationService, |
|||
mocks: [RestService], |
|||
}); |
|||
|
|||
beforeEach(() => (spectator = createHttp())); |
|||
beforeEach(() => (spectator = createService())); |
|||
|
|||
it('should send a GET to application-configuration API', () => { |
|||
spectator.inject(Store).selectSnapshot.andReturn('https://abp.io'); |
|||
const rest = spectator.inject(RestService); |
|||
|
|||
const requestSpy = jest.spyOn(rest, 'request'); |
|||
requestSpy.mockReturnValue(of(null)); |
|||
|
|||
spectator.service.getConfiguration().subscribe(); |
|||
spectator.expectOne('https://abp.io/api/abp/application-configuration', HttpMethod.GET); |
|||
|
|||
expect(requestSpy).toHaveBeenCalledWith( |
|||
{ method: 'GET', url: '/api/abp/application-configuration' }, |
|||
{}, |
|||
); |
|||
}); |
|||
}); |
|||
|
|||
@ -1,59 +1,179 @@ |
|||
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest'; |
|||
import { Store } from '@ngxs/store'; |
|||
import * as ConfigActions from '../actions'; |
|||
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; |
|||
import { ApplicationConfiguration } from '../models/application-configuration'; |
|||
import { Config } from '../models/config'; |
|||
import { ConfigStateService } from '../services/config-state.service'; |
|||
import { ConfigState } from '../states'; |
|||
import { ConfigStateService } from '../services'; |
|||
|
|||
describe('ConfigStateService', () => { |
|||
let service: ConfigStateService; |
|||
export const CONFIG_STATE_DATA = ({ |
|||
environment: { |
|||
production: false, |
|||
application: { |
|||
name: 'MyProjectName', |
|||
}, |
|||
oAuthConfig: { |
|||
issuer: 'https://localhost:44305', |
|||
}, |
|||
apis: { |
|||
default: { |
|||
url: 'https://localhost:44305', |
|||
}, |
|||
other: { |
|||
url: 'https://localhost:44306', |
|||
}, |
|||
}, |
|||
localization: { |
|||
defaultResourceName: 'MyProjectName', |
|||
}, |
|||
}, |
|||
requirements: { |
|||
layouts: [null, null, null], |
|||
}, |
|||
localization: { |
|||
values: { |
|||
MyProjectName: { |
|||
"'{0}' and '{1}' do not match.": "'{0}' and '{1}' do not match.", |
|||
}, |
|||
AbpIdentity: { |
|||
Identity: 'identity', |
|||
}, |
|||
}, |
|||
languages: [ |
|||
{ |
|||
cultureName: 'cs', |
|||
uiCultureName: 'cs', |
|||
displayName: 'Čeština', |
|||
flagIcon: null, |
|||
}, |
|||
], |
|||
currentCulture: { |
|||
displayName: 'English', |
|||
englishName: 'English', |
|||
threeLetterIsoLanguageName: 'eng', |
|||
twoLetterIsoLanguageName: 'en', |
|||
isRightToLeft: false, |
|||
cultureName: 'en', |
|||
name: 'en', |
|||
nativeName: 'English', |
|||
dateTimeFormat: { |
|||
calendarAlgorithmType: 'SolarCalendar', |
|||
dateTimeFormatLong: 'dddd, MMMM d, yyyy', |
|||
shortDatePattern: 'M/d/yyyy', |
|||
fullDateTimePattern: 'dddd, MMMM d, yyyy h:mm:ss tt', |
|||
dateSeparator: '/', |
|||
shortTimePattern: 'h:mm tt', |
|||
longTimePattern: 'h:mm:ss tt', |
|||
}, |
|||
}, |
|||
defaultResourceName: null, |
|||
}, |
|||
auth: { |
|||
policies: { |
|||
'AbpIdentity.Roles': true, |
|||
}, |
|||
grantedPolicies: { |
|||
'Abp.Identity': false, |
|||
'Abp.Account': true, |
|||
}, |
|||
}, |
|||
setting: { |
|||
values: { |
|||
'Abp.Custom.SomeSetting': 'X', |
|||
'Abp.Localization.DefaultLanguage': 'en', |
|||
}, |
|||
}, |
|||
currentUser: { |
|||
isAuthenticated: false, |
|||
id: null, |
|||
tenantId: null, |
|||
userName: null, |
|||
email: null, |
|||
roles: [], |
|||
} as ApplicationConfiguration.CurrentUser, |
|||
features: { |
|||
values: { |
|||
'Chat.Enable': 'True', |
|||
}, |
|||
}, |
|||
registerLocaleFn: () => Promise.resolve(), |
|||
} as any) as ApplicationConfiguration.Response; |
|||
|
|||
describe('ConfigState', () => { |
|||
let spectator: SpectatorService<ConfigStateService>; |
|||
let store: SpyObject<Store>; |
|||
let configState: ConfigStateService; |
|||
|
|||
const createService = createServiceFactory({ |
|||
service: ConfigStateService, |
|||
}); |
|||
|
|||
const createService = createServiceFactory({ service: ConfigStateService, mocks: [Store] }); |
|||
beforeEach(() => { |
|||
spectator = createService(); |
|||
service = spectator.service; |
|||
store = spectator.inject(Store); |
|||
}); |
|||
test('should have the all ConfigState static methods', () => { |
|||
const reg = /(?<=static )(.*)(?=\()/gm; |
|||
ConfigState.toString() |
|||
.match(reg) |
|||
.forEach(fnName => { |
|||
expect(service[fnName]).toBeTruthy(); |
|||
|
|||
const spy = jest.spyOn(store, 'selectSnapshot'); |
|||
spy.mockClear(); |
|||
|
|||
const isDynamicSelector = ConfigState[fnName].name !== 'memoized'; |
|||
|
|||
if (isDynamicSelector) { |
|||
ConfigState[fnName] = jest.fn((...args) => args); |
|||
service[fnName]('test', 0, {}); |
|||
expect(ConfigState[fnName]).toHaveBeenCalledWith('test', 0, {}); |
|||
} else { |
|||
service[fnName](); |
|||
expect(spy).toHaveBeenCalledWith(ConfigState[fnName]); |
|||
} |
|||
}); |
|||
configState = spectator.service; |
|||
|
|||
configState.setState(CONFIG_STATE_DATA); |
|||
}); |
|||
|
|||
describe('#getAll', () => { |
|||
it('should return CONFIG_STATE_DATA', () => { |
|||
expect(configState.getAll()).toEqual(CONFIG_STATE_DATA); |
|||
configState.getAll$().subscribe(data => expect(data).toEqual(CONFIG_STATE_DATA)); |
|||
}); |
|||
}); |
|||
|
|||
test('should have a dispatch method for every ConfigState action', () => { |
|||
const reg = /(?<=dispatch)(\w+)(?=\()/gm; |
|||
ConfigStateService.toString() |
|||
.match(reg) |
|||
.forEach(fnName => { |
|||
expect(ConfigActions[fnName]).toBeTruthy(); |
|||
describe('#getOne', () => { |
|||
it('should return one property', () => { |
|||
expect(configState.getOne('localization')).toEqual(CONFIG_STATE_DATA.localization); |
|||
configState |
|||
.getOne$('localization') |
|||
.subscribe(localization => expect(localization).toEqual(CONFIG_STATE_DATA.localization)); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getDeep', () => { |
|||
it('should return deeper', () => { |
|||
expect(configState.getDeep('localization.languages')).toEqual( |
|||
CONFIG_STATE_DATA.localization.languages, |
|||
); |
|||
|
|||
const spy = jest.spyOn(store, 'dispatch'); |
|||
spy.mockClear(); |
|||
configState |
|||
.getDeep$('localization.languages') |
|||
.subscribe(languages => |
|||
expect(languages).toEqual(CONFIG_STATE_DATA.localization.languages), |
|||
); |
|||
|
|||
const params = Array.from(new Array(ConfigActions[fnName].length)); |
|||
expect(configState.getDeep('test')).toBeFalsy(); |
|||
}); |
|||
}); |
|||
|
|||
service[`dispatch${fnName}`](...params); |
|||
expect(spy).toHaveBeenCalledWith(new ConfigActions[fnName](...params)); |
|||
describe('#getFeature', () => { |
|||
it('should return a setting', () => { |
|||
expect(configState.getFeature('Chat.Enable')).toEqual( |
|||
CONFIG_STATE_DATA.features.values['Chat.Enable'], |
|||
); |
|||
configState |
|||
.getFeature$('Chat.Enable') |
|||
.subscribe(data => expect(data).toEqual(CONFIG_STATE_DATA.features.values['Chat.Enable'])); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getSetting', () => { |
|||
it('should return a setting', () => { |
|||
expect(configState.getSetting('Abp.Localization.DefaultLanguage')).toEqual( |
|||
CONFIG_STATE_DATA.setting.values['Abp.Localization.DefaultLanguage'], |
|||
); |
|||
configState.getSetting$('Abp.Localization.DefaultLanguage').subscribe(data => { |
|||
expect(data).toEqual(CONFIG_STATE_DATA.setting.values['Abp.Localization.DefaultLanguage']); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getSettings', () => { |
|||
test.each` |
|||
keyword | expected |
|||
${undefined} | ${CONFIG_STATE_DATA.setting.values} |
|||
${'Localization'} | ${{ 'Abp.Localization.DefaultLanguage': 'en' }} |
|||
${'X'} | ${{}} |
|||
${'localization'} | ${{}} |
|||
`('should return $expected when keyword is given as $keyword', ({ keyword, expected }) => {
|
|||
expect(configState.getSettings(keyword)).toEqual(expected); |
|||
configState.getSettings$(keyword).subscribe(data => expect(data).toEqual(expected)); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
@ -1,35 +0,0 @@ |
|||
import { InitState } from '@ngxs/store'; |
|||
import { ABP } from '../models'; |
|||
import { ConfigPlugin } from '../plugins'; |
|||
|
|||
const options: ABP.Root = { |
|||
environment: { |
|||
production: false, |
|||
}, |
|||
registerLocaleFn: () => Promise.resolve(), |
|||
}; |
|||
|
|||
const event = new InitState(); |
|||
|
|||
const state = { |
|||
ConfigState: { |
|||
foo: 'bar', |
|||
...options, |
|||
}, |
|||
}; |
|||
|
|||
describe('ConfigPlugin', () => { |
|||
it('should ConfigState must be create with correct datas', () => { |
|||
const next = jest.fn(); |
|||
const plugin = new ConfigPlugin(options); |
|||
plugin.handle({ ConfigState: { foo: 'bar' } }, event, next); |
|||
expect(next).toHaveBeenCalledWith(state, event); |
|||
expect(next).toHaveBeenCalledTimes(1); |
|||
next.mockClear(); |
|||
|
|||
delete state.ConfigState.environment; |
|||
plugin.handle(state, event, next); |
|||
expect(next).toHaveBeenCalledWith(state, event); |
|||
expect(next).toHaveBeenCalledTimes(1); |
|||
}); |
|||
}); |
|||
@ -1,282 +0,0 @@ |
|||
import { HttpClient } from '@angular/common/http'; |
|||
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest'; |
|||
import { Store } from '@ngxs/store'; |
|||
import { of, ReplaySubject, timer } from 'rxjs'; |
|||
import { ApplicationConfiguration } from '../models/application-configuration'; |
|||
import { Config } from '../models/config'; |
|||
import { |
|||
ApplicationConfigurationService, |
|||
ConfigStateService, |
|||
SessionStateService, |
|||
} from '../services'; |
|||
import { ConfigState } from '../states'; |
|||
|
|||
export const CONFIG_STATE_DATA = ({ |
|||
environment: { |
|||
production: false, |
|||
application: { |
|||
name: 'MyProjectName', |
|||
}, |
|||
oAuthConfig: { |
|||
issuer: 'https://localhost:44305', |
|||
}, |
|||
apis: { |
|||
default: { |
|||
url: 'https://localhost:44305', |
|||
}, |
|||
other: { |
|||
url: 'https://localhost:44306', |
|||
}, |
|||
}, |
|||
localization: { |
|||
defaultResourceName: 'MyProjectName', |
|||
}, |
|||
}, |
|||
requirements: { |
|||
layouts: [null, null, null], |
|||
}, |
|||
localization: { |
|||
values: { |
|||
MyProjectName: { |
|||
"'{0}' and '{1}' do not match.": "'{0}' and '{1}' do not match.", |
|||
}, |
|||
AbpIdentity: { |
|||
Identity: 'identity', |
|||
}, |
|||
}, |
|||
languages: [ |
|||
{ |
|||
cultureName: 'cs', |
|||
uiCultureName: 'cs', |
|||
displayName: 'Čeština', |
|||
flagIcon: null, |
|||
}, |
|||
], |
|||
currentCulture: { |
|||
displayName: 'English', |
|||
englishName: 'English', |
|||
threeLetterIsoLanguageName: 'eng', |
|||
twoLetterIsoLanguageName: 'en', |
|||
isRightToLeft: false, |
|||
cultureName: 'en', |
|||
name: 'en', |
|||
nativeName: 'English', |
|||
dateTimeFormat: { |
|||
calendarAlgorithmType: 'SolarCalendar', |
|||
dateTimeFormatLong: 'dddd, MMMM d, yyyy', |
|||
shortDatePattern: 'M/d/yyyy', |
|||
fullDateTimePattern: 'dddd, MMMM d, yyyy h:mm:ss tt', |
|||
dateSeparator: '/', |
|||
shortTimePattern: 'h:mm tt', |
|||
longTimePattern: 'h:mm:ss tt', |
|||
}, |
|||
}, |
|||
defaultResourceName: null, |
|||
}, |
|||
auth: { |
|||
policies: { |
|||
'AbpIdentity.Roles': true, |
|||
}, |
|||
grantedPolicies: { |
|||
'Abp.Identity': false, |
|||
'Abp.Account': true, |
|||
}, |
|||
}, |
|||
setting: { |
|||
values: { |
|||
'Abp.Custom.SomeSetting': 'X', |
|||
'Abp.Localization.DefaultLanguage': 'en', |
|||
}, |
|||
}, |
|||
currentUser: { |
|||
isAuthenticated: false, |
|||
id: null, |
|||
tenantId: null, |
|||
userName: null, |
|||
email: null, |
|||
roles: [], |
|||
} as ApplicationConfiguration.CurrentUser, |
|||
features: { |
|||
values: { |
|||
'Chat.Enable': 'True', |
|||
}, |
|||
}, |
|||
registerLocaleFn: () => Promise.resolve(), |
|||
} as any) as Config.State; |
|||
|
|||
describe('ConfigState', () => { |
|||
let spectator: SpectatorService<ConfigStateService>; |
|||
let store: SpyObject<Store>; |
|||
let service: ConfigStateService; |
|||
let state: ConfigState; |
|||
|
|||
const createService = createServiceFactory({ |
|||
service: ConfigStateService, |
|||
mocks: [ApplicationConfigurationService, Store, HttpClient], |
|||
}); |
|||
|
|||
beforeEach(() => { |
|||
spectator = createService(); |
|||
store = spectator.inject(Store); |
|||
service = spectator.service; |
|||
state = new ConfigState( |
|||
spectator.inject(HttpClient), |
|||
store, |
|||
spectator.inject(SessionStateService), |
|||
); |
|||
}); |
|||
|
|||
describe('#getAll', () => { |
|||
it('should return CONFIG_STATE_DATA', () => { |
|||
expect(ConfigState.getAll(CONFIG_STATE_DATA)).toEqual(CONFIG_STATE_DATA); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getApplicationInfo', () => { |
|||
it('should return application property', () => { |
|||
expect(ConfigState.getApplicationInfo(CONFIG_STATE_DATA)).toEqual( |
|||
CONFIG_STATE_DATA.environment.application, |
|||
); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getOne', () => { |
|||
it('should return one property', () => { |
|||
expect(ConfigState.getOne('environment')(CONFIG_STATE_DATA)).toEqual( |
|||
CONFIG_STATE_DATA.environment, |
|||
); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getDeep', () => { |
|||
it('should return deeper', () => { |
|||
expect( |
|||
ConfigState.getDeep('environment.localization.defaultResourceName')(CONFIG_STATE_DATA), |
|||
).toEqual(CONFIG_STATE_DATA.environment.localization.defaultResourceName); |
|||
expect( |
|||
ConfigState.getDeep(['environment', 'localization', 'defaultResourceName'])( |
|||
CONFIG_STATE_DATA, |
|||
), |
|||
).toEqual(CONFIG_STATE_DATA.environment.localization.defaultResourceName); |
|||
|
|||
expect(ConfigState.getDeep('test')(null)).toBeFalsy(); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getApiUrl', () => { |
|||
it('should return api url', () => { |
|||
expect(ConfigState.getApiUrl('other')(CONFIG_STATE_DATA)).toEqual( |
|||
CONFIG_STATE_DATA.environment.apis.other.url, |
|||
); |
|||
expect(ConfigState.getApiUrl()(CONFIG_STATE_DATA)).toEqual( |
|||
CONFIG_STATE_DATA.environment.apis.default.url, |
|||
); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getFeature', () => { |
|||
it('should return a setting', () => { |
|||
expect(ConfigState.getFeature('Chat.Enable')(CONFIG_STATE_DATA)).toEqual( |
|||
CONFIG_STATE_DATA.features.values['Chat.Enable'], |
|||
); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getSetting', () => { |
|||
it('should return a setting', () => { |
|||
expect(ConfigState.getSetting('Abp.Localization.DefaultLanguage')(CONFIG_STATE_DATA)).toEqual( |
|||
CONFIG_STATE_DATA.setting.values['Abp.Localization.DefaultLanguage'], |
|||
); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getSettings', () => { |
|||
test.each` |
|||
keyword | expected |
|||
${undefined} | ${CONFIG_STATE_DATA.setting.values} |
|||
${'Localization'} | ${{ 'Abp.Localization.DefaultLanguage': 'en' }} |
|||
${'X'} | ${{}} |
|||
${'localization'} | ${{}} |
|||
`('should return $expected when keyword is given as $keyword', ({ keyword, expected }) => {
|
|||
expect(ConfigState.getSettings(keyword)(CONFIG_STATE_DATA)).toEqual(expected); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getGrantedPolicy', () => { |
|||
it('should return a granted policy', () => { |
|||
expect(ConfigState.getGrantedPolicy('Abp.Identity')(CONFIG_STATE_DATA)).toBe(false); |
|||
expect(ConfigState.getGrantedPolicy('Abp.Identity || Abp.Account')(CONFIG_STATE_DATA)).toBe( |
|||
true, |
|||
); |
|||
expect(ConfigState.getGrantedPolicy('Abp.Account && Abp.Identity')(CONFIG_STATE_DATA)).toBe( |
|||
false, |
|||
); |
|||
expect(ConfigState.getGrantedPolicy('Abp.Account &&')(CONFIG_STATE_DATA)).toBe(false); |
|||
expect(ConfigState.getGrantedPolicy('|| Abp.Account')(CONFIG_STATE_DATA)).toBe(false); |
|||
expect(ConfigState.getGrantedPolicy('')(CONFIG_STATE_DATA)).toBe(true); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getLocalization', () => { |
|||
it('should return a localization', () => { |
|||
expect(ConfigState.getLocalization('AbpIdentity::Identity')(CONFIG_STATE_DATA)).toBe( |
|||
'identity', |
|||
); |
|||
|
|||
expect(ConfigState.getLocalization('AbpIdentity::NoIdentity')(CONFIG_STATE_DATA)).toBe( |
|||
'NoIdentity', |
|||
); |
|||
|
|||
expect( |
|||
ConfigState.getLocalization({ key: '', defaultValue: 'default' })(CONFIG_STATE_DATA), |
|||
).toBe('default'); |
|||
|
|||
expect( |
|||
ConfigState.getLocalization( |
|||
"::'{0}' and '{1}' do not match.", |
|||
'first', |
|||
'second', |
|||
)(CONFIG_STATE_DATA), |
|||
).toBe('first and second do not match.'); |
|||
|
|||
expect( |
|||
ConfigState.getLocalization('::Test')({ |
|||
...CONFIG_STATE_DATA, |
|||
environment: { |
|||
...CONFIG_STATE_DATA.environment, |
|||
localization: {} as any, |
|||
}, |
|||
}), |
|||
).toBe('Test'); |
|||
}); |
|||
}); |
|||
|
|||
describe('#GetAppConfiguration', () => { |
|||
it('should call the app-configuration API and patch the state', done => { |
|||
let patchStateArg; |
|||
let dispatchArg; |
|||
|
|||
const configuration = { |
|||
localization: { currentCulture: { cultureName: 'en;EN' } }, |
|||
}; |
|||
|
|||
const res$ = new ReplaySubject(1); |
|||
res$.next(configuration); |
|||
|
|||
const patchState = jest.fn(s => (patchStateArg = s)); |
|||
const dispatch = jest.fn(a => { |
|||
dispatchArg = a; |
|||
return of(a); |
|||
}); |
|||
const httpClient = spectator.inject(HttpClient); |
|||
httpClient.get.andReturn(res$); |
|||
|
|||
state.addData({ patchState, dispatch } as any).subscribe(); |
|||
|
|||
timer(0).subscribe(() => { |
|||
expect(patchStateArg).toEqual(configuration); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,72 @@ |
|||
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; |
|||
import { Environment } from '../models'; |
|||
import { EnvironmentService } from '../services'; |
|||
|
|||
export const ENVIRONMENT_DATA = ({ |
|||
production: false, |
|||
application: { |
|||
name: 'MyProjectName', |
|||
}, |
|||
oAuthConfig: { |
|||
issuer: 'https://localhost:44305', |
|||
}, |
|||
apis: { |
|||
default: { |
|||
url: 'https://localhost:44305', |
|||
}, |
|||
other: { |
|||
url: 'https://localhost:44306', |
|||
}, |
|||
}, |
|||
localization: { |
|||
defaultResourceName: 'MyProjectName', |
|||
}, |
|||
} as any) as Environment; |
|||
|
|||
describe('ConfigState', () => { |
|||
let spectator: SpectatorService<EnvironmentService>; |
|||
let environment: EnvironmentService; |
|||
|
|||
const createService = createServiceFactory({ |
|||
service: EnvironmentService, |
|||
}); |
|||
|
|||
beforeEach(() => { |
|||
spectator = createService(); |
|||
environment = spectator.service; |
|||
|
|||
environment.setState(ENVIRONMENT_DATA); |
|||
}); |
|||
|
|||
describe('#getEnvironment', () => { |
|||
it('should return ENVIRONMENT_DATA', () => { |
|||
expect(environment.getEnvironment()).toEqual(ENVIRONMENT_DATA); |
|||
environment.getEnvironment$().subscribe(data => expect(data).toEqual(ENVIRONMENT_DATA)); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getApiUrl', () => { |
|||
it('should return api url', () => { |
|||
expect(environment.getApiUrl()).toEqual(ENVIRONMENT_DATA.apis.default.url); |
|||
environment |
|||
.getApiUrl$('other') |
|||
.subscribe(data => expect(data).toEqual(ENVIRONMENT_DATA.apis.other.url)); |
|||
}); |
|||
}); |
|||
|
|||
// TODO: create permission.service.spec.ts
|
|||
// describe('#getGrantedPolicy', () => {
|
|||
// it('should return a granted policy', () => {
|
|||
// expect(ConfigState.getGrantedPolicy('Abp.Identity')(CONFIG_STATE_DATA)).toBe(false);
|
|||
// expect(ConfigState.getGrantedPolicy('Abp.Identity || Abp.Account')(CONFIG_STATE_DATA)).toBe(
|
|||
// true,
|
|||
// );
|
|||
// expect(ConfigState.getGrantedPolicy('Abp.Account && Abp.Identity')(CONFIG_STATE_DATA)).toBe(
|
|||
// false,
|
|||
// );
|
|||
// expect(ConfigState.getGrantedPolicy('Abp.Account &&')(CONFIG_STATE_DATA)).toBe(false);
|
|||
// expect(ConfigState.getGrantedPolicy('|| Abp.Account')(CONFIG_STATE_DATA)).toBe(false);
|
|||
// expect(ConfigState.getGrantedPolicy('')(CONFIG_STATE_DATA)).toBe(true);
|
|||
// });
|
|||
// });
|
|||
}); |
|||
@ -1,28 +1,29 @@ |
|||
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest'; |
|||
import { LocalizationPipe } from '../pipes'; |
|||
import { Store } from '@ngxs/store'; |
|||
import { ConfigState } from '../states'; |
|||
import { LocalizationService } from '../services'; |
|||
|
|||
describe('LocalizationPipe', () => { |
|||
let spectator: SpectatorService<LocalizationPipe>; |
|||
let pipe: LocalizationPipe; |
|||
let store: SpyObject<Store>; |
|||
let localizationService: SpyObject<LocalizationService>; |
|||
|
|||
const createService = createServiceFactory({ service: LocalizationPipe, mocks: [Store] }); |
|||
const createService = createServiceFactory({ |
|||
service: LocalizationPipe, |
|||
mocks: [LocalizationService], |
|||
}); |
|||
|
|||
beforeEach(() => { |
|||
spectator = createService(); |
|||
pipe = spectator.inject(LocalizationPipe); |
|||
store = spectator.inject(Store); |
|||
localizationService = spectator.inject(LocalizationService); |
|||
}); |
|||
|
|||
it('should call getLocalization selector', () => { |
|||
const storeSpy = jest.spyOn(store, 'selectSnapshot'); |
|||
const configStateSpy = jest.spyOn(ConfigState, 'getLocalization'); |
|||
const translateSpy = jest.spyOn(localizationService, 'instant'); |
|||
|
|||
pipe.transform('test', '1', '2'); |
|||
pipe.transform('test2', ['3', '4'] as any); |
|||
expect(configStateSpy).toHaveBeenCalledWith('test', '1', '2'); |
|||
expect(configStateSpy).toHaveBeenCalledWith('test2', '3', '4'); |
|||
expect(translateSpy).toHaveBeenCalledWith('test', '1', '2'); |
|||
expect(translateSpy).toHaveBeenCalledWith('test2', '3', '4'); |
|||
}); |
|||
}); |
|||
|
|||
@ -1 +0,0 @@ |
|||
export * from './routes-service.spec.utils'; |
|||
@ -1,12 +0,0 @@ |
|||
import { RoutesService } from '../../services'; |
|||
import { mockPermissionService } from './permission-service.spec.utils'; |
|||
import { DummyInjector, mockActions } from './common.utils'; |
|||
|
|||
export const mockRoutesService = (injectorPayload = {} as { [key: string]: any }) => { |
|||
const injector = new DummyInjector({ |
|||
PermissionService: mockPermissionService(), |
|||
Actions: mockActions, |
|||
...injectorPayload, |
|||
}); |
|||
return new RoutesService(injector); |
|||
}; |
|||
Loading…
Reference in new issue