diff --git a/lib/tunnelbroker/tunnelbroker-context.js b/lib/tunnelbroker/tunnelbroker-context.js --- a/lib/tunnelbroker/tunnelbroker-context.js +++ b/lib/tunnelbroker/tunnelbroker-context.js @@ -2,9 +2,12 @@ import invariant from 'invariant'; import * as React from 'react'; +import uuid from 'uuid'; import { tunnnelbrokerURL } from '../facts/tunnelbroker.js'; import type { MessageReceiveConfirmation } from '../types/tunnelbroker/message-receive-confirmation-types.js'; +import type { MessageSentStatus } from '../types/tunnelbroker/message-to-device-request-status-types.js'; +import type { MessageToDeviceRequest } from '../types/tunnelbroker/message-to-device-request-types.js'; import { type TunnelbrokerMessage, tunnelbrokerMessageTypes, @@ -21,6 +24,12 @@ message: TunnelbrokerMessage, ) => mixed; +type PromiseCallbacks = { + +resolve: () => void, + +reject: (error: string) => void, +}; +type Promises = { [clientMessageID: string]: PromiseCallbacks }; + type TunnelbrokerContextType = { +sendMessage: (message: ClientMessageToDevice) => Promise, +addListener: (listener: TunnelbrokerSocketListener) => void, @@ -41,6 +50,7 @@ const [connected, setConnected] = React.useState(false); const listeners = React.useRef>(new Set()); const socket = React.useRef(null); + const promises = React.useRef({}); React.useEffect(() => { if (connected || !initMessage) { @@ -107,15 +117,53 @@ messageIDs: [message.messageID], }; socket.current?.send(JSON.stringify(confirmation)); + } else if ( + message.type === + tunnelbrokerMessageTypes.MESSAGE_TO_DEVICE_REQUEST_STATUS + ) { + for (const status: MessageSentStatus of message.clientMessageIDs) { + if (status.type === 'Success') { + promises.current[status.data]?.resolve(); + delete promises.current[status.data]; + } else if (status.type === 'Error') { + promises.current[status.data.id]?.reject(status.data.error); + delete promises.current[status.data.id]; + } else if (status.type === 'SerializationError') { + console.log('SerializationError for message: ', status.data); + } else if (status.type === 'InvalidRequest') { + console.log('Tunnelbroker recorded InvalidRequest'); + } + } } }; socket.current = tunnelbrokerSocket; }, [connected, initMessage]); - const sendMessage: () => Promise = React.useCallback(() => { - return new Promise(() => {}); - }, []); + const sendMessage: (message: ClientMessageToDevice) => Promise = + React.useCallback( + (message: ClientMessageToDevice) => { + if (!connected || !socket.current) { + throw new Error('Tunnelbroker not connected'); + } + const clientMessageID = uuid.v4(); + const messageToDevice: MessageToDeviceRequest = { + type: tunnelbrokerMessageTypes.MESSAGE_TO_DEVICE_REQUEST, + clientMessageID, + deviceID: message.deviceID, + payload: message.payload, + }; + + return new Promise((resolve, reject) => { + promises.current[clientMessageID] = { + resolve, + reject, + }; + socket.current?.send(JSON.stringify(messageToDevice)); + }); + }, + [connected], + ); const addListener = React.useCallback( (listener: TunnelbrokerSocketListener) => {