2024-06-28 10:34:57 +00:00
|
|
|
{.push raises: [].}
|
2022-07-25 11:01:37 +00:00
|
|
|
|
2024-07-09 11:14:28 +00:00
|
|
|
import std/[options, sequtils], results
|
2024-03-15 23:08:47 +00:00
|
|
|
import ../waku_core, ../common/paging, ./common
|
2022-11-09 17:50:18 +00:00
|
|
|
|
|
|
|
## Wire protocol
|
|
|
|
|
2023-12-19 14:10:27 +00:00
|
|
|
const HistoryQueryDirectionDefaultValue = default(type HistoryQuery.direction)
|
|
|
|
|
2022-11-09 17:50:18 +00:00
|
|
|
type PagingIndexRPC* = object
|
|
|
|
## This type contains the description of an Index used in the pagination of WakuMessages
|
|
|
|
pubsubTopic*: PubsubTopic
|
|
|
|
senderTime*: Timestamp # the time at which the message is generated
|
|
|
|
receiverTime*: Timestamp
|
|
|
|
digest*: MessageDigest # calculated over payload and content topic
|
|
|
|
|
|
|
|
proc `==`*(x, y: PagingIndexRPC): bool =
|
|
|
|
## receiverTime plays no role in index equality
|
2024-03-15 23:08:47 +00:00
|
|
|
(x.senderTime == y.senderTime) and (x.digest == y.digest) and
|
|
|
|
(x.pubsubTopic == y.pubsubTopic)
|
|
|
|
|
|
|
|
proc compute*(
|
|
|
|
T: type PagingIndexRPC,
|
|
|
|
msg: WakuMessage,
|
|
|
|
receivedTime: Timestamp,
|
|
|
|
pubsubTopic: PubsubTopic,
|
|
|
|
): T =
|
2022-11-09 17:50:18 +00:00
|
|
|
## Takes a WakuMessage with received timestamp and returns its Index.
|
|
|
|
let
|
|
|
|
digest = computeDigest(msg)
|
|
|
|
senderTime = msg.timestamp
|
|
|
|
|
|
|
|
PagingIndexRPC(
|
|
|
|
pubsubTopic: pubsubTopic,
|
|
|
|
senderTime: senderTime,
|
2022-11-23 09:08:00 +00:00
|
|
|
receiverTime: receivedTime,
|
2024-03-15 23:08:47 +00:00
|
|
|
digest: digest,
|
2022-11-09 17:50:18 +00:00
|
|
|
)
|
|
|
|
|
2024-03-15 23:08:47 +00:00
|
|
|
type PagingInfoRPC* = object
|
|
|
|
## This type holds the information needed for the pagination
|
|
|
|
pageSize*: Option[uint64]
|
|
|
|
cursor*: Option[PagingIndexRPC]
|
|
|
|
direction*: Option[PagingDirection]
|
2022-07-25 11:01:37 +00:00
|
|
|
|
|
|
|
type
|
2022-11-09 17:50:18 +00:00
|
|
|
HistoryContentFilterRPC* = object
|
2022-07-25 11:01:37 +00:00
|
|
|
contentTopic*: ContentTopic
|
|
|
|
|
2022-11-09 17:50:18 +00:00
|
|
|
HistoryQueryRPC* = object
|
|
|
|
contentFilters*: seq[HistoryContentFilterRPC]
|
2022-11-17 19:40:08 +00:00
|
|
|
pubsubTopic*: Option[PubsubTopic]
|
|
|
|
pagingInfo*: Option[PagingInfoRPC]
|
|
|
|
startTime*: Option[int64]
|
|
|
|
endTime*: Option[int64]
|
2022-07-25 11:01:37 +00:00
|
|
|
|
2022-11-09 17:50:18 +00:00
|
|
|
HistoryResponseErrorRPC* {.pure.} = enum
|
2022-11-23 09:08:00 +00:00
|
|
|
## HistoryResponseErrorRPC contains error message to inform the querying node about
|
2022-11-09 17:50:18 +00:00
|
|
|
## the state of its request
|
2022-07-25 11:01:37 +00:00
|
|
|
NONE = uint32(0)
|
|
|
|
INVALID_CURSOR = uint32(1)
|
2024-04-15 13:28:35 +00:00
|
|
|
TOO_MANY_REQUESTS = uint32(429)
|
2022-09-20 09:39:52 +00:00
|
|
|
SERVICE_UNAVAILABLE = uint32(503)
|
2022-07-25 11:01:37 +00:00
|
|
|
|
2022-11-09 17:50:18 +00:00
|
|
|
HistoryResponseRPC* = object
|
2022-07-25 11:01:37 +00:00
|
|
|
messages*: seq[WakuMessage]
|
2022-11-17 19:40:08 +00:00
|
|
|
pagingInfo*: Option[PagingInfoRPC]
|
2022-11-09 17:50:18 +00:00
|
|
|
error*: HistoryResponseErrorRPC
|
2022-07-25 11:01:37 +00:00
|
|
|
|
|
|
|
HistoryRPC* = object
|
|
|
|
requestId*: string
|
2022-11-17 19:40:08 +00:00
|
|
|
query*: Option[HistoryQueryRPC]
|
|
|
|
response*: Option[HistoryResponseRPC]
|
2022-11-09 17:50:18 +00:00
|
|
|
|
|
|
|
proc parse*(T: type HistoryResponseErrorRPC, kind: uint32): T =
|
2024-03-15 23:08:47 +00:00
|
|
|
case kind
|
2024-04-15 13:28:35 +00:00
|
|
|
of 0, 1, 429, 503:
|
2024-10-10 12:40:09 +00:00
|
|
|
cast[HistoryResponseErrorRPC](kind)
|
2022-11-09 17:50:18 +00:00
|
|
|
else:
|
|
|
|
# TODO: Improve error variants/move to satus codes
|
|
|
|
HistoryResponseErrorRPC.INVALID_CURSOR
|
|
|
|
|
|
|
|
## Wire protocol type mappings
|
|
|
|
|
2024-03-15 23:08:47 +00:00
|
|
|
proc toRPC*(cursor: HistoryCursor): PagingIndexRPC {.gcsafe.} =
|
2022-11-09 17:50:18 +00:00
|
|
|
PagingIndexRPC(
|
|
|
|
pubsubTopic: cursor.pubsubTopic,
|
|
|
|
senderTime: cursor.senderTime,
|
|
|
|
receiverTime: cursor.storeTime,
|
2024-03-15 23:08:47 +00:00
|
|
|
digest: cursor.digest,
|
2022-11-09 17:50:18 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
proc toAPI*(rpc: PagingIndexRPC): HistoryCursor =
|
|
|
|
HistoryCursor(
|
|
|
|
pubsubTopic: rpc.pubsubTopic,
|
|
|
|
senderTime: rpc.senderTime,
|
|
|
|
storeTime: rpc.receiverTime,
|
2024-03-15 23:08:47 +00:00
|
|
|
digest: rpc.digest,
|
2022-11-09 17:50:18 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
proc toRPC*(query: HistoryQuery): HistoryQueryRPC =
|
2022-11-17 19:40:08 +00:00
|
|
|
var rpc = HistoryQueryRPC()
|
|
|
|
|
2024-03-15 23:08:47 +00:00
|
|
|
rpc.contentFilters =
|
|
|
|
query.contentTopics.mapIt(HistoryContentFilterRPC(contentTopic: it))
|
2022-11-17 19:40:08 +00:00
|
|
|
|
|
|
|
rpc.pubsubTopic = query.pubsubTopic
|
|
|
|
|
|
|
|
rpc.pagingInfo = block:
|
2024-03-15 23:08:47 +00:00
|
|
|
if query.cursor.isNone() and query.pageSize == default(type query.pageSize) and
|
|
|
|
query.direction == HistoryQueryDirectionDefaultValue:
|
|
|
|
none(PagingInfoRPC)
|
|
|
|
else:
|
|
|
|
let
|
|
|
|
pageSize = some(query.pageSize)
|
|
|
|
cursor = query.cursor.map(toRPC)
|
|
|
|
direction = some(query.direction)
|
2023-12-19 14:10:27 +00:00
|
|
|
|
2024-03-15 23:08:47 +00:00
|
|
|
some(PagingInfoRPC(pageSize: pageSize, cursor: cursor, direction: direction))
|
2022-11-23 09:08:00 +00:00
|
|
|
|
2022-11-17 19:40:08 +00:00
|
|
|
rpc.startTime = query.startTime
|
|
|
|
rpc.endTime = query.endTime
|
|
|
|
|
|
|
|
rpc
|
2022-11-09 17:50:18 +00:00
|
|
|
|
|
|
|
proc toAPI*(rpc: HistoryQueryRPC): HistoryQuery =
|
2022-11-23 09:08:00 +00:00
|
|
|
let
|
2022-11-17 19:40:08 +00:00
|
|
|
pubsubTopic = rpc.pubsubTopic
|
2022-11-23 09:08:00 +00:00
|
|
|
|
2022-11-09 17:50:18 +00:00
|
|
|
contentTopics = rpc.contentFilters.mapIt(it.contentTopic)
|
|
|
|
|
2024-03-15 23:08:47 +00:00
|
|
|
cursor =
|
|
|
|
if rpc.pagingInfo.isNone() or rpc.pagingInfo.get().cursor.isNone():
|
|
|
|
none(HistoryCursor)
|
|
|
|
else:
|
|
|
|
rpc.pagingInfo.get().cursor.map(toAPI)
|
2022-11-09 17:50:18 +00:00
|
|
|
|
2022-11-17 19:40:08 +00:00
|
|
|
startTime = rpc.startTime
|
2022-11-09 17:50:18 +00:00
|
|
|
|
2022-11-17 19:40:08 +00:00
|
|
|
endTime = rpc.endTime
|
2022-11-23 09:08:00 +00:00
|
|
|
|
2024-03-15 23:08:47 +00:00
|
|
|
pageSize =
|
|
|
|
if rpc.pagingInfo.isNone() or rpc.pagingInfo.get().pageSize.isNone():
|
|
|
|
0'u64
|
|
|
|
else:
|
|
|
|
rpc.pagingInfo.get().pageSize.get()
|
2022-11-09 17:50:18 +00:00
|
|
|
|
2024-03-15 23:08:47 +00:00
|
|
|
direction =
|
|
|
|
if rpc.pagingInfo.isNone() or rpc.pagingInfo.get().direction.isNone():
|
|
|
|
HistoryQueryDirectionDefaultValue
|
|
|
|
else:
|
|
|
|
rpc.pagingInfo.get().direction.get()
|
2022-11-23 09:08:00 +00:00
|
|
|
|
2022-11-09 17:50:18 +00:00
|
|
|
HistoryQuery(
|
|
|
|
pubsubTopic: pubsubTopic,
|
|
|
|
contentTopics: contentTopics,
|
|
|
|
cursor: cursor,
|
|
|
|
startTime: startTime,
|
|
|
|
endTime: endTime,
|
|
|
|
pageSize: pageSize,
|
2024-03-15 23:08:47 +00:00
|
|
|
direction: direction,
|
2022-11-09 17:50:18 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
proc toRPC*(err: HistoryError): HistoryResponseErrorRPC =
|
|
|
|
# TODO: Better error mappings/move to error codes
|
2024-03-15 23:08:47 +00:00
|
|
|
case err.kind
|
2022-11-09 17:50:18 +00:00
|
|
|
of HistoryErrorKind.BAD_REQUEST:
|
|
|
|
# TODO: Respond aksi with the reason
|
|
|
|
HistoryResponseErrorRPC.INVALID_CURSOR
|
2024-04-15 13:28:35 +00:00
|
|
|
of HistoryErrorKind.TOO_MANY_REQUESTS:
|
|
|
|
HistoryResponseErrorRPC.TOO_MANY_REQUESTS
|
2022-11-09 17:50:18 +00:00
|
|
|
of HistoryErrorKind.SERVICE_UNAVAILABLE:
|
|
|
|
HistoryResponseErrorRPC.SERVICE_UNAVAILABLE
|
|
|
|
else:
|
2022-11-17 19:40:08 +00:00
|
|
|
HistoryResponseErrorRPC.INVALID_CURSOR
|
2022-11-09 17:50:18 +00:00
|
|
|
|
|
|
|
proc toAPI*(err: HistoryResponseErrorRPC): HistoryError =
|
|
|
|
# TODO: Better error mappings/move to error codes
|
2024-03-15 23:08:47 +00:00
|
|
|
case err
|
2022-11-09 17:50:18 +00:00
|
|
|
of HistoryResponseErrorRPC.INVALID_CURSOR:
|
|
|
|
HistoryError(kind: HistoryErrorKind.BAD_REQUEST, cause: "invalid cursor")
|
2024-04-15 13:28:35 +00:00
|
|
|
of HistoryResponseErrorRPC.TOO_MANY_REQUESTS:
|
|
|
|
HistoryError(kind: HistoryErrorKind.TOO_MANY_REQUESTS)
|
2022-11-09 17:50:18 +00:00
|
|
|
of HistoryResponseErrorRPC.SERVICE_UNAVAILABLE:
|
|
|
|
HistoryError(kind: HistoryErrorKind.SERVICE_UNAVAILABLE)
|
|
|
|
else:
|
|
|
|
HistoryError(kind: HistoryErrorKind.UNKNOWN)
|
|
|
|
|
|
|
|
proc toRPC*(res: HistoryResult): HistoryResponseRPC =
|
|
|
|
if res.isErr():
|
|
|
|
let error = res.error.toRPC()
|
2022-11-23 09:08:00 +00:00
|
|
|
|
2022-11-09 17:50:18 +00:00
|
|
|
HistoryResponseRPC(error: error)
|
|
|
|
else:
|
|
|
|
let resp = res.get()
|
|
|
|
|
|
|
|
let
|
|
|
|
messages = resp.messages
|
|
|
|
|
|
|
|
pagingInfo = block:
|
|
|
|
if resp.cursor.isNone():
|
2022-11-17 19:40:08 +00:00
|
|
|
none(PagingInfoRPC)
|
2022-11-23 09:08:00 +00:00
|
|
|
else:
|
|
|
|
some(PagingInfoRPC(cursor: resp.cursor.map(toRPC)))
|
2022-11-09 17:50:18 +00:00
|
|
|
|
|
|
|
error = HistoryResponseErrorRPC.NONE
|
|
|
|
|
2024-03-15 23:08:47 +00:00
|
|
|
HistoryResponseRPC(messages: messages, pagingInfo: pagingInfo, error: error)
|
2022-11-09 17:50:18 +00:00
|
|
|
|
|
|
|
proc toAPI*(rpc: HistoryResponseRPC): HistoryResult =
|
|
|
|
if rpc.error != HistoryResponseErrorRPC.NONE:
|
|
|
|
err(rpc.error.toAPI())
|
|
|
|
else:
|
|
|
|
let
|
|
|
|
messages = rpc.messages
|
|
|
|
|
2024-03-15 23:08:47 +00:00
|
|
|
cursor =
|
|
|
|
if rpc.pagingInfo.isNone():
|
|
|
|
none(HistoryCursor)
|
|
|
|
else:
|
|
|
|
rpc.pagingInfo.get().cursor.map(toAPI)
|
2022-11-09 17:50:18 +00:00
|
|
|
|
2024-03-15 23:08:47 +00:00
|
|
|
ok(HistoryResponse(messages: messages, cursor: cursor))
|