Skip to content

Instantly share code, notes, and snippets.

@shrickus
Last active August 28, 2018 19:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shrickus/87705f8d39d18c6e48ed0a01e54b8e3b to your computer and use it in GitHub Desktop.
Save shrickus/87705f8d39d18c6e48ed0a01e54b8e3b to your computer and use it in GitHub Desktop.
Admin /flows "Introspection" with Swagger testing UI

The Node-RED Admin API provides an endpoint called /flows to retrieve a JSON version of the currently deployed flows file. This raw data can be very useful for producing reports of the total nodes deployed, and how many of each type exist in your flows. Using the /nodes endpoint gives some additional information, so the node types can be grouped by the installed "module" name.

These 3 flows are used to:

  • Show total node counts by type
  • Get node properties for all or specific types
  • Group node names by module name and version

image|230x104

Each flow begins with either an http in or inject node -- so it can be executed from either a browser or the Node-RED editor. There is also a Swagger doc for each of these endpoints, so they can be executed directly from the swagger sidebar (when node-red-node-swagger is installed).

The output JSON from the admin APIs is converted into various forms, using change nodes with the appropriate JSONata expressions. For instance, to count all the nodes by type, this simple (yet powerful) expression works nicely:

payload^($uppercase(type)) {
    "*": $count($$.payload),
    $substringBefore(type, ":"): $count([$])
}

The full source of the flows can be found below...

