mirror of
https://github.com/logos-messaging/logos-delivery.git
synced 2026-06-04 13:09:32 +00:00
Simplify config improvement PR
* Remove WakuNodeConfOverlay + optionalize macro * Remove createNode(preset/mode/overrides/additions) and seedDeveloperProfile * conf_from_json: drop messaging-shape parser, keep flat-conf only * Tests: drop messaging-API/Overlay tests
This commit is contained in:
parent
5b04105766
commit
37ee2d53ee
@ -3,7 +3,6 @@
|
|||||||
import
|
import
|
||||||
./test_entry_nodes,
|
./test_entry_nodes,
|
||||||
./test_node_conf,
|
./test_node_conf,
|
||||||
./test_messaging_conf,
|
|
||||||
./test_api_send,
|
./test_api_send,
|
||||||
./test_api_subscription,
|
./test_api_subscription,
|
||||||
./test_api_receive,
|
./test_api_receive,
|
||||||
|
|||||||
@ -1,199 +0,0 @@
|
|||||||
{.used.}
|
|
||||||
|
|
||||||
import std/[algorithm, json, options, sequtils]
|
|
||||||
|
|
||||||
import results, testutils/unittests
|
|
||||||
|
|
||||||
import tools/confutils/conf_from_json, tools/confutils/cli_args
|
|
||||||
import tools/confutils/messaging_conf
|
|
||||||
|
|
||||||
suite "Messaging conf JSON parser":
|
|
||||||
test "Routes to messaging shape when mode and overrides are present":
|
|
||||||
let res = parseConfJson("""{"mode": "Core", "overrides": {}}""")
|
|
||||||
require res.isOk()
|
|
||||||
let conf = res.get()
|
|
||||||
check conf.mode == cli_args.WakuMode.Core
|
|
||||||
|
|
||||||
test "Routes to full conf shape when only mode key is present":
|
|
||||||
let res = parseConfJson("""{"mode": "Edge"}""")
|
|
||||||
require res.isOk()
|
|
||||||
let conf = res.get()
|
|
||||||
check conf.mode == cli_args.WakuMode.Edge
|
|
||||||
|
|
||||||
test "Messaging shape applies overrides":
|
|
||||||
let res = parseConfJson(
|
|
||||||
"""{"mode": "Core", "overrides": {"clusterId": 42, "tcpPort": 12345}}"""
|
|
||||||
)
|
|
||||||
require res.isOk()
|
|
||||||
let conf = res.get()
|
|
||||||
check:
|
|
||||||
conf.clusterId == some(42'u16)
|
|
||||||
conf.tcpPort == Port(12345)
|
|
||||||
|
|
||||||
test "Messaging shape applies preset":
|
|
||||||
let res = parseConfJson("""{"mode": "Core", "preset": "twn", "overrides": {}}""")
|
|
||||||
require res.isOk()
|
|
||||||
let conf = res.get()
|
|
||||||
check conf.preset == "twn"
|
|
||||||
|
|
||||||
test "Messaging shape applies additions to list fields":
|
|
||||||
let res = parseConfJson(
|
|
||||||
"""{"mode": "Core", "overrides": {}, "additions": {"staticnodes": ["/ip4/1.2.3.4/tcp/60000/p2p/16Uiu2HAmTUbnxLGT9JvV6mu9oPyDjqHK4Phs1VDJNUgESgNSkuby"]}}"""
|
|
||||||
)
|
|
||||||
require res.isOk()
|
|
||||||
let conf = res.get()
|
|
||||||
check conf.staticnodes.len == 1
|
|
||||||
|
|
||||||
test "Messaging shape: additions concat after overrides on same list field":
|
|
||||||
let res = parseConfJson(
|
|
||||||
"""{"mode": "Core", "additions": {"staticnodes": ["/ip4/1.2.3.4/tcp/60000/p2p/16Uiu2HAmTUbnxLGT9JvV6mu9oPyDjqHK4Phs1VDJNUgESgNSkuby"]}, "overrides": {"staticnodes": ["/ip4/5.6.7.8/tcp/60000/p2p/16Uiu2HAmTUbnxLGT9JvV6mu9oPyDjqHK4Phs1VDJNUgESgNSkuby"]}}"""
|
|
||||||
)
|
|
||||||
require res.isOk()
|
|
||||||
let conf = res.get()
|
|
||||||
check:
|
|
||||||
conf.staticnodes.len == 2
|
|
||||||
conf.staticnodes[0] ==
|
|
||||||
"/ip4/5.6.7.8/tcp/60000/p2p/16Uiu2HAmTUbnxLGT9JvV6mu9oPyDjqHK4Phs1VDJNUgESgNSkuby"
|
|
||||||
conf.staticnodes[1] ==
|
|
||||||
"/ip4/1.2.3.4/tcp/60000/p2p/16Uiu2HAmTUbnxLGT9JvV6mu9oPyDjqHK4Phs1VDJNUgESgNSkuby"
|
|
||||||
|
|
||||||
test "Messaging shape rejects missing mode":
|
|
||||||
let res = parseConfJson("""{"overrides": {}}""")
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Messaging shape rejects unknown override field":
|
|
||||||
let res = parseConfJson("""{"mode": "Core", "overrides": {"bogusField": 1}}""")
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Messaging shape rejects addition on non-list field":
|
|
||||||
let res = parseConfJson(
|
|
||||||
"""{"mode": "Core", "overrides": {}, "additions": {"clusterId": [1]}}"""
|
|
||||||
)
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Messaging shape rejects unknown top-level key":
|
|
||||||
let res = parseConfJson("""{"mode": "Core", "overrides": {}, "garbage": 1}""")
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Full conf shape parses arbitrary WakuNodeConf fields":
|
|
||||||
let res = parseConfJson("""{"clusterId": 7, "tcpPort": 22222}""")
|
|
||||||
require res.isOk()
|
|
||||||
let conf = res.get()
|
|
||||||
check:
|
|
||||||
conf.clusterId == some(7'u16)
|
|
||||||
conf.tcpPort == Port(22222)
|
|
||||||
|
|
||||||
test "Full conf shape rejects unknown field":
|
|
||||||
let res = parseConfJson("""{"completelyMadeUp": 1}""")
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Malformed JSON returns error":
|
|
||||||
let res = parseConfJson("{ not json }")
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Rejects top-level JSON array":
|
|
||||||
let res = parseConfJson("""[1, 2]""")
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Rejects top-level scalar":
|
|
||||||
let res = parseConfJson("""42""")
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Rejects top-level null":
|
|
||||||
let res = parseConfJson("""null""")
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Messaging shape rejects 'mode' inside 'overrides'":
|
|
||||||
let res = parseConfJson("""{"mode": "Core", "overrides": {"mode": "Edge"}}""")
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Messaging shape rejects 'preset' inside 'overrides'":
|
|
||||||
let res = parseConfJson(
|
|
||||||
"""{"mode": "Core", "preset": "twn", "overrides": {"preset": "logos.dev"}}"""
|
|
||||||
)
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Messaging shape rejects 'mode' inside 'additions'":
|
|
||||||
let res = parseConfJson(
|
|
||||||
"""{"mode": "Core", "overrides": {}, "additions": {"mode": "Edge"}}"""
|
|
||||||
)
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Messaging shape rejects 'preset' inside 'additions'":
|
|
||||||
let res = parseConfJson(
|
|
||||||
"""{"mode": "Core", "overrides": {}, "additions": {"preset": "twn"}}"""
|
|
||||||
)
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Rejects duplicate normalized keys":
|
|
||||||
let res = parseConfJson("""{"clusterId": 1, "ClusterId": 2}""")
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Case-insensitive override matching":
|
|
||||||
let res = parseConfJson("""{"mode": "Core", "overrides": {"CLUSTERID": 99}}""")
|
|
||||||
require res.isOk()
|
|
||||||
let conf = res.get()
|
|
||||||
check conf.clusterId == some(99'u16)
|
|
||||||
|
|
||||||
test "Rejects 'overrides' that isn't a JSON object":
|
|
||||||
let res = parseConfJson("""{"mode": "Core", "overrides": "not an object"}""")
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "Rejects 'additions' that isn't a JSON object":
|
|
||||||
let res = parseConfJson(
|
|
||||||
"""{"mode": "Core", "overrides": {}, "additions": ["not an object"]}"""
|
|
||||||
)
|
|
||||||
check res.isErr()
|
|
||||||
|
|
||||||
test "JBool maps to Option[bool] field":
|
|
||||||
let res = parseConfJson("""{"mode": "Core", "overrides": {"rlnRelay": true}}""")
|
|
||||||
require res.isOk()
|
|
||||||
let conf = res.get()
|
|
||||||
check conf.rlnRelay == some(true)
|
|
||||||
|
|
||||||
suite "WakuNodeConfOverlay structure":
|
|
||||||
proc fieldNamesOfWakuNodeConf(): seq[string] =
|
|
||||||
var c: WakuNodeConf
|
|
||||||
for name, _ in fieldPairs(c):
|
|
||||||
result.add(name)
|
|
||||||
|
|
||||||
proc fieldNamesOfOverlay(): seq[string] =
|
|
||||||
var o: WakuNodeConfOverlay
|
|
||||||
for name, _ in fieldPairs(o):
|
|
||||||
result.add(name)
|
|
||||||
|
|
||||||
test "Overlay field names match WakuNodeConf minus excludes":
|
|
||||||
let expected =
|
|
||||||
fieldNamesOfWakuNodeConf().filterIt(it notin WakuNodeConfOverlayExcludes)
|
|
||||||
let actual = fieldNamesOfOverlay()
|
|
||||||
check sorted(actual) == sorted(expected)
|
|
||||||
|
|
||||||
test "Every overlay field is Option-typed":
|
|
||||||
var o: WakuNodeConfOverlay
|
|
||||||
var allOption = true
|
|
||||||
for _, value in fieldPairs(o):
|
|
||||||
when typeof(value) isnot Option:
|
|
||||||
allOption = false
|
|
||||||
check allOption
|
|
||||||
|
|
||||||
test "Excluded names are absent from overlay":
|
|
||||||
let actual = fieldNamesOfOverlay()
|
|
||||||
for excluded in WakuNodeConfOverlayExcludes:
|
|
||||||
check excluded notin actual
|
|
||||||
|
|
||||||
test "Overlay inner types match WakuNodeConf field types":
|
|
||||||
var c: WakuNodeConf
|
|
||||||
var o: WakuNodeConfOverlay
|
|
||||||
for oname, ovalue in fieldPairs(o):
|
|
||||||
for cname, cvalue in fieldPairs(c):
|
|
||||||
when oname == cname:
|
|
||||||
when typeof(cvalue) is Option:
|
|
||||||
ovalue = cvalue
|
|
||||||
else:
|
|
||||||
ovalue = some(cvalue)
|
|
||||||
|
|
||||||
test "Overlay default-constructs every field as none":
|
|
||||||
var o: WakuNodeConfOverlay
|
|
||||||
for _, value in fieldPairs(o):
|
|
||||||
when typeof(value) is Option:
|
|
||||||
check value.isNone()
|
|
||||||
@ -90,119 +90,3 @@ suite "Waku API - Create node":
|
|||||||
node.conf.staticNodes.len == 1
|
node.conf.staticNodes.len == 1
|
||||||
node.conf.staticNodes[0] ==
|
node.conf.staticNodes[0] ==
|
||||||
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc"
|
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc"
|
||||||
|
|
||||||
asyncTest "Create node via messaging API with overrides":
|
|
||||||
let
|
|
||||||
clusterId = 3'u16
|
|
||||||
numShards = 1'u16
|
|
||||||
let overrides = WakuNodeConfOverlay(
|
|
||||||
clusterId: some(clusterId), rest: some(false), numShardsInNetwork: some(numShards)
|
|
||||||
)
|
|
||||||
|
|
||||||
let node = (await createNode(mode = cli_args.WakuMode.Core, overrides = overrides)).valueOr:
|
|
||||||
raiseAssert "createNode (overrides only) failed: " & error
|
|
||||||
|
|
||||||
check:
|
|
||||||
not node.isNil()
|
|
||||||
node.conf.clusterId == clusterId
|
|
||||||
node.conf.shardingConf.numShardsInCluster == numShards
|
|
||||||
|
|
||||||
asyncTest "Create node via messaging API with overrides + additions":
|
|
||||||
let
|
|
||||||
clusterId = 7'u16
|
|
||||||
staticnode =
|
|
||||||
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc"
|
|
||||||
let overrides = WakuNodeConfOverlay(
|
|
||||||
clusterId: some(clusterId), rest: some(false), numShardsInNetwork: some(1'u16)
|
|
||||||
)
|
|
||||||
let additions = WakuNodeConfOverlay(staticnodes: some(@[staticnode]))
|
|
||||||
|
|
||||||
let node = (
|
|
||||||
await createNode(
|
|
||||||
mode = cli_args.WakuMode.Core, overrides = overrides, additions = additions
|
|
||||||
)
|
|
||||||
).valueOr:
|
|
||||||
raiseAssert "createNode (overrides + additions) failed: " & error
|
|
||||||
|
|
||||||
check:
|
|
||||||
not node.isNil()
|
|
||||||
node.conf.clusterId == clusterId
|
|
||||||
node.conf.staticNodes.len == 1
|
|
||||||
node.conf.staticNodes[0] == staticnode
|
|
||||||
|
|
||||||
asyncTest "Create node via messaging API with preset":
|
|
||||||
let
|
|
||||||
preset = "twn"
|
|
||||||
twn = NetworkConf.TheWakuNetworkConf()
|
|
||||||
let overrides = WakuNodeConfOverlay(rest: some(false))
|
|
||||||
|
|
||||||
let node = (
|
|
||||||
await createNode(
|
|
||||||
preset = preset, mode = cli_args.WakuMode.Edge, overrides = overrides
|
|
||||||
)
|
|
||||||
).valueOr:
|
|
||||||
raiseAssert "createNode (preset = " & preset & ") failed: " & error
|
|
||||||
|
|
||||||
check:
|
|
||||||
not node.isNil()
|
|
||||||
node.conf.clusterId == twn.clusterId
|
|
||||||
node.conf.shardingConf.kind == twn.shardingConf.kind
|
|
||||||
node.conf.shardingConf.numShardsInCluster == twn.shardingConf.numShardsInCluster
|
|
||||||
node.conf.discv5Conf.isSome()
|
|
||||||
node.conf.discv5Conf.get().bootstrapNodes.len == twn.discv5BootstrapNodes.len
|
|
||||||
|
|
||||||
asyncTest "Create node via messaging API: additions concat with preset's bootstrap nodes":
|
|
||||||
let
|
|
||||||
preset = "twn"
|
|
||||||
twn = NetworkConf.TheWakuNetworkConf()
|
|
||||||
addedBootstrapNode =
|
|
||||||
"enr:-QESuED0qW1BCmF-oH_ARGPr97Nv767bl_43uoy70vrbah3EaCAdK3Q0iRQ6wkSTTpdrg_dU_NC2ydO8leSlRpBX4pxiAYJpZIJ2NIJpcIRA4VDAim11bHRpYWRkcnO4XAArNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwAtNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQOTd-h5owwj-cx7xrmbvQKU8CV3Fomfdvcv1MBc-67T5oN0Y3CCdl-DdWRwgiMohXdha3UyDw"
|
|
||||||
let overrides = WakuNodeConfOverlay(rest: some(false))
|
|
||||||
let additions =
|
|
||||||
WakuNodeConfOverlay(discv5BootstrapNodes: some(@[addedBootstrapNode]))
|
|
||||||
|
|
||||||
let node = (
|
|
||||||
await createNode(
|
|
||||||
preset = preset,
|
|
||||||
mode = cli_args.WakuMode.Edge,
|
|
||||||
overrides = overrides,
|
|
||||||
additions = additions,
|
|
||||||
)
|
|
||||||
).valueOr:
|
|
||||||
raiseAssert "createNode (preset = " & preset & " + additions) failed: " & error
|
|
||||||
|
|
||||||
check:
|
|
||||||
not node.isNil()
|
|
||||||
node.conf.discv5Conf.isSome()
|
|
||||||
node.conf.discv5Conf.get().bootstrapNodes.len == twn.discv5BootstrapNodes.len + 1
|
|
||||||
node.conf.discv5Conf.get().bootstrapNodes.contains(addedBootstrapNode)
|
|
||||||
|
|
||||||
asyncTest "Messaging API seeds 3 user-ports to 0; metrics/REST keep concrete defaults":
|
|
||||||
let overrides = WakuNodeConfOverlay(
|
|
||||||
discv5Discovery: some(true),
|
|
||||||
websocketSupport: some(true),
|
|
||||||
rest: some(true),
|
|
||||||
restRelayCacheCapacity: some(50'u32),
|
|
||||||
metricsServer: some(true),
|
|
||||||
)
|
|
||||||
let node = (await createNode(mode = cli_args.WakuMode.Core, overrides = overrides)).valueOr:
|
|
||||||
raiseAssert "createNode (port-seeding check) failed: " & error
|
|
||||||
|
|
||||||
check:
|
|
||||||
node.conf.endpointConf.p2pTcpPort == Port(0)
|
|
||||||
node.conf.discv5Conf.get().udpPort == Port(0)
|
|
||||||
node.conf.webSocketConf.get().port == Port(0)
|
|
||||||
node.conf.restServerConf.get().port == DefaultRestPort
|
|
||||||
node.conf.metricsServerConf.get().httpPort == DefaultMetricsHttpPort
|
|
||||||
|
|
||||||
asyncTest "Messaging API: explicit user overrides win over developer-profile seeding":
|
|
||||||
# Caller's explicit tcpPort must take precedence over seedDeveloperProfile's 0.
|
|
||||||
let overrides = WakuNodeConfOverlay(tcpPort: some(Port(12345)))
|
|
||||||
let node = (await createNode(mode = cli_args.WakuMode.Core, overrides = overrides)).valueOr:
|
|
||||||
raiseAssert "createNode (override-wins check) failed: " & error
|
|
||||||
check node.conf.endpointConf.p2pTcpPort == Port(12345)
|
|
||||||
|
|
||||||
asyncTest "Messaging API with no overrides: p2pTcpPort seeded to 0":
|
|
||||||
let node = (await createNode(mode = cli_args.WakuMode.Core)).valueOr:
|
|
||||||
raiseAssert "createNode (no overrides) failed: " & error
|
|
||||||
check node.conf.endpointConf.p2pTcpPort == Port(0)
|
|
||||||
|
|||||||
@ -1,18 +1,6 @@
|
|||||||
import std/[json, strutils, tables]
|
import std/[json, strutils, tables]
|
||||||
import confutils, confutils/std/net, results
|
import confutils, confutils/std/net, results
|
||||||
import ./cli_args
|
import ./cli_args
|
||||||
import ./messaging_conf
|
|
||||||
|
|
||||||
const
|
|
||||||
KeyMode = "mode"
|
|
||||||
KeyPreset = "preset"
|
|
||||||
KeyOverrides = "overrides"
|
|
||||||
KeyAdditions = "additions"
|
|
||||||
|
|
||||||
const CreateNodeWithOverridesExplicitKeys = [KeyMode, KeyPreset]
|
|
||||||
## Keys that map to explicit parameters of `createNode(preset, mode, ...)`,
|
|
||||||
## hence parsed at the messaging shape's top level and rejected inside
|
|
||||||
## `overrides`/`additions` to avoid ambiguity.
|
|
||||||
|
|
||||||
proc collectJsonFields*(
|
proc collectJsonFields*(
|
||||||
jsonNode: JsonNode
|
jsonNode: JsonNode
|
||||||
@ -41,22 +29,6 @@ proc unknownKeysError(
|
|||||||
keys.add(jsonKey)
|
keys.add(jsonKey)
|
||||||
return prefix & ": " & $keys
|
return prefix & ": " & $keys
|
||||||
|
|
||||||
proc rejectOverridesExplicitKeys(
|
|
||||||
node: JsonNode, blockName: string
|
|
||||||
): Result[void, string] =
|
|
||||||
## Error if `node` contains any key from `CreateNodeWithOverridesExplicitKeys`.
|
|
||||||
for k, _ in node:
|
|
||||||
if k.toLowerAscii() in CreateNodeWithOverridesExplicitKeys:
|
|
||||||
return err("'" & k & "' must be a top-level key, not inside '" & blockName & "'")
|
|
||||||
return ok()
|
|
||||||
|
|
||||||
proc rejectOverlayExcludes(node: JsonNode): Result[void, string] =
|
|
||||||
## Error if `node` contains any key from `WakuNodeConfOverlayExcludes`.
|
|
||||||
for k, _ in node:
|
|
||||||
if k.toLowerAscii() in WakuNodeConfOverlayExcludes:
|
|
||||||
return err("'" & k & "' is not settable via JSON configuration")
|
|
||||||
return ok()
|
|
||||||
|
|
||||||
proc jsonScalarToString(node: JsonNode): Result[string, string] =
|
proc jsonScalarToString(node: JsonNode): Result[string, string] =
|
||||||
## Convert a scalar JSON value to its string form.
|
## Convert a scalar JSON value to its string form.
|
||||||
case node.kind
|
case node.kind
|
||||||
@ -126,105 +98,6 @@ proc applyJsonFieldsToConf(
|
|||||||
return err(unknownKeysError(jsonFields, unknownErrPrefix))
|
return err(unknownKeysError(jsonFields, unknownErrPrefix))
|
||||||
return ok()
|
return ok()
|
||||||
|
|
||||||
proc applyJsonAsOverride*(
|
|
||||||
conf: var WakuNodeConf, overrides: JsonNode
|
|
||||||
): Result[void, string] =
|
|
||||||
## Apply `overrides` JSON onto `conf` with replace semantics for both scalars and lists.
|
|
||||||
var jsonFields = ?collectJsonFields(overrides)
|
|
||||||
return applyJsonFieldsToConf(
|
|
||||||
conf, jsonFields, "Failed to parse override field",
|
|
||||||
"Unrecognized override field(s) found",
|
|
||||||
)
|
|
||||||
|
|
||||||
proc applyJsonAsAddition*(
|
|
||||||
conf: var WakuNodeConf, additions: JsonNode
|
|
||||||
): Result[void, string] =
|
|
||||||
## Append JSON array in `additions` to `conf` seq fields.
|
|
||||||
var jsonFields = ?collectJsonFields(additions)
|
|
||||||
for confField, confValue in fieldPairs(conf):
|
|
||||||
let lowerField = confField.toLowerAscii()
|
|
||||||
if jsonFields.hasKey(lowerField):
|
|
||||||
let (jsonKey, jsonValue) = jsonFields[lowerField]
|
|
||||||
when confValue is seq:
|
|
||||||
if jsonValue.kind != JArray:
|
|
||||||
return err(
|
|
||||||
"Addition field '" & confField & "' from JSON key '" & jsonKey &
|
|
||||||
"' must be a JSON array"
|
|
||||||
)
|
|
||||||
for item in jsonValue:
|
|
||||||
let formattedItem = jsonScalarToString(item).valueOr:
|
|
||||||
return err(
|
|
||||||
"Failed to parse addition item for field '" & confField & "': " & error
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
type ElemType = typeof(confValue[0])
|
|
||||||
confValue.add(parseCmdArg(ElemType, formattedItem))
|
|
||||||
except CatchableError as e:
|
|
||||||
return err(
|
|
||||||
"Failed to parse addition item for field '" & confField & "': " & e.msg &
|
|
||||||
". Value: " & formattedItem
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return err(
|
|
||||||
"Field '" & confField & "' from JSON key '" & jsonKey &
|
|
||||||
"' is not a list and cannot be in additions"
|
|
||||||
)
|
|
||||||
jsonFields.del(lowerField)
|
|
||||||
if jsonFields.len > 0:
|
|
||||||
return err(unknownKeysError(jsonFields, "Unrecognized addition field(s) found"))
|
|
||||||
return ok()
|
|
||||||
|
|
||||||
proc assembleMessagingConf*(
|
|
||||||
jsonFields: Table[string, (string, JsonNode)]
|
|
||||||
): Result[WakuNodeConf, string] =
|
|
||||||
## Build a WakuNodeConf from the messaging shape
|
|
||||||
## `{mode, overrides, preset?, additions?}`. `mode` and `overrides` are
|
|
||||||
## required. Order: overrides applied first, then additions concat.
|
|
||||||
var conf = ?defaultWakuNodeConf()
|
|
||||||
var fields = jsonFields
|
|
||||||
|
|
||||||
if not fields.hasKey(KeyMode):
|
|
||||||
return err("messaging shape requires '" & KeyMode & "' key")
|
|
||||||
if not fields.hasKey(KeyOverrides):
|
|
||||||
return err("messaging shape requires '" & KeyOverrides & "' key")
|
|
||||||
|
|
||||||
let modeStr = jsonScalarToString(fields[KeyMode][1]).valueOr:
|
|
||||||
return err("Failed to parse '" & KeyMode & "': " & error)
|
|
||||||
try:
|
|
||||||
conf.mode = parseCmdArg(WakuMode, modeStr)
|
|
||||||
except CatchableError as e:
|
|
||||||
return err("Failed to parse '" & KeyMode & "': " & e.msg & ". Value: " & modeStr)
|
|
||||||
fields.del(KeyMode)
|
|
||||||
|
|
||||||
if fields.hasKey(KeyPreset):
|
|
||||||
let presetStr = jsonScalarToString(fields[KeyPreset][1]).valueOr:
|
|
||||||
return err("Failed to parse '" & KeyPreset & "': " & error)
|
|
||||||
conf.preset = presetStr
|
|
||||||
fields.del(KeyPreset)
|
|
||||||
|
|
||||||
let overridesNode = fields[KeyOverrides][1]
|
|
||||||
if overridesNode.kind != JObject:
|
|
||||||
return err("'" & KeyOverrides & "' must be a JSON object")
|
|
||||||
?rejectOverlayExcludes(overridesNode)
|
|
||||||
?rejectOverridesExplicitKeys(overridesNode, KeyOverrides)
|
|
||||||
?applyJsonAsOverride(conf, overridesNode)
|
|
||||||
fields.del(KeyOverrides)
|
|
||||||
|
|
||||||
if fields.hasKey(KeyAdditions):
|
|
||||||
let additionsNode = fields[KeyAdditions][1]
|
|
||||||
if additionsNode.kind != JObject:
|
|
||||||
return err("'" & KeyAdditions & "' must be a JSON object")
|
|
||||||
?rejectOverlayExcludes(additionsNode)
|
|
||||||
?rejectOverridesExplicitKeys(additionsNode, KeyAdditions)
|
|
||||||
?applyJsonAsAddition(conf, additionsNode)
|
|
||||||
fields.del(KeyAdditions)
|
|
||||||
|
|
||||||
if fields.len > 0:
|
|
||||||
return
|
|
||||||
err(unknownKeysError(fields, "Unrecognized top-level key(s) in messaging shape"))
|
|
||||||
|
|
||||||
return ok(conf)
|
|
||||||
|
|
||||||
proc assembleFullConf*(
|
proc assembleFullConf*(
|
||||||
jsonFields: Table[string, (string, JsonNode)]
|
jsonFields: Table[string, (string, JsonNode)]
|
||||||
): Result[WakuNodeConf, string] =
|
): Result[WakuNodeConf, string] =
|
||||||
@ -237,21 +110,12 @@ proc assembleFullConf*(
|
|||||||
return ok(conf)
|
return ok(conf)
|
||||||
|
|
||||||
proc parseConfJson*(jsonStr: string): Result[WakuNodeConf, string] =
|
proc parseConfJson*(jsonStr: string): Result[WakuNodeConf, string] =
|
||||||
## Parse a JSON config, route to messaging or full-config shape based on
|
## Parse a flat JSON config whose keys are WakuNodeConf field names.
|
||||||
## whether `overrides` or `additions` fields are in the config object top-level.
|
|
||||||
var jsonNode: JsonNode
|
var jsonNode: JsonNode
|
||||||
try:
|
try:
|
||||||
jsonNode = parseJson(jsonStr)
|
jsonNode = parseJson(jsonStr)
|
||||||
except CatchableError as e:
|
except CatchableError as e:
|
||||||
return err("Failed to parse config JSON: " & e.msg)
|
return err("Failed to parse config JSON: " & e.msg)
|
||||||
|
|
||||||
if jsonNode.kind == JObject:
|
|
||||||
?rejectOverlayExcludes(jsonNode)
|
|
||||||
|
|
||||||
let jsonFields = ?collectJsonFields(jsonNode)
|
let jsonFields = ?collectJsonFields(jsonNode)
|
||||||
let isMessagingShape =
|
|
||||||
jsonFields.hasKey(KeyOverrides) or jsonFields.hasKey(KeyAdditions)
|
|
||||||
if isMessagingShape:
|
|
||||||
return assembleMessagingConf(jsonFields)
|
|
||||||
else:
|
|
||||||
return assembleFullConf(jsonFields)
|
return assembleFullConf(jsonFields)
|
||||||
|
|||||||
@ -1,40 +0,0 @@
|
|||||||
{.push raises: [].}
|
|
||||||
|
|
||||||
import std/options
|
|
||||||
import results
|
|
||||||
import ./cli_args
|
|
||||||
import ./optionalize
|
|
||||||
|
|
||||||
const WakuNodeConfOverlayExcludes* = ["cmd", "execute"]
|
|
||||||
## Variant-safety: `cmd` is the CLI subcommand discriminator (not dispatched
|
|
||||||
## on by the library) and `execute` lives in its inactive branch. Excluded
|
|
||||||
## from the overlay AND used as the JSON parser's hard-reject list.
|
|
||||||
|
|
||||||
# Generates the WakuNodeConfOverlay type from the WakuNodeConf type.
|
|
||||||
# The generated type converts fields from type T to Option[T] if T != Option.
|
|
||||||
# Skips fields that are in WakuNodeConfOverlayExcludes.
|
|
||||||
optionalizeType(WakuNodeConfOverlay, WakuNodeConf, WakuNodeConfOverlayExcludes)
|
|
||||||
|
|
||||||
proc init*(T: type WakuNodeConfOverlay): WakuNodeConfOverlay =
|
|
||||||
## Default config overlay where every field is `none`.
|
|
||||||
return WakuNodeConfOverlay()
|
|
||||||
|
|
||||||
proc applyAsOverride*(conf: var WakuNodeConf, overlay: WakuNodeConfOverlay) =
|
|
||||||
## For all fields, overlay.some() overrides field of same name in conf.
|
|
||||||
for confName, confValue in fieldPairs(conf):
|
|
||||||
for ovName, ovValue in fieldPairs(overlay):
|
|
||||||
when confName == ovName:
|
|
||||||
if ovValue.isSome():
|
|
||||||
when typeof(confValue) is Option:
|
|
||||||
confValue = ovValue
|
|
||||||
else:
|
|
||||||
confValue = ovValue.get()
|
|
||||||
|
|
||||||
proc applyAsAddition*(conf: var WakuNodeConf, overlay: WakuNodeConfOverlay) =
|
|
||||||
## For all seq fields, overlay.some() concats to field of same name in conf.
|
|
||||||
for confName, confValue in fieldPairs(conf):
|
|
||||||
for ovName, ovValue in fieldPairs(overlay):
|
|
||||||
when confName == ovName:
|
|
||||||
when typeof(confValue) is seq:
|
|
||||||
if ovValue.isSome() and ovValue.get().len > 0:
|
|
||||||
confValue = confValue & ovValue.get()
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
{.push raises: [].}
|
|
||||||
|
|
||||||
import std/[macros, options]
|
|
||||||
|
|
||||||
proc isOptionType(n: NimNode): bool =
|
|
||||||
if n.kind == nnkBracketExpr and n.len >= 1:
|
|
||||||
let head = n[0]
|
|
||||||
return head.eqIdent("Option")
|
|
||||||
return false
|
|
||||||
|
|
||||||
proc unwrapName(n: NimNode): NimNode =
|
|
||||||
var cur = n
|
|
||||||
if cur.kind == nnkPragmaExpr:
|
|
||||||
cur = cur[0]
|
|
||||||
if cur.kind == nnkPostfix:
|
|
||||||
cur = cur[1]
|
|
||||||
return cur
|
|
||||||
|
|
||||||
proc collectFields(rec: NimNode, target: NimNode, excluded: seq[string]) =
|
|
||||||
for child in rec:
|
|
||||||
case child.kind
|
|
||||||
of nnkIdentDefs:
|
|
||||||
let nameNode = child[0]
|
|
||||||
let fieldType = child[^2]
|
|
||||||
let plainName = unwrapName(nameNode)
|
|
||||||
if plainName.kind notin {nnkIdent, nnkSym}:
|
|
||||||
continue
|
|
||||||
if $plainName in excluded:
|
|
||||||
continue
|
|
||||||
let newType =
|
|
||||||
if isOptionType(fieldType):
|
|
||||||
fieldType
|
|
||||||
else:
|
|
||||||
nnkBracketExpr.newTree(ident("Option"), fieldType)
|
|
||||||
let exported = postfix(ident($plainName), "*")
|
|
||||||
target.add(newIdentDefs(exported, newType, newEmptyNode()))
|
|
||||||
of nnkRecCase:
|
|
||||||
for branch in child[1 ..^ 1]:
|
|
||||||
case branch.kind
|
|
||||||
of nnkOfBranch:
|
|
||||||
collectFields(branch[^1], target, excluded)
|
|
||||||
of nnkElse:
|
|
||||||
collectFields(branch[0], target, excluded)
|
|
||||||
else:
|
|
||||||
discard
|
|
||||||
of nnkRecList:
|
|
||||||
collectFields(child, target, excluded)
|
|
||||||
else:
|
|
||||||
discard
|
|
||||||
|
|
||||||
macro optionalizeType*(
|
|
||||||
newName: untyped, source: typedesc, exclude: static[openArray[string]] = []
|
|
||||||
): untyped =
|
|
||||||
var typImpl = source.getTypeImpl
|
|
||||||
if typImpl.kind == nnkBracketExpr and typImpl.len >= 2:
|
|
||||||
typImpl = typImpl[1].getTypeImpl
|
|
||||||
if typImpl.kind != nnkObjectTy:
|
|
||||||
error("optionalizeType: expected object type, got " & $typImpl.kind, source)
|
|
||||||
|
|
||||||
var excluded: seq[string] = @[]
|
|
||||||
for e in exclude:
|
|
||||||
excluded.add(e)
|
|
||||||
|
|
||||||
let recList = typImpl[2]
|
|
||||||
let newRecList = newNimNode(nnkRecList)
|
|
||||||
collectFields(recList, newRecList, excluded)
|
|
||||||
|
|
||||||
let typeDef = nnkTypeDef.newTree(
|
|
||||||
postfix(newName, "*"),
|
|
||||||
newEmptyNode(),
|
|
||||||
nnkObjectTy.newTree(newEmptyNode(), newEmptyNode(), newRecList),
|
|
||||||
)
|
|
||||||
|
|
||||||
result = nnkTypeSection.newTree(typeDef)
|
|
||||||
@ -8,10 +8,9 @@ import waku/[requests/health_requests, waku_core, waku_node]
|
|||||||
import waku/node/delivery_service/send_service
|
import waku/node/delivery_service/send_service
|
||||||
import waku/node/subscription_manager
|
import waku/node/subscription_manager
|
||||||
import ../../tools/confutils/cli_args
|
import ../../tools/confutils/cli_args
|
||||||
import ../../tools/confutils/messaging_conf
|
|
||||||
import ./[api_conf, types]
|
import ./[api_conf, types]
|
||||||
|
|
||||||
export cli_args, messaging_conf
|
export cli_args
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "api"
|
topics = "api"
|
||||||
@ -27,30 +26,6 @@ proc createNode*(conf: WakuNodeConf): Future[Result[Waku, string]] {.async.} =
|
|||||||
|
|
||||||
return ok(wakuRes)
|
return ok(wakuRes)
|
||||||
|
|
||||||
proc seedDeveloperProfile(conf: var WakuNodeConf) =
|
|
||||||
# TODO: Remember to add QUIC port here as well when that is added.
|
|
||||||
var devPorts = WakuNodeConfOverlay.init()
|
|
||||||
devPorts.tcpPort = some(Port(0))
|
|
||||||
devPorts.discv5UdpPort = some(Port(0))
|
|
||||||
devPorts.websocketPort = some(Port(0))
|
|
||||||
applyAsOverride(conf, devPorts)
|
|
||||||
|
|
||||||
proc createNode*(
|
|
||||||
preset = "",
|
|
||||||
mode = cli_args.WakuMode.Core,
|
|
||||||
overrides = WakuNodeConfOverlay.init(),
|
|
||||||
additions = WakuNodeConfOverlay.init(),
|
|
||||||
): Future[Result[Waku, string]] {.async.} =
|
|
||||||
## Create a Waku node from messaging-API parameters.
|
|
||||||
var conf = defaultWakuNodeConf().valueOr:
|
|
||||||
return err("Failed creating default conf: " & error)
|
|
||||||
conf.mode = mode
|
|
||||||
conf.preset = preset
|
|
||||||
seedDeveloperProfile(conf)
|
|
||||||
applyAsOverride(conf, overrides)
|
|
||||||
applyAsAddition(conf, additions)
|
|
||||||
return await createNode(conf)
|
|
||||||
|
|
||||||
proc checkApiAvailability(w: Waku): Result[void, string] =
|
proc checkApiAvailability(w: Waku): Result[void, string] =
|
||||||
if w.isNil():
|
if w.isNil():
|
||||||
return err("Waku node is not initialized")
|
return err("Waku node is not initialized")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user