Skip to content

Commit

Permalink
Merge pull request #505 from getwud/feature/#500_add_trigger_testing_…
Browse files Browse the repository at this point in the history
…feature

⭐ [UI/API] - Add support for manually running triggers to help w…
  • Loading branch information
fmartinou authored Dec 6, 2024
2 parents 48d5f28 + fb02feb commit 1d925ba
Show file tree
Hide file tree
Showing 17 changed files with 297 additions and 93 deletions.
10 changes: 1 addition & 9 deletions app/api/authentication.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
const registry = require('../registry');
const component = require('./component');

/**
* Return registered authentications.
* @returns {{id: string}[]}
*/
function getAuthentications() {
return registry.getState().authentication;
}
/**
* Init Router.
* @returns {*}
*/
function init() {
return component.init(getAuthentications);
return component.init('authentication');
}

module.exports = {
Expand Down
32 changes: 20 additions & 12 deletions app/api/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const { byValues, byString } = require('sort-es');

const express = require('express');
const nocache = require('nocache');
const registry = require('../registry');

/**
* Map a Component to a displayable (api/ui) item.
Expand All @@ -23,9 +24,9 @@ function mapComponentToItem(key, component) {
* @param listFunction
* @returns {{id: string}[]}
*/
function mapComponentsToList(listFunction) {
return Object.keys(listFunction())
.map((key) => mapComponentToItem(key, listFunction()[key]))
function mapComponentsToList(components) {
return Object.keys(components)
.map((key) => mapComponentToItem(key, components[key]))
.sort(byValues([
[(x) => x.type, byString()],
[(x) => x.name, byString()],
Expand All @@ -37,8 +38,8 @@ function mapComponentsToList(listFunction) {
* @param req
* @param res
*/
function getAll(req, res, listFunction) {
res.status(200).json(mapComponentsToList(listFunction));
function getAll(req, res, kind) {
res.status(200).json(mapComponentsToList(registry.getState()[kind]));
}

/**
Expand All @@ -47,9 +48,15 @@ function getAll(req, res, listFunction) {
* @param res
* @param listFunction
*/
function getById(req, res, listFunction) {
const { id } = req.params;
const component = listFunction()[id];
function getById(req, res, kind) {
const { type, name } = req.params;
let id = `${kind}.${type}.${name}`;

// Hack for registries because id and name are equivalent
if (kind === 'registry') {
id = `${name}`;
}
const component = registry.getState()[kind][id];
if (component) {
res.status(200).json(mapComponentToItem(id, component));
} else {
Expand All @@ -59,18 +66,19 @@ function getById(req, res, listFunction) {

/**
* Init the component router.
* @param listFunction
* @param kind
* @returns {*|Router}
*/
function init(listFunction) {
function init(kind) {
const router = express.Router();
router.use(nocache());
router.get('/', (req, res) => getAll(req, res, listFunction));
router.get('/:id', (req, res) => getById(req, res, listFunction));
router.get('/', (req, res) => getAll(req, res, kind));
router.get('/:type/:name', (req, res) => getById(req, res, kind));
return router;
}

module.exports = {
init,
mapComponentsToList,
getById,
};
21 changes: 3 additions & 18 deletions app/api/registry.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
const registry = require('../registry');
const component = require('./component');

/**
* Return registered registries.
* @returns {{id: string}[]}
*/
function getRegistries() {
return registry.getState().registry;
}
/**
* Init Router.
* @returns {*}
*/
function init() {
return component.init(getRegistries);
}

/**
* Get all triggers.
* @returns {{id: string}[]}
*/
function getAllRegistries() {
return component.mapComponentsToList(getRegistries);
const router = component.init('registry');
router.get('/:name', (req, res) => component.getById(req, res, 'registry'));
return router;
}

module.exports = {
init,
getAllRegistries,
};
51 changes: 36 additions & 15 deletions app/api/trigger.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,51 @@
const registry = require('../registry');
const component = require('./component');
const registry = require('../registry');

/**
* Return registered triggers.
* @returns {{id: string}[]}
* Run a specific trigger on a specific container provided in the payload.
* @param {*} req
* @param {*} res
* @returns
*/
function getTriggers() {
return registry.getState().trigger;
async function runTrigger(req, res) {
const triggerType = req.params.type;
const triggerName = req.params.name;
const containerToTrigger = req.body;

const triggerToRun = registry.getState().trigger[`trigger.${triggerType}.${triggerName}`];
if (!triggerToRun) {
res.status(404).json({
error: `Error when running trigger ${triggerType}.${triggerName} (trigger not found)`,
});
return;
}
if (!containerToTrigger) {
res.status(400).json({
error: `Error when running trigger ${triggerType}.${triggerName} (container is undefined)`,
});
return;
}

try {
await triggerToRun.trigger(containerToTrigger);
res.status(200).json({});
} catch (e) {
res.status(500).json({
error: `Error when running trigger ${triggerType}.${triggerName} (${e.message})`,
});
}
}

/**
* Init Router.
* @returns {*}
*/
function init() {
return component.init(getTriggers);
}

/**
* Get all triggers.
* @returns {{id: string}[]}
*/
function getAllTriggers() {
return component.mapComponentsToList(getTriggers);
const router = component.init('trigger');
router.post('/:type/:name', (req, res) => runTrigger(req, res));
return router;
}

module.exports = {
init,
getAllTriggers,
};
20 changes: 1 addition & 19 deletions app/api/watcher.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,13 @@
const registry = require('../registry');
const component = require('./component');

/**
* Return registered watchers.
* @returns {{id: string}[]}
*/
function getWatchers() {
return registry.getState().watcher;
}

/**
* Init Router.
* @returns {*}
*/
function init() {
return component.init(getWatchers);
}

/**
* Get all watchers.
* @returns {{id: string}[]}
*/
function getAllWatchers() {
return component.mapComponentsToList(getWatchers);
return component.init('watcher');
}

module.exports = {
init,
getAllWatchers,
};
5 changes: 4 additions & 1 deletion app/nodemon.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"WUD_STORE_PATH": ".store",
"WUD_WATCHER_LOCAL_WATCHBYDEFAULT": "true",
"WUD_AUTH_BASIC_JOHN_USER": "john",
"WUD_AUTH_BASIC_JOHN_HASH": "$apr1$aefKbZEa$ZSA5Y3zv9vDQOxr283NGx/"
"WUD_AUTH_BASIC_JOHN_HASH": "$apr1$aefKbZEa$ZSA5Y3zv9vDQOxr283NGx/",
"WUD_TRIGGER_NTFY_ONE_TOPIC": "NnlZCpSXEKRvLSfw",
"WUD_TRIGGER_NTFY_TWO_TOPIC": "NnlZCpSXEKRvLSfw",
"WUD_TRIGGER_NTFY_THREE_TOPIC": "NnlZCpSXEKRvLSfw"
}
}
10 changes: 9 additions & 1 deletion docs/api/trigger.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ curl http://wud:3000/api/triggers
This operation lets you get a specific Trigger.

```bash
curl http://wud:3000/api/triggers/smtp.gmail
curl http://wud:3000/api/triggers/smtp/gmail

{
"id":"smtp.gmail",
Expand All @@ -47,3 +47,11 @@ curl http://wud:3000/api/triggers/smtp.gmail
}
}
```

## Running a trigger
This operation lets you run a specific Trigger with simulated data.

```bash
export CONTAINER='{"id":"123456789","name":"container_test","watcher":"watcher_test","updateKind":{"kind":"tag","semverDiff":"patch","localValue":"1.2.3","remoteValue":"1.2.4","result":{"link":"https://my-container/release-notes/"}}}'
curl -X POST -H "Content-Type: application/json" -d $CONTAINER http://wud:3000/api/triggers/smtp/gmail
```
2 changes: 1 addition & 1 deletion docs/api/watcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ curl http://wud:3000/api/watchers
This operation lets you get a specific Watcher.

```bash
curl http://wud:3000/api/watchers/docker.local
curl http://wud:3000/api/watchers/docker/local

[
{
Expand Down
1 change: 1 addition & 0 deletions docs/changelog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

# 7.1.2 (wip)
- :star: [UI] - Some ux improvements
- :star: [UI/API] - Add support for manually running triggers to help with configuration

# 7.1.1
- :fire: [NTFY] - Fix basic/bearer authentication
Expand Down
2 changes: 1 addition & 1 deletion e2e/features/api-authentications.feature
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Feature: WUD Authentications API Exposure
And response body path $[0].configuration.hash should be .\*.*.

Scenario: WUD must allow to get specific Authentication state
When I GET /api/authentications/authentication.basic.john
When I GET /api/authentications/basic/john
Then response code should be 200
And response body should be valid json
And response body path $.id should be authentication.basic.john
Expand Down
12 changes: 6 additions & 6 deletions e2e/features/api-container.feature
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ Feature: WUD Container API Exposure
| 0 | ecr | ecr_sub_sub_test | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/sub/test | 1.0.0 | 2.0.0 | true |
| 1 | ecr | ecr_sub_test | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/test | 1.0.0 | 2.0.0 | true |
| 2 | ecr | ecr_test | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | test | 1.0.0 | 2.0.0 | true |
| 3 | ghcr | ghcr_radarr | https://ghcr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 | 5.15.1.9463-ls246 | true |
| 3 | ghcr | ghcr_radarr | https://ghcr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 | 5.15.1.9463-ls247 | true |
| 4 | gitlab | gitlab_test | https://registry.gitlab.com/v2 | manfred-martin/docker-registry-test | 1.0.0 | 2.0.0 | true |
| 5 | hub | hub_homeassistant_202161 | https://registry-1.docker.io/v2 | homeassistant/home-assistant | 2021.6.1 | 2024.11.3 | true |
| 5 | hub | hub_homeassistant_202161 | https://registry-1.docker.io/v2 | homeassistant/home-assistant | 2021.6.1 | 2024.12.1 | true |
| 6 | hub | hub_homeassistant_latest | https://registry-1.docker.io/v2 | homeassistant/home-assistant | latest | latest | false |
| 7 | hub | hub_nginx_120 | https://registry-1.docker.io/v2 | library/nginx | 1.20-alpine | 1.27-alpine | true |
| 8 | hub | hub_nginx_latest | https://registry-1.docker.io/v2 | library/nginx | latest | latest | true |
Expand All @@ -36,8 +36,8 @@ Feature: WUD Container API Exposure
| 15 | hub | hub_vaultwarden_1222 | https://registry-1.docker.io/v2 | vaultwarden/server | 1.32.5-alpine | 1.32.5-alpine | false |
| 16 | hub | hub_vaultwarden_latest | https://registry-1.docker.io/v2 | vaultwarden/server | latest | latest | false |
| 17 | hub | hub_youtubedb_latest | https://registry-1.docker.io/v2 | jeeaaasustest/youtube-dl | latest | latest | false |
| 18 | lscr | lscr_radarr | https://lscr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 | 5.15.1.9463-ls246 | true |
| 19 | quay | quay_prometheus | https://quay.io/v2 | prometheus/prometheus | v2.52.0 | v3.0.0 | true |
| 18 | lscr | lscr_radarr | https://lscr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 | 5.15.1.9463-ls247 | true |
| 19 | quay | quay_prometheus | https://quay.io/v2 | prometheus/prometheus | v2.52.0 | v3.0.1 | true |

Scenario: WUD must allow to get a container with semver
Given I GET /api/containers
Expand Down Expand Up @@ -75,7 +75,7 @@ Feature: WUD Container API Exposure
And response body path $.image.tag.semver should be false
And response body path $.image.digest.value should be sha256:f94d6dd9b5761f33a21bb92848a1f70ea11a1c15f3a142c19a44ea3a4c545a4d
And response body path $.result.tag should be latest
And response body path $.result.digest should be sha256:5341b734e75ce46bbb8e02476434217fd771e23df9a4bfea756a6f3a4a521d3e
And response body path $.result.digest should be sha256:3d696e8357051647b844d8c7cf4a0aa71e84379999a4f6af9b8ca1f7919ade42
And response body path $.updateAvailable should be true

Scenario: WUD must allow to get a container with its link
Expand All @@ -85,7 +85,7 @@ Feature: WUD Container API Exposure
Then response code should be 200
And response body should be valid json
And response body path $.link should be https://github.com/home-assistant/core/releases/tag/2021.6.1
And response body path $.result.link should be https://github.com/home-assistant/core/releases/tag/2024.11.3
And response body path $.result.link should be https://github.com/home-assistant/core/releases/tag/2024.12.1

Scenario: WUD must allow to trigger a watch on a container
Given I GET /api/containers
Expand Down
2 changes: 1 addition & 1 deletion e2e/features/api-trigger.feature
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Feature: WUD Trigger API Exposure
And response body path $[0].configuration.mock should be mock

Scenario: WUD must allow to get specific Triggers state
When I GET /api/triggers/trigger.mock.example
When I GET /api/triggers/mock/example
Then response code should be 200
And response body should be valid json
And response body path $.id should be trigger.mock.example
Expand Down
2 changes: 1 addition & 1 deletion e2e/features/api-watcher.feature
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Feature: WUD Watcher API Exposure
And response body path $[0].configuration.watchbydefault should be false

Scenario: WUD must allow to get specific Watcher state
When I GET /api/watchers/watcher.docker.local
When I GET /api/watchers/docker/local
Then response code should be 200
And response body should be valid json
And response body path $.id should be watcher.docker.local
Expand Down
8 changes: 4 additions & 4 deletions e2e/features/prometheus.feature
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ Feature: Prometheus exposure
| ecr_sub_sub_test | ecr | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/sub/test | 1.0.0 | 2.0.0 | true |
| ecr_sub_test | ecr | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/test | 1.0.0 | 2.0.0 | true |
| ecr_test | ecr | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | test | 1.0.0 | 2.0.0 | true |
| ghcr_radarr | ghcr | https://ghcr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 |5.15.1.9463-ls246 | true |
| ghcr_radarr | ghcr | https://ghcr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 |5.15.1.9463-ls247 | true |
| gitlab_test | gitlab | https://registry.gitlab.com/v2 | manfred-martin/docker-registry-test | 1.0.0 | 2.0.0 | true |
| hub_homeassistant_202161 | hub | https://registry-1.docker.io/v2 | homeassistant/home-assistant | 2021.6.1 | 2024.11.3 | true |
| hub_homeassistant_202161 | hub | https://registry-1.docker.io/v2 | homeassistant/home-assistant | 2021.6.1 | 2024.12.1 | true |
| hub_homeassistant_latest | hub | https://registry-1.docker.io/v2 | homeassistant/home-assistant | latest | latest | false |
| hub_nginx_120 | hub | https://registry-1.docker.io/v2 | library/nginx | 1.20-alpine | 1.27-alpine | true |
| hub_nginx_latest | hub | https://registry-1.docker.io/v2 | library/nginx | latest | latest | true |
Expand All @@ -40,5 +40,5 @@ Feature: Prometheus exposure
| hub_vaultwarden_1222 | hub | https://registry-1.docker.io/v2 | vaultwarden/server | 1.32.5-alpine | 1.32.5-alpine | false |
| hub_vaultwarden_latest | hub | https://registry-1.docker.io/v2 | vaultwarden/server | latest | latest | false |
| hub_youtubedb_latest | hub | https://registry-1.docker.io/v2 | jeeaaasustest/youtube-dl | latest | latest | false |
| lscr_radarr | lscr | https://lscr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 |5.15.1.9463-ls246 | true |
| quay_prometheus | quay | https://quay.io/v2 | prometheus/prometheus | v2.52.0 |v3.0.0 | true |
| lscr_radarr | lscr | https://lscr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 |5.15.1.9463-ls247 | true |
| quay_prometheus | quay | https://quay.io/v2 | prometheus/prometheus | v2.52.0 |v3.0.1 | true |
Loading

0 comments on commit 1d925ba

Please sign in to comment.