-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Mohaban, Assaf <[email protected]>
- Loading branch information
Showing
6 changed files
with
178 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
|
||
export class HttpAttributes { | ||
static ClientIdAttribute = "X-RPC-CLIENT-ID"; | ||
static ClientPollingPeriod = 500; | ||
static ClientPollingRetryCount = 10; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import {RxRpcHttpConnection, RxRpcHttpTransport} from './rxrpc-http-transport'; | ||
import axios from 'axios'; | ||
|
||
jest.mock('axios'); | ||
const mockedAxios = axios as jest.Mocked<typeof axios> | ||
|
||
function delay(ms: number){ | ||
return new Promise( resolve => setTimeout(resolve, ms) ); | ||
} | ||
|
||
describe('RxRpc Http Transport test suite', function () { | ||
let transport: RxRpcHttpTransport; | ||
let clientId: string; | ||
let incomingMessages: any[]; | ||
let resp: {} | ||
|
||
beforeEach(() => { | ||
transport = new RxRpcHttpTransport("http://funnyName/"); | ||
clientId = "12345678"; | ||
incomingMessages = []; | ||
resp = { | ||
headers: {"x-rpc-client-id": clientId} | ||
}; | ||
}); | ||
|
||
it('Connect', async () => { | ||
mockedAxios.post.mockImplementation(() => Promise.resolve(resp)); | ||
|
||
transport.connect().subscribe(connection => incomingMessages.push(connection['clientId'])) | ||
await delay(1000); | ||
expect(incomingMessages[0]).toEqual(clientId) | ||
}) | ||
|
||
it('Poll with data as a string', async () => { | ||
const data1 = "{\"invocationId\":1,\"result\":{\"type\":\"Data\",\"data\":\"Hello, Angular #0\",\"error\":null}}" | ||
const data2 = "{\"invocationId\":1,\"result\":{\"type\":\"Data\",\"data\":\"Hello, Angular #1\",\"error\":null}}" | ||
resp['data'] = `${data1}\n${data2}` | ||
mockedAxios.post.mockImplementation(() => Promise.resolve(resp)); | ||
|
||
transport.connect().subscribe(connection => connection.poll().subscribe(msg => { | ||
incomingMessages.push(msg); | ||
})) | ||
await delay(1000); | ||
expect(incomingMessages.length).toEqual(2); | ||
expect(incomingMessages[0]).toEqual(JSON.parse(data1)); | ||
expect(incomingMessages[1]).toEqual(JSON.parse(data2)); | ||
}) | ||
|
||
it('Poll with data as an object', async () => { | ||
const data = "{\"invocationId\":1,\"result\":{\"type\":\"Data\",\"data\":\"Hello, Angular #0\",\"error\":null}}" | ||
resp['data'] = JSON.parse(data) | ||
mockedAxios.post.mockImplementation(() => Promise.resolve(resp)); | ||
|
||
transport.connect().subscribe(connection => connection.poll().subscribe(msg => { | ||
incomingMessages.push(msg); | ||
})) | ||
await delay(1000); | ||
expect(incomingMessages.length).toEqual(1); | ||
expect(incomingMessages[0]).toEqual(JSON.parse(data)); | ||
}) | ||
|
||
it('Close', async () => { | ||
mockedAxios.post.mockImplementation(() => Promise.resolve(resp)); | ||
|
||
transport.connect().subscribe(connection => incomingMessages.push(connection)) | ||
await delay(1000); | ||
const connection = incomingMessages[0] as RxRpcHttpConnection; | ||
connection.close() | ||
expect(connection['pollingSubscription'].closed).toEqual(true) | ||
}) | ||
|
||
it('Error', async () => { | ||
mockedAxios.post.mockImplementation(() => Promise.resolve(resp)); | ||
|
||
transport.connect().subscribe(connection => incomingMessages.push(connection)) | ||
await delay(1000); | ||
const connection = incomingMessages[0] as RxRpcHttpConnection; | ||
connection.error(null) | ||
expect(connection['pollingSubscription'].closed).toEqual(true) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import {RxRpcConnection, RxRpcTransport} from './rxrpc-transport'; | ||
import {from, interval, observable, Observable, of, Subject, Subscription} from 'rxjs'; | ||
import {mergeMap, map, retry, tap, filter} from "rxjs/operators"; | ||
import {HttpAttributes} from "./rxrpc-http-attributes"; | ||
import axios, {AxiosResponse} from 'axios'; | ||
import {fromPromise} from "rxjs/internal-compatibility"; | ||
import {fromArray} from "rxjs/internal/observable/fromArray"; | ||
|
||
|
||
export class RxRpcHttpConnection implements RxRpcConnection { | ||
readonly messages: Observable<any>; | ||
private pollingSubscription: Subscription; | ||
private readonly incoming = new Subject<any>(); | ||
|
||
constructor(private readonly uri: string, private readonly clientId: string) { | ||
this.messages = this.incoming; | ||
this.pollingSubscription = interval(HttpAttributes.ClientPollingPeriod) | ||
.pipe( | ||
mergeMap(() => this.poll()), | ||
retry(HttpAttributes.ClientPollingRetryCount)) | ||
.subscribe( | ||
() => {}, | ||
err => this.incoming.error(err), | ||
() => this.incoming.complete()); | ||
} | ||
|
||
close() { | ||
this.pollingSubscription.unsubscribe(); | ||
} | ||
|
||
error(error: any) { | ||
this.close(); | ||
} | ||
|
||
send(msg: any) { | ||
this.post('message', msg).subscribe(); | ||
} | ||
|
||
poll(): Observable<any> { | ||
return this.post('polling') | ||
.pipe( | ||
map(resp => resp.data), | ||
filter(data => data !== ""), | ||
mergeMap(data => { | ||
if(typeof data === 'string') { | ||
return fromArray(data.split("\n").map(s => JSON.parse(s))); | ||
} | ||
return of(data); | ||
}), | ||
tap(obj => this.incoming.next(obj))); | ||
} | ||
|
||
post(path: string, msg?: any): Observable<AxiosResponse<string>> { | ||
const headers = {}; | ||
headers[HttpAttributes.ClientIdAttribute] = this.clientId; | ||
return fromPromise(axios.post<string>(`${this.uri}/${path}`, msg, {headers: headers})) | ||
} | ||
} | ||
|
||
export class RxRpcHttpTransport implements RxRpcTransport { | ||
|
||
constructor(private readonly uri: string) {} | ||
|
||
connect(): Observable<RxRpcHttpConnection> { | ||
return fromPromise(axios.post<string>(`${this.uri}/connect`)) | ||
.pipe( | ||
map( res => { | ||
const clientId = res.headers[HttpAttributes.ClientIdAttribute.toLowerCase()]; | ||
return new RxRpcHttpConnection(this.uri, clientId); | ||
})); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters