mirror of https://github.com/abpframework/abp.git
47 changed files with 945 additions and 790 deletions
@ -0,0 +1,24 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.test.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.IdentityModel\Volo.Abp.IdentityModel.csproj" /> |
|||
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="appsettings.json" /> |
|||
<Content Include="appsettings.json"> |
|||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> |
|||
</Content> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,51 @@ |
|||
using System; |
|||
using Microsoft.Extensions.Options; |
|||
using Shouldly; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.IdentityModel |
|||
{ |
|||
public class AbpIdentityClientOptions_Tests : AbpIdentityModelTestBase |
|||
{ |
|||
private readonly ICurrentTenant _currentTenant; |
|||
private readonly AbpIdentityClientOptions _identityClientOptions; |
|||
|
|||
public AbpIdentityClientOptions_Tests() |
|||
{ |
|||
_currentTenant = GetRequiredService<ICurrentTenant>(); |
|||
_identityClientOptions = GetRequiredService<IOptions<AbpIdentityClientOptions>>().Value; |
|||
} |
|||
|
|||
[Fact] |
|||
public void GetClientConfiguration_Test() |
|||
{ |
|||
var hostDefaultConfiguration = _identityClientOptions.GetClientConfiguration(_currentTenant); |
|||
hostDefaultConfiguration.UserName.ShouldBe("host_default_admin"); |
|||
|
|||
var hostIdentityConfiguration = _identityClientOptions.GetClientConfiguration(_currentTenant, "Identity"); |
|||
hostIdentityConfiguration.UserName.ShouldBe("host_identity_admin"); |
|||
|
|||
using (_currentTenant.Change(Guid.Parse("f72a344f-651e-49f0-85f6-be260a10e4df"), "Test_Tenant1")) |
|||
{ |
|||
var tenantDefaultConfiguration = _identityClientOptions.GetClientConfiguration(_currentTenant); |
|||
tenantDefaultConfiguration.UserName.ShouldBe("tenant_default_admin"); |
|||
} |
|||
|
|||
using (_currentTenant.Change(Guid.Parse("f72a344f-651e-49f0-85f6-be260a10e4df"))) |
|||
{ |
|||
var tenantIdentityConfiguration = _identityClientOptions.GetClientConfiguration(_currentTenant, "Identity"); |
|||
tenantIdentityConfiguration.UserName.ShouldBe("tenant_identity_admin"); |
|||
} |
|||
|
|||
using (_currentTenant.Change(Guid.NewGuid())) |
|||
{ |
|||
var configuration = _identityClientOptions.GetClientConfiguration(_currentTenant); |
|||
configuration.UserName.ShouldBe("host_default_admin"); |
|||
|
|||
configuration = _identityClientOptions.GetClientConfiguration(_currentTenant, "Identity"); |
|||
configuration.UserName.ShouldBe("host_identity_admin"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using Volo.Abp.Testing; |
|||
|
|||
namespace Volo.Abp.IdentityModel |
|||
{ |
|||
public abstract class AbpIdentityModelTestBase : AbpIntegratedTest<AbpIdentityModelTestModule> |
|||
{ |
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.IdentityModel |
|||
{ |
|||
[DependsOn(typeof(AbpIdentityModelModule))] |
|||
public class AbpIdentityModelTestModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
{ |
|||
"IdentityClients": { |
|||
"Default": { |
|||
"GrantType": "password", |
|||
"ClientId": "Test_App", |
|||
"ClientSecret": "1q2w3e*", |
|||
"UserName": "host_default_admin", |
|||
"UserPassword": "1q2w3E*", |
|||
"Authority": "https://localhost:44395", |
|||
"Scope": "Test_Scope" |
|||
}, |
|||
"Default.Test_Tenant1": { |
|||
"GrantType": "password", |
|||
"ClientId": "Test_App", |
|||
"ClientSecret": "1q2w3e*", |
|||
"UserName": "tenant_default_admin", |
|||
"UserPassword": "1q2w3E*", |
|||
"Authority": "https://localhost:44395", |
|||
"Scope": "Test_Scope" |
|||
}, |
|||
"Identity": { |
|||
"GrantType": "password", |
|||
"ClientId": "Test_App", |
|||
"ClientSecret": "1q2w3e*", |
|||
"UserName": "host_identity_admin", |
|||
"UserPassword": "1q2w3E*", |
|||
"Authority": "https://localhost:44395", |
|||
"Scope": "Test_Scope" |
|||
}, |
|||
"Identity.f72a344f-651e-49f0-85f6-be260a10e4df": { |
|||
"GrantType": "password", |
|||
"ClientId": "Test_App", |
|||
"ClientSecret": "1q2w3e*", |
|||
"UserName": "tenant_identity_admin", |
|||
"UserPassword": "1q2w3E*", |
|||
"Authority": "https://localhost:44395", |
|||
"Scope": "Test_Scope" |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", |
|||
"dest": "../../../dist/packages/components/chart.js", |
|||
"lib": { |
|||
"entryFile": "src/public-api.ts" |
|||
} |
|||
} |
|||
@ -0,0 +1,149 @@ |
|||
import { |
|||
AfterViewInit, |
|||
ChangeDetectionStrategy, |
|||
ChangeDetectorRef, |
|||
Component, |
|||
ElementRef, |
|||
EventEmitter, |
|||
Input, |
|||
OnChanges, |
|||
OnDestroy, |
|||
Output, |
|||
SimpleChanges, |
|||
ViewChild, |
|||
} from '@angular/core'; |
|||
|
|||
let Chart: any; |
|||
|
|||
@Component({ |
|||
selector: 'abp-chart', |
|||
template: ` |
|||
<div |
|||
style="position:relative" |
|||
[style.width]="responsive && !width ? null : width" |
|||
[style.height]="responsive && !height ? null : height" |
|||
> |
|||
<canvas |
|||
#canvas |
|||
[attr.width]="responsive && !width ? null : width" |
|||
[attr.height]="responsive && !height ? null : height" |
|||
(click)="onCanvasClick($event)" |
|||
></canvas> |
|||
</div> |
|||
`,
|
|||
changeDetection: ChangeDetectionStrategy.OnPush, |
|||
exportAs: 'abpChart', |
|||
}) |
|||
export class ChartComponent implements AfterViewInit, OnDestroy, OnChanges { |
|||
@Input() type: string; |
|||
|
|||
@Input() data: any = {}; |
|||
|
|||
@Input() options: any = {}; |
|||
|
|||
@Input() plugins: any[] = []; |
|||
|
|||
@Input() width: string; |
|||
|
|||
@Input() height: string; |
|||
|
|||
@Input() responsive = true; |
|||
|
|||
@Output() dataSelect = new EventEmitter(); |
|||
|
|||
@Output() initialized = new EventEmitter<boolean>(); |
|||
|
|||
@ViewChild('canvas') canvas: ElementRef<HTMLCanvasElement>; |
|||
|
|||
chart: any; |
|||
|
|||
constructor(public el: ElementRef, private cdr: ChangeDetectorRef) {} |
|||
|
|||
ngAfterViewInit() { |
|||
import('chart.js/auto').then(module => { |
|||
Chart = module.default; |
|||
this.initChart(); |
|||
this.initialized.emit(true); |
|||
}); |
|||
} |
|||
|
|||
onCanvasClick(event) { |
|||
if (this.chart) { |
|||
const element = this.chart.getElementsAtEventForMode( |
|||
event, |
|||
'nearest', |
|||
{ intersect: true }, |
|||
false, |
|||
); |
|||
const dataset = this.chart.getElementsAtEventForMode( |
|||
event, |
|||
'dataset', |
|||
{ intersect: true }, |
|||
false, |
|||
); |
|||
|
|||
if (element && element[0] && dataset) { |
|||
this.dataSelect.emit({ originalEvent: event, element: element[0], dataset: dataset }); |
|||
} |
|||
} |
|||
} |
|||
|
|||
initChart = () => { |
|||
const opts = this.options || {}; |
|||
opts.responsive = this.responsive; |
|||
|
|||
// allows chart to resize in responsive mode
|
|||
if (opts.responsive && (this.height || this.width)) { |
|||
opts.maintainAspectRatio = false; |
|||
} |
|||
|
|||
this.chart = new Chart(this.canvas.nativeElement, { |
|||
type: this.type as any, |
|||
data: this.data, |
|||
options: this.options, |
|||
}); |
|||
}; |
|||
|
|||
getCanvas = () => { |
|||
return this.canvas.nativeElement; |
|||
}; |
|||
|
|||
getBase64Image = () => { |
|||
return this.chart.toBase64Image(); |
|||
}; |
|||
|
|||
generateLegend = () => { |
|||
if (this.chart) { |
|||
return this.chart.generateLegend(); |
|||
} |
|||
}; |
|||
|
|||
refresh = () => { |
|||
if (this.chart) { |
|||
this.chart.update(); |
|||
this.cdr.detectChanges(); |
|||
} |
|||
}; |
|||
|
|||
reinit = () => { |
|||
if (!this.chart) return; |
|||
this.chart.destroy(); |
|||
this.initChart(); |
|||
}; |
|||
|
|||
ngOnDestroy() { |
|||
if (this.chart) { |
|||
this.chart.destroy(); |
|||
this.chart = null; |
|||
} |
|||
} |
|||
|
|||
ngOnChanges(changes: SimpleChanges) { |
|||
if (!this.chart) return; |
|||
|
|||
if (changes.data?.currentValue || changes.options?.currentValue) { |
|||
this.chart.destroy(); |
|||
this.initChart(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
import { CommonModule } from '@angular/common'; |
|||
import { NgModule } from '@angular/core'; |
|||
import { ChartComponent } from './chart.component'; |
|||
|
|||
@NgModule({ |
|||
imports: [CommonModule], |
|||
exports: [ChartComponent], |
|||
declarations: [ChartComponent], |
|||
providers: [], |
|||
}) |
|||
export class ChartModule {} |
|||
@ -0,0 +1,4 @@ |
|||
export * from './chart.component'; |
|||
export * from './chart.module'; |
|||
export * from './widget-utils'; |
|||
|
|||
@ -1 +1,3 @@ |
|||
import 'jest-canvas-mock'; |
|||
import 'jest-preset-angular/setup-jest'; |
|||
|
|||
|
|||
@ -1,11 +0,0 @@ |
|||
<div |
|||
style="position:relative" |
|||
[style.width]="responsive && !width ? null : width" |
|||
[style.height]="responsive && !height ? null : height" |
|||
> |
|||
<canvas |
|||
[attr.width]="responsive && !width ? null : width" |
|||
[attr.height]="responsive && !height ? null : height" |
|||
(click)="onCanvasClick($event)" |
|||
></canvas> |
|||
</div> |
|||
@ -1,141 +0,0 @@ |
|||
import { |
|||
AfterViewInit, |
|||
ChangeDetectorRef, |
|||
Component, |
|||
ElementRef, |
|||
EventEmitter, |
|||
Input, |
|||
OnDestroy, |
|||
Output, |
|||
} from '@angular/core'; |
|||
import { BehaviorSubject } from 'rxjs'; |
|||
import { chartJsLoaded$ } from '../../utils/widget-utils'; |
|||
declare const Chart: any; |
|||
|
|||
@Component({ |
|||
selector: 'abp-chart', |
|||
templateUrl: './chart.component.html', |
|||
}) |
|||
export class ChartComponent implements AfterViewInit, OnDestroy { |
|||
@Input() type: string; |
|||
|
|||
@Input() options: any = {}; |
|||
|
|||
@Input() plugins: any[] = []; |
|||
|
|||
@Input() width: string; |
|||
|
|||
@Input() height: string; |
|||
|
|||
@Input() responsive = true; |
|||
|
|||
// eslint-disable-next-line @angular-eslint/no-output-on-prefix
|
|||
@Output() readonly onDataSelect: EventEmitter<any> = new EventEmitter(); |
|||
|
|||
@Output() readonly initialized = new BehaviorSubject(this); |
|||
|
|||
private _initialized: boolean; |
|||
|
|||
_data: any; |
|||
|
|||
chart: any; |
|||
|
|||
constructor(public el: ElementRef, private cdRef: ChangeDetectorRef) {} |
|||
|
|||
@Input() get data(): any { |
|||
return this._data; |
|||
} |
|||
|
|||
set data(val: any) { |
|||
this._data = val; |
|||
this.reinit(); |
|||
} |
|||
|
|||
get canvas() { |
|||
return this.el.nativeElement.children[0].children[0]; |
|||
} |
|||
|
|||
get base64Image() { |
|||
return this.chart.toBase64Image(); |
|||
} |
|||
|
|||
ngAfterViewInit() { |
|||
chartJsLoaded$.subscribe(() => { |
|||
this.testChartJs(); |
|||
|
|||
this.initChart(); |
|||
this._initialized = true; |
|||
}); |
|||
} |
|||
|
|||
testChartJs() { |
|||
try { |
|||
Chart; |
|||
} catch (error) { |
|||
throw new Error(`Chart is not found. Import the Chart from app.module like shown below:
|
|||
import('chart.js'); |
|||
`);
|
|||
} |
|||
} |
|||
|
|||
onCanvasClick = event => { |
|||
if (this.chart) { |
|||
const element = this.chart.getElementAtEvent(event); |
|||
const dataset = this.chart.getDatasetAtEvent(event); |
|||
if (element && element.length && dataset) { |
|||
this.onDataSelect.emit({ |
|||
originalEvent: event, |
|||
element: element[0], |
|||
dataset, |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
initChart = () => { |
|||
const opts = this.options || {}; |
|||
opts.responsive = this.responsive; |
|||
|
|||
// allows chart to resize in responsive mode
|
|||
if (opts.responsive && (this.height || this.width)) { |
|||
opts.maintainAspectRatio = false; |
|||
} |
|||
|
|||
this.chart = new Chart(this.canvas, { |
|||
type: this.type, |
|||
data: this.data, |
|||
options: this.options, |
|||
plugins: this.plugins, |
|||
}); |
|||
|
|||
this.cdRef.detectChanges(); |
|||
}; |
|||
|
|||
generateLegend = () => { |
|||
if (this.chart) { |
|||
return this.chart.generateLegend(); |
|||
} |
|||
}; |
|||
|
|||
refresh = () => { |
|||
if (this.chart) { |
|||
this.chart.update(); |
|||
this.cdRef.detectChanges(); |
|||
} |
|||
}; |
|||
|
|||
reinit = () => { |
|||
if (this.chart) { |
|||
this.chart.destroy(); |
|||
this.initChart(); |
|||
} |
|||
}; |
|||
|
|||
ngOnDestroy() { |
|||
if (this.chart) { |
|||
this.chart.destroy(); |
|||
this._initialized = false; |
|||
this.chart = null; |
|||
} |
|||
} |
|||
} |
|||
@ -1,2 +1,2 @@ |
|||
import 'jest-canvas-mock'; |
|||
import 'jest-preset-angular/setup-jest'; |
|||
|
|||
|
|||
File diff suppressed because it is too large
Loading…
Reference in new issue