mirror of
https://github.com/valitydev/control-center.git
synced 2024-11-06 02:25:17 +00:00
FRONTEND-507: New thrift client integration (#259)
This commit is contained in:
parent
fbde1e47c1
commit
99071f0335
4
package-lock.json
generated
4
package-lock.json
generated
@ -9015,8 +9015,8 @@
|
||||
}
|
||||
},
|
||||
"fistful-proto": {
|
||||
"version": "git+ssh://git@github.com/rbkmoney/fistful-proto.git#7b3f125aa7cbc069f740f598295bb56de713c39f",
|
||||
"from": "git+ssh://git@github.com/rbkmoney/fistful-proto.git#7b3f125aa7cbc069f740f598295bb56de713c39f"
|
||||
"version": "git+ssh://git@github.com/rbkmoney/fistful-proto.git#e340259cdd3add024f0139e21f0a2453312ef901",
|
||||
"from": "git+ssh://git@github.com/rbkmoney/fistful-proto.git#e340259cdd3add024f0139e21f0a2453312ef901"
|
||||
},
|
||||
"flake-idgen": {
|
||||
"version": "1.1.0",
|
||||
|
@ -40,7 +40,7 @@
|
||||
"damsel": "git+ssh://git@github.com/rbkmoney/damsel.git#8851c242d2953cc52397af3d916b52b164ffe4c0",
|
||||
"deanonimus-proto": "github:rbkmoney/deanonimus-proto#b9fab4fd1c7690186efdc5974d113c82bd5765e9",
|
||||
"file-storage-proto": "git+ssh://git@github.com:rbkmoney/file-storage-proto.git#281e1ca4cea9bf32229a6c389f0dcf5d49c05a0b",
|
||||
"fistful-proto": "git+ssh://git@github.com/rbkmoney/fistful-proto.git#7b3f125aa7cbc069f740f598295bb56de713c39f",
|
||||
"fistful-proto": "git+ssh://git@github.com/rbkmoney/fistful-proto.git#e340259cdd3add024f0139e21f0a2453312ef901",
|
||||
"humanize-duration": "~3.21.0",
|
||||
"jsonc-parser": "~2.0.2",
|
||||
"keycloak-angular": "^8.0.1",
|
||||
|
1
src/app/api/fistful/index.ts
Normal file
1
src/app/api/fistful/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './wallet';
|
2
src/app/api/fistful/wallet/index.ts
Normal file
2
src/app/api/fistful/wallet/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './wallet.module';
|
||||
export * from './management.service';
|
20
src/app/api/fistful/wallet/management.service.ts
Normal file
20
src/app/api/fistful/wallet/management.service.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { KeycloakTokenInfoService } from '@cc/app/shared/services';
|
||||
|
||||
import { ThriftConnector } from '../../thrift-connector';
|
||||
import { WalletState, EventRange as EventRangeModel } from '../gen-model/wallet';
|
||||
import * as Management from './gen-nodejs/Management';
|
||||
import { EventRange } from './gen-nodejs/base_types';
|
||||
|
||||
@Injectable()
|
||||
export class ManagementService extends ThriftConnector {
|
||||
constructor(protected keycloakTokenInfoService: KeycloakTokenInfoService) {
|
||||
super(keycloakTokenInfoService, Management, '/v1/wallet');
|
||||
}
|
||||
|
||||
get(walletID: string, range: EventRangeModel = new EventRange()): Observable<WalletState> {
|
||||
return this.callThriftServiceMethod('Get', walletID, range);
|
||||
}
|
||||
}
|
8
src/app/api/fistful/wallet/wallet.module.ts
Normal file
8
src/app/api/fistful/wallet/wallet.module.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { ManagementService } from './management.service';
|
||||
|
||||
@NgModule({
|
||||
providers: [ManagementService],
|
||||
})
|
||||
export class WalletModule {}
|
1
src/app/api/index.ts
Normal file
1
src/app/api/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './fistful';
|
1
src/app/api/thrift-connector/index.ts
Normal file
1
src/app/api/thrift-connector/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './thrift-connector';
|
44
src/app/api/thrift-connector/thrift-connector.ts
Normal file
44
src/app/api/thrift-connector/thrift-connector.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { switchMap, shareReplay, map, first } from 'rxjs/operators';
|
||||
|
||||
import { KeycloakTokenInfoService } from '@cc/app/shared/services';
|
||||
|
||||
import {
|
||||
connectToThriftService,
|
||||
prepareThriftServiceMethod,
|
||||
toConnectOptions,
|
||||
ThriftService,
|
||||
ThriftServiceConnection,
|
||||
} from './utils';
|
||||
|
||||
export class ThriftConnector {
|
||||
private connection$: Observable<ThriftServiceConnection>;
|
||||
|
||||
constructor(
|
||||
protected keycloakTokenInfoService: KeycloakTokenInfoService,
|
||||
protected service: ThriftService,
|
||||
protected endpoint: string
|
||||
) {
|
||||
this.connection$ = this.keycloakTokenInfoService.decoded$.pipe(
|
||||
map((token) => toConnectOptions(token)),
|
||||
switchMap((connectOptions) =>
|
||||
connectToThriftService(endpoint, service, connectOptions)
|
||||
),
|
||||
shareReplay({
|
||||
bufferSize: 1,
|
||||
refCount: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
protected callThriftServiceMethod<T, P extends any[]>(
|
||||
serviceMethodName: string,
|
||||
...args: P
|
||||
): Observable<T> {
|
||||
return this.connection$.pipe(
|
||||
first(),
|
||||
map((connection) => prepareThriftServiceMethod<T>(connection, serviceMethodName)),
|
||||
switchMap((fn) => fn(...args))
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import connectClient from 'woody_js';
|
||||
import { ConnectOptions } from 'woody_js/src/connect-options';
|
||||
|
||||
import { ThriftService, ThriftServiceConnection } from './types';
|
||||
|
||||
export const connectToThriftService = (
|
||||
endpoint: string,
|
||||
service: ThriftService,
|
||||
connectionOptions: ConnectOptions,
|
||||
hostname: string = location.hostname,
|
||||
port: string = location.port
|
||||
): Observable<ThriftServiceConnection> =>
|
||||
new Observable((observer) => {
|
||||
const connection = connectClient(
|
||||
hostname,
|
||||
port,
|
||||
endpoint,
|
||||
service,
|
||||
connectionOptions,
|
||||
(err) => {
|
||||
observer.error(err);
|
||||
observer.complete();
|
||||
}
|
||||
);
|
||||
observer.next(connection);
|
||||
observer.complete();
|
||||
});
|
4
src/app/api/thrift-connector/utils/index.ts
Normal file
4
src/app/api/thrift-connector/utils/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './connect-to-thrift-service';
|
||||
export * from './prepare-thrift-service-method';
|
||||
export * from './to-connect-options';
|
||||
export * from './types';
|
@ -0,0 +1,20 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import isNil from 'lodash-es/isNil';
|
||||
|
||||
import { ThriftServiceConnection, ThriftServiceMethod } from './types';
|
||||
|
||||
export const prepareThriftServiceMethod = <T>(
|
||||
connection: ThriftServiceConnection,
|
||||
serviceMethodName: string
|
||||
): ThriftServiceMethod<T> => (...args): Observable<T> =>
|
||||
new Observable((observer) => {
|
||||
const serviceMethod = connection[serviceMethodName];
|
||||
if (isNil(serviceMethod)) {
|
||||
observer.error(`Service method: "${serviceMethodName}" is not found in thrift client`);
|
||||
observer.complete();
|
||||
}
|
||||
serviceMethod.bind(connection)(...args, (err, result) => {
|
||||
err ? observer.error(err) : observer.next(result);
|
||||
observer.complete();
|
||||
});
|
||||
});
|
32
src/app/api/thrift-connector/utils/to-connect-options.ts
Normal file
32
src/app/api/thrift-connector/utils/to-connect-options.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { ConnectOptions } from 'woody_js/src/connect-options';
|
||||
|
||||
import { KeycloakToken } from '@cc/app/shared/services';
|
||||
|
||||
const toDepricatedHeaders = (email: string, username: string, partyID: string, realm: string) => ({
|
||||
'x-rbk-meta-user-identity.email': email,
|
||||
'x-rbk-meta-user-identity.realm': realm,
|
||||
'x-rbk-meta-user-identity.username': username,
|
||||
'x-rbk-meta-user-identity.id': partyID,
|
||||
});
|
||||
|
||||
const toHeaders = (email: string, username: string, partyID: string, realm: string) => ({
|
||||
'woody.meta.user-identity.email': email,
|
||||
'woody.meta.user-identity.realm': realm,
|
||||
'woody.meta.user-identity.username': username,
|
||||
'woody.meta.user-identity.id': partyID,
|
||||
});
|
||||
|
||||
export const toConnectOptions = (
|
||||
{ email, name, sub }: KeycloakToken,
|
||||
deprecatedHeaders = false,
|
||||
realm = 'internal'
|
||||
): ConnectOptions => ({
|
||||
headers: {
|
||||
...toHeaders(email, name, sub, realm),
|
||||
...(deprecatedHeaders ? toDepricatedHeaders(email, name, sub, realm) : undefined),
|
||||
},
|
||||
deadlineConfig: {
|
||||
amount: 3,
|
||||
unitOfTime: 'm',
|
||||
},
|
||||
});
|
5
src/app/api/thrift-connector/utils/types.ts
Normal file
5
src/app/api/thrift-connector/utils/types.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
export type ThriftService = any;
|
||||
export type ThriftServiceConnection = any;
|
||||
export type ThriftServiceMethod<T> = (...args) => Observable<T>;
|
@ -16,6 +16,8 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import * as moment from 'moment';
|
||||
import 'moment/locale/ru';
|
||||
|
||||
import { KeycloakTokenInfoModule } from '@cc/app/shared/services';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { ClaimMgtModule } from './claim-mgt/claim-mgt.module';
|
||||
@ -78,6 +80,7 @@ moment.locale('en');
|
||||
SearchClaimsModule,
|
||||
OperationsModule,
|
||||
DomainConfigModule,
|
||||
KeycloakTokenInfoModule,
|
||||
// It is important that NotFoundModule module should be last
|
||||
NotFoundModule,
|
||||
],
|
||||
|
@ -1,30 +1,35 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { merge, NEVER, ReplaySubject } from 'rxjs';
|
||||
import { catchError, switchMap, shareReplay } from 'rxjs/operators';
|
||||
import { progress } from '@rbkmoney/partial-fetcher/dist/progress';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { BehaviorSubject, NEVER, ReplaySubject, Subject } from 'rxjs';
|
||||
import { catchError, switchMap, shareReplay, tap } from 'rxjs/operators';
|
||||
|
||||
import { WalletManagementService } from '../../../../../thrift-services/fistful/wallet-management.service';
|
||||
import { ManagementService as WalletManagementService } from '@cc/app/api/fistful';
|
||||
|
||||
@UntilDestroy()
|
||||
@Injectable()
|
||||
export class ReceiveWalletService {
|
||||
private receiveWallet$ = new ReplaySubject<string>();
|
||||
private error$ = new ReplaySubject<boolean>();
|
||||
private error$ = new Subject<boolean>();
|
||||
private loading$ = new BehaviorSubject(false);
|
||||
|
||||
wallet$ = this.receiveWallet$.pipe(
|
||||
tap(() => this.loading$.next(true)),
|
||||
switchMap((id) =>
|
||||
this.walletManagementService.getWallet(id).pipe(
|
||||
this.walletManagementService.get(id).pipe(
|
||||
catchError((e) => {
|
||||
console.log(e);
|
||||
console.error(e);
|
||||
this.loading$.next(false);
|
||||
this.error$.next(true);
|
||||
return NEVER;
|
||||
})
|
||||
)
|
||||
),
|
||||
tap(() => this.loading$.next(false)),
|
||||
untilDestroyed(this),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
isLoading$ = progress(this.receiveWallet$, merge(this.wallet$, this.error$));
|
||||
|
||||
isLoading$ = this.loading$.asObservable();
|
||||
hasError$ = this.error$.asObservable();
|
||||
|
||||
constructor(private walletManagementService: WalletManagementService) {}
|
||||
|
@ -1,4 +0,0 @@
|
||||
export interface ReceiveWalletParams {
|
||||
destinationID: string;
|
||||
identityID: string;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
<span *ngIf="wallet$ | async as wallet"> {{ walletID }} - {{ wallet.name }} </span>
|
||||
<span *ngIf="wallet$ | async as wallet"> {{ wallet.id }} - {{ wallet.name }} </span>
|
||||
<span *ngIf="isLoading$ | async">
|
||||
Loading...
|
||||
</span>
|
||||
<span *ngIf="hasError$ | async">
|
||||
An error occurred while destination receiving
|
||||
An error occurred while wallet receiving
|
||||
</span>
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { FistfulModule } from '../../../thrift-services/fistful/fistful.module';
|
||||
import { WalletModule } from '@cc/app/api/fistful/wallet';
|
||||
|
||||
import { WalletInfoComponent } from './wallet-info.component';
|
||||
|
||||
const DECLARATIONS = [WalletInfoComponent];
|
||||
@ -9,6 +10,6 @@ const DECLARATIONS = [WalletInfoComponent];
|
||||
@NgModule({
|
||||
declarations: DECLARATIONS,
|
||||
exports: DECLARATIONS,
|
||||
imports: [CommonModule, FistfulModule],
|
||||
imports: [CommonModule, WalletModule],
|
||||
})
|
||||
export class WalletInfoModule {}
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from './query-params-store';
|
||||
export * from './app-auth-guard';
|
||||
export * from './fetch-parties.service';
|
||||
export * from './keycloak-token-info';
|
||||
|
3
src/app/shared/services/keycloak-token-info/index.ts
Normal file
3
src/app/shared/services/keycloak-token-info/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './keycloak-token-info.module';
|
||||
export * from './keycloak-token-info.service';
|
||||
export * from './types';
|
@ -0,0 +1,8 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { KeycloakTokenInfoService } from './keycloak-token-info.service';
|
||||
|
||||
@NgModule({
|
||||
providers: [KeycloakTokenInfoService],
|
||||
})
|
||||
export class KeycloakTokenInfoModule {}
|
@ -0,0 +1,20 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import jwt_decode from 'jwt-decode';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import { from, Observable } from 'rxjs';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
|
||||
import { KeycloakToken } from './types/keycloak-token';
|
||||
|
||||
@UntilDestroy()
|
||||
@Injectable()
|
||||
export class KeycloakTokenInfoService {
|
||||
decoded$: Observable<KeycloakToken> = from(this.keycloakService.getToken()).pipe(
|
||||
map((token) => jwt_decode<KeycloakToken>(token)),
|
||||
untilDestroyed(this),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
constructor(private keycloakService: KeycloakService) {}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export * from './keycloak-token';
|
@ -0,0 +1,24 @@
|
||||
export interface KeycloakToken {
|
||||
acr: string;
|
||||
'allowed-origins': string[];
|
||||
aud: string;
|
||||
auth_time: number;
|
||||
azp: string;
|
||||
email: string;
|
||||
exp: number;
|
||||
family_name: string;
|
||||
given_name: string;
|
||||
iat: number;
|
||||
iss: string;
|
||||
jti: string;
|
||||
name: string;
|
||||
nbf: number;
|
||||
nonce: string;
|
||||
preferred_username: string;
|
||||
realm_access: object;
|
||||
resource_access: object;
|
||||
scope: string;
|
||||
session_state: string;
|
||||
sub: string;
|
||||
typ: string;
|
||||
}
|
@ -2,15 +2,9 @@ import { NgModule } from '@angular/core';
|
||||
|
||||
import { FistfulAdminService } from './fistful-admin.service';
|
||||
import { RepairerService } from './repairer.service';
|
||||
import { WalletManagementService } from './wallet-management.service';
|
||||
import { RevertManagementService } from './revert-management.service';
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
RepairerService,
|
||||
FistfulAdminService,
|
||||
WalletManagementService,
|
||||
RevertManagementService,
|
||||
],
|
||||
providers: [RepairerService, FistfulAdminService, RevertManagementService],
|
||||
})
|
||||
export class FistfulModule {}
|
||||
|
@ -1,26 +0,0 @@
|
||||
import { Injectable, NgZone } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
|
||||
import { ThriftService } from '../services/thrift/thrift-service';
|
||||
import * as WalletManagement from './gen-nodejs/Management';
|
||||
import { EventRange, WalletState } from './gen-model/wallet';
|
||||
import { EventRange as ApiEventRange } from './gen-nodejs/base_types';
|
||||
|
||||
@Injectable()
|
||||
export class WalletManagementService extends ThriftService {
|
||||
constructor(keycloakTokenInfoService: KeycloakTokenInfoService, zone: NgZone) {
|
||||
super(zone, keycloakTokenInfoService, '/v1/wallet', WalletManagement);
|
||||
}
|
||||
|
||||
// @TODO thrift have many Get methods inside different Management services, that's why method returns DepositState with WalletState values
|
||||
getWallet(id: string, range: EventRange = new ApiEventRange()): Observable<WalletState> {
|
||||
return this.toObservableAction('Get')(id, range).pipe(
|
||||
map(
|
||||
(depositState) =>
|
||||
({ id: depositState.source_id, name: depositState.id } as WalletState)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -116,8 +116,12 @@ function toPathConfig(
|
||||
};
|
||||
}
|
||||
|
||||
async function clear({ model, meta, services }: PathsConfig) {
|
||||
await del([model.outputFolder, ...services.map((s) => s.outputFolder), meta.outputFile]);
|
||||
async function clear({ model, meta, services }: PathsConfig, outputServiceDirName = 'gen-nodejs') {
|
||||
await del([
|
||||
model.outputFolder,
|
||||
...services.map((s) => path.join(s.outputFolder, outputServiceDirName)),
|
||||
meta.outputFile,
|
||||
]);
|
||||
}
|
||||
|
||||
function prepareOutputDirs({ services, outputNamespacePath }: PathsConfig) {
|
||||
|
@ -19,7 +19,8 @@
|
||||
"paths": {
|
||||
"@cc/components/*": ["src/components/*"],
|
||||
"@cc/utils/*": ["src/utils/*"],
|
||||
"@cc/app/shared/*": ["src/app/shared/*"]
|
||||
"@cc/app/shared/*": ["src/app/shared/*"],
|
||||
"@cc/app/api/*": ["src/app/api/*"]
|
||||
}
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
|
Loading…
Reference in New Issue
Block a user