forked from linagora/jmap-client-ts
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Related to linagora#33
- Loading branch information
Showing
8 changed files
with
355 additions
and
18 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,142 @@ | ||
import { defer, Observable, Subject } from 'rxjs'; | ||
import { Client } from '.'; | ||
import { ENTITY_TYPES, IEntityType, IStateChange, ITypeState, IWebSocketPushEnable } from './types'; | ||
import { Transport } from './utils/transport'; | ||
import * as ws from 'isomorphic-ws'; | ||
|
||
export class PushClient { | ||
private client: Client; | ||
private transport: Transport; | ||
private httpHeaders: { [headerName: string]: string }; | ||
private webSocket?: ws; | ||
private webSocketSubjects: { [_ in IEntityType]: Subject<{ [accountId: string]: string }> }; | ||
|
||
private started?: Promise<void>; | ||
private enabledDataTypes: { [_ in IEntityType]?: boolean }; | ||
|
||
constructor({ | ||
client, | ||
transport, | ||
httpHeaders, | ||
}: { | ||
client: Client; | ||
transport: Transport; | ||
httpHeaders: { [headerName: string]: string }; | ||
}) { | ||
this.client = client; | ||
this.transport = transport; | ||
this.httpHeaders = httpHeaders; | ||
this.webSocketSubjects = { | ||
Mailbox: new Subject(), | ||
Email: new Subject(), | ||
EmailSubmission: new Subject(), | ||
}; | ||
this.enabledDataTypes = {}; | ||
} | ||
|
||
public start(): Promise<void> { | ||
if (!this.started) { | ||
this.started = new Promise(resolve => { | ||
this.transport | ||
.post<{ | ||
value: string; | ||
}>(`${this.client.getApiUrl()}/ws/ticket`, '', this.httpHeaders) | ||
.then(response => { | ||
const ticket = response.value; | ||
const webSocket = new ws(`${this.client.getPushUrl()}?ticket=${ticket}`); | ||
webSocket.onopen = () => { | ||
this.webSocket = webSocket; | ||
this.sendSubscriptions(webSocket, this.enabledDataTypes); | ||
resolve(); | ||
}; | ||
webSocket.onclose = () => { | ||
for (const subject of Object.values(this.webSocketSubjects)) { | ||
subject.complete(); | ||
} | ||
|
||
delete this.webSocket; | ||
}; | ||
webSocket.onmessage = message => { | ||
const data = JSON.parse(message.data as string); | ||
if (data['@type'] == 'StateChange') { | ||
for (const entityType of ENTITY_TYPES) { | ||
if (this.stateChangeContainsType(data, entityType)) { | ||
this.webSocketSubjects[entityType].next( | ||
this.transformStateChange(data.changed, entityType), | ||
); | ||
} | ||
} | ||
} | ||
}; | ||
webSocket.onerror = event => { | ||
console.log(`Error with websocket: ${JSON.stringify(event)}`); | ||
for (const subject of Object.values(this.webSocketSubjects)) { | ||
subject.error(event); | ||
} | ||
|
||
delete this.webSocket; | ||
}; | ||
}); | ||
}); | ||
} | ||
|
||
return this.started; | ||
} | ||
|
||
public stop(): void { | ||
this.webSocket?.close(); | ||
} | ||
|
||
public mailbox(): Observable<{ [accountId: string]: string }> { | ||
return defer(() => { | ||
if (!this.enabledDataTypes.Mailbox) { | ||
this.enabledDataTypes.Mailbox = true; | ||
|
||
if (this.webSocket) { | ||
this.sendSubscriptions(this.webSocket, this.enabledDataTypes); | ||
} | ||
} | ||
|
||
return this.webSocketSubjects.Mailbox.asObservable(); | ||
}); | ||
} | ||
|
||
private sendSubscriptions( | ||
webSocket: ws, | ||
enabledDataTypes: { | ||
[_ in IEntityType]?: boolean; | ||
}, | ||
) { | ||
const payload: IWebSocketPushEnable = { | ||
'@type': 'WebSocketPushEnable', | ||
dataTypes: Object.keys(enabledDataTypes), | ||
}; | ||
webSocket.send(JSON.stringify(payload)); | ||
} | ||
|
||
private stateChangeContainsType(stateChange: IStateChange, type: IEntityType): boolean { | ||
for (const accountId of Object.keys(stateChange.changed)) { | ||
if (stateChange.changed[accountId][type]) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private transformStateChange( | ||
changed: { [accountId: string]: ITypeState }, | ||
type: IEntityType, | ||
): { [accountId: string]: string } { | ||
const changedFlattened: { [accountId: string]: string } = {}; | ||
|
||
for (const accountId of Object.keys(changed)) { | ||
const entityState = changed[accountId][type]; | ||
if (entityState) { | ||
changedFlattened[accountId] = entityState; | ||
} | ||
} | ||
|
||
return changedFlattened; | ||
} | ||
} |
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
Oops, something went wrong.