mirror of
https://github.com/logos-messaging/logos-delivery.git
synced 2026-07-22 20:50:22 +00:00
* WIP logosdeliverynode app initial commit * WIP - extra cli option * WIP: messaging client REST endpoints * Add event poll for messaging rest with cache mechanism * Messaging rest tests * test: assert 404 via raw string client in messaging REST test presto's typed REST client raises RestDecodingError when it cannot decode a non-2xx text error body into the response type. Add a RestResponse[string] stub (messagingGetSendEventsByIdRawV1) and point the "already-polled id -> 404" assertion at it, matching the relay REST test pattern. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * remove customized cli args as confutils has no support for it * Introduce --entry-layer and re-introduce --mode flags into cli args, applied new driver into LogosDelivery + tests * Add messaging REST client test * Add docker image build of logosdeliverynode for CI builds * Fix tests * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Refactor Messaging REST API to better match Messaging Send and Receive APIs * chore: migrate messaging REST API to Opt[T] Follow-up to the rebase onto master's repo-wide Option[T] -> Opt[T] change (#4035). Converts the code this branch adds to the new convention: - messaging/rest_api/types.nim: MessagingJsonEnvelope fields to Opt[T], Opt.some/Opt.none, and json_serialization/pkg/results instead of json_serialization/std/options. - tests: WakuNodeConf.clusterId is now Opt[uint16]; DTO fields are Opt. `Option[ContentBody]` in the handlers is presto's own API and stays as-is. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
191 lines
6.6 KiB
Nim
191 lines
6.6 KiB
Nim
{.push raises: [].}
|
|
|
|
import std/[json, strutils, tables]
|
|
import results
|
|
|
|
import tools/confutils/conf_from_json
|
|
import logos_delivery/api/conf/logos_delivery_conf
|
|
|
|
const
|
|
# Lowercased, since `collectJsonFields` keys the object case-insensitively.
|
|
KeyEntryLayer = "entrylayer"
|
|
KeyMode = "mode"
|
|
KeyPreset = "preset"
|
|
KeyKernelConf = "kernelconf"
|
|
KeyMessagingOverrides = "messagingoverrides"
|
|
KeyChannelsOverrides = "channelsoverrides"
|
|
# [Legacy flat JSON config] Keys that left the kernel and so must be lifted out of
|
|
# a flat blob before the WakuNodeConf walker sees them (it would reject them).
|
|
KeyReliabilityEnabled = "reliabilityenabled"
|
|
KeyReliability = "reliability"
|
|
|
|
proc parseMode(s: string): Result[LogosDeliveryMode, string] =
|
|
case s.strip().toLowerAscii()
|
|
of "core":
|
|
return ok(LogosDeliveryMode.Core)
|
|
of "edge":
|
|
return ok(LogosDeliveryMode.Edge)
|
|
else:
|
|
return err("invalid mode: '" & s & "' (expected 'Core' or 'Edge')")
|
|
|
|
proc parseEntryLayer(s: string): Result[EntryLayer, string] =
|
|
case s.strip().toLowerAscii()
|
|
of "kernel":
|
|
return ok(EntryLayer.kernel)
|
|
of "messaging":
|
|
return ok(EntryLayer.messaging)
|
|
of "channels":
|
|
return ok(EntryLayer.channels)
|
|
else:
|
|
return err(
|
|
"invalid entryLayer: '" & s & "' (expected 'kernel', 'messaging' or 'channels')"
|
|
)
|
|
|
|
proc parseOverrides[T](defaults: T, node: JsonNode, label: string): Result[T, string] =
|
|
## Parse the JSON object `node` as overrides on top of `defaults`.
|
|
if node.kind != JObject:
|
|
return err(label & " must be a JSON object")
|
|
var fields = ?collectJsonFields(node)
|
|
var conf = defaults
|
|
?applyJsonFieldsToConf(
|
|
conf,
|
|
fields,
|
|
"Failed to parse " & label & " field",
|
|
"Unrecognized " & label & " option(s) found",
|
|
)
|
|
return ok(conf)
|
|
|
|
proc parseFlatConf(
|
|
mode: LogosDeliveryMode, topJsonNode: var Table[string, (string, JsonNode)]
|
|
): ConfResult[LogosDeliveryConf] =
|
|
## [Legacy flat JSON config] Flat shape: a blob of `WakuNodeConf` fields. `mode`
|
|
## expands to protocol flags over raw kernel defaults, `reliabilityEnabled` routes
|
|
## to the messaging conf (it left the kernel), and the rest parses as a
|
|
## `WakuNodeConf`. Full stack. Delete this proc and its call site to drop support.
|
|
var messaging = MessagingClientConf()
|
|
var reliabilityFields: Table[string, (string, JsonNode)]
|
|
for key in [KeyReliabilityEnabled, KeyReliability]:
|
|
if topJsonNode.hasKey(key):
|
|
reliabilityFields[key] = topJsonNode.getOrDefault(key)
|
|
topJsonNode.del(key)
|
|
if reliabilityFields.len > 0:
|
|
?applyJsonFieldsToConf(
|
|
messaging, reliabilityFields, "Failed to parse reliability field",
|
|
"Unrecognized reliability option(s) found",
|
|
)
|
|
|
|
# [Legacy flat JSON config] The blob is a raw WakuNodeConf, exactly as the
|
|
# pre-refactor flat create_node parsed it: start from the kernel defaults and apply
|
|
# the mode's protocol flags (the kernel no longer owns `mode`, so we expand it here,
|
|
# like the old kernel builder did), then let explicit fields override.
|
|
var kernel = ?defaultWakuNodeConf()
|
|
?applyMode(kernel, mode)
|
|
?applyJsonFieldsToConf(
|
|
kernel, topJsonNode, "Failed to parse config field",
|
|
"Unrecognized configuration option(s) found",
|
|
)
|
|
|
|
# [Legacy flat JSON config] Reliability is resolved from the preset by the messaging
|
|
# layer (the kernel no longer carries it), so a flat blob's `preset` must lift it
|
|
# here to stay faithful to master. An explicit reliability in the blob still wins.
|
|
if kernel.preset.len > 0:
|
|
messaging = merge(?resolvePreset(kernel.preset), messaging)
|
|
|
|
return ok(
|
|
LogosDeliveryConf(
|
|
kernelConf: KernelConf(kernel),
|
|
messagingConf: Opt.some(messaging),
|
|
channelsConf: Opt.some(ReliableChannelManagerConf()),
|
|
)
|
|
)
|
|
|
|
proc parseLogosDeliveryConf*(jsonStr: string): ConfResult[LogosDeliveryConf] =
|
|
var node: JsonNode
|
|
try:
|
|
node = parseJson(jsonStr)
|
|
except CatchableError as e:
|
|
return err("invalid JSON: " & e.msg)
|
|
if node.kind != JObject:
|
|
return err("configuration JSON must be an object")
|
|
|
|
var top = ?collectJsonFields(node)
|
|
|
|
var mode = LogosDeliveryMode.Core
|
|
if top.hasKey(KeyMode):
|
|
let (_, v) = top.getOrDefault(KeyMode)
|
|
if v.kind != JString:
|
|
return err("mode must be a string")
|
|
mode = ?parseMode(v.getStr())
|
|
top.del(KeyMode)
|
|
|
|
var entryLayer = EntryLayer.channels
|
|
if top.hasKey(KeyEntryLayer):
|
|
let (_, v) = top.getOrDefault(KeyEntryLayer)
|
|
if v.kind != JString:
|
|
return err("entryLayer must be a string")
|
|
entryLayer = ?parseEntryLayer(v.getStr())
|
|
top.del(KeyEntryLayer)
|
|
|
|
if entryLayer == EntryLayer.kernel:
|
|
# Kernel-only: a raw kernelConf and no upper layers; mode is ignored.
|
|
if not top.hasKey(KeyKernelConf):
|
|
return err("kernel entry layer requires a 'kernelConf' object")
|
|
let (_, v) = top.getOrDefault(KeyKernelConf)
|
|
let kernel = ?parseOverrides(?defaultWakuNodeConf(), v, "kernelConf")
|
|
top.del(KeyKernelConf)
|
|
if top.len > 0:
|
|
return err(
|
|
unknownKeysError(top, "kernel entry layer takes only 'kernelConf'; unexpected")
|
|
)
|
|
return ok(LogosDeliveryConf.init(KernelConf(kernel)))
|
|
|
|
# [Legacy flat JSON config] A wrapper key marks our structured shape. Otherwise any
|
|
# leftover top-level key besides `preset` (mode is already consumed) is a bare
|
|
# kernel field -> flat blob. Delete this block to drop flat-shape support.
|
|
let hasWrapper =
|
|
top.hasKey(KeyMessagingOverrides) or top.hasKey(KeyChannelsOverrides) or
|
|
top.hasKey(KeyKernelConf)
|
|
if not hasWrapper:
|
|
var bareField = false
|
|
for k in top.keys:
|
|
if k != KeyPreset:
|
|
bareField = true
|
|
break
|
|
if bareField:
|
|
return parseFlatConf(mode, top)
|
|
|
|
var preset = ""
|
|
var messagingOverrides = MessagingClientConf()
|
|
var channelsOverrides = ReliableChannelManagerConf()
|
|
|
|
if top.hasKey(KeyPreset):
|
|
let (_, v) = top.getOrDefault(KeyPreset)
|
|
if v.kind != JString:
|
|
return err("preset must be a string")
|
|
preset = v.getStr().strip()
|
|
top.del(KeyPreset)
|
|
|
|
if top.hasKey(KeyMessagingOverrides):
|
|
let (_, v) = top.getOrDefault(KeyMessagingOverrides)
|
|
messagingOverrides = ?parseOverrides(MessagingClientConf(), v, "messagingOverrides")
|
|
top.del(KeyMessagingOverrides)
|
|
|
|
if top.hasKey(KeyChannelsOverrides):
|
|
let (_, v) = top.getOrDefault(KeyChannelsOverrides)
|
|
channelsOverrides =
|
|
?parseOverrides(ReliableChannelManagerConf(), v, "channelsOverrides")
|
|
top.del(KeyChannelsOverrides)
|
|
|
|
if top.len > 0:
|
|
return err(unknownKeysError(top, "Unrecognized configuration option(s) found"))
|
|
|
|
return LogosDeliveryConf.init(
|
|
entryLayer = entryLayer,
|
|
mode = mode,
|
|
preset = preset,
|
|
messagingOverrides = messagingOverrides,
|
|
channelsOverrides = channelsOverrides,
|
|
)
|
|
|
|
{.pop.}
|