diff --git a/lib/types/tunnelbroker/farcaster-messages-types.js b/lib/types/tunnelbroker/farcaster-messages-types.js new file mode 100644 --- /dev/null +++ b/lib/types/tunnelbroker/farcaster-messages-types.js @@ -0,0 +1,96 @@ +// @flow + +import type { TInterface } from 'tcomb'; +import t from 'tcomb'; + +import { tShape, tString } from '../../utils/validation-utils.js'; + +type APIMethod = { +type: 'GET' } | { +type: 'PUT' } | { +type: 'STREAM' }; + +export type FarcasterAPIRequest = { + +type: 'FarcasterAPIRequest', + +requestID: string, + +userID: string, + +apiVersion: string, + +endpoint: string, + +method: APIMethod, + +payload: string, +}; + +type FarcasterAPIResponseError = { + +type: 'FarcasterAPIResponseError', + +status: number, + +message: string, +}; +export const farcasterAPIResponseErrorValidator: TInterface = + tShape({ + type: tString('FarcasterAPIResponseError'), + status: t.Number, + message: t.String, + }); + +type FarcasterAPIResponseDataSuccess = { +type: 'Success', +data: string }; +type FarcasterAPIResponseDataErrorResponse = { + +type: 'ErrorResponse', + +data: FarcasterAPIResponseError, +}; +type FarcasterAPIResponseDataError = { +type: 'Error', +data: string }; +type FarcasterAPIResponseDataInvalidRequest = { +type: 'InvalidRequest' }; +type FarcasterAPIResponseDataUnauthenticated = { +type: 'Unauthenticated' }; +type FarcasterAPIResponseDataSerializationError = { + +type: 'SerializationError', + +data: string, +}; +type FarcasterAPIResponseDataMissingFarcasterDCsToken = { + +type: 'MissingFarcasterDCsToken', +}; + +export type FarcasterAPIResponseData = + | FarcasterAPIResponseDataSuccess + | FarcasterAPIResponseDataErrorResponse + | FarcasterAPIResponseDataError + | FarcasterAPIResponseDataInvalidRequest + | FarcasterAPIResponseDataUnauthenticated + | FarcasterAPIResponseDataSerializationError + | FarcasterAPIResponseDataMissingFarcasterDCsToken; + +const farcasterAPIResponseDataValidator = t.union([ + tShape({ + type: tString('Success'), + data: t.String, + }), + tShape({ + type: tString('ErrorResponse'), + data: farcasterAPIResponseErrorValidator, + }), + tShape({ + type: tString('Error'), + data: t.String, + }), + tShape({ + type: tString('InvalidRequest'), + }), + tShape({ + type: tString('Unauthenticated'), + }), + tShape({ + type: tString('SerializationError'), + data: t.String, + }), + tShape({ + type: tString('MissingFarcasterDCsToken'), + }), +]); + +export type FarcasterAPIResponse = { + +type: 'FarcasterAPIResponse', + +requestID: string, + +response: FarcasterAPIResponseData, +}; + +export const farcasterAPIResponseValidator: TInterface = + tShape({ + type: tString('FarcasterAPIResponse'), + requestID: t.String, + response: farcasterAPIResponseDataValidator, + }); diff --git a/lib/types/tunnelbroker/messages.js b/lib/types/tunnelbroker/messages.js --- a/lib/types/tunnelbroker/messages.js +++ b/lib/types/tunnelbroker/messages.js @@ -7,6 +7,11 @@ messageToDeviceRequestStatusValidator, type DeviceToTunnelbrokerRequestStatus, } from './device-to-tunnelbroker-request-status-types.js'; +import { farcasterAPIResponseValidator } from './farcaster-messages-types.js'; +import type { + FarcasterAPIRequest, + FarcasterAPIResponse, +} from './farcaster-messages-types.js'; import { type MessageReceiveConfirmation } from './message-receive-confirmation-types.js'; import { type MessageToDeviceRequest } from './message-to-device-request-types.js'; import { @@ -50,6 +55,7 @@ MESSAGE_TO_DEVICE_REQUEST: 'MessageToDeviceRequest', MESSAGE_RECEIVE_CONFIRMATION: 'MessageReceiveConfirmation', MESSAGE_TO_TUNNELBROKER_REQUEST: 'MessageToTunnelbrokerRequest', + FARCASTER_API_REQUEST: 'FarcasterAPIRequest', HEARTBEAT: 'Heartbeat', }); @@ -60,22 +66,25 @@ | MessageToDeviceRequest | MessageReceiveConfirmation | MessageToTunnelbrokerRequest + | FarcasterAPIRequest | Heartbeat; -// Types having `clientMessageID` prop. +// Types having `clientMessageID` or `requestID` prop. // When using this type, it is possible to use Promise abstraction, // and await sending a message until Tunnelbroker responds that // the request was processed. export type DeviceToTunnelbrokerRequest = | TunnelbrokerNotif | MessageToDeviceRequest - | MessageToTunnelbrokerRequest; + | MessageToTunnelbrokerRequest + | FarcasterAPIRequest; // Messages sent from Tunnelbroker to Device. export const tunnelbrokerToDeviceMessageTypes = Object.freeze({ CONNECTION_INITIALIZATION_RESPONSE: 'ConnectionInitializationResponse', DEVICE_TO_TUNNELBROKER_REQUEST_STATUS: 'MessageToDeviceRequestStatus', MESSAGE_TO_DEVICE: 'MessageToDevice', + FARCASTER_API_RESPONSE: 'FarcasterAPIResponse', HEARTBEAT: 'Heartbeat', }); @@ -83,6 +92,7 @@ | ConnectionInitializationResponse | DeviceToTunnelbrokerRequestStatus | MessageToDevice + | FarcasterAPIResponse | Heartbeat; export const tunnelbrokerToDeviceMessageValidator: TUnion = @@ -90,5 +100,6 @@ connectionInitializationResponseValidator, messageToDeviceRequestStatusValidator, messageToDeviceValidator, + farcasterAPIResponseValidator, heartbeatValidator, ]); diff --git a/services/tunnelbroker/src/farcaster/mod.rs b/services/tunnelbroker/src/farcaster/mod.rs --- a/services/tunnelbroker/src/farcaster/mod.rs +++ b/services/tunnelbroker/src/farcaster/mod.rs @@ -3,25 +3,9 @@ use crate::farcaster::error::Error::MissingFarcasterToken; use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; use tracing::{debug, error}; -pub mod error; - -#[derive(Debug)] -pub enum APIMethod { - PUT, - GET, - STREAM, -} +use tunnelbroker_messages::farcaster::{APIMethod, FarcasterAPIRequest}; -pub struct FarcasterAPIRequest { - pub request_id: String, - pub user_id: String, - /// API version, examples: "v2", "fc" - pub api_version: String, - pub endpoint: String, - pub method: APIMethod, - /// query, body, or stream message - pub payload: String, -} +pub mod error; #[derive(Clone)] pub struct FarcasterClient { diff --git a/shared/tunnelbroker_messages/src/messages/farcaster.rs b/shared/tunnelbroker_messages/src/messages/farcaster.rs new file mode 100644 --- /dev/null +++ b/shared/tunnelbroker_messages/src/messages/farcaster.rs @@ -0,0 +1,61 @@ +use serde::{Deserialize, Serialize}; +use util_macros::TagAwareDeserialize; + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +#[serde(tag = "type")] +pub enum APIMethod { + PUT, + GET, + STREAM, +} + +#[derive(Serialize, Deserialize, TagAwareDeserialize, PartialEq, Debug)] +#[serde(tag = "type", remote = "Self", rename_all = "camelCase")] +pub struct FarcasterAPIRequest { + #[serde(rename = "requestID")] + pub request_id: String, + #[serde(rename = "userID")] + pub user_id: String, + /// API version, examples: "v2", "fc" + #[serde(rename = "apiVersion")] + pub api_version: String, + pub endpoint: String, + pub method: APIMethod, + /// query, body, or stream message + pub payload: String, +} + +#[derive(Serialize, Deserialize, TagAwareDeserialize, PartialEq, Debug)] +#[serde(tag = "type", remote = "Self", rename_all = "camelCase")] +pub struct FarcasterAPIResponseError { + pub status: u32, + pub message: String, +} + +#[derive(Serialize, PartialEq, Debug)] +#[serde(tag = "type", content = "data")] +pub enum FarcasterAPIResponseData { + /// JSON stringified response + Success(String), + /// ErrorMessage + ErrorResponse(FarcasterAPIResponseError), + Error(String), + /// The request was invalid (e.g., Bytes instead of Text). + /// In this case, the request ID cannot be retrieved. + InvalidRequest, + /// Unauthenticated client tried to send a message. + Unauthenticated, + /// The JSON could not be serialized, which is why the entire request is + /// returned back. + /// It becomes impossible to retrieve the request ID in such circumstances. + SerializationError(String), + MissingFarcasterDCsToken, +} + +#[derive(Serialize, TagAwareDeserialize, PartialEq, Debug)] +#[serde(tag = "type", remote = "Self", rename_all = "camelCase")] +pub struct FarcasterAPIResponse { + #[serde(rename = "requestID")] + pub request_id: String, + pub response: FarcasterAPIResponseData, +} diff --git a/shared/tunnelbroker_messages/src/messages/mod.rs b/shared/tunnelbroker_messages/src/messages/mod.rs --- a/shared/tunnelbroker_messages/src/messages/mod.rs +++ b/shared/tunnelbroker_messages/src/messages/mod.rs @@ -2,6 +2,7 @@ pub mod bad_device_token; pub mod device_list_updated; +pub mod farcaster; pub mod keys; pub mod message_receive_confirmation; pub mod message_to_device; @@ -26,6 +27,7 @@ }; use crate::bad_device_token::BadDeviceToken; +use crate::messages::farcaster::{FarcasterAPIRequest, FarcasterAPIResponse}; use crate::notif::*; use serde::{Deserialize, Serialize}; @@ -50,6 +52,7 @@ MessageToDeviceRequest(MessageToDeviceRequest), MessageReceiveConfirmation(MessageReceiveConfirmation), MessageToTunnelbrokerRequest(MessageToTunnelbrokerRequest), + FarcasterAPIRequest(FarcasterAPIRequest), Heartbeat(Heartbeat), } @@ -59,6 +62,7 @@ pub enum TunnelbrokerToDeviceMessage { ConnectionInitializationResponse(ConnectionInitializationResponse), DeviceToTunnelbrokerRequestStatus(DeviceToTunnelbrokerRequestStatus), + FarcasterAPIResponse(FarcasterAPIResponse), MessageToDevice(MessageToDevice), BadDeviceToken(BadDeviceToken), Heartbeat(Heartbeat),