[
{
"id": "c9d31bae.be4a78",
"type": "tab",
"label": "Introspection",
"disabled": false,
"info": ""
},
{
"id": "1af1379.13d21c8",
"type": "inject",
"z": "c9d31bae.be4a78",
"name": "get counts",
"topic": "counts/*",
"payload": "{}",
"payloadType": "json",
"repeat": "",
"crontab": "",
"once": false,
"x": 120,
"y": 120,
"wires": [
[
"db65453d.202d68"
]
]
},
{
"id": "db65453d.202d68",
"type": "http request",
"z": "c9d31bae.be4a78",
"name": "GET /admin/flows",
"method": "GET",
"ret": "obj",
"url": "http://localhost:18806/admin/flows",
"tls": "",
"x": 370,
"y": 80,
"wires": [
[
"f772177f.65d988",
"66cffc9b.2a1c24"
]
]
},
{
"id": "f772177f.65d988",
"type": "debug",
"z": "c9d31bae.be4a78",
"name": "",
"active": true,
"console": "false",
"complete": "false",
"x": 770,
"y": 120,
"wires": []
},
{
"id": "3a2b89d7.4b2876",
"type": "change",
"z": "c9d31bae.be4a78",
"name": "filter by :type",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "(\t $path := req.url ? req.url : topic ? topic : \"nodes/*\";\t $type := $replace($path, /.*\\//, \"\");\t $type = \"\" or $type = \"*\" ? payload : payload[type=$type]\t)",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 450,
"y": 260,
"wires": [
[
"d546586f.d94838",
"db827381.974e8"
]
]
},
{
"id": "66cffc9b.2a1c24",
"type": "change",
"z": "c9d31bae.be4a78",
"name": "Node counts",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload^($uppercase(type)) {\t \"*\": $count($$.payload),\t $substringBefore(type, \":\"): $count([$])\t}\t",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 450,
"y": 120,
"wires": [
[
"73e3ba50.38de24",
"f772177f.65d988"
]
]
},
{
"id": "821620e8.9501f",
"type": "http response",
"z": "c9d31bae.be4a78",
"name": "",
"statusCode": "",
"headers": {},
"x": 790,
"y": 80,
"wires": []
},
{
"id": "73e3ba50.38de24",
"type": "switch",
"z": "c9d31bae.be4a78",
"name": "msg.req?",
"property": "req",
"propertyType": "msg",
"rules": [
{
"t": "nnull"
}
],
"checkall": "true",
"outputs": 1,
"x": 640,
"y": 80,
"wires": [
[
"821620e8.9501f"
]
]
},
{
"id": "94fa3e74.8a892",
"type": "http request",
"z": "c9d31bae.be4a78",
"name": "GET /admin/flows",
"method": "GET",
"ret": "obj",
"url": "http://localhost:18806/admin/flows",
"tls": "",
"x": 370,
"y": 220,
"wires": [
[
"3a2b89d7.4b2876",
"d546586f.d94838"
]
]
},
{
"id": "2a6d52e6.22420e",
"type": "http response",
"z": "c9d31bae.be4a78",
"name": "",
"statusCode": "",
"headers": {},
"x": 790,
"y": 220,
"wires": []
},
{
"id": "901fb477.155218",
"type": "http in",
"z": "c9d31bae.be4a78",
"name": "",
"url": "/counts",
"method": "get",
"upload": false,
"swaggerDoc": "6fb6a3f6.efbc8c",
"x": 120,
"y": 80,
"wires": [
[
"db65453d.202d68"
]
]
},
{
"id": "d546586f.d94838",
"type": "debug",
"z": "c9d31bae.be4a78",
"name": "",
"active": true,
"console": "false",
"complete": "payload",
"x": 770,
"y": 260,
"wires": []
},
{
"id": "ad688582.c363a8",
"type": "inject",
"z": "c9d31bae.be4a78",
"name": "get nodes/*",
"topic": "nodes/*",
"payload": "{}",
"payloadType": "json",
"repeat": "",
"crontab": "",
"once": false,
"x": 130,
"y": 260,
"wires": [
[
"94fa3e74.8a892"
]
]
},
{
"id": "db827381.974e8",
"type": "switch",
"z": "c9d31bae.be4a78",
"name": "msg.req?",
"property": "req",
"propertyType": "msg",
"rules": [
{
"t": "nnull"
}
],
"checkall": "true",
"outputs": 1,
"x": 640,
"y": 220,
"wires": [
[
"2a6d52e6.22420e"
]
]
},
{
"id": "5ac50f4.4a06ef",
"type": "http in",
"z": "c9d31bae.be4a78",
"name": "",
"url": "/nodes/:type",
"method": "get",
"upload": false,
"swaggerDoc": "75449af9.7eeb84",
"x": 140,
"y": 220,
"wires": [
[
"94fa3e74.8a892"
]
]
},
{
"id": "e529e485.d72b28",
"type": "comment",
"z": "c9d31bae.be4a78",
"name": "Swagger API -- /red/counts",
"info": "",
"x": 130,
"y": 40,
"wires": []
},
{
"id": "8981aee4.3b395",
"type": "comment",
"z": "c9d31bae.be4a78",
"name": "Swagger API -- /red/nodes/:type",
"info": "",
"x": 150,
"y": 180,
"wires": []
},
{
"id": "eece3000.aeeab",
"type": "inject",
"z": "c9d31bae.be4a78",
"name": "get modules",
"topic": "modules/*",
"payload": "{}",
"payloadType": "json",
"repeat": "",
"crontab": "",
"once": false,
"x": 130,
"y": 440,
"wires": [
[
"68a04660.c33098"
]
]
},
{
"id": "80994199.430b3",
"type": "http request",
"z": "c9d31bae.be4a78",
"name": "GET /admin/nodes",
"method": "GET",
"ret": "obj",
"url": "http://localhost:18806/admin/nodes",
"tls": "",
"x": 370,
"y": 440,
"wires": [
[
"65748e53.c8661",
"60a59ee4.5b5cc"
]
]
},
{
"id": "65748e53.c8661",
"type": "debug",
"z": "c9d31bae.be4a78",
"name": "",
"active": true,
"console": "false",
"complete": "payload",
"x": 770,
"y": 440,
"wires": []
},
{
"id": "60a59ee4.5b5cc",
"type": "change",
"z": "c9d31bae.be4a78",
"name": "Module nodes",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload^($uppercase(module))\t{\t module: (\t $t := [];\t {\t \"version\": [version][0],\t \"nodes\": $append($t, types)^($)\t }\t )\t}\t",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 460,
"y": 480,
"wires": [
[
"1ddcf8d.1c88507",
"65748e53.c8661"
]
]
},
{
"id": "9f903c31.d4b84",
"type": "http response",
"z": "c9d31bae.be4a78",
"name": "",
"statusCode": "",
"headers": {},
"x": 790,
"y": 400,
"wires": []
},
{
"id": "1ddcf8d.1c88507",
"type": "switch",
"z": "c9d31bae.be4a78",
"name": "msg.req?",
"property": "req",
"propertyType": "msg",
"rules": [
{
"t": "nnull"
}
],
"checkall": "true",
"outputs": 1,
"x": 640,
"y": 400,
"wires": [
[
"9f903c31.d4b84"
]
]
},
{
"id": "da6b4f15.1ed33",
"type": "comment",
"z": "c9d31bae.be4a78",
"name": "Swagger API -- /red/modules",
"info": "",
"x": 140,
"y": 360,
"wires": []
},
{
"id": "ec43fba1.6f9f88",
"type": "http in",
"z": "c9d31bae.be4a78",
"name": "",
"url": "/modules",
"method": "get",
"upload": false,
"swaggerDoc": "722c0781.22fce8",
"x": 130,
"y": 400,
"wires": [
[
"68a04660.c33098"
]
]
},
{
"id": "68a04660.c33098",
"type": "change",
"z": "c9d31bae.be4a78",
"name": "accept: application/json",
"rules": [
{
"t": "set",
"p": "headers.accept",
"pt": "msg",
"to": "application/json",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 390,
"y": 400,
"wires": [
[
"80994199.430b3",
"6f830e4e.c0fc7"
]
]
},
{
"id": "7a0e2d34.825b74",
"type": "inject",
"z": "c9d31bae.be4a78",
"name": "nodes/ui_tab",
"topic": "nodes/ui_tab",
"payload": "{}",
"payloadType": "json",
"repeat": "",
"crontab": "",
"once": false,
"x": 130,
"y": 300,
"wires": [
[
"94fa3e74.8a892"
]
]
},
{
"id": "6f830e4e.c0fc7",
"type": "debug",
"z": "c9d31bae.be4a78",
"name": "",
"active": true,
"console": "false",
"complete": "true",
"x": 630,
"y": 360,
"wires": []
},
{
"id": "6fb6a3f6.efbc8c",
"type": "swagger-doc",
"z": "",
"summary": "Node counts",
"description": "List of all node types in use with total count",
"tags": "",
"consumes": "",
"produces": "",
"parameters": [],
"responses": {},
"deprecated": false
},
{
"id": "75449af9.7eeb84",
"type": "swagger-doc",
"z": "",
"summary": "Nodes by type",
"description": "Returns a list of nodes matching the given type",
"tags": "",
"consumes": "",
"produces": "",
"parameters": [
{
"name": "type",
"in": "path",
"description": "Node type name",
"required": true,
"type": "string"
}
],
"responses": {},
"deprecated": false
},
{
"id": "722c0781.22fce8",
"type": "swagger-doc",
"z": "",
"summary": "Module nodes",
"description": "List of installed modules with node types",
"tags": "",
"consumes": "",
"produces": "",
"parameters": [],
"responses": {},
"deprecated": false
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment