Add change delegate ruleset with refactor (#235)

This commit is contained in:
Rinat Arsaev 2021-01-15 18:30:26 +03:00 committed by GitHub
parent f3c7bae9c9
commit 9c9b59602f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 954 additions and 537 deletions

69
package-lock.json generated
View File

@ -3760,13 +3760,13 @@
}
},
"@rbkmoney/angular-templates": {
"version": "0.1.2",
"resolved": "https://npm.pkg.github.com/download/@rbkmoney/angular-templates/0.1.2/03923ba6c89fd4ba87e9acf77f1074d3693dbcfdc0e775f707d0338b7ad4b997",
"integrity": "sha512-io9DKeEbTQ0/h1UVkl18KjqWg3I2BxwHqvbSTpR7xkJ9J4VKqXS92BB1JYpOinQzY8W7qAxVYDosGBazhRIftA==",
"version": "0.2.2",
"resolved": "https://npm.pkg.github.com/download/@rbkmoney/angular-templates/0.2.2/0cf7c24313ca0e4f6b12ca9d666e0f83dcb3ca373cd95e4b57b9df41087b2f8a",
"integrity": "sha512-8QgSANZyXixa2HmwjlSXgppaR9p2NTzfn7Gt5Q6mmP1jHrHCfaN+Cso6e9Z2xth9Wae4BPz/tWYCRSWF0aLfSA==",
"dev": true,
"requires": {
"genry": "^0.15.0",
"lodash": "^4.17.15"
"genry": "^0.16.0",
"lodash": "^4.17.20"
}
},
"@rbkmoney/partial-fetcher": {
@ -7669,9 +7669,9 @@
}
},
"easy-stack": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz",
"integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz",
"integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==",
"dev": true
},
"ecc-jsbn": {
@ -8933,9 +8933,9 @@
"dev": true
},
"genry": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/genry/-/genry-0.15.0.tgz",
"integrity": "sha512-XWjWNZzeXTKDtGsVyQSS6hnBlBDY/H7wMi0spj3bQv2yZ8+fVZy9M0TaS3W23NvRdNIZWx2iL6WGbLmqDQcNxA==",
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/genry/-/genry-0.16.0.tgz",
"integrity": "sha512-mgRduClrLDBS0JkaiFZBK2mP0ogFydKWIDMGmGOF2dS6YlbHa47ru6ZyYdx9AgUxFmWB1SFGTyahCRcAIK3gvg==",
"dev": true,
"requires": {
"cosmiconfig": "^6.0.0",
@ -8944,7 +8944,7 @@
"ora": "^4.0.3",
"pkg-up": "^3.1.0",
"prettier": "^2.0.4",
"prompts": "^2.3.2",
"prompts": "^2.4.0",
"ts-node": "^8.8.2",
"yargs": "^15.3.1"
},
@ -8956,12 +8956,11 @@
"dev": true
},
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
@ -9021,9 +9020,9 @@
"dev": true
},
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
"integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
"dev": true,
"requires": {
"parent-module": "^1.0.0",
@ -10459,18 +10458,18 @@
}
},
"js-message": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz",
"integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz",
"integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==",
"dev": true
},
"js-queue": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz",
"integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.2.tgz",
"integrity": "sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==",
"dev": true,
"requires": {
"easy-stack": "^1.0.0"
"easy-stack": "^1.0.1"
}
},
"js-sha256": {
@ -11919,14 +11918,14 @@
"integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs="
},
"node-ipc": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz",
"integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==",
"version": "9.1.3",
"resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.3.tgz",
"integrity": "sha512-8RS4RZyS/KMKKYG8mrje+cLxwATe9dBCuOiqKFSWND4oOuKytfuKCiR9yinvhoXF/nGdX/WnbywaUee+9U87zA==",
"dev": true,
"requires": {
"event-pubsub": "4.3.0",
"js-message": "1.0.5",
"js-queue": "2.0.0"
"js-message": "1.0.7",
"js-queue": "2.0.2"
}
},
"node-libs-browser": {
@ -13830,13 +13829,13 @@
}
},
"prompts": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz",
"integrity": "sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz",
"integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==",
"dev": true,
"requires": {
"kleur": "^3.0.3",
"sisteransi": "^1.0.4"
"sisteransi": "^1.0.5"
}
},
"protoduck": {

View File

@ -65,7 +65,7 @@
"@angular-devkit/build-angular": "^0.1000.6",
"@angular/cli": "^10.0.6",
"@angular/compiler-cli": "^10.0.10",
"@rbkmoney/angular-templates": "^0.1.2",
"@rbkmoney/angular-templates": "^0.2.2",
"@types/del": "^4.0.0",
"@types/glob": "^7.1.3",
"@types/humanize-duration": "~3.18.0",

View File

@ -35,11 +35,11 @@ import { SearchClaimsModule } from './sections/search-claims/search-claims.modul
import { SearchPartiesModule } from './sections/search-parties/search-parties.module';
import { SettingsModule } from './settings';
import { ThemeManager, ThemeManagerModule, ThemeName } from './theme-manager';
import { DEFAULT_DIALOG_CONFIG, DEFAULT_SEARCH_LIMIT, DIALOG_CONFIG, SEARCH_LIMIT } from './tokens';
/**
* For use in specific locations (for example, questionary PDF document)
*/
moment.locale('en');
@NgModule({
@ -78,6 +78,8 @@ moment.locale('en');
{ provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS },
{ provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] },
{ provide: MAT_DATE_LOCALE, useValue: 'en' },
{ provide: SEARCH_LIMIT, useValue: DEFAULT_SEARCH_LIMIT },
{ provide: DIALOG_CONFIG, useValue: DEFAULT_DIALOG_CONFIG },
],
bootstrap: [AppComponent],
})

View File

