Browse Source

UI: oauth - switched to entity-list for clients, introduce entity-chips for tables, fixes improvements

pull/11231/head
rusikv 2 years ago
parent
commit
3c02537454
  1. 4
      ui-ngx/src/app/core/http/domain.service.ts
  2. 12
      ui-ngx/src/app/core/http/entity.service.ts
  3. 4
      ui-ngx/src/app/core/http/oauth2.service.ts
  4. 8
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.html
  5. 4
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts
  6. 6
      ui-ngx/src/app/modules/home/components/entity/entity-chips.component.html
  7. 2
      ui-ngx/src/app/modules/home/components/entity/entity-chips.component.scss
  8. 63
      ui-ngx/src/app/modules/home/components/entity/entity-chips.component.ts
  9. 2
      ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts
  10. 7
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  11. 10
      ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts
  12. 40
      ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-chips.component.ts
  13. 2
      ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-dialog.component.html
  14. 53
      ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-list.component.html
  15. 182
      ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-list.component.ts
  16. 6
      ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-table-header.component.html
  17. 4
      ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client.component.html
  18. 39
      ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client.component.ts
  19. 5
      ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/clients-table-config.resolver.ts
  20. 16
      ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts
  21. 5
      ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-header.component.html
  22. 8
      ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.html
  23. 17
      ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.ts
  24. 14
      ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app-table-config.resolver.ts
  25. 5
      ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app-table-header.component.html
  26. 6
      ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app.component.html
  27. 20
      ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app.component.ts
  28. 2
      ui-ngx/src/app/modules/home/pages/admin/oauth2/oauth2.module.ts
  29. 4
      ui-ngx/src/app/shared/models/entity-type.models.ts
  30. 7
      ui-ngx/src/app/shared/models/oauth2.models.ts
  31. 1
      ui-ngx/src/assets/locale/locale.constant-en_US.json

4
ui-ngx/src/app/core/http/domain.service.ts

