FE-954: claim management API integration (#89)

This commit is contained in:
Alexandra Usacheva 2019-12-16 19:22:16 +03:00 committed by GitHub
parent f42a011eff
commit 3374d55ee6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 432 additions and 197 deletions

2
.npmrc Normal file
View File

@ -0,0 +1,2 @@
@rbkmoney:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}

2
Jenkinsfile vendored
View File

@ -15,9 +15,11 @@ build('control-center', 'docker-host') {
def pipeline = { def pipeline = {
runStage('init') { runStage('init') {
withGithubSshCredentials { withGithubSshCredentials {
withGithubToken {
sh 'make wc_init' sh 'make wc_init'
} }
} }
}
runStage('build') { runStage('build') {
sh 'make wc_build' sh 'make wc_build'
} }

View File

@ -20,7 +20,7 @@ BASE_IMAGE_TAG := 2b4570bc1d9631c10aaed2132eb87eb9003f3471
BUILD_IMAGE_TAG := f3732d29a5e622aabf80542b5138b3631a726adb BUILD_IMAGE_TAG := f3732d29a5e622aabf80542b5138b3631a726adb
GIT_SSH_COMMAND := GIT_SSH_COMMAND :=
DOCKER_RUN_OPTS = -e GIT_SSH_COMMAND='$(GIT_SSH_COMMAND)' -e NG_CLI_ANALYTICS=ci DOCKER_RUN_OPTS = -e GIT_SSH_COMMAND='$(GIT_SSH_COMMAND)' -e NG_CLI_ANALYTICS=ci -e NPM_TOKEN='$(GITHUB_TOKEN)'
CALL_W_CONTAINER := init build clean submodules CALL_W_CONTAINER := init build clean submodules
@ -53,7 +53,7 @@ clean:
compile-damsel: damsel-client damsel-model damsel-meta compile-damsel: damsel-client damsel-model damsel-meta
damsel-client: damsel-client:
@$(foreach file,domain_config payment_processing merch_stat,echo $(file); thrift -r -gen js:node,runtime_package=woody_js/dist/thrift -o ./src/app/thrift ./node_modules/damsel/proto/$(file).thrift;) @$(foreach file,domain_config payment_processing merch_stat claim_management,echo $(file); thrift -r -gen js:node,runtime_package=woody_js/dist/thrift -o ./src/app/thrift ./node_modules/damsel/proto/$(file).thrift;)
damsel-meta: damsel-meta:
npm run damsel-meta npm run damsel-meta

62
package-lock.json generated
View File

@ -2402,6 +2402,12 @@
} }
} }
}, },
"@rbkmoney/partial-fetcher": {
"version": "1.0.4",
"requires": {
"rxjs": "^6.5.3"
}
},
"@schematics/angular": { "@schematics/angular": {
"version": "8.3.6", "version": "8.3.6",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-8.3.6.tgz", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-8.3.6.tgz",
@ -2477,6 +2483,12 @@
"@types/jasmine": "*" "@types/jasmine": "*"
} }
}, },
"@types/jwt-decode": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@types/jwt-decode/-/jwt-decode-2.2.1.tgz",
"integrity": "sha512-aWw2YTtAdT7CskFyxEX2K21/zSDStuf/ikI3yBqmwpwJF0pS+/IX5DWv+1UFffZIbruP6cnT9/LAJV1gFwAT1A==",
"dev": true
},
"@types/lodash": { "@types/lodash": {
"version": "4.14.116", "version": "4.14.116",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.116.tgz", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.116.tgz",
@ -5222,8 +5234,8 @@
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk="
}, },
"damsel": { "damsel": {
"version": "git+ssh://git@github.com/rbkmoney/damsel.git#b563890354447a5e175a9a318b33233a926a5e9c", "version": "git+ssh://git@github.com/rbkmoney/damsel.git#5a78a602632a1dbbc5f294361d1538cff4a27040",
"from": "git+ssh://git@github.com/rbkmoney/damsel.git#b563890354447a5e175a9a318b33233a926a5e9c" "from": "git+ssh://git@github.com/rbkmoney/damsel.git#5a78a602632a1dbbc5f294361d1538cff4a27040"
}, },
"dashdash": { "dashdash": {
"version": "1.14.1", "version": "1.14.1",
@ -7884,6 +7896,11 @@
} }
} }
}, },
"jwt-decode": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz",
"integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk="
},
"karma": { "karma": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/karma/-/karma-4.3.0.tgz", "resolved": "https://registry.npmjs.org/karma/-/karma-4.3.0.tgz",
@ -11755,27 +11772,6 @@
"integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==", "integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==",
"dev": true "dev": true
}, },
"uglify-es": {
"version": "3.3.9",
"resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
"integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
"requires": {
"commander": "~2.13.0",
"source-map": "~0.6.1"
},
"dependencies": {
"commander": {
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
"integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA=="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
}
},
"uglify-js": { "uglify-js": {
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz",
@ -11838,6 +11834,11 @@
"y18n": "^4.0.0" "y18n": "^4.0.0"
} }
}, },
"commander": {
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
"integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA=="
},
"find-cache-dir": { "find-cache-dir": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz",
@ -11954,6 +11955,15 @@
"requires": { "requires": {
"safe-buffer": "^5.1.1" "safe-buffer": "^5.1.1"
} }
},
"uglify-es": {
"version": "3.3.9",
"resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
"integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
"requires": {
"commander": "~2.13.0",
"source-map": "~0.6.1"
}
} }
} }
}, },
@ -14068,8 +14078,8 @@
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
}, },
"woody_js": { "woody_js": {
"version": "git+ssh://git@github.com/rbkmoney/woody_js.git#d2c90a54861593fa794afe3b09c4febb9d6e37d0", "version": "git+ssh://git@github.com/rbkmoney/woody_js.git#bc2c9f86cae470a0fe99730ca4a30b204058214b",
"from": "git+ssh://git@github.com/rbkmoney/woody_js.git#d2c90a54861593fa794afe3b09c4febb9d6e37d0", "from": "git+ssh://git@github.com/rbkmoney/woody_js.git#bc2c9f86cae470a0fe99730ca4a30b204058214b",
"requires": { "requires": {
"babel-core": "6.26.3", "babel-core": "6.26.3",
"babel-loader": "7.1.4", "babel-loader": "7.1.4",
@ -14271,7 +14281,7 @@
}, },
"stream-http": { "stream-http": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "http://registry.npmjs.org/stream-http/-/stream-http-2.3.1.tgz", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.3.1.tgz",
"integrity": "sha1-fh3IcQLD4xsy5mDwTKMfI929HVI=", "integrity": "sha1-fh3IcQLD4xsy5mDwTKMfI929HVI=",
"requires": { "requires": {
"builtin-status-codes": "^2.0.0", "builtin-status-codes": "^2.0.0",

View File

@ -12,6 +12,7 @@
"damsel-meta": "thrift-ts node_modules/damsel/proto -o src/assets/meta-damsel.json --json --pack --prettify", "damsel-meta": "thrift-ts node_modules/damsel/proto -o src/assets/meta-damsel.json --json --pack --prettify",
"machinegun-model": "thrift-ts node_modules/machinegun_proto/proto -o src/app/machinegun/gen-model -d false", "machinegun-model": "thrift-ts node_modules/machinegun_proto/proto -o src/app/machinegun/gen-model -d false",
"fistful-model": "thrift-ts node_modules/fistful-proto/proto -o src/app/fistful/gen-model -d false", "fistful-model": "thrift-ts node_modules/fistful-proto/proto -o src/app/fistful/gen-model -d false",
"codegen": "npm run damsel-model; npm run damsel-meta; npm run machinegun-model; npm run fistful-model",
"prettier": "prettier \"**/*.{html,js,ts,css,md,json,prettierrc,svg}\" --write", "prettier": "prettier \"**/*.{html,js,ts,css,md,json,prettierrc,svg}\" --write",
"check": "prettier \"**/*.{html,js,ts,css,md,json,prettierrc,svg}\" --list-different" "check": "prettier \"**/*.{html,js,ts,css,md,json,prettierrc,svg}\" --list-different"
}, },
@ -30,12 +31,14 @@
"@angular/platform-browser-dynamic": "^8.2.8", "@angular/platform-browser-dynamic": "^8.2.8",
"@angular/platform-server": "^8.2.8", "@angular/platform-server": "^8.2.8",
"@angular/router": "^8.2.8", "@angular/router": "^8.2.8",
"@rbkmoney/partial-fetcher": "^1.0.4",
"angular2-prettyjson": "3.0.1", "angular2-prettyjson": "3.0.1",
"core-js": "^2.5.4", "core-js": "^2.5.4",
"damsel": "git+ssh://git@github.com/rbkmoney/damsel.git#b563890354447a5e175a9a318b33233a926a5e9c", "damsel": "git+ssh://git@github.com/rbkmoney/damsel.git#5a78a602632a1dbbc5f294361d1538cff4a27040",
"fistful-proto": "git+ssh://git@github.com/rbkmoney/fistful-proto.git#59b72ba54ec0e7f1af1d9b83aba10883ac985019", "fistful-proto": "git+ssh://git@github.com/rbkmoney/fistful-proto.git#59b72ba54ec0e7f1af1d9b83aba10883ac985019",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
"jsonc-parser": "^2.0.2", "jsonc-parser": "^2.0.2",
"jwt-decode": "^2.2.0",
"keycloak-angular": "6.0.0", "keycloak-angular": "6.0.0",
"keycloak-js": "4.5.0", "keycloak-js": "4.5.0",
"lodash-es": "^4.17.10", "lodash-es": "^4.17.10",
@ -45,7 +48,7 @@
"rxjs": "^6.5.3", "rxjs": "^6.5.3",
"thrift-ts": "git+ssh://git@github.com/rbkmoney/thrift-ts.git#a5e3830ad30d5717e5e627ddcefa8f4d8918b174", "thrift-ts": "git+ssh://git@github.com/rbkmoney/thrift-ts.git#a5e3830ad30d5717e5e627ddcefa8f4d8918b174",
"uuid": "^3.3.2", "uuid": "^3.3.2",
"woody_js": "git+ssh://git@github.com/rbkmoney/woody_js.git#d2c90a54861593fa794afe3b09c4febb9d6e37d0", "woody_js": "git+ssh://git@github.com/rbkmoney/woody_js.git#bc2c9f86cae470a0fe99730ca4a30b204058214b",
"zone.js": "~0.8.26" "zone.js": "~0.8.26"
}, },
"devDependencies": { "devDependencies": {
@ -54,6 +57,7 @@
"@angular/compiler-cli": "^8.2.8", "@angular/compiler-cli": "^8.2.8",
"@types/jasmine": "~2.8.6", "@types/jasmine": "~2.8.6",
"@types/jasminewd2": "~2.0.3", "@types/jasminewd2": "~2.0.3",
"@types/jwt-decode": "^2.2.1",
"@types/lodash-es": "^4.17.1", "@types/lodash-es": "^4.17.1",
"@types/node": "~8.9.4", "@types/node": "~8.9.4",
"@types/uuid": "^3.4.3", "@types/uuid": "^3.4.3",

View File

@ -20,7 +20,6 @@ import {
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module'; import { CoreModule } from './core/core.module';
import { ClaimsModule } from './claims/claims.module';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { ClaimModule } from './claim/claim.module'; import { ClaimModule } from './claim/claim.module';
import { PayoutsModule } from './payouts/payouts.module'; import { PayoutsModule } from './payouts/payouts.module';
@ -30,6 +29,7 @@ import { PartyModule } from './party/party.module';
import { DomainModule } from './domain'; import { DomainModule } from './domain';
import { RepairingModule } from './repairing/repairing.module'; import { RepairingModule } from './repairing/repairing.module';
import { DepositsModule } from './deposits/deposits.module'; import { DepositsModule } from './deposits/deposits.module';
import { ClaimMgtModule } from './claim-mgt/claim-mgt.module';
@NgModule({ @NgModule({
declarations: [AppComponent], declarations: [AppComponent],
@ -44,8 +44,8 @@ import { DepositsModule } from './deposits/deposits.module';
MatMenuModule, MatMenuModule,
MatSidenavModule, MatSidenavModule,
MatListModule, MatListModule,
ClaimsModule,
ClaimModule, ClaimModule,
ClaimMgtModule,
PayoutsModule, PayoutsModule,
PaymentAdjustmentModule, PaymentAdjustmentModule,
PartiesModule, PartiesModule,

View File

@ -0,0 +1,28 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AppAuthGuardService } from '../app-auth-guard.service';
@NgModule({
imports: [
RouterModule.forChild([
{
path: 'claims',
loadChildren: () => import('./claims').then(m => m.ClaimsModule),
canActivate: [AppAuthGuardService],
data: {
roles: ['claim:get']
}
},
{
path: 'party',
loadChildren: () => import('./claim').then(m => m.ClaimModule),
canActivate: [AppAuthGuardService],
data: {
roles: ['claim:get']
}
}
])
],
exports: [RouterModule]
})
export class ClaimMgtRouting {}

View File

@ -0,0 +1,7 @@
import { NgModule } from '@angular/core';
import { ClaimMgtRouting } from './claim-mgt-routing.module';
@NgModule({
imports: [ClaimMgtRouting]
})
export class ClaimMgtModule {}

View File

@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AppAuthGuardService } from '../../app-auth-guard.service';
import { ClaimComponent } from './claim.component';
@NgModule({
imports: [
RouterModule.forChild([
{
path: ':party_id/claim/:claim_id',
component: ClaimComponent,
canActivate: [AppAuthGuardService],
data: {
roles: ['claim:get']
}
}
])
],
exports: [RouterModule]
})
export class ClaimRoutingModule {}

View File

@ -0,0 +1 @@
Claim

View File

@ -0,0 +1,6 @@
import { Component } from '@angular/core';
@Component({
templateUrl: 'claim.component.html'
})
export class ClaimComponent {}

View File

@ -0,0 +1,9 @@
import { NgModule } from '@angular/core';
import { ClaimComponent } from './claim.component';
import { ClaimRoutingModule } from './claim-routing.module';
@NgModule({
imports: [ClaimRoutingModule],
declarations: [ClaimComponent]
})
export class ClaimModule {}

View File

@ -0,0 +1 @@
export * from './claim.module';

View File

@ -1,14 +1,13 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { ClaimsComponent } from './claims.component'; import { ClaimsComponent } from './claims.component';
import { AppAuthGuardService } from '../app-auth-guard.service'; import { AppAuthGuardService } from '../../app-auth-guard.service';
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forChild([ RouterModule.forChild([
{ {
path: 'claims', path: '',
component: ClaimsComponent, component: ClaimsComponent,
canActivate: [AppAuthGuardService], canActivate: [AppAuthGuardService],
data: { data: {

View File

@ -1,15 +1,15 @@
<table mat-table [dataSource]="claims"> <table mat-table [dataSource]="claims">
<ng-container matColumnDef="partyID"> <ng-container matColumnDef="partyID">
<th mat-header-cell *matHeaderCellDef>Party ID</th> <th mat-header-cell *matHeaderCellDef>Party ID</th>
<td mat-cell *matCellDef="let claim">{{ claim.partyId }}</td> <td mat-cell *matCellDef="let claim">{{ claim.party_id }}</td>
</ng-container> </ng-container>
<ng-container matColumnDef="claimID"> <ng-container matColumnDef="claimID">
<th fxHide.sm fxHide.xs mat-header-cell *matHeaderCellDef>Claim ID</th> <th fxHide.sm fxHide.xs mat-header-cell *matHeaderCellDef>Claim ID</th>
<td fxHide.sm fxHide.xs mat-cell *matCellDef="let claim">{{ claim.claimId }}</td> <td fxHide.sm fxHide.xs mat-cell *matCellDef="let claim">{{ claim.id }}</td>
</ng-container> </ng-container>
<ng-container matColumnDef="status"> <ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef>Status</th> <th mat-header-cell *matHeaderCellDef>Status</th>
<td mat-cell *matCellDef="let claim">{{ claim.status }}</td> <td mat-cell *matCellDef="let claim">{{ getClaimStatus(claim.status) }}</td>
</ng-container> </ng-container>
<ng-container matColumnDef="revision"> <ng-container matColumnDef="revision">
<th fxHide.sm fxHide.xs mat-header-cell *matHeaderCellDef>Revision</th> <th fxHide.sm fxHide.xs mat-header-cell *matHeaderCellDef>Revision</th>
@ -18,19 +18,19 @@
<ng-container matColumnDef="createdAt"> <ng-container matColumnDef="createdAt">
<th fxHide.xs mat-header-cell *matHeaderCellDef>Created at</th> <th fxHide.xs mat-header-cell *matHeaderCellDef>Created at</th>
<td fxHide.xs mat-cell *matCellDef="let claim"> <td fxHide.xs mat-cell *matCellDef="let claim">
{{ claim.createdAt | date: 'dd.MM.yyyy HH:mm:ss' }} {{ claim.created_at | date: 'dd.MM.yyyy HH:mm:ss' }}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="updatedAt"> <ng-container matColumnDef="updatedAt">
<th fxHide.sm fxHide.xs mat-header-cell *matHeaderCellDef>Updated at</th> <th fxHide.sm fxHide.xs mat-header-cell *matHeaderCellDef>Updated at</th>
<td fxHide.sm fxHide.xs mat-cell *matCellDef="let claim"> <td fxHide.sm fxHide.xs mat-cell *matCellDef="let claim">
{{ claim.updatedAt | date: 'dd.MM.yyyy HH:mm:ss' }} {{ claim.updated_at | date: 'dd.MM.yyyy HH:mm:ss' }}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="claimDetailButton"> <ng-container matColumnDef="claimDetailButton">
<th mat-header-cell *matHeaderCellDef class="action-cell"></th> <th mat-header-cell *matHeaderCellDef class="action-cell"></th>
<td mat-cell *matCellDef="let claim" class="action-cell"> <td mat-cell *matCellDef="let claim" class="action-cell">
<button mat-icon-button (click)="navigateToClaim(claim)"> <button mat-icon-button (click)="navigateToClaim(claim.party_id, claim.id)">
<mat-icon>more_vert</mat-icon> <mat-icon>more_vert</mat-icon>
</button> </button>
</td> </td>

View File

@ -1,8 +1,8 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { ClaimInfo } from '../../papi/model'; import { Claim, ClaimStatus } from '../../../gen-damsel/claim_management';
import { ClaimActionType } from '../../claim/claim-action-type'; import { extractClaimStatus } from '../../../shared/extract-claim-status';
@Component({ @Component({
selector: 'cc-claims-table', selector: 'cc-claims-table',
@ -11,10 +11,9 @@ import { ClaimActionType } from '../../claim/claim-action-type';
}) })
export class ClaimsTableComponent { export class ClaimsTableComponent {
@Input() @Input()
claims: ClaimInfo[]; claims: Claim[];
displayedColumns = [ displayedColumns = [
'partyID',
'claimID', 'claimID',
'status', 'status',
'revision', 'revision',
@ -25,8 +24,11 @@ export class ClaimsTableComponent {
constructor(private router: Router) {} constructor(private router: Router) {}
navigateToClaim(claim: ClaimInfo) { navigateToClaim(partyID: string, claimID: number) {
const c = claim as any; this.router.navigate([`party/${partyID}/claim/${claimID}`]);
this.router.navigate([`/claims/${c.partyId}/${ClaimActionType.edit}/${c.claimId}`]); }
getClaimStatus(status: ClaimStatus) {
return extractClaimStatus(status);
} }
} }

View File

@ -4,7 +4,7 @@
<mat-card-content> <mat-card-content>
<cc-search-form (valueChanges)="search($event)"></cc-search-form> <cc-search-form (valueChanges)="search($event)"></cc-search-form>
</mat-card-content> </mat-card-content>
<mat-card-footer *ngIf="isLoading"> <mat-card-footer *ngIf="(isLoading$ | async)">
<mat-progress-bar mode="indeterminate"></mat-progress-bar> <mat-progress-bar mode="indeterminate"></mat-progress-bar>
</mat-card-footer> </mat-card-footer>
</mat-card> </mat-card>
@ -14,5 +14,15 @@
<cc-claim-actions></cc-claim-actions> <cc-claim-actions></cc-claim-actions>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
<div class="mat-elevation-z2"><cc-claims-table [claims]="claims"></cc-claims-table></div> <div class="mat-elevation-z2" fxLayout="column">
<cc-claims-table [claims]="claims$ | async"></cc-claims-table>
<button
mat-raised-button
*ngIf="(hasMore$ | async)"
(click)="fetchMore()"
[disabled]="isLoading$ | async"
>
Load more...
</button>
</div>
</cc-card-container> </cc-card-container>

View File

@ -0,0 +1,29 @@
import { Component, OnInit } from '@angular/core';
import { ClaimsService } from './claims.service';
import { SearchFormValue } from './search-form/search-form-value';
import { ClaimStatus } from '../../papi/model/claim-statuses';
@Component({
templateUrl: 'claims.component.html',
styleUrls: []
})
export class ClaimsComponent implements OnInit {
isLoading$ = this.claimService.isLoading$;
claims$ = this.claimService.claims$;
hasMore$ = this.claimService.hasMore$;
constructor(private claimService: ClaimsService) {}
ngOnInit(): void {
this.search({ statuses: [ClaimStatus.pending] });
}
search(searchFormValue: SearchFormValue) {
this.claimService.search(searchFormValue);
}
fetchMore() {
this.claimService.fetchMore();
}
}

View File

@ -18,18 +18,20 @@ import { ReactiveFormsModule } from '@angular/forms';
import { CdkTableModule } from '@angular/cdk/table'; import { CdkTableModule } from '@angular/cdk/table';
import { ClaimsComponent } from './claims.component'; import { ClaimsComponent } from './claims.component';
import { ClaimsRoutingModule } from './claims-routing.module'; import { PapiModule } from '../../papi/papi.module';
import { PapiModule } from '../papi/papi.module';
import { SearchFormComponent } from './search-form/search-form.component'; import { SearchFormComponent } from './search-form/search-form.component';
import { ClaimsTableComponent } from './claims-table/claims-table.component'; import { ClaimsTableComponent } from './claims-table/claims-table.component';
import { ClaimActionsComponent } from './claim-actions/claim-actions.component'; import { ClaimActionsComponent } from './claim-actions/claim-actions.component';
import { CreateClaimComponent } from './create-claim/create-claim.component'; import { CreateClaimComponent } from './create-claim/create-claim.component';
import { SharedModule } from '../shared/shared.module'; import { SharedModule } from '../../shared/shared.module';
import { ClaimsService } from './claims.service';
import { ClaimManagementService } from '../../thrift/claim-management.service';
import { ClaimsRoutingModule } from './claims-routing.module';
@NgModule({ @NgModule({
imports: [ imports: [
CommonModule,
ClaimsRoutingModule, ClaimsRoutingModule,
CommonModule,
FlexLayoutModule, FlexLayoutModule,
PapiModule, PapiModule,
ReactiveFormsModule, ReactiveFormsModule,
@ -54,6 +56,7 @@ import { SharedModule } from '../shared/shared.module';
ClaimActionsComponent, ClaimActionsComponent,
CreateClaimComponent CreateClaimComponent
], ],
entryComponents: [CreateClaimComponent] entryComponents: [CreateClaimComponent],
providers: [ClaimsService, ClaimManagementService]
}) })
export class ClaimsModule {} export class ClaimsModule {}

View File

@ -0,0 +1,53 @@
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { Observable } from 'rxjs';
import { catchError, map, shareReplay } from 'rxjs/operators';
import { FetchResult, PartialFetcher } from '@rbkmoney/partial-fetcher';
import { ClaimManagementService } from '../../thrift/claim-management.service';
import { SearchFormValue } from './search-form/search-form-value';
import { booleanDebounceTime } from '../../shared/operators';
import { convertFormValueToParams } from './convert-form-value-to-params';
import { Claim } from '../../gen-damsel/claim_management';
@Injectable()
export class ClaimsService extends PartialFetcher<Claim, SearchFormValue> {
private readonly searchLimit = 20;
claims$: Observable<Claim> = this.searchResult$.pipe(
catchError(() => {
this.snackBar.open('An error occurred while processing your search', 'OK');
return [];
})
);
isLoading$: Observable<boolean> = this.doAction$.pipe(
booleanDebounceTime(),
shareReplay(1)
);
constructor(
private claimManagementService: ClaimManagementService,
private snackBar: MatSnackBar
) {
super();
}
protected fetch(
searchFormValue: SearchFormValue,
continuationToken: string
): Observable<FetchResult<Claim>> {
return this.claimManagementService
.searchClaims({
...convertFormValueToParams(searchFormValue),
continuation_token: continuationToken,
limit: this.searchLimit
})
.pipe(
map(r => ({
result: r.result,
continuationToken: r.continuation_token
}))
);
}
}

View File

@ -0,0 +1,19 @@
import { SearchFormValue } from './search-form/search-form-value';
export const convertFormValueToParams = (params: SearchFormValue) => {
const result = {};
for (const k in params) {
if (params.hasOwnProperty(k)) {
if (k === 'statuses') {
result[k] = (params[k] as string[]).reduce((acc, cv) => [...acc, { [cv]: {} }], []);
} else {
const v = params[k].trim();
if (v === '') {
break;
}
result[k] = v;
}
}
}
return result;
};

View File

@ -3,7 +3,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatDialogRef } from '@angular/material'; import { MatDialogRef } from '@angular/material';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { ClaimActionType } from '../../claim/claim-action-type'; import { ClaimActionType } from '../../../claim/claim-action-type';
@Component({ @Component({
templateUrl: 'create-claim.component.html' templateUrl: 'create-claim.component.html'

View File

@ -0,0 +1 @@
export * from './claims.module';

View File

@ -0,0 +1,7 @@
import * as domain from '../../../gen-damsel/domain';
import { ClaimStatus } from '../../../papi/model/claim-statuses';
export interface SearchFormValue {
party_id?: domain.PartyID;
statuses?: ClaimStatus[];
}

View File

@ -1,13 +1,12 @@
<form fxLayout="row" fxLayout.xs="column" fxLayoutGap="20px" [formGroup]="form"> <form fxLayout="row" fxLayout.xs="column" fxLayoutGap="20px" [formGroup]="form">
<mat-form-field fxFlex="15" fxFlex.sm="30"> <mat-form-field fxFlex="15" fxFlex.sm="30">
<mat-select placeholder="Claim status" formControlName="claimStatus"> <mat-select placeholder="Claim status" formControlName="statuses" multiple>
<mat-option [value]="null">any</mat-option>
<mat-option *ngFor="let status of claimStatuses" [value]="status">{{ <mat-option *ngFor="let status of claimStatuses" [value]="status">{{
status status
}}</mat-option> }}</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field fxFlex="30" fxFlex.sm="70"> <mat-form-field fxFlex="30" fxFlex.sm="70">
<input matInput placeholder="Party ID" formControlName="partyId" /> <input matInput placeholder="Party ID" formControlName="party_id" />
</mat-form-field> </mat-form-field>
</form> </form>

View File

@ -2,8 +2,8 @@ import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { SearchFormService } from './search-form.service'; import { SearchFormService } from './search-form.service';
import { ClaimSearchParams } from '../../papi/params';
import { debounceTime } from 'rxjs/internal/operators'; import { debounceTime } from 'rxjs/internal/operators';
import { SearchFormValue } from './search-form-value';
@Component({ @Component({
selector: 'cc-search-form', selector: 'cc-search-form',
@ -12,7 +12,7 @@ import { debounceTime } from 'rxjs/internal/operators';
}) })
export class SearchFormComponent implements OnInit { export class SearchFormComponent implements OnInit {
@Output() @Output()
valueChanges: EventEmitter<ClaimSearchParams> = new EventEmitter(); valueChanges: EventEmitter<SearchFormValue> = new EventEmitter();
form: FormGroup; form: FormGroup;
@ -21,11 +21,11 @@ export class SearchFormComponent implements OnInit {
constructor(private searchFormService: SearchFormService) {} constructor(private searchFormService: SearchFormService) {}
ngOnInit() { ngOnInit() {
const { claimStatuses, form, formValueToSearchParams } = this.searchFormService; const { form, claimStatuses } = this.searchFormService;
this.claimStatuses = claimStatuses; this.claimStatuses = claimStatuses;
this.form = form; this.form = form;
this.form.valueChanges this.form.valueChanges.pipe(debounceTime(300)).subscribe(value => {
.pipe(debounceTime(300)) this.valueChanges.emit(value);
.subscribe(value => this.valueChanges.emit(formValueToSearchParams(value))); });
} }
} }

View File

@ -0,0 +1,24 @@
import { Injectable } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import values from 'lodash-es/values';
import { ClaimStatus } from '../../../papi/model/claim-statuses';
@Injectable()
export class SearchFormService {
form: FormGroup;
claimStatuses: string[];
constructor(private fb: FormBuilder) {
this.form = this.prepareForm();
this.claimStatuses = values(ClaimStatus);
this.form.patchValue({ statuses: [ClaimStatus.pending] });
}
private prepareForm(): FormGroup {
return this.fb.group({
statuses: '',
party_id: ''
});
}
}

View File

@ -1,6 +1,5 @@
import { async, TestBed } from '@angular/core/testing'; import { async, TestBed } from '@angular/core/testing';
import { ClaimInfoDetailsComponent } from './claim-info-details.component'; import { ClaimInfoDetailsComponent } from './claim-info-details.component';
import { By } from '@angular/platform-browser';
describe('ClaimInfoDetailsComponent', () => { describe('ClaimInfoDetailsComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {

View File

@ -1,39 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { HttpErrorResponse } from '@angular/common/http';
import { ClaimService } from '../papi/claim.service';
import { ClaimSearchParams } from '../papi/params';
import { ClaimInfo } from '../papi/model';
@Component({
templateUrl: 'claims.component.html',
styleUrls: []
})
export class ClaimsComponent implements OnInit {
isLoading = false;
claims: ClaimInfo[];
constructor(private claimService: ClaimService, private snackBar: MatSnackBar) {}
ngOnInit() {
this.search({ claimStatus: 'pending' });
}
search(params: ClaimSearchParams) {
this.isLoading = true;
this.claimService.getClaims(params).subscribe(
claims => {
this.isLoading = false;
this.claims = claims.reverse();
},
(error: HttpErrorResponse) => {
this.isLoading = false;
this.snackBar.open(`${error.status}: ${error.message}`, 'OK', {
duration: 1500
});
}
);
}
}

View File

@ -1,39 +0,0 @@
import { Injectable } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import values from 'lodash-es/values';
import mapValues from 'lodash-es/mapValues';
import isString from 'lodash-es/isString';
import { ClaimStatus } from '../../papi/model/claim-statuses';
import { ClaimSearchParams } from '../../papi/params';
@Injectable()
export class SearchFormService {
form: FormGroup;
claimStatuses: string[];
constructor(private fb: FormBuilder) {
this.form = this.prepareForm();
this.claimStatuses = values(ClaimStatus);
}
formValueToSearchParams(formValue): ClaimSearchParams {
return mapValues(formValue, value => {
let result = value;
if (value === '') {
result = null;
} else if (isString(value)) {
result = value.trim();
}
return result;
});
}
private prepareForm(): FormGroup {
return this.fb.group({
claimStatus: 'pending',
partyId: ''
});
}
}

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { DomainPair } from './domain-group/domain-group'; import { DomainPair } from './domain-group';
@Injectable() @Injectable()
export class DomainDetailsService { export class DomainDetailsService {

View File

@ -2,12 +2,12 @@ import { Component, OnInit, Input, OnChanges, SimpleChanges, ViewChild } from '@
import { MatTableDataSource, MatPaginator, MatSort } from '@angular/material'; import { MatTableDataSource, MatPaginator, MatSort } from '@angular/material';
import { DomainGroup } from '../domain-group'; import { DomainGroup } from '../domain-group';
import { DomainDetailsService } from '../../../domain-info/domain-details.service'; import { DomainDetailsService } from '../../domain-details.service';
import { toTableGroup, toDataSource } from './table-group'; import { toTableGroup, toDataSource } from './table-group';
import { sortData } from './sort-table-data'; import { sortData } from './sort-table-data';
import { filterPredicate } from './filter-predicate'; import { filterPredicate } from './filter-predicate';
import { TableDataSource, TableGroup } from './model'; import { TableDataSource, TableGroup } from './model';
import { DetailsContainerService } from '../../../domain-info/details-container.service'; import { DetailsContainerService } from '../../details-container.service';
@Component({ @Component({
selector: 'cc-group-table', selector: 'cc-group-table',

View File

@ -1,11 +1,10 @@
import { parse } from '../../jsonc/json-parser';
import { import {
CodeLensProvider, CodeLensProvider,
ITextModel, ITextModel,
CancellationToken, CancellationToken,
ICodeLensSymbol, ICodeLensSymbol,
ProviderResult ProviderResult
} from '../../monaco-editor/model'; } from '../../monaco-editor';
export class DomainObjCodeLensProvider implements CodeLensProvider { export class DomainObjCodeLensProvider implements CodeLensProvider {
get language() { get language() {

View File

@ -3,7 +3,7 @@ import { MatSnackBar, MatDialog } from '@angular/material';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { MonacoFile, CodeLensProvider, CompletionProvider } from '../../monaco-editor/model'; import { MonacoFile, CodeLensProvider, CompletionProvider } from '../../monaco-editor';
import { DomainObjModificationService } from './domain-obj-modification.service'; import { DomainObjModificationService } from './domain-obj-modification.service';
import { DomainObjCodeLensProvider } from './domain-obj-code-lens-provider'; import { DomainObjCodeLensProvider } from './domain-obj-code-lens-provider';
import { DomainObjCompletionProvider } from './domain-obj-completion-provider'; import { DomainObjCompletionProvider } from './domain-obj-completion-provider';

View File

@ -11,7 +11,7 @@ import { MatIconModule } from '@angular/material/icon';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { DomainObjModificationComponent } from './domain-obj-modification.component'; import { DomainObjModificationComponent } from './domain-obj-modification.component';
import { MonacoEditorModule } from '../../monaco-editor/monaco-editor.module'; import { MonacoEditorModule } from '../../monaco-editor';
import { SharedModule } from '../../shared/shared.module'; import { SharedModule } from '../../shared/shared.module';
import { ResetConfirmDialogComponent } from './reset-confirm-dialog/reset-confirm-dialog.component'; import { ResetConfirmDialogComponent } from './reset-confirm-dialog/reset-confirm-dialog.component';

View File

@ -3,7 +3,7 @@ import { Router } from '@angular/router';
import { MatCheckboxChange, MatSnackBar } from '@angular/material'; import { MatCheckboxChange, MatSnackBar } from '@angular/material';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { MonacoFile, IDiffEditorOptions } from '../../monaco-editor/model'; import { MonacoFile, IDiffEditorOptions } from '../../monaco-editor';
import { toMonacoFile } from '../utils'; import { toMonacoFile } from '../utils';
import { DomainModificationModel } from '../domain-modification-model'; import { DomainModificationModel } from '../domain-modification-model';
import { DomainObjReviewService } from './domain-obj-review.service'; import { DomainObjReviewService } from './domain-obj-review.service';

View File

@ -10,7 +10,7 @@ import {
} from '@angular/material'; } from '@angular/material';
import { DomainObjReviewComponent } from './domain-obj-review.component'; import { DomainObjReviewComponent } from './domain-obj-review.component';
import { MonacoEditorModule } from '../../monaco-editor/monaco-editor.module'; import { MonacoEditorModule } from '../../monaco-editor';
import { SharedModule } from '../../shared/shared.module'; import { SharedModule } from '../../shared/shared.module';
@NgModule({ @NgModule({

View File

@ -4,7 +4,7 @@ import { DomainRoutingModule } from './domain-routing.module';
import { DomainService } from './domain.service'; import { DomainService } from './domain.service';
import { MetadataService } from './metadata.service'; import { MetadataService } from './metadata.service';
import { DomainObjModificationModule } from './domain-obj-modification'; import { DomainObjModificationModule } from './domain-obj-modification';
import { DomainInfoModule } from './domain-info/domain-info.module'; import { DomainInfoModule } from './domain-info';
import { DamselMetaModule } from '../damsel-meta/damsel-meta.module'; import { DamselMetaModule } from '../damsel-meta/damsel-meta.module';
import { DomainObjReviewModule } from './domain-obj-review'; import { DomainObjReviewModule } from './domain-obj-review';
import { DomainReviewService } from './domain-review.service'; import { DomainReviewService } from './domain-review.service';

View File

@ -5,11 +5,12 @@ import { DepositParams } from './gen-model/fistful';
import { DepositParams as DepositParamsObject } from './gen-nodejs/fistful_types'; import { DepositParams as DepositParamsObject } from './gen-nodejs/fistful_types';
import { ThriftService } from '../thrift'; import { ThriftService } from '../thrift';
import * as FistfulAdmin from './gen-nodejs/FistfulAdmin'; import * as FistfulAdmin from './gen-nodejs/FistfulAdmin';
import { KeycloakService } from 'keycloak-angular';
@Injectable() @Injectable()
export class FistfulAdminService extends ThriftService { export class FistfulAdminService extends ThriftService {
constructor(zone: NgZone) { constructor(zone: NgZone, keycloakService: KeycloakService) {
super(zone, '/v1/admin', FistfulAdmin); super(zone, keycloakService, '/v1/admin', FistfulAdmin);
} }
createDeposit(params: DepositParams): Observable<void> { createDeposit(params: DepositParams): Observable<void> {

View File

@ -5,11 +5,12 @@ import { ThriftService } from '../thrift';
import { RepairScenario, SessionID } from './gen-model/withdrawal_session'; import { RepairScenario, SessionID } from './gen-model/withdrawal_session';
import { RepairScenario as RepairScenarioObject } from './gen-nodejs/withdrawal_session_types'; import { RepairScenario as RepairScenarioObject } from './gen-nodejs/withdrawal_session_types';
import * as Repairer from './gen-nodejs/Repairer'; import * as Repairer from './gen-nodejs/Repairer';
import { KeycloakService } from 'keycloak-angular';
@Injectable() @Injectable()
export class RepairerService extends ThriftService { export class RepairerService extends ThriftService {
constructor(zone: NgZone) { constructor(zone: NgZone, keycloakService: KeycloakService) {
super(zone, '/v1/repair/withdrawal/session', Repairer); super(zone, keycloakService, '/v1/repair/withdrawal/session', Repairer);
} }
repair = (id: SessionID, scenario: RepairScenario): Observable<void> => repair = (id: SessionID, scenario: RepairScenario): Observable<void> =>

View File

@ -9,11 +9,12 @@ import {
import { ThriftService } from '../thrift'; import { ThriftService } from '../thrift';
import { Namespace } from './gen-model/base'; import { Namespace } from './gen-model/base';
import { Reference, MachineDescriptor, Machine } from './gen-model/state_processing'; import { Reference, MachineDescriptor, Machine } from './gen-model/state_processing';
import { KeycloakService } from 'keycloak-angular';
@Injectable() @Injectable()
export class AutomatonService extends ThriftService { export class AutomatonService extends ThriftService {
constructor(zone: NgZone) { constructor(zone: NgZone, keycloakService: KeycloakService) {
super(zone, '/v1/automaton', Automaton); super(zone, keycloakService, '/v1/automaton', Automaton);
} }
simpleRepair = (ns: Namespace, ref: Reference): Observable<void> => simpleRepair = (ns: Namespace, ref: Reference): Observable<void> =>

View File

@ -6,7 +6,8 @@ import { map } from 'rxjs/operators';
import { ClaimCreated, ClaimInfo, PartyModificationUnit } from './model'; import { ClaimCreated, ClaimInfo, PartyModificationUnit } from './model';
import { ConfigService } from '../core/config.service'; import { ConfigService } from '../core/config.service';
import { decode, encode } from '../shared/java-thrift-formatter'; import { decode, encode } from '../shared/java-thrift-formatter';
import { ClaimAcceptParams, ClaimDenyParams, ClaimSearchParams } from './params'; import { ClaimAcceptParams, ClaimDenyParams } from './params';
import { ClaimSearchQuery } from '../gen-damsel/claim_management';
@Injectable() @Injectable()
export class ClaimService { export class ClaimService {
@ -16,7 +17,7 @@ export class ClaimService {
this.papiEndpoint = configService.config.papiEndpoint; this.papiEndpoint = configService.config.papiEndpoint;
} }
getClaims(params: ClaimSearchParams): Observable<ClaimInfo[]> { getClaims(params: ClaimSearchQuery): Observable<ClaimInfo[]> {
return this.http.post<ClaimInfo[]>(`${this.papiEndpoint}/walk/claim/search`, params); return this.http.post<ClaimInfo[]>(`${this.papiEndpoint}/walk/claim/search`, params);
} }

View File

@ -2,5 +2,7 @@ export enum ClaimStatus {
accepted = 'accepted', accepted = 'accepted',
denied = 'denied', denied = 'denied',
revoked = 'revoked', revoked = 'revoked',
pending = 'pending' pending = 'pending',
review = 'review',
pending_acceptance = 'pending_acceptance'
} }

View File

@ -0,0 +1,14 @@
import { ClaimStatus as UnionClaimStatus } from '../gen-damsel/claim_management';
import { ClaimStatus } from '../papi/model/claim-statuses';
export const claimStatusByUnionClaimStatus: { [name in keyof UnionClaimStatus]-?: ClaimStatus } = {
accepted: ClaimStatus.accepted,
denied: ClaimStatus.denied,
revoked: ClaimStatus.revoked,
pending: ClaimStatus.pending,
review: ClaimStatus.review,
pending_acceptance: ClaimStatus.pending_acceptance
};
export const extractClaimStatus = (status: UnionClaimStatus): ClaimStatus =>
claimStatusByUnionClaimStatus[Object.keys(status)[0] as keyof UnionClaimStatus];

View File

@ -0,0 +1,11 @@
import { distinctUntilChanged, debounce } from 'rxjs/operators';
import { timer, empty, Observable } from 'rxjs';
export const booleanDebounceTime = (timeoutMs: number = 500) => (
s: Observable<boolean>
): Observable<boolean> =>
s.pipe(
distinctUntilChanged(),
debounce(v => (v ? timer(timeoutMs) : empty())),
distinctUntilChanged()
);

View File

@ -0,0 +1 @@
export * from './boolean-debounce-time';

View File

@ -0,0 +1,18 @@
import { Injectable, NgZone } from '@angular/core';
import { Observable } from 'rxjs';
import { ThriftService } from '../thrift';
import * as ClaimManagement from './gen-nodejs/ClaimManagement';
import { ClaimSearchQuery, ClaimSearchResponse } from '../gen-damsel/claim_management';
import { ClaimSearchQuery as ClaimSearchQueryType } from './gen-nodejs/claim_management_types';
import { KeycloakService } from 'keycloak-angular';
@Injectable()
export class ClaimManagementService extends ThriftService {
constructor(zone: NgZone, keycloakService: KeycloakService) {
super(zone, keycloakService, '/v1/cm', ClaimManagement);
}
searchClaims = (query: ClaimSearchQuery): Observable<ClaimSearchResponse> =>
this.toObservableAction('SearchClaims')(new ClaimSearchQueryType(query));
}

View File

@ -4,11 +4,12 @@ import { Observable } from 'rxjs';
import * as Repository from './gen-nodejs/Repository'; import * as Repository from './gen-nodejs/Repository';
import { ThriftService } from './thrift-service'; import { ThriftService } from './thrift-service';
import { Reference, Snapshot, Commit, Version, Limit } from '../gen-damsel/domain_config'; import { Reference, Snapshot, Commit, Version, Limit } from '../gen-damsel/domain_config';
import { KeycloakService } from 'keycloak-angular';
@Injectable() @Injectable()
export class DomainService extends ThriftService { export class DomainService extends ThriftService {
constructor(zone: NgZone) { constructor(zone: NgZone, keycloakService: KeycloakService) {
super(zone, '/v1/domain/repository', Repository); super(zone, keycloakService, '/v1/domain/repository', Repository);
} }
checkout: (reference: Reference) => Observable<Snapshot> = this.toObservableAction('Checkout'); checkout: (reference: Reference) => Observable<Snapshot> = this.toObservableAction('Checkout');

View File

@ -5,11 +5,12 @@ import * as MerchantStatistics from './gen-nodejs/MerchantStatistics';
import { ThriftService } from './thrift-service'; import { ThriftService } from './thrift-service';
import { StatRequest, StatResponse } from '../gen-damsel/merch_stat'; import { StatRequest, StatResponse } from '../gen-damsel/merch_stat';
import { StatRequest as ThriftStatRequest } from './gen-nodejs/merch_stat_types'; import { StatRequest as ThriftStatRequest } from './gen-nodejs/merch_stat_types';
import { KeycloakService } from 'keycloak-angular';
@Injectable() @Injectable()
export class MerchantStatisticsService extends ThriftService { export class MerchantStatisticsService extends ThriftService {
constructor(zone: NgZone) { constructor(zone: NgZone, keycloakService: KeycloakService) {
super(zone, '/stat', MerchantStatistics); super(zone, keycloakService, '/stat', MerchantStatistics);
} }
getPayments = (req: StatRequest): Observable<StatResponse> => getPayments = (req: StatRequest): Observable<StatResponse> =>

View File

@ -2,12 +2,7 @@ import cloneDeep from 'lodash-es/cloneDeep';
import last from 'lodash-es/last'; import last from 'lodash-es/last';
import dropRight from 'lodash-es/dropRight'; import dropRight from 'lodash-es/dropRight';
import { import { ProviderObject, TerminalDecision, TerminalRef } from '../../gen-damsel/domain';
ProviderObject,
TerminalSelector,
TerminalDecision,
TerminalRef
} from '../../gen-damsel/domain';
import { toGenTerminalDecision } from '../converters'; import { toGenTerminalDecision } from '../converters';
import { checkSelector } from './utils'; import { checkSelector } from './utils';

View File

@ -16,11 +16,12 @@ import { ThriftService } from './thrift-service';
import * as Invoicing from './gen-nodejs/Invoicing'; import * as Invoicing from './gen-nodejs/Invoicing';
import { InvoiceID } from '../gen-damsel/domain'; import { InvoiceID } from '../gen-damsel/domain';
import { share, switchMap, first } from 'rxjs/operators'; import { share, switchMap, first } from 'rxjs/operators';
import { KeycloakService } from 'keycloak-angular';
@Injectable() @Injectable()
export class PaymentProcessingService extends ThriftService { export class PaymentProcessingService extends ThriftService {
constructor(zone: NgZone) { constructor(zone: NgZone, keycloakService: KeycloakService) {
super(zone, '/v1/processing/invoicing', Invoicing); super(zone, keycloakService, '/v1/processing/invoicing', Invoicing);
} }
getPaymentAdjustment = ( getPaymentAdjustment = (

View File

@ -1,7 +1,9 @@
import { NgZone } from '@angular/core'; import { NgZone } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable, from } from 'rxjs';
import { timeout } from 'rxjs/operators'; import { map, timeout } from 'rxjs/operators';
import connectClient from 'woody_js'; import connectClient from 'woody_js';
import { KeycloakService } from 'keycloak-angular';
import * as jwtDecode from 'jwt-decode';
type Exception<N = string, T = {}> = { type Exception<N = string, T = {}> = {
name: N; name: N;
@ -9,10 +11,17 @@ type Exception<N = string, T = {}> = {
} & T; } & T;
export class ThriftService { export class ThriftService {
protected realm = 'internal';
protected endpoint: string; protected endpoint: string;
protected service: any; protected service: any;
constructor(private zone: NgZone, endpoint: string, thriftService: any) { constructor(
private zone: NgZone,
private keycloakService: KeycloakService,
endpoint: string,
thriftService: any
) {
this.endpoint = endpoint; this.endpoint = endpoint;
this.service = thriftService; this.service = thriftService;
} }
@ -20,30 +29,49 @@ export class ThriftService {
protected toObservableAction<T extends (...A: any[]) => Observable<any>>(name: string): T { protected toObservableAction<T extends (...A: any[]) => Observable<any>>(name: string): T {
return ((...args) => return ((...args) =>
Observable.create(observer => { Observable.create(observer => {
this.zone.run(() => { const cb = msg => {
try {
this.createClient(msg => {
observer.error(msg); observer.error(msg);
observer.complete(); observer.complete();
})[name](...args, (ex: Exception, result) => { };
this.zone.run(() => {
try {
this.createClient(cb).subscribe(client =>
client[name](...args, (ex: Exception, result) => {
ex ? observer.error(ex) : observer.next(result); ex ? observer.error(ex) : observer.next(result);
observer.complete(); observer.complete();
}); })
);
} catch (e) { } catch (e) {
observer.error(e); cb(e);
observer.complete();
} }
}); });
}).pipe(timeout(60000))) as any; }).pipe(timeout(60000))) as any;
} }
private createClient(errorCb: Function) { private createClient(errorCb: Function): Observable<any> {
return from(this.keycloakService.getToken()).pipe(
map(token => {
const { email, preferred_username, sub } = jwtDecode(token);
return connectClient( return connectClient(
location.hostname, location.hostname,
location.port, location.port,
this.endpoint, this.endpoint,
this.service, this.service,
{
headers: {
'woody.meta-user-identity.email': email,
'woody.meta-user-identity.realm': 'external',
'woody.meta-user-identity.username': preferred_username,
'woody.meta-user-identity.id': sub,
'x-rbk-meta-user-identity.email': email,
'x-rbk-meta-user-identity.realm': 'external',
'x-rbk-meta-user-identity.username': preferred_username,
'x-rbk-meta-user-identity.id': sub
}
},
errorCb errorCb
); );
})
);
} }
} }

View File

@ -5,6 +5,7 @@ import { PaymentProcessingService } from './payment-processing.service';
import { MerchantStatisticsService } from './merchant-statistics.service'; import { MerchantStatisticsService } from './merchant-statistics.service';
import { DomainTypedManager } from './domain-typed-manager'; import { DomainTypedManager } from './domain-typed-manager';
import { DomainCacheService } from './domain-cache.service'; import { DomainCacheService } from './domain-cache.service';
import { ClaimManagementService } from './claim-management.service';
@NgModule({ @NgModule({
providers: [ providers: [
@ -12,7 +13,8 @@ import { DomainCacheService } from './domain-cache.service';
DomainTypedManager, DomainTypedManager,
PaymentProcessingService, PaymentProcessingService,
MerchantStatisticsService, MerchantStatisticsService,
DomainCacheService DomainCacheService,
ClaimManagementService
] ]
}) })
export class ThriftModule {} export class ThriftModule {}

View File

@ -5,7 +5,7 @@
"outDir": "./dist/out-tsc", "outDir": "./dist/out-tsc",
"sourceMap": true, "sourceMap": true,
"declaration": false, "declaration": false,
"module": "es2015", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"experimentalDecorators": true, "experimentalDecorators": true,