@ -9,6 +9,9 @@ import { DomainService as ThriftDomainService } from '../thrift-services/damsel/
import { DomainObject, Reference } from '../thrift-services/damsel/gen-model/domain';
import { Commit, Snapshot } from '../thrift-services/damsel/gen-model/domain_config';
/**
* @deprecated duplicates thrift-services/damsel/domain-cache.service
*/
@Injectable()
export class DomainService {
private shapshot$: Observable<Snapshot>;
@ -17,16 +20,25 @@ export class DomainService {
this.updateSnapshot();
}
/**
* @deprecated use DomainCacheService -> snapshot$
*/
get shapshot() {
return this.shapshot$;
}
/**
* @deprecated use DomainCacheService -> version$
*/
get version$(): Observable<number> {
return this.shapshot$.pipe(
map(({ version }) => (version ? version.toNumber() : undefined))
);
}
/**
* @deprecated use DomainCacheService -> getObjects or specific service from thrift-services/damsel
*/
getDomainObject(ref: Reference): Observable<DomainObject | null> {
return this.shapshot$.pipe(
map(({ domain }) => {
@ -42,10 +54,16 @@ export class DomainService {
);
}
/**
* @deprecated use DomainCacheService -> forceReload()
*/
updateSnapshot() {
return (this.shapshot$ = this.thriftDomainService.checkout(toGenReference()));
}
/**
* @deprecated use DomainCacheService -> commit()
*/
commit(commit: Commit) {
return this.shapshot$.pipe(
switchMap(({ version }) =>

View File

@ -0,0 +1,25 @@
<div fxLayout="column" fxLayoutGap="32px">
<div class="cc-headline">Change Delegate Ruleset</div>
<div [formGroup]="form" fxLayout="column" fxLayoutGap="24px">
<mat-form-field>
<mat-label>Delegate Ruleset</mat-label>
<mat-select formControlName="rulesetRefId" required>
<mat-option *ngFor="let i of rulesets$ | async" [value]="i.ref.id">
#{{ i.ref.id }} {{ i.data.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Deligate description" formControlName="description" />
</mat-form-field>
</div>
<div fxLayout fxLayoutAlign="space-between">
<button mat-button (click)="cancel()">CANCEL</button>
<button mat-button color="primary" (click)="changeRuleset()" [disabled]="form.invalid">
CHANGE RULESET
</button>
</div>
</div>

View File

@ -0,0 +1,36 @@
import { Component, DebugElement } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { ChangeDelegateRulesetDialogComponent } from './change-delegate-ruleset-dialog.component';
@Component({
selector: 'cc-host',
template: `<dsh-change-delegate-ruleset-dialog></dsh-change-delegate-ruleset-dialog>`,
})
class HostComponent {}
describe('ChangeDelegateRulesetDialogComponent', () => {
let fixture: ComponentFixture<HostComponent>;
let debugElement: DebugElement;
let component: ChangeDelegateRulesetDialogComponent;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [],
declarations: [HostComponent, ChangeDelegateRulesetDialogComponent],
}).compileComponents();
fixture = TestBed.createComponent(HostComponent);
debugElement = fixture.debugElement.query(
By.directive(ChangeDelegateRulesetDialogComponent)
);
component = debugElement.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,60 @@
import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { map } from 'rxjs/operators';
import { RoutingRulesService } from '../../../thrift-services';
@UntilDestroy()
@Component({
selector: 'cc-change-delegate-ruleset-dialog',
templateUrl: 'change-delegate-ruleset-dialog.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ChangeDelegateRulesetDialogComponent implements OnInit {
form = this.fb.group({
rulesetRefId: [],
description: '',
});
rulesets$ = this.routingRulesService.rulesets$;
constructor(
private fb: FormBuilder,
private dialogRef: MatDialogRef<ChangeDelegateRulesetDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: { mainRulesetRefID: number; delegateIdx: number },
private routingRulesService: RoutingRulesService
) {}
ngOnInit() {
this.routingRulesService
.getRuleset(this.data.mainRulesetRefID)
.pipe(
map((r) => r?.data?.decisions?.delegates?.[this.data?.delegateIdx]),
untilDestroyed(this)
)
.subscribe((delegate) => {
this.form.patchValue({
rulesetRefId: delegate?.ruleset?.id,
description: delegate?.description,
});
});
}
cancel() {
this.dialogRef.close();
}
changeRuleset() {
this.routingRulesService
.changeDelegateRuleset({
mainRulesetRefID: this.data.mainRulesetRefID,
delegateIdx: this.data.delegateIdx,
newDelegateRulesetRefID: this.form.value.rulesetRefId,
description: this.form.value.description,
})
.pipe(untilDestroyed(this))
.subscribe(() => this.dialogRef.close());
}
}

View File

@ -0,0 +1,25 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FlexLayoutModule } from '@angular/flex-layout';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { ChangeDelegateRulesetDialogComponent } from './change-delegate-ruleset-dialog.component';
@NgModule({
imports: [
CommonModule,
FlexLayoutModule,
ReactiveFormsModule,
FormsModule,
MatInputModule,
MatButtonModule,
MatSelectModule,
],
declarations: [ChangeDelegateRulesetDialogComponent],
exports: [ChangeDelegateRulesetDialogComponent],
providers: [],
})
export class ChangeDelegateRulesetDialogModule {}

View File

@ -0,0 +1,2 @@
export * from './change-delegate-ruleset-dialog.component';
export * from './change-delegate-ruleset-dialog.module';

View File

@ -1,5 +1,5 @@
<div fxLayout="column" fxLayoutGap="32px">
<div class="cc-headline">Change party delegate ruleset</div>
<div class="cc-headline">Change main ruleset</div>
<cc-target-ruleset-form
(valueChanges)="targetRuleset$.next($event)"

View File

@ -1,8 +1,8 @@
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
import { MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { BehaviorSubject } from 'rxjs';
import { PaymentRoutingRulesService } from 'src/app/thrift-services';
import { RoutingRulesService } from 'src/app/thrift-services';
import { ErrorService } from '../../../shared/services/error';
import { TargetRuleset } from '../target-ruleset-form';
@ -13,45 +13,37 @@ import { TargetRuleset } from '../target-ruleset-form';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ChangeTargetDialogComponent {
static defaultConfig: MatDialogConfig = {
disableClose: true,
width: '548px',
maxHeight: '90vh',
};
targetRuleset$ = new BehaviorSubject<TargetRuleset>(undefined);
targetRulesetValid$ = new BehaviorSubject<boolean>(undefined);
initValue: Partial<TargetRuleset> = {};
constructor(
private dialogRef: MatDialogRef<ChangeTargetDialogComponent>,
private paymentRoutingRulesService: PaymentRoutingRulesService,
@Inject(MAT_DIALOG_DATA)
public data: { mainRulesetRefID: number; rulesetID: number },
private routingRulesService: RoutingRulesService,
@Inject(MAT_DIALOG_DATA) public data: { mainRulesetRefID: number; delegateIdx: number },
private errorService: ErrorService
) {
this.paymentRoutingRulesService
this.routingRulesService
.getRuleset(data?.mainRulesetRefID)
.pipe(untilDestroyed(this))
.subscribe((ruleset) => {
this.initValue = {
mainRulesetRefID: ruleset.ref.id,
mainDelegateDescription: ruleset?.data?.decisions?.delegates?.find(
(d) => d?.ruleset?.id === data?.rulesetID
)?.description,
mainDelegateDescription:
ruleset?.data?.decisions?.delegates?.[data?.delegateIdx]?.description,
};
});
}
changeTarget() {
const { mainRulesetRefID, mainDelegateDescription } = this.targetRuleset$.value;
const { mainRulesetRefID: previousMainRulesetRefID, rulesetID } = this.data;
this.paymentRoutingRulesService
.changeDelegateRuleset({
const { mainRulesetRefID: previousMainRulesetRefID, delegateIdx } = this.data;
this.routingRulesService
.changeMainRuleset({
previousMainRulesetRefID,
mainRulesetRefID,
mainDelegateDescription,
rulesetID,
delegateIdx,
})
.pipe(untilDestroyed(this))
.subscribe(() => this.dialogRef.close(), this.errorService.error);

View File

@ -1,9 +1,9 @@
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { BehaviorSubject } from 'rxjs';
import { PaymentRoutingRulesService } from 'src/app/thrift-services';
import { RoutingRulesService } from 'src/app/thrift-services';
import { ErrorService } from '../../../../shared/services/error';
import { TargetRuleset } from '../../target-ruleset-form';
@ -14,12 +14,6 @@ import { TargetRuleset } from '../../target-ruleset-form';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AttachNewRulesetDialogComponent {
static defaultConfig: MatDialogConfig = {
disableClose: true,
width: '548px',
maxHeight: '90vh',
};
form = this.fb.group({
ruleset: this.fb.group({
name: 'submain ruleset[by shop id]',
@ -33,7 +27,7 @@ export class AttachNewRulesetDialogComponent {
constructor(
private fb: FormBuilder,
private dialogRef: MatDialogRef<AttachNewRulesetDialogComponent>,
private paymentRoutingRulesService: PaymentRoutingRulesService,
private paymentRoutingRulesService: RoutingRulesService,
@Inject(MAT_DIALOG_DATA) public data: { partyID: string },
private errorService: ErrorService
) {}

View File

@ -2,7 +2,7 @@
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</ng-container>
<ng-template #loaded>
<div fxLayout="column" [fxLayoutGap]="(dataSource$ | async)?.data?.length ? '24px' : '64px'">
<div fxLayout="column" [fxLayoutGap]="(data$ | async)?.length ? '24px' : '64px'">
<div fxLayout fxLayoutGap="8px" fxLayoutAlign="space-between center">
<div class="cc-headline">Party delegate rulesets</div>
<button mat-button color="primary" (click)="attachNewRuleset()">
@ -10,80 +10,10 @@
</button>
</div>
<mat-card *ngIf="(dataSource$ | async)?.data?.length; else emptyPartyDelegateRulesets">
<mat-card-content fxLayout="column">
<table mat-table [dataSource]="dataSource$ | async" fxFlex>
<ng-container matColumnDef="paymentInstitution">
<th mat-header-cell *matHeaderCellDef>Payment institution</th>
<td mat-cell *matCellDef="let element">
{{ element?.paymentInstitution?.data?.name }}
</td>
</ng-container>
<ng-container matColumnDef="mainRuleset">
<th mat-header-cell *matHeaderCellDef>Main Ruleset</th>
<td mat-cell *matCellDef="let element">
{{ element?.mainRuleset?.data?.name }}
</td>
</ng-container>
<ng-container matColumnDef="partyDelegate">
<th mat-header-cell *matHeaderCellDef>Party delegate</th>
<td mat-cell *matCellDef="let element">
{{ element?.partyDelegate?.description }}
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef width="1px"></th>
<td mat-cell *matCellDef="let element">
<button mat-icon-button [matMenuTriggerFor]="menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button
mat-menu-item
(click)="
navigateToPartyRuleset(element?.partyDelegate?.ruleset?.id)
"
>
Details
</button>
<button
mat-menu-item
(click)="
changeTarget(
element?.mainRuleset?.ref?.id,
element?.partyDelegate?.ruleset?.id
)
"
>
Change target
</button>
<button
mat-menu-item
(click)="
deleteRuleset(
element?.mainRuleset?.ref?.id,
element?.partyDelegate?.ruleset?.id
)
"
>
Delete
</button>
</mat-menu>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<mat-paginator
[pageSizeOptions]="[10, 20, 50, 100, 250, 500]"
showFirstLastButtons
></mat-paginator>
</mat-card-content>
</mat-card>
<ng-template #emptyPartyDelegateRulesets>
<div class="cc-display-1" fxFlexAlign="center">Party delegate rulesets not found</div>
</ng-template>
<cc-routing-rules-list
[data]="data$ | async"
[displayedColumns]="displayedColumns"
(toDetails)="navigateToPartyRuleset($event.parentRefId, $event.delegateIdx)"
></cc-routing-rules-list>
</div>
</ng-template>

View File

@ -1,19 +1,15 @@
import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core';
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { combineLatest, ReplaySubject } from 'rxjs';
import { filter, first, map, shareReplay, startWith, switchMap, take } from 'rxjs/operators';
import { ConfirmActionDialogComponent } from '@cc/components/confirm-action-dialog';
import { combineLatest } from 'rxjs';
import { first, map, switchMap, take } from 'rxjs/operators';
import { handleError } from '../../../../utils/operators/handle-error';
import { ErrorService } from '../../../shared/services/error';
import { PaymentRoutingRulesService } from '../../../thrift-services';
import { RoutingRulesService } from '../../../thrift-services';
import { DomainCacheService } from '../../../thrift-services/damsel/domain-cache.service';
import { ChangeTargetDialogComponent } from '../change-target-dialog';
import { DialogConfig, DIALOG_CONFIG } from '../../../tokens';
import { AttachNewRulesetDialogComponent } from './attach-new-ruleset-dialog';
import { PartyDelegateRulesetsService } from './party-delegate-rulesets.service';
@ -25,32 +21,43 @@ import { PartyDelegateRulesetsService } from './party-delegate-rulesets.service'
providers: [PartyDelegateRulesetsService],
})
export class PartyDelegateRulesetsComponent {
displayedColumns = ['paymentInstitution', 'mainRuleset', 'partyDelegate', 'actions'];
displayedColumns = [
{ key: 'paymentInstitution', name: 'Payment institution' },
{ key: 'mainRuleset', name: 'Main ruleset' },
{ key: 'partyDelegate', name: 'Party delegate' },
];
isLoading$ = this.domainService.isLoading$;
@ViewChild(MatPaginator) set paginator(paginator: MatPaginator) {
this.paginator$.next(paginator);
}
paginator$ = new ReplaySubject<MatPaginator>(1);
dataSource$ = combineLatest([
this.partyDelegateRulesetsService.partyDelegateRulesets$,
this.paginator$.pipe(startWith<any, null>(null)),
]).pipe(
map(([v, paginator]) => {
const data = new MatTableDataSource(v);
data.paginator = paginator;
return data;
}),
shareReplay(1)
data$ = this.partyDelegateRulesetsService.getDelegatesWithPaymentInstitution().pipe(
map((rules) =>
rules.map(({ mainRoutingRule, partyDelegate, paymentInstitution }) => ({
parentRefId: mainRoutingRule?.ref?.id,
delegateIdx: mainRoutingRule?.data?.decisions?.delegates?.findIndex(
(d) => d === partyDelegate
),
paymentInstitution: {
text: paymentInstitution?.data?.name,
caption: paymentInstitution?.ref?.id,
},
mainRuleset: {
text: mainRoutingRule?.data?.name,
caption: mainRoutingRule?.ref?.id,
},
partyDelegate: {
text: partyDelegate?.description,
caption: partyDelegate?.ruleset?.id,
},
}))
)
);
constructor(
private partyDelegateRulesetsService: PartyDelegateRulesetsService,
private paymentRoutingRulesService: PaymentRoutingRulesService,
private paymentRoutingRulesService: RoutingRulesService,
private router: Router,
private dialog: MatDialog,
private domainService: DomainCacheService,
private errorService: ErrorService
private errorService: ErrorService,
@Inject(DIALOG_CONFIG) private dialogConfig: DialogConfig
) {}
attachNewRuleset() {
@ -60,7 +67,7 @@ export class PartyDelegateRulesetsComponent {
switchMap((partyID) =>
this.dialog
.open(AttachNewRulesetDialogComponent, {
...AttachNewRulesetDialogComponent.defaultConfig,
...this.dialogConfig.medium,
data: { partyID },
})
.afterClosed()
@ -71,40 +78,19 @@ export class PartyDelegateRulesetsComponent {
.subscribe();
}
navigateToPartyRuleset(id: string) {
this.partyDelegateRulesetsService.partyID$
navigateToPartyRuleset(parentRefId: number, delegateIdx: number) {
combineLatest([
this.partyDelegateRulesetsService.partyID$,
this.paymentRoutingRulesService.getRuleset(parentRefId),
])
.pipe(first(), untilDestroyed(this))
.subscribe((partyID) =>
this.router.navigate(['party', partyID, 'payment-routing-rules', id])
.subscribe(([partyID, parent]) =>
this.router.navigate([
'party',
partyID,
'payment-routing-rules',
parent.data.decisions.delegates[delegateIdx].ruleset.id,
])
);
}
changeTarget(mainRulesetRefID: string, rulesetID: string) {
this.dialog
.open(ChangeTargetDialogComponent, {
...ChangeTargetDialogComponent.defaultConfig,
data: { mainRulesetRefID, rulesetID },
})
.afterClosed()
.pipe(handleError(this.errorService.error), untilDestroyed(this))
.subscribe();
}
deleteRuleset(mainRulesetRefID: number, rulesetRefID: number) {
this.dialog
.open(ConfirmActionDialogComponent)
.afterClosed()
.pipe(
filter((r) => r === 'confirm'),
switchMap(() =>
this.paymentRoutingRulesService.deleteDelegate({
mainRulesetRefID,
rulesetRefID,
})
),
handleError(this.errorService.error),
untilDestroyed(this)
)
.subscribe();
}
}

View File

@ -13,7 +13,6 @@ import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatRadioModule } from '@angular/material/radio';
import { MatSelectModule } from '@angular/material/select';
import { MatTableModule } from '@angular/material/table';
import { RouterModule } from '@angular/router';
import { DetailsItemModule } from '@cc/components/details-item';
@ -21,6 +20,7 @@ import { DetailsItemModule } from '@cc/components/details-item';
import { ErrorModule } from '../../../shared/services/error';
import { ChangeTargetDialogModule } from '../change-target-dialog';
import { PaymentRoutingRulesetHeaderModule } from '../payment-routing-ruleset-header';
import { RoutingRulesListModule } from '../routing-rules-list';
import { TargetRulesetFormModule } from '../target-ruleset-form';
import { AttachNewRulesetDialogComponent } from './attach-new-ruleset-dialog';
import { PartyDelegateRulesetsRoutingModule } from './party-delegate-rulesets-routing.module';
@ -37,7 +37,6 @@ const EXPORTED_DECLARATIONS = [PartyDelegateRulesetsComponent, AttachNewRulesetD
CommonModule,
RouterModule,
MatCardModule,
MatTableModule,
MatIconModule,
MatPaginatorModule,
MatMenuModule,
@ -52,6 +51,7 @@ const EXPORTED_DECLARATIONS = [PartyDelegateRulesetsComponent, AttachNewRulesetD
ErrorModule,
ChangeTargetDialogModule,
TargetRulesetFormModule,
RoutingRulesListModule,
],
declarations: EXPORTED_DECLARATIONS,
exports: EXPORTED_DECLARATIONS,

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { combineLatest } from 'rxjs';
import { map, pluck, shareReplay, startWith } from 'rxjs/operators';
import { map, pluck, startWith, switchMap } from 'rxjs/operators';
import { DomainCacheService } from 'src/app/thrift-services/damsel/domain-cache.service';
import {
PaymentInstitutionObject,
@ -9,68 +9,67 @@ import {
RoutingRulesObject,
} from 'src/app/thrift-services/damsel/gen-model/domain';
import { PaymentRoutingRulesService } from '../../../thrift-services';
import { RoutingRulesService } from '../../../thrift-services';
@Injectable()
export class PartyDelegateRulesetsService {
partyID$ = this.route.params.pipe(startWith(this.route.snapshot.params), pluck('partyID'));
partyDelegateRulesets$ = combineLatest([
this.domainService.getObjects('payment_institution'),
this.paymentRoutingRulesService.rulesets$,
this.partyID$,
]).pipe(
map(([institutions, rules, partyID]) => {
const rulesetsWithInstitution = institutions
.map(
(i) =>
[
rules.find(
(r) => r?.ref?.id === i?.data?.payment_routing_rules?.policies?.id
),
i,
] as const
)
.filter(([r]) => r);
const partyDelegateRulesets = rulesetsWithInstitution
.map(
([r, i]) =>
[
r,
i,
r?.data?.decisions?.delegates
?.map((d) =>
d?.allowed?.condition?.party?.id === partyID ? d : undefined
)
?.filter((d) => d),
] as const
)
.filter(([, , d]) => d?.length)
.reduce(
(acc, [r, i, d]) => {
acc.push(
...d.map((partyDelegate) => ({
partyDelegate,
paymentInstitution: i,
mainRuleset: r,
}))
);
return acc;
},
[] as {
partyDelegate: RoutingDelegate;
paymentInstitution: PaymentInstitutionObject;
mainRuleset: RoutingRulesObject;
}[]
);
return partyDelegateRulesets;
}),
shareReplay(1)
);
constructor(
private domainService: DomainCacheService,
private route: ActivatedRoute,
private paymentRoutingRulesService: PaymentRoutingRulesService
private paymentRoutingRulesService: RoutingRulesService
) {}
getDelegatesWithPaymentInstitution() {
return combineLatest([this.getPaymentInstitutionsWithRoutingRule(), this.partyID$]).pipe(
map(([paymentInstitutionsWithRoutingRule, partyID]) =>
paymentInstitutionsWithRoutingRule
.map(({ routingRule: mainRoutingRule, paymentInstitution }) => ({
mainRoutingRule,
paymentInstitution,
delegates: mainRoutingRule?.data?.decisions?.delegates
?.map((d) =>
d?.allowed?.condition?.party?.id === partyID ? d : undefined
)
?.filter((d) => d),
}))
.filter(({ delegates }) => delegates?.length)
.reduce<
{
partyDelegate: RoutingDelegate;
paymentInstitution: PaymentInstitutionObject;
mainRoutingRule: RoutingRulesObject;
}[]
>(
(acc, { delegates, ...rest }) => [
...acc,
...delegates.map((partyDelegate) => ({ ...rest, partyDelegate })),
],
[]
)
)
);
}
private getPaymentInstitutionsWithRoutingRule() {
return this.domainService.getObjects('payment_institution').pipe(
switchMap((paymentInstitutions) =>
combineLatest(
paymentInstitutions.map((paymentInstitution) =>
this.paymentRoutingRulesService
.getRuleset(
paymentInstitution?.data?.payment_routing_rules?.policies?.id
)
.pipe(
map((routingRule) => ({
paymentInstitution,
routingRule,
}))
)
)
)
)
);
}
}

View File

@ -4,7 +4,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { ErrorService } from '../../../../shared/services/error';
import { PaymentRoutingRulesService } from '../../../../thrift-services';
import { RoutingRulesService } from '../../../../thrift-services';
import { Shop } from '../../../../thrift-services/damsel/gen-model/domain';
@UntilDestroy()
@ -21,7 +21,7 @@ export class AddPartyPaymentRoutingRuleDialogComponent {
constructor(
private fb: FormBuilder,
private dialogRef: MatDialogRef<AddPartyPaymentRoutingRuleDialogComponent>,
private paymentRoutingRulesService: PaymentRoutingRulesService,
private paymentRoutingRulesService: RoutingRulesService,
@Inject(MAT_DIALOG_DATA)
public data: { refID: number; partyID: string; shops: Shop[] },
private errorService: ErrorService

View File

@ -4,7 +4,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { ErrorService } from '../../../../shared/services/error';
import { PaymentRoutingRulesService } from '../../../../thrift-services';
import { RoutingRulesService } from '../../../../thrift-services';
@UntilDestroy()
@Component({
@ -21,7 +21,7 @@ export class InitializePaymentRoutingRulesDialogComponent {
constructor(
private fb: FormBuilder,
private dialogRef: MatDialogRef<InitializePaymentRoutingRulesDialogComponent>,
private paymentRoutingRulesService: PaymentRoutingRulesService,
private paymentRoutingRulesService: RoutingRulesService,
@Inject(MAT_DIALOG_DATA) public data: { partyID: string; refID: number },
private errorService: ErrorService
) {}

View File

@ -15,57 +15,11 @@
Party payment routing rules
</cc-payment-routing-ruleset-header>
<mat-card *ngIf="(dataSource$ | async)?.data?.length; else emptyPartyRuleset">
<mat-card-content fxLayout="column">
<table mat-table [dataSource]="dataSource$ | async" fxFlex>
<ng-container matColumnDef="shop">
<th mat-header-cell *matHeaderCellDef width="50%">Shop</th>
<td mat-cell *matCellDef="let element">
<div fxLayout="column">
<div class="cc-body-1">{{ element.shop.details?.name }}</div>
<div class="cc-caption">{{ element.shop.id }}</div>
</div>
</td>
</ng-container>
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef width="50%">Ruleset Ref ID</th>
<td mat-cell *matCellDef="let element">{{ element.id }}</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let element">
<button mat-icon-button [matMenuTriggerFor]="menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item (click)="navigateToShopRuleset(element.id)">
Details
</button>
<button mat-menu-item (click)="changeTarget(element.id)">
Change target
</button>
<button mat-menu-item (click)="deleteRuleset(element.id)">
Delete
</button>
</mat-menu>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<mat-paginator
[pageSizeOptions]="[10, 20, 50, 100, 250, 500]"
showFirstLastButtons
></mat-paginator>
</mat-card-content>
</mat-card>
<ng-template #emptyPartyRuleset>
<div class="cc-display-1" fxFlexAlign="center">
Party payment routing rules is empty
</div>
</ng-template>
<cc-routing-rules-list
[data]="data$ | async"
[displayedColumns]="displayedColumns"
(toDetails)="navigateToShopRuleset($event.parentRefId, $event.delegateIdx)"
></cc-routing-rules-list>
</div>
<ng-template #emptyPartyDelegate>
@ -75,7 +29,7 @@
fxLayoutAlign=" center"
fxLayoutGap="24px"
>
<div class="cc-display-1">Payment rules is not found</div>
<div class="cc-display-1">Payment rules not found</div>
<button mat-raised-button color="primary" (click)="initialize()" class="init">
INITIALIZE
</button>

View File

@ -1,25 +1,16 @@
import { Component, ViewChild } from '@angular/core';
import { Component, Inject } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { combineLatest, ReplaySubject } from 'rxjs';
import { filter, map, shareReplay, switchMap, take, withLatestFrom } from 'rxjs/operators';
import { combineLatest } from 'rxjs';
import { filter, map, shareReplay, switchMap, take } from 'rxjs/operators';
import { ConfirmActionDialogComponent } from '@cc/components/confirm-action-dialog';
import { handleError } from '../../../../utils/operators/handle-error';
import { ErrorService } from '../../../shared/services/error';
import { PaymentRoutingRulesService } from '../../../thrift-services';
import { DomainCacheService } from '../../../thrift-services/damsel/domain-cache.service';
import { ChangeTargetDialogComponent } from '../change-target-dialog';
import { DialogConfig, DIALOG_CONFIG } from '../../../tokens';
import { AddPartyPaymentRoutingRuleDialogComponent } from './add-party-payment-routing-rule-dialog';
import { InitializePaymentRoutingRulesDialogComponent } from './initialize-payment-routing-rules-dialog';
import { PartyPaymentRoutingRulesetService } from './party-payment-routing-ruleset.service';
const DIALOG_WIDTH = '548px';
@UntilDestroy()
@Component({
selector: 'cc-party-payment-routing-ruleset',
@ -30,44 +21,44 @@ const DIALOG_WIDTH = '548px';
export class PaymentRoutingRulesComponent {
partyRuleset$ = this.partyPaymentRoutingRulesetService.partyRuleset$;
partyID$ = this.partyPaymentRoutingRulesetService.partyID$;
displayedColumns = ['shop', 'id', 'actions'];
isLoading$ = this.domainService.isLoading$;
@ViewChild(MatPaginator) set paginator(paginator: MatPaginator) {
this.paginator$.next(paginator);
}
paginator$ = new ReplaySubject<MatPaginator>(1);
dataSource$ = combineLatest([
this.partyRuleset$,
this.partyPaymentRoutingRulesetService.shops$,
this.paginator$,
]).pipe(
displayedColumns = [
{ key: 'shop', name: 'Shop' },
{ key: 'id', name: 'Delegate (Ruleset Ref ID)' },
];
data$ = combineLatest([this.partyRuleset$, this.partyPaymentRoutingRulesetService.shops$]).pipe(
filter(([r]) => !!r),
map(([ruleset, shops, paginator]) => {
const data = new MatTableDataSource(
ruleset.data.decisions.delegates
.filter((d) => d?.allowed?.condition?.party?.definition?.shop_is)
.map((d) => {
const shopId = d.allowed.condition.party.definition.shop_is;
return {
id: d.ruleset.id,
shop: shops.find((s) => s.id === shopId) || { id: shopId },
};
})
);
data.paginator = paginator;
return data;
}),
map(([ruleset, shops]) =>
ruleset.data.decisions.delegates
.filter((d) => d?.allowed?.condition?.party?.definition?.shop_is)
.map((delegate) => {
const shopId = delegate.allowed.condition.party.definition.shop_is;
return {
parentRefId: ruleset.ref.id,
delegateIdx: ruleset.data.decisions.delegates.findIndex(
(d) => d === delegate
),
id: {
text: delegate?.description,
caption: delegate?.ruleset?.id,
},
shop: {
text: shops?.find((s) => s?.id === shopId)?.details?.name,
caption: shopId,
},
};
})
),
shareReplay(1)
);
constructor(
private dialog: MatDialog,
private partyPaymentRoutingRulesetService: PartyPaymentRoutingRulesetService,
private paymentRoutingRulesService: PaymentRoutingRulesService,
private router: Router,
private domainService: DomainCacheService,
private errorService: ErrorService
@Inject(DIALOG_CONFIG) private dialogConfig: DialogConfig
) {}
initialize() {
@ -80,9 +71,7 @@ export class PaymentRoutingRulesComponent {
switchMap(([partyID, refID]) =>
this.dialog
.open(InitializePaymentRoutingRulesDialogComponent, {
disableClose: true,
width: DIALOG_WIDTH,
maxHeight: '90vh',
...this.dialogConfig.medium,
data: { partyID, refID },
})
.afterClosed()
@ -103,9 +92,7 @@ export class PaymentRoutingRulesComponent {
switchMap(([refID, shops, partyID]) =>
this.dialog
.open(AddPartyPaymentRoutingRuleDialogComponent, {
disableClose: true,
width: DIALOG_WIDTH,
maxHeight: '90vh',
...this.dialogConfig.medium,
data: { refID, shops, partyID },
})
.afterClosed()
@ -115,58 +102,21 @@ export class PaymentRoutingRulesComponent {
.subscribe();
}
deleteRuleset(rulesetRefID: number) {
this.dialog
.open(ConfirmActionDialogComponent)
.afterClosed()
.pipe(
filter((r) => r === 'confirm'),
withLatestFrom(this.partyRuleset$),
switchMap(([, mainRuleset]) =>
this.paymentRoutingRulesService.deleteDelegate({
mainRulesetRefID: mainRuleset.ref.id,
rulesetRefID,
})
),
handleError(this.errorService.error),
untilDestroyed(this)
)
.subscribe();
}
navigateToShopRuleset(refID: string) {
navigateToShopRuleset(parentRefId: number, delegateIdx: number) {
combineLatest([
this.partyPaymentRoutingRulesetService.partyID$,
this.partyPaymentRoutingRulesetService.refID$,
this.partyPaymentRoutingRulesetService.partyRuleset$,
])
.pipe(take(1), untilDestroyed(this))
.subscribe(([partyID, partyRefID]) =>
.subscribe(([partyID, ruleset]) =>
this.router.navigate([
'party',
partyID,
'payment-routing-rules',
partyRefID,
parentRefId,
'shop-ruleset',
refID,
ruleset?.data?.decisions?.delegates?.[delegateIdx]?.ruleset?.id,
])
);
}
changeTarget(rulesetID: string) {
this.partyRuleset$
.pipe(
take(1),
switchMap((mainRuleset) =>
this.dialog
.open(ChangeTargetDialogComponent, {
...ChangeTargetDialogComponent.defaultConfig,
data: { mainRulesetRefID: mainRuleset.ref.id, rulesetID },
})
.afterClosed()
),
handleError(this.errorService.error),
untilDestroyed(this)
)
.subscribe();
}
}

View File

@ -22,6 +22,7 @@ import { ErrorModule } from '../../../shared/services/error';
import { DamselModule } from '../../../thrift-services';
import { ChangeTargetDialogModule } from '../change-target-dialog';
import { PaymentRoutingRulesetHeaderModule } from '../payment-routing-ruleset-header';
import { RoutingRulesListModule } from '../routing-rules-list';
import { AddPartyPaymentRoutingRuleDialogModule } from './add-party-payment-routing-rule-dialog';
import { InitializePaymentRoutingRulesDialogModule } from './initialize-payment-routing-rules-dialog';
import { PartyPaymentRoutingRulesetRoutingModule } from './party-payment-routing-ruleset-routing.module';
@ -54,6 +55,7 @@ import { PaymentRoutingRulesComponent } from './party-payment-routing-ruleset.co
MatProgressBarModule,
ErrorModule,
ChangeTargetDialogModule,
RoutingRulesListModule,
],
declarations: [PaymentRoutingRulesComponent],
})

View File

@ -0,0 +1,2 @@
export * from './routing-rules-list.component';
export * from './routing-rules-list.module';

View File

@ -0,0 +1,72 @@
<mat-card *ngIf="(dataSource$ | async)?.data?.length; else empty">
<mat-card-content fxLayout="column">
<table mat-table [dataSource]="dataSource$ | async">
<ng-container [matColumnDef]="column.key" *ngFor="let column of displayedColumns">
<th mat-header-cell *matHeaderCellDef>{{ column.name }}</th>
<td mat-cell *matCellDef="let element">
<div
fxLayout="column"
*ngIf="
element[column.key]?.text || element[column.key]?.caption;
else onlyBody
"
>
<div class="cc-body-1">{{ element[column.key]?.text || '&nbsp;' }}</div>
<div class="cc-caption">
{{ element[column.key]?.caption || '&nbsp;' }}
</div>
</div>
<ng-template #onlyBody>
{{ element[column.key] }}
</ng-template>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef width="1px"></th>
<td mat-cell *matCellDef="let element">
<button mat-icon-button [matMenuTriggerFor]="menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button
mat-menu-item
(click)="
toDetails.emit({
parentRefId: element?.parentRefId,
delegateIdx: element?.delegateIdx
})
"
>
Details
</button>
<button mat-menu-item (click)="changeDelegateRuleset(element)">
Change delegate ruleset
</button>
<button mat-menu-item (click)="changeTarget(element)">
Change main ruleset
</button>
<button mat-menu-item (click)="cloneDelegateRuleset(element)">
Clone delegate ruleset
</button>
<button mat-menu-item (click)="delete(element)">
Delete
</button>
</mat-menu>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="allDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: allDisplayedColumns"></tr>
</table>
<mat-paginator
[pageSizeOptions]="[10, 20, 50, 100, 250, 500]"
showFirstLastButtons
></mat-paginator>
</mat-card-content>
</mat-card>
<ng-template #empty>
<div class="cc-display-1" fxLayout="column" fxLayoutAlign=" center">
Routing rules not found
</div>
</ng-template>

View File

@ -0,0 +1,34 @@
import { Component, DebugElement } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { RoutingRulesListComponent } from './routing-rules-list.component';
@Component({
selector: 'cc-host',
template: `<dsh-routing-rules-list></dsh-routing-rules-list>`,
})
class HostComponent {}
describe('RoutingRulesListComponent', () => {
let fixture: ComponentFixture<HostComponent>;
let debugElement: DebugElement;
let component: RoutingRulesListComponent;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [],
declarations: [HostComponent, RoutingRulesListComponent],
}).compileComponents();
fixture = TestBed.createComponent(HostComponent);
debugElement = fixture.debugElement.query(By.directive(RoutingRulesListComponent));
component = debugElement.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,149 @@
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Inject,
Input,
Output,
ViewChild,
} from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { combineLatest, ReplaySubject } from 'rxjs';
import { filter, map, shareReplay, startWith, switchMap } from 'rxjs/operators';
import { ConfirmActionDialogComponent } from '../../../../components/confirm-action-dialog';
import { handleError } from '../../../../utils/operators/handle-error';
import { ErrorService } from '../../../shared/services/error';
import { RoutingRulesService } from '../../../thrift-services';
import { DialogConfig, DIALOG_CONFIG } from '../../../tokens';
import { ChangeDelegateRulesetDialogComponent } from '../change-delegate-ruleset-dialog';
import { ChangeTargetDialogComponent } from '../change-target-dialog';
type DelegateId = {
parentRefId: number;
delegateIdx: number;
};
@UntilDestroy()
@Component({
selector: 'cc-routing-rules-list',
templateUrl: 'routing-rules-list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RoutingRulesListComponent<T extends { [N in PropertyKey]: any } & DelegateId = any> {
@Input() displayedColumns: { key: keyof T; name: string }[];
@Input() set data(data: T[]) {
this.data$.next(data);
}
private data$ = new ReplaySubject<T[]>(1);
@Output() toDetails = new EventEmitter<DelegateId>();
@ViewChild(MatPaginator) set paginator(paginator: MatPaginator) {
this.paginator$.next(paginator);
}
private paginator$ = new ReplaySubject<MatPaginator>(1);
dataSource$ = combineLatest([
this.data$,
this.paginator$.pipe(startWith<any, null>(null)),
]).pipe(
map(([d, paginator]) => {
const data = new MatTableDataSource(d);
data.paginator = paginator;
return data;
}),
shareReplay(1)
);
get allDisplayedColumns() {
if (!this.displayedColumns) {
return [];
}
return this.displayedColumns
.concat([
{
key: 'actions',
name: 'Actions',
},
])
.map(({ key }) => key);
}
constructor(
private dialog: MatDialog,
private errorService: ErrorService,
private routingRulesService: RoutingRulesService,
@Inject(DIALOG_CONFIG) private dialogConfig: DialogConfig
) {}
getColumnsKeys(col) {
return col.key;
}
changeDelegateRuleset(delegateId: DelegateId) {
this.dialog
.open(ChangeDelegateRulesetDialogComponent, {
...this.dialogConfig.medium,
data: {
mainRulesetRefID: delegateId.parentRefId,
delegateIdx: delegateId.delegateIdx,
},
})
.afterClosed()
.pipe(handleError(this.errorService.error), untilDestroyed(this))
.subscribe();
}
changeTarget(delegateId: DelegateId) {
this.dialog
.open(ChangeTargetDialogComponent, {
...this.dialogConfig.medium,
data: {
mainRulesetRefID: delegateId.parentRefId,
delegateIdx: delegateId.delegateIdx,
},
})
.afterClosed()
.pipe(untilDestroyed(this))
.subscribe({ error: this.errorService.error });
}
cloneDelegateRuleset(delegateId: DelegateId) {
this.dialog
.open(ConfirmActionDialogComponent, { data: { title: 'Clone delegate ruleset' } })
.afterClosed()
.pipe(
filter((r) => r === 'confirm'),
switchMap(() =>
this.routingRulesService.cloneDelegateRuleset({
mainRulesetRefID: delegateId.parentRefId,
delegateIdx: delegateId.delegateIdx,
})
),
untilDestroyed(this)
)
.subscribe({ error: this.errorService.error });
}
delete(delegateId: DelegateId) {
this.dialog
.open(ConfirmActionDialogComponent, { data: { title: 'Delete delegate' } })
.afterClosed()
.pipe(
filter((r) => r === 'confirm'),
switchMap(() =>
this.routingRulesService.deleteDelegate({
mainRulesetRefID: delegateId.parentRefId,
delegateIdx: delegateId.delegateIdx,
})
),
untilDestroyed(this)
)
.subscribe({ error: this.errorService.error });
}
}

View File

@ -0,0 +1,28 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatTableModule } from '@angular/material/table';
import { RoutingRulesListComponent } from './routing-rules-list.component';
@NgModule({
imports: [
CommonModule,
MatMenuModule,
MatCardModule,
MatTableModule,
MatPaginatorModule,
MatTableModule,
MatIconModule,
FlexLayoutModule,
MatButtonModule,
],
declarations: [RoutingRulesListComponent],
exports: [RoutingRulesListComponent],
})
export class RoutingRulesListModule {}

View File

@ -4,7 +4,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { of } from 'rxjs';
import { startWith, switchMap, take } from 'rxjs/operators';
import { DomainTypedManager, PaymentRoutingRulesService } from '../../../../thrift-services';
import { RoutingRulesService, TerminalService } from '../../../../thrift-services';
import { Predicate } from '../../../../thrift-services/damsel/gen-model/domain';
import { AddShopPaymentRoutingRuleDialogComponent } from './add-shop-payment-routing-rule-dialog.component';
@ -36,8 +36,8 @@ export class AddShopPaymentRoutingRuleDialogService {
constructor(
private fb: FormBuilder,
private dialogRef: MatDialogRef<AddShopPaymentRoutingRuleDialogComponent>,
private paymentRoutingRulesService: PaymentRoutingRulesService,
private domainTypedManager: DomainTypedManager,
private paymentRoutingRulesService: RoutingRulesService,
private terminalService: TerminalService,
@Inject(MAT_DIALOG_DATA) public data: { partyID: string; refID: number }
) {
this.form
@ -72,7 +72,7 @@ export class AddShopPaymentRoutingRuleDialogService {
newTerminal,
} = this.form.value;
(terminalType === TerminalType.new
? this.domainTypedManager.createTerminal({
? this.terminalService.createTerminal({
terminalName: newTerminal.name,
terminalDescription: newTerminal.description,
riskCoverage: newTerminal.riskCoverage,

View File

@ -6,7 +6,7 @@ import { map, pluck, shareReplay, switchMap, take } from 'rxjs/operators';
import { handleError } from '../../../../utils/operators/handle-error';
import { PartyService } from '../../../papi/party.service';
import { ErrorService } from '../../../shared/services/error';
import { PaymentRoutingRulesService as PaymentRoutingRulesDamselService } from '../../../thrift-services';
import { RoutingRulesService as PaymentRoutingRulesDamselService } from '../../../thrift-services';
@Injectable()
export class ShopPaymentRoutingRulesetService {

View File

@ -1,5 +1,5 @@
<div [formGroup]="form" fxLayout="column" fxLayoutGap="24px">
<div class="cc-title">Target Ruleset</div>
<div class="cc-title">Change Main Ruleset</div>
<div fxLayout="column" fxLayoutGap="39px">
<mat-radio-group fxLayout formControlName="target">
@ -39,7 +39,7 @@
</ng-container>
</div>
<mat-form-field *ngIf="form.controls.target.value === target.manual">
<mat-label>Payment Routing Ruleset</mat-label>
<mat-label>Main Ruleset</mat-label>
<mat-select formControlName="mainRulesetRefID" required>
<mat-option *ngFor="let i of rulesets$ | async" [value]="i.ref.id">
#{{ i.ref.id }} {{ i.data.name }}

View File

@ -15,7 +15,7 @@ import { PaymentInstitutionObject } from 'src/app/thrift-services/damsel/gen-mod
import { ComponentChanges } from '@cc/app/shared/utils';
import { PaymentRoutingRulesService } from '../../../thrift-services';
import { RoutingRulesService } from '../../../thrift-services';
import { Target } from './types/target';
import { TargetRuleset } from './types/target-ruleset';
@ -47,7 +47,7 @@ export class TargetRulesetFormComponent implements OnChanges {
constructor(
private fb: FormBuilder,
private domainService: DomainCacheService,
private paymentRoutingRulesService: PaymentRoutingRulesService
private paymentRoutingRulesService: RoutingRulesService
) {
this.form.controls.target.valueChanges
.pipe(startWith(this.form.value.target), untilDestroyed(this))

View File

@ -4,10 +4,7 @@ import { progress } from '@rbkmoney/partial-fetcher/dist/progress';
import { merge, Observable, Subject } from 'rxjs';
import { map, shareReplay, switchMap } from 'rxjs/operators';
import {
AddDecisionToProvider,
DomainTypedManager,
} from '../../../../../../thrift-services/damsel';
import { AddDecisionToProvider, ProviderService } from '../../../../../../thrift-services/damsel';
import { DomainCacheService } from '../../../../../../thrift-services/damsel/domain-cache.service';
import {
PartyID,
@ -35,7 +32,7 @@ export class AddTerminalDecisionService {
terminalID: this.terminalForm.value.id,
})),
switchMap((params) =>
this.domainTypedManager.getProviderFromParams<AddDecisionToProvider>(params)
this.providerService.getProviderFromParams<AddDecisionToProvider>(params)
),
switchMap(([params, providerObject]) =>
this.domainCacheService.commit(addDecisionToProviderCommit(providerObject, params))
@ -47,7 +44,7 @@ export class AddTerminalDecisionService {
constructor(
private domainCacheService: DomainCacheService,
private domainTypedManager: DomainTypedManager,
private providerService: ProviderService,
private fb: FormBuilder
) {
this.terminalAdded$.subscribe();

View File

@ -7,7 +7,7 @@ import { catchError, filter, map, shareReplay, switchMap } from 'rxjs/operators'
import { ConfirmActionDialogComponent } from '@cc/components/confirm-action-dialog';
import { DomainTypedManager } from '../../../../../thrift-services/damsel';
import { ProviderService } from '../../../../../thrift-services/damsel';
import { DomainCacheService } from '../../../../../thrift-services/damsel/domain-cache.service';
import { createRemoveTerminalFromShopCommit } from '../../../../../thrift-services/damsel/operations/create-remove-terminal-from-shop-commit';
import { RemoveTerminalFromShopParams } from '../../../../../thrift-services/damsel/operations/remove-terminal-from-shop-params';
@ -40,7 +40,7 @@ export class RemoveTerminalDecisionService {
])
),
switchMap(([params]) =>
this.domainTypedManager.getProviderFromParams<RemoveTerminalFromShopParams>(params)
this.providerService.getProviderFromParams<RemoveTerminalFromShopParams>(params)
),
map(([params, providerObject]) =>
createRemoveTerminalFromShopCommit(providerObject, params)
@ -65,7 +65,7 @@ export class RemoveTerminalDecisionService {
private dialog: MatDialog,
private snackBar: MatSnackBar,
private domainCacheService: DomainCacheService,
private domainTypedManager: DomainTypedManager
private providerService: ProviderService
) {
this.terminalRemoved$.subscribe();
this.error$.subscribe(() => {

View File

@ -4,7 +4,7 @@ import { Observable } from 'rxjs';
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
import { toGenReference } from '../converters';
import { PartyID } from '../damsel/gen-model/domain';
import { ThriftService } from '../thrift-service';
import { ThriftService } from '../services/thrift/thrift-service';
import {
QuestionaryID,
QuestionaryParams,

View File

@ -3,7 +3,7 @@ import { Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
import { ThriftService } from '../thrift-service';
import { ThriftService } from '../services/thrift/thrift-service';
import {
Claim,
ClaimID,

View File

@ -1,24 +0,0 @@
import metadata from '../../../assets/meta-damsel.json';
import { createThriftInstanceUtils } from '../thrift-instance';
import * as base from './gen-nodejs/base_types';
import * as claim_management from './gen-nodejs/claim_management_types';
import * as domain_config from './gen-nodejs/domain_config_types';
import * as domain from './gen-nodejs/domain_types';
import * as geo_ip from './gen-nodejs/geo_ip_types';
import * as merch_stat from './gen-nodejs/merch_stat_types';
import * as payment_processing from './gen-nodejs/payment_processing_types';
const namespaces = {
base,
domain,
domain_config,
claim_management,
geo_ip,
merch_stat,
payment_processing,
};
export const {
createThriftInstance: createDamselInstance,
thriftInstanceToObject: damselInstanceToObject,
} = createThriftInstanceUtils(metadata, namespaces);

View File

@ -2,21 +2,22 @@ import { NgModule } from '@angular/core';
import { ClaimManagementService } from './claim-management.service';
import { DomainCacheService } from './domain-cache.service';
import { DomainTypedManager } from './domain-typed-manager';
import { DomainService } from './domain.service';
import { MerchantStatisticsService } from './merchant-statistics.service';
import { PaymentProcessingService } from './payment-processing.service';
import { PaymentRoutingRulesService } from './payment-routing-rules.service';
import { ProviderService } from './provider.service';
import { RoutingRulesModule } from './routing-rules';
import { TerminalModule } from './terminal';
@NgModule({
imports: [RoutingRulesModule, TerminalModule],
providers: [
DomainService,
DomainTypedManager,
ProviderService,
PaymentProcessingService,
MerchantStatisticsService,
DomainCacheService,
ClaimManagementService,
PaymentRoutingRulesService,
],
})
export class DamselModule {}

View File

@ -1,16 +1,15 @@
import { Injectable } from '@angular/core';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { BehaviorSubject, Observable, of, ReplaySubject } from 'rxjs';
import { map, pluck, shareReplay, startWith, switchMap, take, tap } from 'rxjs/operators';
import type { Int64 } from 'thrift-ts';
import { map, pluck, share, shareReplay, startWith, switchMap, take, tap } from 'rxjs/operators';
import { getUnionKey } from '@cc/utils/get-union-key';
import { toGenReference } from '../converters';
import { createDamselInstance, damselInstanceToObject } from './create-damsel-instance';
import { DomainService } from './domain.service';
import { Domain, DomainObject } from './gen-model/domain';
import { Commit, Snapshot, Version } from './gen-model/domain_config';
import { createDamselInstance, damselInstanceToObject } from './utils/create-damsel-instance';
@UntilDestroy()
@Injectable()
@ -21,7 +20,7 @@ export class DomainCacheService {
domain: Observable<Domain>;
snapshot$: Observable<Snapshot>;
domain$: Observable<Domain>;
version$: Observable<Int64>;
version$: Observable<number>;
get isLoading$(): Observable<boolean> {
return this._isLoading$.asObservable();
}
@ -43,7 +42,7 @@ export class DomainCacheService {
shareReplay(1)
);
this.domain$ = this.snapshot$.pipe(pluck('domain'));
this.version$ = this.snapshot$.pipe(pluck('version'));
this.version$ = this.snapshot$.pipe(pluck('version')) as Observable<any>;
this.snapshot.pipe(untilDestroyed(this)).subscribe();
}
@ -61,17 +60,26 @@ export class DomainCacheService {
)
);
commit = (commit: Commit, version?: Version) => {
return (version ? of(version) : this.version$).pipe(
take(1),
commit = (commit: Commit, version?: Version | number, reload = true) => {
const version$ = version ? of(version) : this.version$.pipe(take(1));
return version$.pipe(
switchMap((v) =>
this.dmtService.commit(
createDamselInstance('domain_config', 'Version', v),
createDamselInstance('domain_config', 'Version', v as Version),
createDamselInstance('domain_config', 'Commit', commit)
)
),
map((v) => damselInstanceToObject('domain_config', 'Version', v)),
tap(() => this.forceReload())
tap(() => reload && this.forceReload()),
share()
);
};
sequenseCommits([commit, ...otherCommits]: Commit[], version?: Version | number) {
return otherCommits.length
? this.commit(commit, version, false).pipe(
switchMap((v) => this.sequenseCommits(otherCommits, v))
)
: this.commit(commit, version);
}
}

View File

@ -2,7 +2,7 @@ import { Injectable, NgZone } from '@angular/core';
import { Observable } from 'rxjs';
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
import { ThriftService } from '../thrift-service';
import { ThriftService } from '../services/thrift/thrift-service';
import { Commit, Limit, Reference, Snapshot, Version } from './gen-model/domain_config';
import * as Repository from './gen-nodejs/Repository';

View File

@ -1,8 +1,8 @@
export * from './operations';
export * from './domain-typed-manager';
export * from './domain.service';
export * from './get-thrift-instance';
export * from './create-damsel-instance';
export * from './utils/create-damsel-instance';
export * from './damsel.module';
export * from './payment-routing-rules.service';
export * from './routing-rules';
export * from './services';
export * from './terminal';
export * from './provider.service';

View File

@ -3,11 +3,11 @@ import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
import { ThriftService } from '../thrift-service';
import { createDamselInstance, damselInstanceToObject } from './create-damsel-instance';
import { ThriftService } from '../services/thrift/thrift-service';
import { StatRequest, StatResponse } from './gen-model/merch_stat';
import * as MerchantStatistics from './gen-nodejs/MerchantStatistics';
import { StatRequest as ThriftStatRequest } from './gen-nodejs/merch_stat_types';
import { createDamselInstance, damselInstanceToObject } from './utils/create-damsel-instance';
@Injectable()
export class MerchantStatisticsService extends ThriftService {

View File

@ -4,8 +4,7 @@ import { Observable, timer } from 'rxjs';
import { first, map, share, switchMap } from 'rxjs/operators';
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
import { ThriftService } from '../thrift-service';
import { createDamselInstance, damselInstanceToObject } from './create-damsel-instance';
import { ThriftService } from '../services/thrift/thrift-service';
import {
InvoiceID,
InvoicePaymentChargeback,
@ -29,6 +28,7 @@ import {
InvoiceRepairScenario as InvoiceRepairScenarioObject,
UserInfo as UserInfoObject,
} from './gen-nodejs/payment_processing_types';
import { createDamselInstance, damselInstanceToObject } from './utils/create-damsel-instance';
@Injectable()
export class PaymentProcessingService extends ThriftService {

View File

@ -5,16 +5,11 @@ import { map, switchMap, take } from 'rxjs/operators';
import { DomainCacheService } from './domain-cache.service';
import { ProviderObject } from './gen-model/domain';
import { Version } from './gen-model/domain_config';
import {
AddDecisionToProvider,
addDecisionToProviderCommit,
CreateTerminalParams,
getCreateTerminalCommit,
} from './operations';
import { AddDecisionToProvider, addDecisionToProviderCommit } from './operations';
import { findDomainObject } from './operations/utils';
@Injectable()
export class DomainTypedManager {
export class ProviderService {
constructor(private domainCacheService: DomainCacheService) {}
getProviderFromParams<T extends { providerID: number }>(
@ -40,20 +35,4 @@ export class DomainTypedManager {
)
);
}
/**
* @deprecated select in separate service
*/
createTerminal(params: CreateTerminalParams): Observable<number> {
let newTerminalID = null;
return this.domainCacheService.getObjects('terminal').pipe(
take(1),
switchMap((terminalObjects) => {
const { commit, id } = getCreateTerminalCommit(terminalObjects, params);
newTerminalID = id;
return this.domainCacheService.commit(commit);
}),
map(() => newTerminalID)
);
}
}

View File

@ -0,0 +1,2 @@
export * from './routing-rules.module';
export * from './routing-rules.service';

View File

@ -0,0 +1,8 @@
import { NgModule } from '@angular/core';
import { RoutingRulesService } from './routing-rules.service';
@NgModule({
providers: [RoutingRulesService],
})
export class RoutingRulesModule {}

View File

@ -1,19 +1,21 @@
import { Injectable } from '@angular/core';
import cloneDeep from 'lodash-es/cloneDeep';
import { combineLatest, Observable } from 'rxjs';
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
import { map, pluck, shareReplay, switchMap, take } from 'rxjs/operators';
import { DomainCacheService } from './domain-cache.service';
import { createNextId } from '../../utils/create-next-id';
import { DomainCacheService } from '../domain-cache.service';
import {
Predicate,
RoutingCandidate,
RoutingDelegate,
RoutingRulesObject,
} from './gen-model/domain';
import { Version } from './gen-model/domain_config';
} from '../gen-model/domain';
import { Version } from '../gen-model/domain_config';
import { getDelegate } from './utils/get-delegate';
@Injectable()
export class PaymentRoutingRulesService {
export class RoutingRulesService {
constructor(private domainService: DomainCacheService) {}
rulesets$: Observable<RoutingRulesObject[]> = this.domainService
@ -25,7 +27,7 @@ export class PaymentRoutingRulesService {
nextRefID$ = this.rulesets$.pipe(
map((rulesets) => rulesets.map(({ ref }) => ref.id)),
map((ids) => Math.max(...ids) + 1)
map(createNextId)
);
getRuleset(refID: number): Observable<RoutingRulesObject> {
@ -263,19 +265,16 @@ export class PaymentRoutingRulesService {
deleteDelegate({
mainRulesetRefID,
rulesetRefID,
delegateIdx,
}: {
mainRulesetRefID: number;
rulesetRefID: number;
delegateIdx: number;
}): Observable<Version> {
return this.getRuleset(mainRulesetRefID).pipe(
take(1),
switchMap((mainRuleset) => {
const newMainPaymentRoutingRuleset = cloneDeep(mainRuleset);
newMainPaymentRoutingRuleset.data.decisions.delegates.splice(
this.getDelegateIdx(mainRuleset, rulesetRefID),
1
);
newMainPaymentRoutingRuleset.data.decisions.delegates.splice(delegateIdx, 1);
return this.domainService.commit({
ops: [
{
@ -290,15 +289,15 @@ export class PaymentRoutingRulesService {
);
}
changeDelegateRuleset({
changeMainRuleset({
previousMainRulesetRefID,
mainRulesetRefID,
rulesetID,
delegateIdx,
mainDelegateDescription,
}: {
previousMainRulesetRefID: number;
mainRulesetRefID: number;
rulesetID: number;
delegateIdx: number;
mainDelegateDescription?: string;
}): Observable<Version> {
return combineLatest([
@ -309,7 +308,7 @@ export class PaymentRoutingRulesService {
switchMap(([mainRuleset, previousMainRuleset]) => {
const newPreviousMainRuleset = cloneDeep(previousMainRuleset);
const [delegate] = newPreviousMainRuleset.data.decisions.delegates.splice(
this.getDelegateIdx(previousMainRuleset, rulesetID),
delegateIdx,
1
);
const newMainPaymentRoutingRuleset = cloneDeep(mainRuleset);
@ -340,9 +339,84 @@ export class PaymentRoutingRulesService {
);
}
private getDelegateIdx(ruleset: RoutingRulesObject, delegateRulesetRefId: number) {
return ruleset.data.decisions.delegates.findIndex(
(d) => d?.ruleset?.id === delegateRulesetRefId
changeDelegateRuleset({
mainRulesetRefID,
delegateIdx,
newDelegateRulesetRefID,
description,
}: {
mainRulesetRefID: number;
delegateIdx: number;
newDelegateRulesetRefID: number;
description?: string;
}): Observable<Version> {
return this.getRuleset(mainRulesetRefID).pipe(
take(1),
switchMap((mainRuleset) => {
const newMainRuleset = cloneDeep(mainRuleset);
newMainRuleset.data.decisions.delegates[
delegateIdx
].ruleset.id = newDelegateRulesetRefID;
if (description !== undefined) {
newMainRuleset.data.decisions.delegates[delegateIdx].description = description;
}
return this.domainService.commit({
ops: [
{
update: {
old_object: { routing_rules: mainRuleset },
new_object: { routing_rules: newMainRuleset },
},
},
],
});
})
);
}
cloneDelegateRuleset({
mainRulesetRefID,
delegateIdx,
}: {
mainRulesetRefID: number;
delegateIdx: number;
}): Observable<Version> {
return combineLatest([
this.getRuleset(mainRulesetRefID),
this.getRuleset(mainRulesetRefID).pipe(
switchMap((r) => this.getRuleset(getDelegate(r, delegateIdx).ruleset.id))
),
this.nextRefID$,
]).pipe(
take(1),
switchMap(([mainRuleset, delegateRuleset, nextRefID]) => {
const newMainRuleset = cloneDeep(mainRuleset);
getDelegate(newMainRuleset, delegateIdx).ruleset.id = nextRefID;
const newDelegateRuleset = cloneDeep(delegateRuleset);
newDelegateRuleset.ref.id = nextRefID;
return this.domainService.sequenseCommits([
{
ops: [
{
insert: {
object: { routing_rules: newDelegateRuleset },
},
},
],
},
{
ops: [
{
update: {
old_object: { routing_rules: mainRuleset },
new_object: { routing_rules: newMainRuleset },
},
},
],
},
]);
}),
pluck('1')
);
}

View File

@ -0,0 +1,8 @@
import { RoutingDelegate, RoutingRulesObject } from '../../gen-model/domain';
export function getDelegate(
ruleset: RoutingRulesObject,
delegateIdx: number
): RoutingDelegate | undefined {
return ruleset?.data?.decisions?.delegates?.[delegateIdx];
}

View File

@ -5,9 +5,9 @@ import { EMPTY, merge, Subject } from 'rxjs';
import { catchError, shareReplay, switchMap } from 'rxjs/operators';
import { DomainCacheService } from '../domain-cache.service';
import { DomainTypedManager } from '../domain-typed-manager';
import { editTerminalDecisionPropertyForShopCommit } from '../operations/edit-terminal-decision-property-for-shop-commit';
import { EditTerminalDecisionPropertyParams } from '../operations/edit-terminal-decision-property-params';
import { ProviderService } from '../provider.service';
@Injectable()
export class EditTerminalDecisionPropertyForShopService {
@ -17,9 +17,7 @@ export class EditTerminalDecisionPropertyForShopService {
edited$ = this.editProperty$.pipe(
switchMap((params) =>
this.domainTypedManager.getProviderFromParams<EditTerminalDecisionPropertyParams>(
params
)
this.providerService.getProviderFromParams<EditTerminalDecisionPropertyParams>(params)
),
switchMap(([params, provider]) => {
return this.domainCacheService
@ -39,7 +37,7 @@ export class EditTerminalDecisionPropertyForShopService {
constructor(
private domainCacheService: DomainCacheService,
private snackBar: MatSnackBar,
private domainTypedManager: DomainTypedManager
private providerService: ProviderService
) {
this.error$.subscribe(() => {
this.snackBar.open('An error occurred while editing providerObject');

View File

@ -0,0 +1,2 @@
export * from './terminal.service';
export * from './terminal.module';

View File

@ -0,0 +1,8 @@
import { NgModule } from '@angular/core';
import { TerminalService } from './terminal.service';
@NgModule({
providers: [TerminalService],
})
export class TerminalModule {}

View File

@ -0,0 +1,20 @@
import { TestBed } from '@angular/core/testing';
import { TerminalService } from './terminal.service';
describe('TerminalService', () => {
let service: TerminalService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [],
providers: [TerminalService],
});
service = TestBed.inject(TerminalService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,21 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { mapTo, switchMap, take } from 'rxjs/operators';
import { DomainCacheService } from '../domain-cache.service';
import { CreateTerminalParams, getCreateTerminalCommit } from '../operations';
@Injectable()
export class TerminalService {
constructor(private domainCacheService: DomainCacheService) {}
createTerminal(params: CreateTerminalParams): Observable<number> {
return this.domainCacheService.getObjects('terminal').pipe(
take(1),
switchMap((terminalObjects) => {
const { commit, id } = getCreateTerminalCommit(terminalObjects, params);
return this.domainCacheService.commit(commit).pipe(mapTo(id));
})
);
}
}

View File

@ -0,0 +1,24 @@
import metadata from '../../../../assets/meta-damsel.json';
import { createThriftInstanceUtils } from '../../utils/thrift-instance';
import * as base from '../gen-nodejs/base_types';
import * as claim_management from '../gen-nodejs/claim_management_types';
import * as domain_config from '../gen-nodejs/domain_config_types';
import * as domain from '../gen-nodejs/domain_types';
import * as geo_ip from '../gen-nodejs/geo_ip_types';
import * as merch_stat from '../gen-nodejs/merch_stat_types';
import * as payment_processing from '../gen-nodejs/payment_processing_types';
const namespaces = {
base,
domain,
domain_config,
claim_management,
geo_ip,
merch_stat,
payment_processing,
};
export const {
createThriftInstance: createDamselInstance,
thriftInstanceToObject: damselInstanceToObject,
} = createThriftInstanceUtils(metadata, namespaces);

View File

@ -2,7 +2,7 @@ import { Injectable, NgZone } from '@angular/core';
import { Observable } from 'rxjs';
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
import { ThriftService } from '../thrift-service';
import { ThriftService } from '../services/thrift/thrift-service';
import { SearchHit } from './gen-model/deanonimus';
import * as Deanonimus from './gen-nodejs/Deanonimus';

View File

@ -2,7 +2,7 @@ import { Injectable, NgZone } from '@angular/core';
import { Observable } from 'rxjs';
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
import { ThriftService } from '../thrift-service';
import { ThriftService } from '../services/thrift/thrift-service';
import { Timestamp } from './gen-model/base';
import {
FileData,

View File

@ -2,7 +2,7 @@ import { Injectable, NgZone } from '@angular/core';
import { Observable } from 'rxjs';
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
import { ThriftService } from '../thrift-service';
import { ThriftService } from '../services/thrift/thrift-service';
import { DepositParams } from './gen-model/fistful_admin';
import * as FistfulAdmin from './gen-nodejs/FistfulAdmin';
import { DepositParams as DepositParamsObject } from './gen-nodejs/fistful_admin_types';

View File

@ -6,7 +6,7 @@ import { map } from 'rxjs/operators';
import { SearchFormParams } from '../../deposits/search-form/search-form-params';
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
import { QueryDSL } from '../../query-dsl';
import { ThriftService } from '../thrift-service';
import { ThriftService } from '../services/thrift/thrift-service';
import { StatDeposit, StatRequest } from './gen-model/fistful_stat';
import * as FistfulStatistics from './gen-nodejs/FistfulStatistics';
import { StatRequest as ThriftStatRequest } from './gen-nodejs/fistful_stat_types';

View File

@ -2,7 +2,7 @@ import { Injectable, NgZone } from '@angular/core';
import { Observable } from 'rxjs';
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
import { ThriftService } from '../thrift-service';
import { ThriftService } from '../services/thrift/thrift-service';
import { RepairScenario, SessionID } from './gen-model/withdrawal_session';
import * as Repairer from './gen-nodejs/Repairer';
import { RepairScenario as RepairScenarioObject } from './gen-nodejs/withdrawal_session_types';

View File

@ -1,6 +1,6 @@
export * from './thrift-service';
export * from './filters';
export * from './thrift-instance';
export * from './utils/thrift-instance';
export * from './utils/get-thrift-instance';
export * from './ank';
export * from './file-storage';
export * from './messages';

View File

@ -2,7 +2,7 @@ import { Injectable, NgZone } from '@angular/core';
import { Observable } from 'rxjs';
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
import { ThriftService } from '../thrift-service';
import { ThriftService } from '../services/thrift/thrift-service';
import { Namespace } from './gen-model/base';
import { Machine, MachineDescriptor, Reference } from './gen-model/state_processing';
import * as Automaton from './gen-nodejs/Automaton';

View File

@ -2,7 +2,7 @@ import { Injectable, NgZone } from '@angular/core';
import { Observable } from 'rxjs';
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
import { ThriftService } from '../thrift-service';
import { ThriftService } from '../services/thrift/thrift-service';
import {
Conversation,
ConversationFilter,

View File

@ -3,7 +3,7 @@ import { Observable } from 'rxjs';
import { timeout } from 'rxjs/operators';
import connectClient from 'woody_js';
import { KeycloakTokenInfoService } from '../keycloak-token-info.service';
import { KeycloakTokenInfoService } from '../../../keycloak-token-info.service';
type Exception<N = string, T = {}> = {
name: N;

View File

@ -3,7 +3,7 @@ import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
import { ThriftService } from '../thrift-service';
import { ThriftService } from '../services/thrift/thrift-service';
import { ID } from './gen-model/base';
import { ChargebackData, ChargebackEvent, ChargebackFilter } from './gen-model/skipper';
import * as Skipper from './gen-nodejs/Skipper';

View File

@ -1,5 +1,5 @@
import metadata from '../../../assets/meta-skipper.json';
import { createThriftInstanceUtils } from '../thrift-instance';
import { createThriftInstanceUtils } from '../utils/thrift-instance';
import * as base from './gen-nodejs/base_types';
import * as chargeback from './gen-nodejs/chargeback_types';
import * as skipper from './gen-nodejs/skipper_types';

View File

@ -0,0 +1,5 @@
// TODO: numeric overflow, need logic to use the freed
// TODO: return Int64
export function createNextId(ids: number[]) {
return Math.max(...ids) + 1;
}

View File

@ -1,6 +1,6 @@
import * as BaseTypes from './gen-nodejs/base_types';
import * as DomainConfigTypes from './gen-nodejs/domain_config_types';
import * as DomainTypes from './gen-nodejs/domain_types';
import * as BaseTypes from '../damsel/gen-nodejs/base_types';
import * as DomainConfigTypes from '../damsel/gen-nodejs/domain_config_types';
import * as DomainTypes from '../damsel/gen-nodejs/domain_types';
export const SupportedNamespaces = {
base: BaseTypes,

24
src/app/tokens.ts Normal file
View File

@ -0,0 +1,24 @@
import { InjectionToken } from '@angular/core';
import { MatDialogConfig } from '@angular/material/dialog';
export const LAYOUT_GAP = new InjectionToken<string>('layoutGap');
export const SEARCH_LIMIT = new InjectionToken<number>('searchLimit');
export const DEFAULT_SEARCH_LIMIT = 10;
export type DialogConfig = {
small: MatDialogConfig;
medium: MatDialogConfig;
large: MatDialogConfig;
};
export const DIALOG_CONFIG = new InjectionToken<DialogConfig>('dialogConfig');
const baseConfig: MatDialogConfig = {
maxHeight: '90vh',
disableClose: true,
autoFocus: false,
};
export const DEFAULT_DIALOG_CONFIG: DialogConfig = {
small: { ...baseConfig, width: '360px' },
medium: { ...baseConfig, width: '552px' },
large: { ...baseConfig, width: '648px' },
};

View File

@ -1,4 +1,4 @@
<h2 class="mat-headline">{{ data.title ? data.title : 'Confirm this action' }}</h2>
<h2 class="mat-headline">{{ title }}</h2>
<div
mat-dialog-actions
class="dialog-actions"

View File

@ -9,10 +9,13 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
export class ConfirmActionDialogComponent {
constructor(
public dialogRef: MatDialogRef<ConfirmActionDialogComponent, 'cancel' | 'confirm'>,
@Inject(MAT_DIALOG_DATA)
public data: { title?: string }
@Inject(MAT_DIALOG_DATA) public data: { title?: string }
) {}
get title() {
return this.data?.title ?? 'Confirm this action';
}
cancel() {
this.dialogRef.close('cancel');
}