@ -37,8 +37,8 @@ export class DomainService {
domain, defaultHttpOptionsFromConfig(config));
}
public updateOauth2Clients(id: string, oauth2ClientRegistrationIds: Array<string>, config?: RequestConfig): Observable<void> {
return this.http.put<void>(`/api/domain/${id}/oauth2Clients`, oauth2ClientRegistrationIds, defaultHttpOptionsFromConfig(config));
public updateOauth2Clients(id: string, oauth2ClientIds: Array<string>, config?: RequestConfig): Observable<void> {
return this.http.put<void>(`/api/domain/${id}/oauth2Clients`, oauth2ClientIds, defaultHttpOptionsFromConfig(config));
}
public getTenantDomainInfos(pageLink: PageLink, config?: RequestConfig): Observable<PageData<DomainInfo>> {

12
ui-ngx/src/app/core/http/entity.service.ts

@ -96,6 +96,7 @@ import { NotificationType } from '@shared/models/notification.models';
import { UserId } from '@shared/models/id/user-id';
import { AlarmService } from '@core/http/alarm.service';
import { ResourceService } from '@core/http/resource.service';
import { OAuth2Service } from '@core/http/oauth2.service';
@Injectable({
providedIn: 'root'
@ -125,7 +126,8 @@ export class EntityService {
private queueService: QueueService,
private notificationService: NotificationService,
private alarmService: AlarmService,
private resourceService: ResourceService
private resourceService: ResourceService,
private oauth2Service: OAuth2Service
) { }
private getEntityObservable(entityType: EntityType, entityId: string,
@ -272,6 +274,9 @@ export class EntityService {
case EntityType.QUEUE_STATS:
observable = this.queueService.getQueueStatisticsByIds(entityIds, config);
break;
case EntityType.OAUTH2_CLIENT:
observable = this.oauth2Service.findTenantOAuth2ClientInfosByIds(entityIds, config);
break;
}
return observable;
}
@ -452,6 +457,11 @@ export class EntityService {
case EntityType.QUEUE_STATS:
pageLink.sortOrder.property = 'createdTime';
entitiesObservable = this.queueService.getQueueStatistics(pageLink, config);
break;
case EntityType.OAUTH2_CLIENT:
pageLink.sortOrder.property = 'createdTime';
entitiesObservable = this.oauth2Service.findTenantOAuth2ClientInfos(pageLink, config);
break;
}
return entitiesObservable;
}

4
ui-ngx/src/app/core/http/oauth2.service.ts

@ -44,6 +44,10 @@ export class OAuth2Service {
return this.http.get<PageData<OAuth2ClientInfo>>(`/api/oauth2/client/infos${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config));
}
public findTenantOAuth2ClientInfosByIds(clientIds: Array<string>, config?: RequestConfig): Observable<Array<OAuth2ClientInfo>> {
return this.http.get<Array<OAuth2ClientInfo>>(`/api/oauth2/client/infos?clientIds=${clientIds.join(',')}`, defaultHttpOptionsFromConfig(config))
}
public getOAuth2ClientById(id: string, config?: RequestConfig): Observable<OAuth2Client> {
return this.http.get<OAuth2Client>(`/api/oauth2/client/${id}`, defaultHttpOptionsFromConfig(config));
}

8
ui-ngx/src/app/modules/home/components/entity/entities-table.component.html

@ -38,9 +38,7 @@
<mat-toolbar class="mat-mdc-table-toolbar" [fxShow]="!textSearchMode && dataSource.selection.isEmpty()">
<div class="mat-toolbar-tools">
<div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs="center start" class="title-container">
<span *ngIf="!entitiesTableConfig.hideTableTitle && entitiesTableConfig.tableTitle" class="tb-entity-table-title">
{{ entitiesTableConfig.tableTitle }}
</span>
<span *ngIf="entitiesTableConfig.tableTitle" class="tb-entity-table-title">{{ entitiesTableConfig.tableTitle }}</span>
<tb-anchor #entityTableHeader></tb-anchor>
<tb-timewindow *ngIf="entitiesTableConfig.useTimePageLink" [(ngModel)]="timewindow"
(ngModelChange)="onTimewindowChange()"
@ -173,8 +171,8 @@
[routerLink]="detailsPageURL" [innerHTML]="cellContent(entity, column, row)"
(click)="$event.stopPropagation();"></a>
</ng-template>
<ng-template [ngSwitchCase]="'clients'">
<tb-client-chips [clientsEntity]="entity"></tb-client-chips>
<ng-template [ngSwitchCase]="'entityChips'">
<tb-entity-chips [entity]="entity"></tb-entity-chips>
</ng-template>
<ng-template #defaultContent ngSwitchDefault>
<span [innerHTML]="cellContent(entity, column, row)"></span>

4
ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts

@ -46,8 +46,8 @@ import { ActivatedRoute, QueryParamsHandling, Router } from '@angular/router';
import {
CellActionDescriptor,
CellActionDescriptorType,
ClientChipsEntityTableColumn,
EntityActionTableColumn,
EntityChipsEntityTableColumn,
EntityColumn,
EntityLinkTableColumn,
EntityTableColumn,
@ -616,7 +616,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
columnsUpdated(resetData: boolean = false) {
this.entityColumns = this.entitiesTableConfig.columns.filter(
(column) => column instanceof EntityTableColumn || column instanceof EntityLinkTableColumn ||
column instanceof ClientChipsEntityTableColumn)
column instanceof EntityChipsEntityTableColumn)
.map(column => column as EntityTableColumn<BaseData<HasId>>);
this.actionColumns = this.entitiesTableConfig.columns.filter(
(column) => column instanceof EntityActionTableColumn)

6
ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-chips.component.html → ui-ngx/src/app/modules/home/components/entity/entity-chips.component.html

@ -16,7 +16,7 @@
-->
<a *ngFor="let client of clientsEntity?.oauth2ClientInfos" class="tb-client-chip"
routerLink="{{clientPrefixUrl}}/{{client.id.id}}" (click)="$event.stopPropagation();">
{{ client.title }}
<a *ngFor="let subEntity of subEntities" class="tb-entity-chip"
routerLink="{{entityDetailsPrefixUrl}}/{{subEntity.id.id}}" (click)="$event.stopPropagation();">
{{ subEntity.name }}
</a>

2
ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-chips.component.scss → ui-ngx/src/app/modules/home/components/entity/entity-chips.component.scss

@ -19,7 +19,7 @@
flex-wrap: wrap;
gap: 8px;
margin: 4px 0;
a.tb-client-chip {
a.tb-entity-chip {
padding: 4px 8px;
border-radius: 12px;
line-height: 16px;

63
ui-ngx/src/app/modules/home/components/entity/entity-chips.component.ts

@ -0,0 +1,63 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, Input } from '@angular/core';
import { BaseData } from '@shared/models/base-data';
import { EntityId } from '@shared/models/id/entity-id';
import { baseDetailsPageByEntityType, EntityType } from '@app/shared/public-api';
const entityTypeEntitiesPropertyKeyMap = new Map<EntityType, string>([
[EntityType.DOMAIN, 'oauth2ClientInfos'],
[EntityType.MOBILE_APP, 'oauth2ClientInfos']
]);
@Component({
selector: 'tb-entity-chips',
templateUrl: './entity-chips.component.html',
styleUrls: ['./entity-chips.component.scss']
})
export class EntityChipsComponent {
@Input()
set entity(value: BaseData<EntityId>) {
this.entityValue = value;
this.update();
}
get entity(): BaseData<EntityId> {
return this.entityValue;
}
entityDetailsPrefixUrl: string;
subEntities: Array<BaseData<EntityId>>;
private entityValue?: BaseData<EntityId>;
private subEntitiesKey: string;
update(): void {
if (this.entity && this.entity.id) {
const entityType = this.entity.id.entityType as EntityType;
this.subEntitiesKey = entityTypeEntitiesPropertyKeyMap.get(entityType);
this.subEntities = this.entity?.[this.subEntitiesKey];
if (this.subEntities.length) {
this.entityDetailsPrefixUrl = baseDetailsPageByEntityType.get(this.subEntities[0].id.entityType as EntityType);
}
}
}
}

2
ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts

@ -228,7 +228,7 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV
}
hideDetailsTabs(): boolean {
return this.entitiesTableConfig.hideDetailsTabs || this.isEditValue && this.entitiesTableConfig.hideDetailsTabsOnEdit;
return this.isEditValue && this.entitiesTableConfig.hideDetailsTabsOnEdit;
}
reloadEntity(): Observable<BaseData<HasId>> {

7
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -171,7 +171,7 @@ import {
import { WidgetConfigComponentsModule } from '@home/components/widget/config/widget-config-components.module';
import { BasicWidgetConfigModule } from '@home/components/widget/config/basic/basic-widget-config.module';
import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delete-timeseries-panel.component';
import { ClientChipsComponent } from '@home/pages/admin/oauth2/clients/client-chips.component';
import { EntityChipsComponent } from '@home/components/entity/entity-chips.component';
@NgModule({
declarations:
@ -310,7 +310,7 @@ import { ClientChipsComponent } from '@home/pages/admin/oauth2/clients/client-ch
RateLimitsTextComponent,
RateLimitsDetailsDialogComponent,
SendNotificationButtonComponent,
ClientChipsComponent
EntityChipsComponent
],
imports: [
CommonModule,
@ -441,7 +441,8 @@ import { ClientChipsComponent } from '@home/pages/admin/oauth2/clients/client-ch
RateLimitsComponent,
RateLimitsTextComponent,
RateLimitsDetailsDialogComponent,
SendNotificationButtonComponent
SendNotificationButtonComponent,
EntityChipsComponent
],
providers: [
WidgetComponentService,

10
ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts

@ -78,7 +78,7 @@ export interface HeaderActionDescriptor {
onAction: ($event: MouseEvent) => void;
}
export type EntityTableColumnType = 'content' | 'action' | 'link' | 'clients';
export type EntityTableColumnType = 'content' | 'action' | 'link' | 'entityChips';
export class BaseEntityTableColumn<T extends BaseData<HasId>> {
constructor(public type: EntityTableColumnType,
@ -141,15 +141,15 @@ export class DateEntityTableColumn<T extends BaseData<HasId>> extends EntityTabl
}
}
export class ClientChipsEntityTableColumn<T extends BaseData<HasId>> extends BaseEntityTableColumn<T> {
export class EntityChipsEntityTableColumn<T extends BaseData<HasId>> extends BaseEntityTableColumn<T> {
constructor(public key: string,
public title: string,
public width: string = '0px') {
super('clients', key, title, width, false);
super('entityChips', key, title, width, false);
}
}
export type EntityColumn<T extends BaseData<HasId>> = EntityTableColumn<T> | EntityActionTableColumn<T> | EntityLinkTableColumn<T> | ClientChipsEntityTableColumn<T>;
export type EntityColumn<T extends BaseData<HasId>> = EntityTableColumn<T> | EntityActionTableColumn<T> | EntityLinkTableColumn<T> | EntityChipsEntityTableColumn<T>;
export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = PageLink, L extends BaseData<HasId> = T> {
@ -167,13 +167,11 @@ export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = P
defaultTimewindowInterval = historyInterval(DAY);
entityType: EntityType = null;
tableTitle = '';
hideTableTitle = false;
selectionEnabled = true;
searchEnabled = true;
addEnabled = true;
entitiesDeleteEnabled = true;
detailsPanelEnabled = true;
hideDetailsTabs = false;
hideDetailsTabsOnEdit = true;
rowPointer = false;
actionsColumnTitle = null;

40
ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-chips.component.ts

@ -1,40 +0,0 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, Input } from '@angular/core';
import { HasOauth2Clients } from '@shared/models/oauth2.models';
@Component({
selector: 'tb-client-chips',
templateUrl: './client-chips.component.html',
styleUrls: ['./client-chips.component.scss']
})
export class ClientChipsComponent {
clientsEntityValue?: HasOauth2Clients;
clientPrefixUrl = '/security-settings/oauth2/clients/details/';
@Input()
set clientsEntity(value: HasOauth2Clients) {
this.clientsEntityValue = value;
}
get clientsEntity(): HasOauth2Clients {
return this.clientsEntityValue;
}
}

2
ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-dialog.component.html

@ -30,7 +30,7 @@
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div mat-dialog-content style="padding: 0">
<tb-client #clientComponent></tb-client>
<tb-client createNewDialog #clientComponent></tb-client>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<button mat-button color="primary"

53
ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-list.component.html

@ -1,53 +0,0 @@
<!--
Copyright © 2016-2024 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<mat-form-field [formGroup]="entityListFormGroup" class="mat-block" [subscriptSizing]="subscriptSizing">
<mat-label translate>admin.oauth2.clients</mat-label>
<mat-chip-grid #chipList formControlName="entities">
<mat-chip-row
*ngFor="let entity of entities"
[removable]="!disabled"
(removed)="remove(entity)">
{{entity.title}}
<mat-icon matChipRemove *ngIf="!disabled">close</mat-icon>
</mat-chip-row>
<input matInput type="text" placeholder="Add client"
#entityInput
(focusin)="onFocus()"
formControlName="entity"
matAutocompleteOrigin
#origin="matAutocompleteOrigin"
[matAutocompleteConnectedTo]="origin"
[matAutocomplete]="entityAutocomplete"
[matChipInputFor]="chipList">
</mat-chip-grid>
<mat-autocomplete #entityAutocomplete="matAutocomplete"
class="tb-autocomplete"
[displayWith]="displayEntityFn">
<mat-option *ngFor="let entity of filteredEntities | async" [value]="entity">
<span [innerHTML]="entity.title | highlight:searchText"></span>
</mat-option>
<mat-option *ngIf="!(filteredEntities | async)?.length" [value]="null">
<span>
{{ translate.get('entity.no-entities-matching', {entity: searchText}) | async }}
</span>
</mat-option>
</mat-autocomplete>
<div matSuffix>
<ng-content select="[matSuffix]"></ng-content>
</div>
</mat-form-field>

182
ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-list.component.ts

@ -1,182 +0,0 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import { filter, map, mergeMap, share, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { BaseData } from '@shared/models/base-data';
import { EntityId } from '@shared/models/id/entity-id';
import { MatAutocomplete } from '@angular/material/autocomplete';
import { MatChipGrid } from '@angular/material/chips';
import { SubscriptSizing } from '@angular/material/form-field';
import { OAuth2Service } from '@core/http/oauth2.service';
import { OAuth2ClientInfo } from '@shared/models/oauth2.models';
import { PageLink } from '@app/shared/models/page/page-link';
@Component({
selector: 'tb-client-list',
templateUrl: './client-list.component.html',
styleUrls: [],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ClientListComponent),
multi: true
}
]
})
export class ClientListComponent implements ControlValueAccessor, OnInit {
entityListFormGroup: UntypedFormGroup;
modelValue: Array<OAuth2ClientInfo> | null;
@Input()
disabled: boolean;
@Input()
subscriptSizing: SubscriptSizing = 'fixed';
@ViewChild('entityInput') entityInput: ElementRef<HTMLInputElement>;
@ViewChild('entityAutocomplete') matAutocomplete: MatAutocomplete;
@ViewChild('chipList', {static: true}) chipList: MatChipGrid;
entities: Array<BaseData<EntityId>> = [];
filteredEntities: Observable<Array<BaseData<EntityId>>>;
searchText = '';
private dirty = false;
private propagateChange = (v: any) => {
};
constructor(private store: Store<AppState>,
public translate: TranslateService,
private oauth2Service: OAuth2Service,
private fb: UntypedFormBuilder) {
this.entityListFormGroup = this.fb.group({
entities: [this.entities],
entity: [null]
});
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
ngOnInit() {
this.filteredEntities = this.entityListFormGroup.get('entity').valueChanges
.pipe(
tap((value) => {
if (value && typeof value !== 'string') {
this.add(value);
} else if (value === null) {
this.clear(this.entityInput.nativeElement.value);
}
}),
filter((value) => typeof value === 'string'),
map((value) => value ? (typeof value === 'string' ? value : value.title) : ''),
mergeMap(name => this.fetchEntities(name)),
share()
);
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (isDisabled) {
this.entityListFormGroup.disable({emitEvent: false});
} else {
this.entityListFormGroup.enable({emitEvent: false});
}
}
writeValue(value: Array<OAuth2ClientInfo> | null): void {
this.searchText = '';
if (value != null && value.length > 0) {
this.modelValue = [...value];
this.entities = [...value];
} else {
this.entities = [];
this.entityListFormGroup.get('entities').setValue(this.entities);
this.modelValue = null;
}
this.dirty = true;
}
add(entity: OAuth2ClientInfo): void {
if (!this.modelValue || !this.modelValue.find(client => client.id.id === entity.id.id)) {
if (!this.modelValue) {
this.modelValue = [];
}
this.modelValue.push(entity);
this.entities.push(entity);
this.entityListFormGroup.get('entities').setValue(this.entities);
}
this.propagateChange(this.modelValue);
this.clear();
}
remove(entity: OAuth2ClientInfo) {
let index = this.entities.indexOf(entity);
if (index >= 0) {
this.entities.splice(index, 1);
this.entityListFormGroup.get('entities').setValue(this.entities);
index = this.modelValue.indexOf(entity);
this.modelValue.splice(index, 1);
if (!this.modelValue.length) {
this.modelValue = null;
}
this.propagateChange(this.modelValue);
this.clear();
}
}
displayEntityFn(entity?: OAuth2ClientInfo): string | undefined {
return entity ? entity.title : undefined;
}
fetchEntities(searchText?: string): Observable<Array<OAuth2ClientInfo>> {
this.searchText = searchText;
return this.oauth2Service.findTenantOAuth2ClientInfos(new PageLink(100, 0, searchText), {ignoreLoading: true}).pipe(
map((data) => data ? data.data : []));
}
onFocus() {
if (this.dirty) {
this.entityListFormGroup.get('entity').updateValueAndValidity({onlySelf: true, emitEvent: true});
this.dirty = false;
}
}
clear(value: string = '') {
this.entityInput.nativeElement.value = value;
this.entityListFormGroup.get('entity').patchValue(value, {emitEvent: true});
setTimeout(() => {
this.entityInput.nativeElement.blur();
this.entityInput.nativeElement.focus();
}, 0);
}
}

6
ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-table-header.component.html

@ -15,8 +15,4 @@
limitations under the License.
-->
<div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs="center start"
class="title-container">
<span *ngIf="entitiesTableConfig.tableTitle" class="tb-entity-table-title">{{ entitiesTableConfig.tableTitle }}</span>
<div tb-help="oauth2Settings"></div>
</div>
<div tb-help="oauth2Settings" style="margin-left: -20px"></div>

4
ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client.component.html

@ -162,7 +162,7 @@
<div class="tb-form-row tb-standard-fields no-border no-padding column-xs">
<mat-form-field class="flex">
<mat-label translate>admin.oauth2.scope</mat-label>
<mat-chip-grid #scopeList [disabled]="!isEdit">
<mat-chip-grid #scopeList [disabled]="!this.createNewDialog && !(this.isEdit || this.isAdd)">
<mat-chip-row *ngFor="let scope of entityForm.get('scope').value; let k = index; trackBy: trackByParams"
removable (removed)="removeScope(k, entityForm)">
{{scope}}
@ -239,7 +239,7 @@
</div>
<div class="tb-form-row tb-standard-fields no-border no-padding column-xs">
<mat-form-field class="flex">
<mat-label translate>admin.oauth2.first-name-attribute-key</mat-label>
<mat-label translate>admin.oauth2.tenant-name-strategy</mat-label>
<mat-select formControlName="tenantNameStrategy">
<mat-option *ngFor="let tenantNameStrategy of tenantNameStrategies"
[value]="tenantNameStrategy">

39
ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { ChangeDetectorRef, Component, Inject, Optional } from '@angular/core';
import { ChangeDetectorRef, Component, Inject, Input, Optional } from '@angular/core';
import { EntityComponent } from '@home/components/entity/entity.component';
import {
ClientAuthenticationMethod,
@ -47,6 +47,7 @@ import { Subscription } from 'rxjs';
import { MatChipInputEvent } from '@angular/material/chips';
import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { PageLink } from '@shared/models/page/page-link';
import { coerceBoolean } from '@app/shared/decorators/coercion';
@Component({
selector: 'tb-client',
@ -55,6 +56,10 @@ import { PageLink } from '@shared/models/page/page-link';
})
export class ClientComponent extends EntityComponent<OAuth2Client, PageLink, OAuth2ClientInfo> {
@Input()
@coerceBoolean()
readonly createNewDialog = false;
templateProvider = ['Custom'];
private templates = new Map<string, OAuth2ClientRegistrationTemplate>();
private defaultProvider = {
@ -99,8 +104,6 @@ export class ClientComponent extends EntityComponent<OAuth2Client, PageLink, OAu
platformTypeTranslations = platformTypeTranslations;
generalSettingsMode = true;
readonly isDialog = false;
constructor(protected store: Store<AppState>,
protected translate: TranslateService,
private oauth2Service: OAuth2Service,
@ -173,28 +176,7 @@ export class ClientComponent extends EntityComponent<OAuth2Client, PageLink, OAu
activateUser: entity.mapperConfig.activateUser,
type: entity.mapperConfig.type
}
}, {emitEvent: false});
const mapperConfig = this.entityForm.get('mapperConfig') as UntypedFormGroup;
if (entity.mapperConfig.basic) {
mapperConfig.removeControl('custom', {emitEvent: false});
if (mapperConfig.get('basic')) {
this.entityForm.get('mapperConfig').patchValue({
basic: entity.mapperConfig.basic
});
} else {
mapperConfig.addControl('basic', this.formBasicGroup(entity.mapperConfig.basic));
}
} else {
mapperConfig.removeControl('basic', {emitEvent: false});
if (mapperConfig.get('custom')) {
this.entityForm.get('mapperConfig').patchValue({
custom: entity.mapperConfig.custom
});
} else {
mapperConfig.addControl('custom', this.formCustomGroup(entity.mapperConfig.custom));
}
}
});
const scopeControls = this.entityForm.get('scope') as UntypedFormArray;
if (entity.scope.length === scopeControls.length) {
@ -290,7 +272,6 @@ export class ClientComponent extends EntityComponent<OAuth2Client, PageLink, OAu
private changeMapperConfigType(control: AbstractControl, type: MapperType, predefinedValue?: OAuth2MapperConfig) {
const mapperConfig = control.get('mapperConfig') as UntypedFormGroup;
if (type === MapperType.CUSTOM) {
mapperConfig.removeControl('basic');
mapperConfig.addControl('custom', this.formCustomGroup(predefinedValue?.custom));
} else {
mapperConfig.removeControl('custom');
@ -301,7 +282,7 @@ export class ClientComponent extends EntityComponent<OAuth2Client, PageLink, OAu
mapperConfig.get('basic.emailAttributeKey').disable();
mapperConfig.get('basic.emailAttributeKey').patchValue(null, {emitEvent: false});
} else {
if (this.isEdit) {
if (this.createNewDialog || this.isEdit || this.isAdd) {
mapperConfig.get('basic.emailAttributeKey').enable();
}
}
@ -316,7 +297,7 @@ export class ClientComponent extends EntityComponent<OAuth2Client, PageLink, OAu
password: [mapperConfigCustom?.password ? mapperConfigCustom.password : null, Validators.maxLength(255)],
sendToken: [isDefinedAndNotNull(mapperConfigCustom?.sendToken) ? mapperConfigCustom.sendToken : false]
});
if (!this.isEdit) {
if (!this.createNewDialog && !(this.isEdit || this.isAdd)) {
customGroup.disable();
}
return customGroup;
@ -345,7 +326,7 @@ export class ClientComponent extends EntityComponent<OAuth2Client, PageLink, OAu
alwaysFullScreen: [isDefinedAndNotNull(mapperConfigBasic?.alwaysFullScreen) ? mapperConfigBasic.alwaysFullScreen : false]
});
if (!this.isEdit) {
if (!this.createNewDialog && !(this.isEdit || this.isAdd)) {
basicGroup.disable();
}

5
ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/clients-table-config.resolver.ts

@ -47,7 +47,6 @@ export class ClientsTableConfigResolver implements Resolve<EntityTableConfig<OAu
private utilsService: UtilsService,
private oauth2Service: OAuth2Service) {
this.config.tableTitle = this.translate.instant('admin.oauth2.clients');
this.config.hideTableTitle = true;
this.config.selectionEnabled = false;
this.config.entityType = EntityType.OAUTH2_CLIENT;
this.config.rowPointer = true;
@ -60,7 +59,6 @@ export class ClientsTableConfigResolver implements Resolve<EntityTableConfig<OAu
};
this.config.entityComponent = ClientComponent;
this.config.headerComponent = ClientTableHeaderComponent;
this.config.hideDetailsTabs = true;
this.config.addDialogStyle = {width: '850px', maxHeight: '100vh'};
this.config.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC};
this.config.displayPagination = false;
@ -68,7 +66,8 @@ export class ClientsTableConfigResolver implements Resolve<EntityTableConfig<OAu
this.config.columns.push(
new DateEntityTableColumn<OAuth2ClientInfo>('createdTime', 'common.created-time', this.datePipe, '170px'),
new EntityTableColumn<OAuth2ClientInfo>('title', 'admin.oauth2.provider', '170px'),
new EntityTableColumn<OAuth2ClientInfo>('title', 'admin.oauth2.title', '170px'),
new EntityTableColumn<OAuth2ClientInfo>('providerName', 'admin.oauth2.provider', '170px'),
new EntityTableColumn<OAuth2ClientInfo>('platforms', 'admin.oauth2.allowed-platforms', '100%',
(clientInfo) => {
return clientInfo.platforms && clientInfo.platforms.length ?

16
ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts

@ -17,9 +17,9 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
import {
ClientChipsEntityTableColumn,
DateEntityTableColumn,
EntityActionTableColumn,
EntityChipsEntityTableColumn,
EntityTableColumn,
EntityTableConfig
} from '@home/models/entity/entities-table-config.models';
@ -44,7 +44,6 @@ export class DomainTableConfigResolver implements Resolve<EntityTableConfig<Doma
private utilsService: UtilsService,
private domainService: DomainService) {
this.config.tableTitle = this.translate.instant('admin.oauth2.domains');
this.config.hideTableTitle = true;
this.config.selectionEnabled = false;
this.config.entityType = EntityType.DOMAIN;
this.config.rowPointer = true;
@ -52,8 +51,7 @@ export class DomainTableConfigResolver implements Resolve<EntityTableConfig<Doma
this.config.entityResources = entityTypeResources.get(EntityType.DOMAIN);
this.config.entityComponent = DomainComponent;
this.config.headerComponent = DomainTableHeaderComponent;
this.config.hideDetailsTabs = true;
this.config.addDialogStyle = {width: '850px', height: '688px'};
this.config.addDialogStyle = {width: '850px', maxHeight: '100vh'};
this.config.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC};
this.config.displayPagination = false;
this.config.pageMode = false;
@ -61,14 +59,14 @@ export class DomainTableConfigResolver implements Resolve<EntityTableConfig<Doma
this.config.columns.push(
new DateEntityTableColumn<DomainInfo>('createdTime', 'common.created-time', this.datePipe, '170px'),
new EntityTableColumn<DomainInfo>('name', 'admin.oauth2.domain-name', '170px'),
new ClientChipsEntityTableColumn<DomainInfo>('oauth2ClientInfos', 'admin.oauth2.clients', '40%'),
new EntityChipsEntityTableColumn<DomainInfo>('oauth2ClientInfos', 'admin.oauth2.clients', '40%'),
new EntityActionTableColumn('oauth2Enabled', 'admin.oauth2.enable',
{
name: '',
nameFunction: (domain) =>
this.translate.instant(domain.oauth2Enabled ? 'admin.oauth2.disable' : 'admin.oauth2.enable'),
icon: 'mdi:toggle-switch',
iconFunction: (entity) => entity.oauth2Enabled ? 'mdi:toggle-switch' : 'mdi:toggle-switch-off-outline',
iconFunction: (domain) => domain.oauth2Enabled ? 'mdi:toggle-switch' : 'mdi:toggle-switch-off-outline',
isEnabled: () => true,
onAction: ($event, entity) => this.toggleEnableOAuth($event, entity)
}),
@ -88,9 +86,9 @@ export class DomainTableConfigResolver implements Resolve<EntityTableConfig<Doma
this.config.deleteEntityContent = () => this.translate.instant('admin.oauth2.delete-domain-text');
this.config.entitiesFetchFunction = pageLink => this.domainService.getTenantDomainInfos(pageLink);
this.config.loadEntity = id => this.domainService.getDomainInfoById(id.id);
this.config.saveEntity = (domain, originalEntity) => {
const clientsIds = domain.oauth2ClientInfos ? domain.oauth2ClientInfos.map(clientInfo => clientInfo.id.id) : [];
if (domain.id && !isEqual(domain.oauth2ClientInfos, originalEntity.oauth2ClientInfos)) {
this.config.saveEntity = (domain, originalDomain) => {
const clientsIds = domain.oauth2ClientInfos as Array<string>;
if (domain.id && !isEqual(domain.oauth2ClientInfos.sort(), originalDomain.oauth2ClientInfos.map(info => info.id.id).sort())) {
this.domainService.updateOauth2Clients(domain.id.id, clientsIds).subscribe();
}
delete domain.oauth2ClientInfos;

5
ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-header.component.html

@ -15,7 +15,4 @@
limitations under the License.
-->
<div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs="center start" class="title-container">
<span *ngIf="entitiesTableConfig.tableTitle" class="tb-entity-table-title">{{ entitiesTableConfig.tableTitle }}</span>
<div tb-help="oauth2Settings"></div>
</div>
<div tb-help="oauth2Settings" style="margin-left: -20px"></div>

8
ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.html

@ -46,19 +46,19 @@
</tb-copy-button>
</mat-form-field>
</div>
<div class="tb-form-row no-border no-padding">
<div class="tb-form-row no-border no-padding" style="margin-bottom: 22px">
<mat-slide-toggle class="mat-slide" formControlName="oauth2Enabled">
{{ 'admin.oauth2.enable' | translate }}
</mat-slide-toggle>
</div>
<tb-client-list formControlName="oauth2ClientInfos">
<tb-entity-list [entityType]="entityType.OAUTH2_CLIENT" formControlName="oauth2ClientInfos">
<button #createTargetButton
mat-button color="primary" matSuffix
[disabled]="!isEdit"
(click)="createClient($event, createTargetButton)">
<span style="white-space: nowrap">{{ 'notification.create-new' | translate }}</span>
<span style="white-space: nowrap">{{ 'admin.oauth2.create-new' | translate }}</span>
</button>
</tb-client-list>
</tb-entity-list>
<div class="tb-form-row no-border no-padding">
<mat-slide-toggle class="mat-slide" formControlName="propagateToEdge">
{{ 'admin.oauth2.edge' | translate }}

17
ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.ts

@ -29,6 +29,7 @@ import { isDefinedAndNotNull } from '@core/utils';
import { MatButton } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
import { ClientDialogComponent } from '@home/pages/admin/oauth2/clients/client-dialog.component';
import { EntityType } from '@shared/models/entity-type.models';
@Component({
selector: 'tb-domain',
@ -39,6 +40,8 @@ export class DomainComponent extends EntityComponent<DomainInfo> {
private loginProcessingUrl: string = '';
entityType = EntityType;
constructor(protected store: Store<AppState>,
protected translate: TranslateService,
private oauth2Service: OAuth2Service,
@ -59,8 +62,8 @@ export class DomainComponent extends EntityComponent<DomainInfo> {
return this.fb.group({
name: [entity?.name ? entity.name : '', [
Validators.required, Validators.maxLength(255), Validators.pattern('^(?:\\w+(?::\\w+)?@)?[^\\s/]+(?::\\d+)?$')]],
oauth2Enabled: isDefinedAndNotNull(entity?.oauth2Enabled) ? entity.oauth2Enabled : false,
oauth2ClientInfos: entity?.oauth2ClientInfos ? entity.oauth2ClientInfos : [],
oauth2Enabled: isDefinedAndNotNull(entity?.oauth2Enabled) ? entity.oauth2Enabled : true,
oauth2ClientInfos: entity?.oauth2ClientInfos ? entity.oauth2ClientInfos.map(info => info.id.id) : [],
propagateToEdge: isDefinedAndNotNull(entity?.propagateToEdge) ? entity.propagateToEdge : false
});
}
@ -69,7 +72,7 @@ export class DomainComponent extends EntityComponent<DomainInfo> {
this.entityForm.patchValue({
name: entity.name,
oauth2Enabled: entity.oauth2Enabled,
oauth2ClientInfos: entity.oauth2ClientInfos,
oauth2ClientInfos: entity.oauth2ClientInfos?.map(info => info.id.id),
propagateToEdge: entity.propagateToEdge
})
}
@ -84,20 +87,18 @@ export class DomainComponent extends EntityComponent<DomainInfo> {
$event.stopPropagation();
$event.preventDefault();
}
button._elementRef.nativeElement.blur();
this.dialog.open<ClientDialogComponent>(ClientDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {}
}).afterClosed()
.subscribe((res) => {
if (res) {
.subscribe((client) => {
if (client) {
const formValue = this.entityForm.get('oauth2ClientInfos').value ?
[...this.entityForm.get('oauth2ClientInfos').value] : [];
formValue.push({id: res.id, title: res.title});
formValue.push(client.id.id);
this.entityForm.get('oauth2ClientInfos').patchValue(formValue);
this.entityForm.get('oauth2ClientInfos').markAsDirty();
this.entityForm.updateValueAndValidity();
}
});
}

14
ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app-table-config.resolver.ts

@ -18,9 +18,9 @@ import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
import {
CellActionDescriptorType,
ClientChipsEntityTableColumn,
DateEntityTableColumn,
EntityActionTableColumn,
EntityChipsEntityTableColumn,
EntityTableColumn,
EntityTableConfig
} from '@home/models/entity/entities-table-config.models';
@ -45,7 +45,6 @@ export class MobileAppTableConfigResolver implements Resolve<EntityTableConfig<M
private utilsService: UtilsService,
private mobileAppService: MobileAppService) {
this.config.tableTitle = this.translate.instant('admin.oauth2.mobile-apps');
this.config.hideTableTitle = true;
this.config.selectionEnabled = false;
this.config.entityType = EntityType.MOBILE_APP;
this.config.rowPointer = true;
@ -53,8 +52,7 @@ export class MobileAppTableConfigResolver implements Resolve<EntityTableConfig<M
this.config.entityResources = entityTypeResources.get(EntityType.MOBILE_APP);
this.config.entityComponent = MobileAppComponent;
this.config.headerComponent = MobileAppTableHeaderComponent;
this.config.hideDetailsTabs = true;
this.config.addDialogStyle = {width: '850px', height: '688px'};
this.config.addDialogStyle = {width: '850px', maxHeight: '100vh'};
this.config.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC};
this.config.displayPagination = false;
this.config.pageMode = false;
@ -77,7 +75,7 @@ export class MobileAppTableConfigResolver implements Resolve<EntityTableConfig<M
onAction: ($event, entity) => entity.appSecret,
type: CellActionDescriptorType.COPY_BUTTON
}),
new ClientChipsEntityTableColumn<MobileAppInfo>('oauth2ClientInfos', 'admin.oauth2.clients', '20%'),
new EntityChipsEntityTableColumn<MobileAppInfo>('oauth2ClientInfos', 'admin.oauth2.clients', '20%'),
new EntityActionTableColumn('oauth2Enabled', 'admin.oauth2.enable',
{
name: '',
@ -94,9 +92,9 @@ export class MobileAppTableConfigResolver implements Resolve<EntityTableConfig<M
this.config.deleteEntityContent = () => this.translate.instant('admin.oauth2.delete-mobile-app-text');
this.config.entitiesFetchFunction = pageLink => this.mobileAppService.getTenantMobileAppInfos(pageLink);
this.config.loadEntity = id => this.mobileAppService.getMobileAppInfoById(id.id);
this.config.saveEntity = (mobileApp, originalEntity) => {
const clientsIds = mobileApp.oauth2ClientInfos ? mobileApp.oauth2ClientInfos.map(clientInfo => clientInfo.id.id) : [];
if (mobileApp.id && !isEqual(mobileApp.oauth2ClientInfos, originalEntity.oauth2ClientInfos)) {
this.config.saveEntity = (mobileApp, originalMobileApp) => {
const clientsIds = mobileApp.oauth2ClientInfos as Array<string>;
if (mobileApp.id && !isEqual(mobileApp.oauth2ClientInfos.sort(), originalMobileApp.oauth2ClientInfos.map(info => info.id.id).sort())) {
this.mobileAppService.updateOauth2Clients(mobileApp.id.id, clientsIds).subscribe();
}
delete mobileApp.oauth2ClientInfos;

5
ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app-table-header.component.html

@ -15,7 +15,4 @@
limitations under the License.
-->
<div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs="center start" class="title-container">
<span *ngIf="entitiesTableConfig.tableTitle" class="tb-entity-table-title">{{ entitiesTableConfig.tableTitle }}</span>
<div tb-help="oauth2Settings"></div>
</div>
<div tb-help="oauth2Settings" style="margin-left: -20px"></div>

6
ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app.component.html

@ -57,13 +57,13 @@
{{ 'admin.oauth2.enable' | translate }}
</mat-slide-toggle>
</div>
<tb-client-list formControlName="oauth2ClientInfos">
<tb-entity-list [entityType]="entityType.OAUTH2_CLIENT" formControlName="oauth2ClientInfos">
<button #createTargetButton
mat-button color="primary" matSuffix
[disabled]="!isEdit"
(click)="createClient($event, createTargetButton)">
<span style="white-space: nowrap">{{ 'notification.create-new' | translate }}</span>
<span style="white-space: nowrap">{{ 'admin.oauth2.create-new' | translate }}</span>
</button>
</tb-client-list>
</tb-entity-list>
</section>

20
ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app.component.ts

@ -25,10 +25,11 @@ import { TranslateService } from '@ngx-translate/core';
import { Store } from '@ngrx/store';
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
import { WINDOW } from '@core/services/window.service';
import { isDefinedAndNotNull } from '@core/utils';
import { isDefinedAndNotNull, randomAlphanumeric } from '@core/utils';
import { MatButton } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
import { ClientDialogComponent } from '@home/pages/admin/oauth2/clients/client-dialog.component';
import { EntityType } from '@shared/models/entity-type.models';
@Component({
selector: 'tb-mobile-app',
@ -37,6 +38,8 @@ import { ClientDialogComponent } from '@home/pages/admin/oauth2/clients/client-d
})
export class MobileAppComponent extends EntityComponent<MobileAppInfo> {
entityType = EntityType;
constructor(protected store: Store<AppState>,
protected translate: TranslateService,
private oauth2Service: OAuth2Service,
@ -52,9 +55,10 @@ export class MobileAppComponent extends EntityComponent<MobileAppInfo> {
buildForm(entity: MobileAppInfo): UntypedFormGroup {
return this.fb.group({
pkgName: [entity?.pkgName ? entity.pkgName : '', [Validators.required]],
appSecret: [entity?.appSecret ? entity.appSecret : '', [Validators.required, this.base64Format]],
appSecret: [entity?.appSecret ? entity.appSecret : btoa(randomAlphanumeric(64)),
[Validators.required, this.base64Format]],
oauth2Enabled: isDefinedAndNotNull(entity?.oauth2Enabled) ? entity.oauth2Enabled : false,
oauth2ClientInfos: entity?.oauth2ClientInfos ? entity.oauth2ClientInfos : []
oauth2ClientInfos: entity?.oauth2ClientInfos ? entity.oauth2ClientInfos.map(info => info.id.id) : []
});
}
@ -63,7 +67,7 @@ export class MobileAppComponent extends EntityComponent<MobileAppInfo> {
pkgName: entity.pkgName,
appSecret: entity.appSecret,
oauth2Enabled: entity.oauth2Enabled,
oauth2ClientInfos: entity.oauth2ClientInfos
oauth2ClientInfos: entity.oauth2ClientInfos?.map(info => info.id.id)
})
}
@ -72,20 +76,18 @@ export class MobileAppComponent extends EntityComponent<MobileAppInfo> {
$event.stopPropagation();
$event.preventDefault();
}
button._elementRef.nativeElement.blur();
this.dialog.open<ClientDialogComponent>(ClientDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {}
}).afterClosed()
.subscribe((res) => {
if (res) {
.subscribe((client) => {
if (client) {
const formValue = this.entityForm.get('oauth2ClientInfos').value ?
[...this.entityForm.get('oauth2ClientInfos').value] : [];
formValue.push({id: res.id, title: res.title});
formValue.push(client.id.id);
this.entityForm.get('oauth2ClientInfos').patchValue(formValue);
this.entityForm.get('oauth2ClientInfos').markAsDirty();
this.entityForm.updateValueAndValidity();
}
});
}

2
ui-ngx/src/app/modules/home/pages/admin/oauth2/oauth2.module.ts

@ -22,7 +22,6 @@ import { HomeComponentsModule } from '@home/components/home-components.module';
import { CommonModule } from '@angular/common';
import { ClientTableHeaderComponent } from '@home/pages/admin/oauth2/clients/client-table-header.component';
import { DomainComponent } from '@home/pages/admin/oauth2/domains/domain.component';
import { ClientListComponent } from '@home/pages/admin/oauth2/clients/client-list.component';
import { ClientDialogComponent } from '@home/pages/admin/oauth2/clients/client-dialog.component';
import { DomainTableHeaderComponent } from '@home/pages/admin/oauth2/domains/domain-table-header.component';
import { MobileAppComponent } from '@home/pages/admin/oauth2/mobile-apps/mobile-app.component';
@ -32,7 +31,6 @@ import { MobileAppTableHeaderComponent } from '@home/pages/admin/oauth2/mobile-a
declarations: [
ClientComponent,
ClientDialogComponent,
ClientListComponent,
ClientTableHeaderComponent,
DomainComponent,
DomainTableHeaderComponent,

4
ui-ngx/src/app/shared/models/entity-type.models.ts

@ -609,7 +609,9 @@ export const baseDetailsPageByEntityType = new Map<EntityType, string>([
[EntityType.QUEUE, '/settings/queues'],
[EntityType.WIDGETS_BUNDLE, '/resources/widgets-library/widgets-bundles/details'],
[EntityType.WIDGET_TYPE, '/resources/widgets-library/widget-types/details'],
[EntityType.OAUTH2_CLIENT, '/security-settings/oauth2/clients/details']
[EntityType.OAUTH2_CLIENT, '/security-settings/oauth2/clients/details'],
[EntityType.DOMAIN, '/security-settings/oauth2/clients/details'],
[EntityType.MOBILE_APP, '/security-settings/oauth2/clients/details']
]);
export interface EntitySubtype {

7
ui-ngx/src/app/shared/models/oauth2.models.ts

@ -81,11 +81,11 @@ export interface Domain extends BaseData<DomainId>, HasTenantId {
}
export interface HasOauth2Clients {
oauth2ClientInfos?: Array<OAuth2ClientInfo>;
oauth2ClientInfos?: Array<OAuth2ClientInfo> | Array<string>;
}
export interface DomainInfo extends Domain, HasOauth2Clients {
oauth2ClientInfos?: Array<OAuth2ClientInfo>;
oauth2ClientInfos?: Array<OAuth2ClientInfo> | Array<string>;
}
export interface MobileApp extends BaseData<MobileAppId>, HasTenantId {
@ -96,7 +96,7 @@ export interface MobileApp extends BaseData<MobileAppId>, HasTenantId {
}
export interface MobileAppInfo extends MobileApp, HasOauth2Clients {
oauth2ClientInfos?: Array<OAuth2ClientInfo>;
oauth2ClientInfos?: Array<OAuth2ClientInfo> | Array<string>;
}
export interface OAuth2Client extends BaseData<OAuth2ClientId>, HasTenantId {
@ -167,6 +167,7 @@ export const platformTypeTranslations = new Map<PlatformType, string>(
export interface OAuth2ClientInfo extends BaseData<OAuth2ClientId> {
title: string;
providerName: string;
platforms?: Array<PlatformType>;
}

1
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -243,6 +243,7 @@
"no-mobile-apps": "Search mobile applications",
"search-mobile-apps": "No mobile applications found",
"send-token": "Send token",
"create-new": "Create new",
"client-authentication-method": "Client authentication method",
"client-id": "Client ID",
"client-id-required": "Client ID is required.",

Loading…
Cancel
Save