// @flow import JsonRpcWs, { InvalidReply } from './jsonrpc-ws-ipc'; import { object, string, arrayOf, number } from 'validated/schema'; import { validate } from 'validated/object'; import type { Coordinate2d } from '../types'; export type AccountData = {paid_until: string}; export type AccountNumber = string; export type Ip = string; export type Location = { latlong: Coordinate2d, country: string, city: string, }; const LocationSchema = object({ latlong: arrayOf(number), country: string, city: string, }); export type SecurityState = 'secured' | 'unsecured'; export type BackendState = { state: SecurityState, target_state: SecurityState, }; export interface IpcFacade { setConnectionString(string): void, getAccountData(AccountNumber): Promise, getAccount(): Promise, setAccount(accountNumber: AccountNumber): Promise, setCountry(address: string): Promise, connect(): Promise, disconnect(): Promise, getIp(): Promise, getLocation(): Promise, getState(): Promise, registerStateListener((BackendState) => void): void, } export class RealIpc implements IpcFacade { _ipc: JsonRpcWs; constructor(connectionString: string) { this._ipc = new JsonRpcWs(connectionString); } setConnectionString(str: string) { this._ipc.setConnectionString(str); } getAccountData(accountNumber: AccountNumber): Promise { return this._ipc.send('get_account_data', accountNumber) .then(raw => { if (typeof raw === 'object' && raw && raw.paid_until) { return raw; } else { throw new InvalidReply(raw, 'Expected an object with paid_until'); } }); } getAccount(): Promise { return this._ipc.send('get_account') .then( raw => { if (raw === undefined || raw === null || typeof raw === 'string') { return raw; } else { throw new InvalidReply(raw); } }); } setAccount(accountNumber: AccountNumber): Promise { return this._ipc.send('set_account', accountNumber) .then(this._ignoreResponse); } _ignoreResponse(_response: mixed): void { return; } setCountry(address: string): Promise { return this._ipc.send('set_country', address) .then(this._ignoreResponse); } connect(): Promise { return this._ipc.send('connect') .then(this._ignoreResponse); } disconnect(): Promise { return this._ipc.send('disconnect') .then(this._ignoreResponse); } getIp(): Promise { return this._ipc.send('get_ip') .then(raw => { if (typeof raw === 'string' && raw) { return raw; } else { throw new InvalidReply(raw, 'Expected a string'); } }); } getLocation(): Promise { return this._ipc.send('get_location') .then(raw => { try { const validated: any = validate(LocationSchema, raw); return (validated: Location); } catch (e) { throw new InvalidReply(raw, e); } }); } getState(): Promise { return this._ipc.send('get_state') .then(raw => { return this._parseBackendState(raw); }); } _parseBackendState(raw: mixed): BackendState { if (raw && raw.state && raw.target_state) { const uncheckedRaw: any = raw; const states: Array = ['secured', 'unsecured']; const correctState = states.includes(uncheckedRaw.state); const correctTargetState = states.includes(uncheckedRaw.target_state); if (!correctState || !correctTargetState) { throw new InvalidReply(raw); } return (uncheckedRaw: BackendState); } else { throw new InvalidReply(raw); } } registerStateListener(listener: (BackendState) => void) { this._ipc.on('new_state', (rawEvent) => { const parsedEvent : BackendState = this._parseBackendState(rawEvent); listener(parsedEvent); }); } }