diff --git a/waku/waku_api/rest/admin/client.nim b/waku/waku_api/rest/admin/client.nim index 9980cadae..7c0ee8a66 100644 --- a/waku/waku_api/rest/admin/client.nim +++ b/waku/waku_api/rest/admin/client.nim @@ -13,6 +13,7 @@ import import ../serdes, ../responses, + ../rest_serdes, ./types export types @@ -21,37 +22,9 @@ export types logScope: topics = "waku node rest admin api" -proc decodeBytes*(t: typedesc[seq[WakuPeer]], data: openArray[byte], - contentType: Opt[ContentTypeData]): RestResult[seq[WakuPeer]] = - if MediaType.init($contentType) != MIMETYPE_JSON: - error "Unsupported response contentType value", contentType = contentType - return err("Unsupported response contentType") - - let decoded = decodeFromJsonBytes(seq[WakuPeer], data).valueOr: - return err("Invalid response from server, could not decode.") - - return ok(decoded) - -proc decodeBytes*(t: typedesc[string], value: openArray[byte], - contentType: Opt[ContentTypeData]): RestResult[string] = - if MediaType.init($contentType) != MIMETYPE_TEXT: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - var res: string - if len(value) > 0: - res = newString(len(value)) - copyMem(addr res[0], unsafeAddr value[0], len(value)) - return ok(res) - proc encodeBytes*(value: seq[string], contentType: string): RestResult[seq[byte]] = - if MediaType.init(contentType) != MIMETYPE_JSON: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - let encoded = ?encodeIntoJsonBytes(value) - return ok(encoded) + return encodeBytesOf(value, contentType) proc getPeers*(): RestResponse[seq[WakuPeer]] diff --git a/waku/waku_api/rest/admin/handlers.nim b/waku/waku_api/rest/admin/handlers.nim index ec295fe3a..4e47b47ba 100644 --- a/waku/waku_api/rest/admin/handlers.nim +++ b/waku/waku_api/rest/admin/handlers.nim @@ -21,6 +21,7 @@ import ../../../node/peer_manager, ../responses, ../serdes, + ../rest_serdes, ./types export types @@ -32,23 +33,6 @@ const ROUTE_ADMIN_V1_PEERS* = "/admin/v1/peers" type PeerProtocolTuple = tuple[multiaddr: string, protocol: string, connected: bool] -func decodeRequestBody[T](contentBody: Option[ContentBody]) : Result[T, RestApiResponse] = - if contentBody.isNone(): - return err(RestApiResponse.badRequest("Missing content body")) - - let reqBodyContentType = MediaType.init($contentBody.get().contentType) - if reqBodyContentType != MIMETYPE_JSON: - return err(RestApiResponse.badRequest("Wrong Content-Type, expected application/json")) - - let reqBodyData = contentBody.get().data - - let requestResult = decodeFromJsonBytes(T, reqBodyData) - if requestResult.isErr(): - return err(RestApiResponse.badRequest("Invalid content body, could not decode. " & - $requestResult.error)) - - return ok(requestResult.get()) - proc tuplesToWakuPeers(peers: var WakuPeers, peersTup: seq[PeerProtocolTuple]) = for peer in peersTup: peers.add(peer.multiaddr, peer.protocol, peer.connected) diff --git a/waku/waku_api/rest/debug/client.nim b/waku/waku_api/rest/debug/client.nim index 03a31341b..f086fd351 100644 --- a/waku/waku_api/rest/debug/client.nim +++ b/waku/waku_api/rest/debug/client.nim @@ -11,6 +11,7 @@ import import ../serdes, ../responses, + ../rest_serdes, ./types export types @@ -19,31 +20,8 @@ export types logScope: topics = "waku node rest debug_api" - -proc decodeBytes*(t: typedesc[DebugWakuInfo], data: openArray[byte], - contentType: Opt[ContentTypeData]): RestResult[DebugWakuInfo] = - if MediaType.init($contentType) != MIMETYPE_JSON: - error "Unsupported respose contentType value", contentType = contentType - return err("Unsupported response contentType") - - let decoded = ?decodeFromJsonBytes(DebugWakuInfo, data) - return ok(decoded) - # TODO: Check how we can use a constant to set the method endpoint (improve "rest" pragma under nim-presto) proc debugInfoV1*(): RestResponse[DebugWakuInfo] {.rest, endpoint: "/debug/v1/info", meth: HttpMethod.MethodGet.} - -proc decodeBytes*(t: typedesc[string], value: openArray[byte], - contentType: Opt[ContentTypeData]): RestResult[string] = - if MediaType.init($contentType) != MIMETYPE_TEXT: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - var res: string - if len(value) > 0: - res = newString(len(value)) - copyMem(addr res[0], unsafeAddr value[0], len(value)) - return ok(res) - # TODO: Check how we can use a constant to set the method endpoint (improve "rest" pragma under nim-presto) proc debugVersionV1*(): RestResponse[string] {.rest, endpoint: "/debug/v1/version", meth: HttpMethod.MethodGet.} diff --git a/waku/waku_api/rest/filter/client.nim b/waku/waku_api/rest/filter/client.nim index f4d8fd262..e16a39ada 100644 --- a/waku/waku_api/rest/filter/client.nim +++ b/waku/waku_api/rest/filter/client.nim @@ -16,6 +16,7 @@ import ../../../waku_core, ../serdes, ../responses, + ../rest_serdes, ./types export types @@ -25,52 +26,19 @@ logScope: proc encodeBytes*(value: FilterSubscribeRequest, contentType: string): RestResult[seq[byte]] = - if MediaType.init(contentType) != MIMETYPE_JSON: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - let encoded = ?encodeIntoJsonBytes(value) - return ok(encoded) + return encodeBytesOf(value, contentType) proc encodeBytes*(value: FilterSubscriberPing, contentType: string): RestResult[seq[byte]] = - if MediaType.init(contentType) != MIMETYPE_JSON: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - let encoded = ?encodeIntoJsonBytes(value) - return ok(encoded) + return encodeBytesOf(value, contentType) proc encodeBytes*(value: FilterUnsubscribeRequest, contentType: string): RestResult[seq[byte]] = - if MediaType.init(contentType) != MIMETYPE_JSON: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - let encoded = ?encodeIntoJsonBytes(value) - return ok(encoded) + return encodeBytesOf(value, contentType) proc encodeBytes*(value: FilterUnsubscribeAllRequest, contentType: string): RestResult[seq[byte]] = - if MediaType.init(contentType) != MIMETYPE_JSON: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - let encoded = ?encodeIntoJsonBytes(value) - return ok(encoded) - -proc decodeBytes*(t: typedesc[FilterSubscriptionResponse], - value: openarray[byte], - contentType: Opt[ContentTypeData]): - - RestResult[FilterSubscriptionResponse] = - - if MediaType.init($contentType) != MIMETYPE_JSON: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - let decoded = ?decodeFromJsonBytes(FilterSubscriptionResponse, value) - return ok(decoded) + return encodeBytesOf(value, contentType) proc filterSubscriberPing*(requestId: string): RestResponse[FilterSubscriptionResponse] @@ -92,16 +60,6 @@ proc filterDeleteAllSubscriptions*(body: FilterUnsubscribeAllRequest): RestResponse[FilterSubscriptionResponse] {.rest, endpoint: "/filter/v2/subscriptions/all", meth: HttpMethod.MethodDelete.} -proc decodeBytes*(t: typedesc[FilterGetMessagesResponse], - data: openArray[byte], - contentType: Opt[ContentTypeData]): RestResult[FilterGetMessagesResponse] = - if MediaType.init($contentType) != MIMETYPE_JSON: - error "Unsupported response contentType value", contentType = contentType - return err("Unsupported response contentType") - - let decoded = ?decodeFromJsonBytes(FilterGetMessagesResponse, data) - return ok(decoded) - proc filterGetMessagesV1*(contentTopic: string): RestResponse[FilterGetMessagesResponse] {.rest, endpoint: "/filter/v2/messages/{contentTopic}", meth: HttpMethod.MethodGet.} diff --git a/waku/waku_api/rest/filter/handlers.nim b/waku/waku_api/rest/filter/handlers.nim index a3dda79c1..3b72d1b2d 100644 --- a/waku/waku_api/rest/filter/handlers.nim +++ b/waku/waku_api/rest/filter/handlers.nim @@ -24,6 +24,7 @@ import ../../handlers, ../serdes, ../responses, + ../rest_serdes, ./types export types @@ -44,23 +45,6 @@ const filterMessageCacheDefaultCapacity* = 30 type MessageCache* = message_cache.MessageCache[ContentTopic] -func decodeRequestBody[T](contentBody: Option[ContentBody]) : Result[T, RestApiResponse] = - if contentBody.isNone(): - return err(RestApiResponse.badRequest("Missing content body")) - - let reqBodyContentType = MediaType.init($contentBody.get().contentType) - if reqBodyContentType != MIMETYPE_JSON: - return err(RestApiResponse.badRequest("Wrong Content-Type, expected application/json")) - - let reqBodyData = contentBody.get().data - - let requestResult = decodeFromJsonBytes(T, reqBodyData) - if requestResult.isErr(): - return err(RestApiResponse.badRequest("Invalid content body, could not decode. " & - $requestResult.error)) - - return ok(requestResult.get()) - proc getErrorCause(err: filter_protocol_type.FilterSubscribeError): string = ## Retrieve proper error cause of FilterSubscribeError - due stringify make some parts of text double @@ -169,7 +153,7 @@ proc filterPostPutSubscriptionRequestHandler( let peer = node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: let handler = discHandler.valueOr: - return makeRestResponse(req.requestId, NoPeerNoDiscoError) + return makeRestResponse(req.requestId, NoPeerNoDiscoError) let peerOp = (await handler()).valueOr: return RestApiResponse.internalServerError($error) @@ -233,7 +217,7 @@ proc installFilterDeleteSubscriptionsHandler( let peer = node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: let handler = discHandler.valueOr: - return makeRestResponse(req.requestId, NoPeerNoDiscoError) + return makeRestResponse(req.requestId, NoPeerNoDiscoError) let peerOp = (await handler()).valueOr: return RestApiResponse.internalServerError($error) @@ -276,7 +260,7 @@ proc installFilterDeleteAllSubscriptionsHandler( let peer = node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: let handler = discHandler.valueOr: - return makeRestResponse(req.requestId, NoPeerNoDiscoError) + return makeRestResponse(req.requestId, NoPeerNoDiscoError) let peerOp = (await handler()).valueOr: return RestApiResponse.internalServerError($error) @@ -285,7 +269,7 @@ proc installFilterDeleteAllSubscriptionsHandler( return makeRestResponse(req.requestId, NoPeerNoneFoundError) let unsubFut = node.filterUnsubscribeAll(peer) - + if not await unsubFut.withTimeout(futTimeoutForSubscriptionProcessing): error "Failed to unsubscribe from contentFilters due to timeout!" return makeRestResponse(req.requestId, @@ -310,7 +294,7 @@ proc installFilterPingSubscriberHandler( let peer = node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: let handler = discHandler.valueOr: - return makeRestResponse(requestId.get(), NoPeerNoDiscoError) + return makeRestResponse(requestId.get(), NoPeerNoDiscoError) let peerOp = (await handler()).valueOr: return RestApiResponse.internalServerError($error) diff --git a/waku/waku_api/rest/filter/legacy_client.nim b/waku/waku_api/rest/filter/legacy_client.nim index 9434dd6e0..b8d7168e4 100644 --- a/waku/waku_api/rest/filter/legacy_client.nim +++ b/waku/waku_api/rest/filter/legacy_client.nim @@ -14,6 +14,7 @@ import ../../../waku_core, ../serdes, ../responses, + ../rest_serdes, ./types export types @@ -23,46 +24,19 @@ logScope: proc encodeBytes*(value: FilterLegacySubscribeRequest, contentType: string): RestResult[seq[byte]] = - if MediaType.init(contentType) != MIMETYPE_JSON: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - let encoded = ?encodeIntoJsonBytes(value) - return ok(encoded) - -proc decodeBytes*(t: typedesc[string], value: openarray[byte], - contentType: Opt[ContentTypeData]): RestResult[string] = - if MediaType.init($contentType) != MIMETYPE_TEXT: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - var res: string - if len(value) > 0: - res = newString(len(value)) - copyMem(addr res[0], unsafeAddr value[0], len(value)) - return ok(res) + return encodeBytesOf(value, contentType) # TODO: Check how we can use a constant to set the method endpoint (improve "rest" pragma under nim-presto) -proc filterPostSubscriptionsV1*(body: FilterLegacySubscribeRequest): - RestResponse[string] +proc filterPostSubscriptionsV1*(body: FilterLegacySubscribeRequest): + RestResponse[string] {.rest, endpoint: "/filter/v1/subscriptions", meth: HttpMethod.MethodPost.} # TODO: Check how we can use a constant to set the method endpoint (improve "rest" pragma under nim-presto) -proc filterDeleteSubscriptionsV1*(body: FilterLegacySubscribeRequest): - RestResponse[string] +proc filterDeleteSubscriptionsV1*(body: FilterLegacySubscribeRequest): + RestResponse[string] {.rest, endpoint: "/filter/v1/subscriptions", meth: HttpMethod.MethodDelete.} -proc decodeBytes*(t: typedesc[FilterGetMessagesResponse], - data: openArray[byte], - contentType: Opt[ContentTypeData]): RestResult[FilterGetMessagesResponse] = - if MediaType.init($contentType) != MIMETYPE_JSON: - error "Unsupported response contentType value", contentType = contentType - return err("Unsupported response contentType") - - let decoded = ?decodeFromJsonBytes(FilterGetMessagesResponse, data) - return ok(decoded) - # TODO: Check how we can use a constant to set the method endpoint (improve "rest" pragma under nim-presto) -proc filterGetMessagesV1*(contentTopic: string): - RestResponse[FilterGetMessagesResponse] +proc filterGetMessagesV1*(contentTopic: string): + RestResponse[FilterGetMessagesResponse] {.rest, endpoint: "/filter/v1/messages/{contentTopic}", meth: HttpMethod.MethodGet.} diff --git a/waku/waku_api/rest/filter/legacy_handlers.nim b/waku/waku_api/rest/filter/legacy_handlers.nim index 09de827fc..86d51f035 100644 --- a/waku/waku_api/rest/filter/legacy_handlers.nim +++ b/waku/waku_api/rest/filter/legacy_handlers.nim @@ -20,6 +20,7 @@ import ../../message_cache, ../serdes, ../responses, + ../rest_serdes, ./types export types @@ -38,23 +39,6 @@ const filterMessageCacheDefaultCapacity* = 30 type MessageCache* = message_cache.MessageCache[ContentTopic] -func decodeRequestBody[T](contentBody: Option[ContentBody]) : Result[T, RestApiResponse] = - if contentBody.isNone(): - return err(RestApiResponse.badRequest("Missing content body")) - - let reqBodyContentType = MediaType.init($contentBody.get().contentType) - if reqBodyContentType != MIMETYPE_JSON: - return err(RestApiResponse.badRequest("Wrong Content-Type, expected application/json")) - - let reqBodyData = contentBody.get().data - - let requestResult = decodeFromJsonBytes(T, reqBodyData) - if requestResult.isErr(): - return err(RestApiResponse.badRequest("Invalid content body, could not decode. " & - $requestResult.error)) - - return ok(requestResult.get()) - proc installFilterV1PostSubscriptionsV1Handler*(router: var RestRouter, node: WakuNode, cache: MessageCache) = diff --git a/waku/waku_api/rest/health/client.nim b/waku/waku_api/rest/health/client.nim index c1a0ccfe6..309af9803 100644 --- a/waku/waku_api/rest/health/client.nim +++ b/waku/waku_api/rest/health/client.nim @@ -10,21 +10,10 @@ import presto/[route, client] import ../serdes, - ../responses + ../responses, + ../rest_serdes logScope: topics = "waku node rest health_api" -proc decodeBytes*(t: typedesc[string], value: openArray[byte], - contentType: Opt[ContentTypeData]): RestResult[string] = - if MediaType.init($contentType) != MIMETYPE_TEXT: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - var res: string - if len(value) > 0: - res = newString(len(value)) - copyMem(addr res[0], unsafeAddr value[0], len(value)) - return ok(res) - proc healthCheck*(): RestResponse[string] {.rest, endpoint: "/health", meth: HttpMethod.MethodGet.} diff --git a/waku/waku_api/rest/lightpush/client.nim b/waku/waku_api/rest/lightpush/client.nim index 96caa818e..9a49fbd04 100644 --- a/waku/waku_api/rest/lightpush/client.nim +++ b/waku/waku_api/rest/lightpush/client.nim @@ -16,6 +16,7 @@ import ../../../waku_core, ../serdes, ../responses, + ../rest_serdes, ./types export types @@ -25,24 +26,8 @@ logScope: proc encodeBytes*(value: PushRequest, contentType: string): RestResult[seq[byte]] = - if MediaType.init(contentType) != MIMETYPE_JSON: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") + return encodeBytesOf(value, contentType) - let encoded = ?encodeIntoJsonBytes(value) - return ok(encoded) - -proc decodeBytes*(t: typedesc[string], value: openarray[byte], - contentType: Opt[ContentTypeData]): RestResult[string] = - if MediaType.init($contentType) != MIMETYPE_TEXT: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - var res: string - if len(value) > 0: - res = newString(len(value)) - copyMem(addr res[0], unsafeAddr value[0], len(value)) - return ok(res) proc sendPushRequest*(body: PushRequest): RestResponse[string] diff --git a/waku/waku_api/rest/lightpush/handlers.nim b/waku/waku_api/rest/lightpush/handlers.nim index 222267cfb..df0ae9869 100644 --- a/waku/waku_api/rest/lightpush/handlers.nim +++ b/waku/waku_api/rest/lightpush/handlers.nim @@ -21,6 +21,7 @@ import ../../handlers, ../serdes, ../responses, + ../rest_serdes, ./types export types @@ -40,23 +41,6 @@ const NoPeerNoneFoundError = RestApiResponse.serviceUnavailable( const ROUTE_LIGHTPUSH* = "/lightpush/v1/message" -func decodeRequestBody[T](contentBody: Option[ContentBody]) : Result[T, RestApiResponse] = - if contentBody.isNone(): - return err(RestApiResponse.badRequest("Missing content body")) - - let reqBodyContentType = MediaType.init($contentBody.get().contentType) - if reqBodyContentType != MIMETYPE_JSON: - return err(RestApiResponse.badRequest("Wrong Content-Type, expected application/json")) - - let reqBodyData = contentBody.get().data - - let requestResult = decodeFromJsonBytes(T, reqBodyData) - if requestResult.isErr(): - return err(RestApiResponse.badRequest("Invalid content body, could not decode. " & - $requestResult.error)) - - return ok(requestResult.get()) - proc installLightPushRequestHandler*( router: var RestRouter, node: WakuNode, @@ -73,7 +57,7 @@ proc installLightPushRequestHandler*( return decodedBody.error() let req: PushRequest = decodedBody.value() - + let msg = req.message.toWakuMessage().valueOr: return RestApiResponse.badRequest("Invalid message: " & $error) diff --git a/waku/waku_api/rest/relay/client.nim b/waku/waku_api/rest/relay/client.nim index 65f12e19d..4b84210b2 100644 --- a/waku/waku_api/rest/relay/client.nim +++ b/waku/waku_api/rest/relay/client.nim @@ -14,6 +14,7 @@ import ../../../waku_core, ../serdes, ../responses, + ../rest_serdes, ./types export types @@ -25,24 +26,7 @@ logScope: proc encodeBytes*(value: seq[PubSubTopic], contentType: string): RestResult[seq[byte]] = - if MediaType.init(contentType) != MIMETYPE_JSON: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - let encoded = ?encodeIntoJsonBytes(value) - return ok(encoded) - -proc decodeBytes*(t: typedesc[string], value: openarray[byte], - contentType: Opt[ContentTypeData]): RestResult[string] = - if MediaType.init($contentType) != MIMETYPE_TEXT: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - var res: string - if len(value) > 0: - res = newString(len(value)) - copyMem(addr res[0], unsafeAddr value[0], len(value)) - return ok(res) + return encodeBytesOf(value, contentType) # TODO: Check how we can use a constant to set the method endpoint (improve "rest" pragma under nim-presto) proc relayPostSubscriptionsV1*(body: seq[PubsubTopic]): RestResponse[string] {.rest, endpoint: "/relay/v1/subscriptions", meth: HttpMethod.MethodPost.} @@ -52,22 +36,9 @@ proc relayPostAutoSubscriptionsV1*(body: seq[ContentTopic]): RestResponse[string proc relayDeleteSubscriptionsV1*(body: seq[PubsubTopic]): RestResponse[string] {.rest, endpoint: "/relay/v1/subscriptions", meth: HttpMethod.MethodDelete.} proc relayDeleteAutoSubscriptionsV1*(body: seq[ContentTopic]): RestResponse[string] {.rest, endpoint: "/relay/v1/auto/subscriptions", meth: HttpMethod.MethodDelete.} -proc decodeBytes*(t: typedesc[RelayGetMessagesResponse], data: openArray[byte], contentType: Opt[ContentTypeData]): RestResult[RelayGetMessagesResponse] = - if MediaType.init($contentType) != MIMETYPE_JSON: - error "Unsupported respose contentType value", contentType = contentType - return err("Unsupported response contentType") - - let decoded = ?decodeFromJsonBytes(RelayGetMessagesResponse, data) - return ok(decoded) - proc encodeBytes*(value: RelayPostMessagesRequest, contentType: string): RestResult[seq[byte]] = - if MediaType.init(contentType) != MIMETYPE_JSON: - error "Unsupported contentType value", contentType = contentType - return err("Unsupported contentType") - - let encoded = ?encodeIntoJsonBytes(value) - return ok(encoded) + return encodeBytesOf(value, contentType) # TODO: Check how we can use a constant to set the method endpoint (improve "rest" pragma under nim-presto) proc relayGetMessagesV1*(pubsubTopic: string): RestResponse[RelayGetMessagesResponse] {.rest, endpoint: "/relay/v1/messages/{pubsubTopic}", meth: HttpMethod.MethodGet.} diff --git a/waku/waku_api/rest/relay/handlers.nim b/waku/waku_api/rest/relay/handlers.nim index 0033aa834..7a755cceb 100644 --- a/waku/waku_api/rest/relay/handlers.nim +++ b/waku/waku_api/rest/relay/handlers.nim @@ -20,6 +20,7 @@ import ../../handlers, ../serdes, ../responses, + ../rest_serdes, ./types from std/times import getTime @@ -55,16 +56,8 @@ proc installRelayApiHandlers*(router: var RestRouter, node: WakuNode, cache: Mes if contentBody.isNone(): return RestApiResponse.badRequest() - let reqBodyContentType = MediaType.init($contentBody.get().contentType) - if reqBodyContentType != MIMETYPE_JSON: - return RestApiResponse.badRequest() - - let reqBodyData = contentBody.get().data - let reqResult = decodeFromJsonBytes(seq[PubsubTopic], reqBodyData) - if reqResult.isErr(): - return RestApiResponse.badRequest() - - let req: seq[PubsubTopic] = reqResult.get() + let req: seq[PubsubTopic] = decodeRequestBody[seq[PubsubTopic]](contentBody).valueOr: + return error # Only subscribe to topics for which we have no subscribed topic handlers yet let newTopics = req.filterIt(not cache.isSubscribed(it)) @@ -83,16 +76,8 @@ proc installRelayApiHandlers*(router: var RestRouter, node: WakuNode, cache: Mes if contentBody.isNone(): return RestApiResponse.badRequest() - let reqBodyContentType = MediaType.init($contentBody.get().contentType) - if reqBodyContentType != MIMETYPE_JSON: - return RestApiResponse.badRequest() - - let reqBodyData = contentBody.get().data - let reqResult = decodeFromJsonBytes(seq[PubsubTopic], reqBodyData) - if reqResult.isErr(): - return RestApiResponse.badRequest() - - let req: seq[PubsubTopic] = reqResult.get() + let req: seq[PubsubTopic] = decodeRequestBody[seq[PubsubTopic]](contentBody).valueOr: + return error # Unsubscribe all handlers from requested topics for pubsubTopic in req: @@ -139,21 +124,12 @@ proc installRelayApiHandlers*(router: var RestRouter, node: WakuNode, cache: Mes if contentBody.isNone(): return RestApiResponse.badRequest() - let reqBodyContentType = MediaType.init($contentBody.get().contentType) - if reqBodyContentType != MIMETYPE_JSON: - return RestApiResponse.badRequest() + let reqWakuMessage: RelayWakuMessage = decodeRequestBody[RelayWakuMessage](contentBody).valueOr: + return error - let reqBodyData = contentBody.get().data - let reqResult = decodeFromJsonBytes(RelayPostMessagesRequest, reqBodyData) - if reqResult.isErr(): + var message: WakuMessage = reqWakuMessage.toWakuMessage(version = 0).valueOr: return RestApiResponse.badRequest() - let resMessage = reqResult.value.toWakuMessage(version = 0) - if resMessage.isErr(): - return RestApiResponse.badRequest() - - var message = resMessage.get() - # if RLN is mounted, append the proof to the message if not node.wakuRlnRelay.isNil(): # append the proof to the message @@ -191,16 +167,8 @@ proc installRelayApiHandlers*(router: var RestRouter, node: WakuNode, cache: Mes if contentBody.isNone(): return RestApiResponse.badRequest() - let reqBodyContentType = MediaType.init($contentBody.get().contentType) - if reqBodyContentType != MIMETYPE_JSON: - return RestApiResponse.badRequest() - - let reqBodyData = contentBody.get().data - let reqResult = decodeFromJsonBytes(seq[ContentTopic], reqBodyData) - if reqResult.isErr(): - return RestApiResponse.badRequest() - - let req: seq[ContentTopic] = reqResult.get() + let req: seq[ContentTopic] = decodeRequestBody[seq[ContentTopic]](contentBody).valueOr: + return error # Only subscribe to topics for which we have no subscribed topic handlers yet let newTopics = req.filterIt(not cache.isSubscribed(it)) @@ -219,16 +187,8 @@ proc installRelayApiHandlers*(router: var RestRouter, node: WakuNode, cache: Mes if contentBody.isNone(): return RestApiResponse.badRequest() - let reqBodyContentType = MediaType.init($contentBody.get().contentType) - if reqBodyContentType != MIMETYPE_JSON: - return RestApiResponse.badRequest() - - let reqBodyData = contentBody.get().data - let reqResult = decodeFromJsonBytes(seq[ContentTopic], reqBodyData) - if reqResult.isErr(): - return RestApiResponse.badRequest() - - let req: seq[ContentTopic] = reqResult.get() + let req: seq[ContentTopic] = decodeRequestBody[seq[ContentTopic]](contentBody).valueOr: + return error # Unsubscribe all handlers from requested topics for contentTopic in req: @@ -266,24 +226,15 @@ proc installRelayApiHandlers*(router: var RestRouter, node: WakuNode, cache: Mes if contentBody.isNone(): return RestApiResponse.badRequest() - let reqBodyContentType = MediaType.init($contentBody.get().contentType) - if reqBodyContentType != MIMETYPE_JSON: + let req: RelayWakuMessage = decodeRequestBody[RelayWakuMessage](contentBody).valueOr: + return error + + if req.contentTopic.isNone(): return RestApiResponse.badRequest() - let reqBodyData = contentBody.get().data - let reqResult = decodeFromJsonBytes(RelayPostMessagesRequest, reqBodyData) - if reqResult.isErr(): + var message: WakuMessage = req.toWakuMessage(version = 0).valueOr: return RestApiResponse.badRequest() - if reqResult.value.contentTopic.isNone(): - return RestApiResponse.badRequest() - - let resMessage = reqResult.value.toWakuMessage(version = 0) - if resMessage.isErr(): - return RestApiResponse.badRequest() - - var message = resMessage.get() - # if RLN is mounted, append the proof to the message if not node.wakuRlnRelay.isNil(): # append the proof to the message diff --git a/waku/waku_api/rest/rest_serdes.nim b/waku/waku_api/rest/rest_serdes.nim new file mode 100644 index 000000000..b9251a14d --- /dev/null +++ b/waku/waku_api/rest/rest_serdes.nim @@ -0,0 +1,79 @@ +when (NimMajor, NimMinor) < (1, 4): + {.push raises: [Defect].} +else: + {.push raises: [].} + +import + std/typetraits, + std/os, + stew/results, + chronicles, + serialization, + json_serialization, + json_serialization/std/options, + json_serialization/std/net, + json_serialization/std/sets, + presto/common + +import + ./serdes, + ./responses + +logScope: + topics = "waku node rest" + +proc encodeBytesOf*[T](value: T, + contentType: string): RestResult[seq[byte]]= + let reqContentType = MediaType.init(contentType) + + if reqContentType != MIMETYPE_JSON: + error "Unsupported contentType value", contentType = contentType, typ = value.type.name + return err("Unsupported contentType") + + let encoded = ?encodeIntoJsonBytes(value) + return ok(encoded) + +func decodeRequestBody*[T](contentBody: Option[ContentBody]) : Result[T, RestApiResponse] = + if contentBody.isNone(): + return err(RestApiResponse.badRequest("Missing content body")) + + let reqBodyContentType = contentBody.get().contentType.mediaType + + if reqBodyContentType != MIMETYPE_JSON and reqBodyContentType != MIMETYPE_TEXT: + return err(RestApiResponse.badRequest("Wrong Content-Type, expected application/json or text/plain")) + + let reqBodyData = contentBody.get().data + + let requestResult = decodeFromJsonBytes(T, reqBodyData) + if requestResult.isErr(): + return err(RestApiResponse.badRequest("Invalid content body, could not decode. " & + $requestResult.error)) + + return ok(requestResult.get()) + +proc decodeBytes*(t: typedesc[string], value: openarray[byte], + contentType: Opt[ContentTypeData]): RestResult[string] = + if MediaType.init($contentType) != MIMETYPE_TEXT: + error "Unsupported contentType value", contentType = contentType + return err("Unsupported contentType") + + var res: string + if len(value) > 0: + res = newString(len(value)) + copyMem(addr res[0], unsafeAddr value[0], len(value)) + return ok(res) + +proc decodeBytes*[T](t: typedesc[T], + data: openArray[byte], + contentType: Opt[ContentTypeData]): RestResult[T] = + + let reqContentType = contentType.valueOr(): + error "Unsupported response, missing contentType value" + return err("Unsupported response, missing contentType") + + if reqContentType.mediaType != MIMETYPE_JSON and reqContentType.mediaType != MIMETYPE_TEXT: + error "Unsupported response contentType value", contentType = contentType + return err("Unsupported response contentType") + + let decoded = ?decodeFromJsonBytes(T, data) + return ok(decoded) diff --git a/waku/waku_api/rest/serdes.nim b/waku/waku_api/rest/serdes.nim index f87c157c0..57541e60c 100644 --- a/waku/waku_api/rest/serdes.nim +++ b/waku/waku_api/rest/serdes.nim @@ -3,7 +3,7 @@ when (NimMajor, NimMinor) < (1, 4): else: {.push raises: [].} -import +import std/typetraits, stew/results, stew/byteutils, @@ -15,7 +15,8 @@ import json_serialization/std/sets, presto/common -logScope: + +logScope: topics = "waku node rest" Json.createFlavor RestJson