rpc-js/src/client/client.ts
2018-08-26 23:57:43 +09:00

181 lines
4.1 KiB
TypeScript

import { Observable, Subject } from 'rxjs';
import { Message } from '../core/type';
import {
ClientCodec,
ClientNotificationCodec,
ClientResponseCodec
} from '../protocol/client_codec';
import { RPCClientError } from '../protocol/error';
import { ClientRWC } from './client_rwc';
export interface RequestState {
subject: Subject<any>;
request: {
method: string;
params: any[] | null;
};
}
export abstract class Client {
private requestID: number;
private pendingRequestsCount: number;
private pendingRequests: Map<number, RequestState>;
private notiSubject: Subject<ClientNotificationCodec> | undefined;
protected clientCodec: ClientCodec;
protected clientRWC: ClientRWC;
public constructor(
clientCodec: ClientCodec,
clientRWC: ClientRWC,
) {
this.clientCodec = clientCodec;
this.clientRWC = clientRWC;
this.requestID = 0;
this.pendingRequestsCount = 0;
this.pendingRequests = new Map();
}
private getRequestID(): number {
return ++this.requestID;
}
public getPendingRequestsCount(): number {
return this.pendingRequestsCount;
}
/**
* connect
*/
public connect(queryString?: string): void {
this.clientRWC.connect(queryString);
this.clientRWC.read().subscribe(
(value: Message) => {
this.onMessage(value);
},
(error: any) => {
console.error(error);
},
() => {
console.log('');
},
);
}
/**
* close
*/
public disconnect() {
this.clientRWC.disconnect();
}
/**
* notify
*/
public send(method: string, ...args: any[]): void {
this.sendInternal(false, method, args);
}
/**
* call
*/
public call<T>(method: string, ...args: any[]): Observable<T> {
const o = this.sendInternal<T>(true, method, args);
if (undefined === o) {
throw new Error('Error');
}
return o;
}
/**
* callTimeout
*/
public callTimeout<T>(ms: number, method: string, ...args: any[]): Observable<T> | undefined {
return undefined;
}
private sendInternal<T>(hasResponse: boolean, method: string, args?: any[]): Observable<T> | undefined {
let id = -1;
let resSubject: Subject<T> | undefined;
const params = undefined === args ? null : args;
if (hasResponse) {
id = this.getRequestID();
resSubject = new Subject<T>();
const reqState: RequestState = {
subject: resSubject,
request: {
method,
params,
},
};
this.pendingRequests.set(id, reqState);
this.pendingRequestsCount++;
}
this.clientRWC.write(this.clientCodec.request(method, params, id));
if (undefined !== resSubject) {
return resSubject.asObservable();
}
return undefined;
}
private onMessage(message: Message): void {
const resCodec = this.clientCodec.response(message);
if (resCodec.isNotification()) {
const notiCodec = resCodec.notification();
if (undefined !== notiCodec) {
if (undefined !== this.notiSubject) {
this.notiSubject.next(resCodec.notification());
}
}
} else {
this.onResponse(resCodec);
}
}
protected onResponse(resCodec: ClientResponseCodec): void {
const id = resCodec.id();
const result = resCodec.result();
const error = resCodec.error();
if (undefined === id) {
throw new Error(`This is not response because does not have ID. ${resCodec}`);
}
const reqState = this.pendingRequests.get(id);
if (undefined === reqState) {
console.log(`The message does not exist. ${resCodec}`);
return;
}
this.pendingRequests.delete(id);
this.pendingRequestsCount--;
if (undefined !== error) {
const rpcClientError: RPCClientError = {
request: reqState.request,
response: error,
};
console.error(rpcClientError);
reqState.subject.error(rpcClientError);
} else {
reqState.subject.next(result);
}
}
public notification(): Observable<ClientNotificationCodec> {
if (undefined === this.notiSubject) {
this.notiSubject = new Subject<ClientNotificationCodec>();
}
return this.notiSubject.asObservable();
}
}