From 07bbadbcc5ce2e9bc1c616ad23aeb34f3ccd295d Mon Sep 17 00:00:00 2001 From: georgweiss Date: Wed, 3 Jun 2026 11:09:44 +0200 Subject: [PATCH 1/2] Support simplified access to save&restore root node --- .../impl/elasticsearch/ElasticsearchDAO.java | 6 +++++ .../web/controllers/NodeController.java | 24 ++++++++++++------- .../persistence/dao/impl/DAOTestIT.java | 18 ++++++++++++++ .../web/controllers/NodeControllerTest.java | 19 +++++++++++++++ 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/impl/elasticsearch/ElasticsearchDAO.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/impl/elasticsearch/ElasticsearchDAO.java index afc894bc3a..201b9db87d 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/impl/elasticsearch/ElasticsearchDAO.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/impl/elasticsearch/ElasticsearchDAO.java @@ -95,6 +95,9 @@ public class ElasticsearchDAO implements NodeDAO { @Override public List getChildNodes(String uniqueNodeId) { + if("root".equals(uniqueNodeId)) { + uniqueNodeId = ROOT_FOLDER_UNIQUE_ID; + } Optional elasticsearchNode = elasticsearchTreeRepository.findById(uniqueNodeId); if (elasticsearchNode.isEmpty()) { @@ -111,6 +114,9 @@ public List getChildNodes(String uniqueNodeId) { @Override public Node getNode(String uniqueNodeId) { + if("root".equals(uniqueNodeId)) { + uniqueNodeId = ROOT_FOLDER_UNIQUE_ID; + } Optional optional = elasticsearchTreeRepository.findById(uniqueNodeId); if (optional.isEmpty()) { diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java index 87281910cd..1a83548bca 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java @@ -99,6 +99,9 @@ public Node createNode(@RequestParam(name = "parentNodeId") String parentsUnique @SuppressWarnings("unused") @GetMapping(value = "/node/{uniqueNodeId}", produces = JSON) public Node getNode(@PathVariable final String uniqueNodeId) { + if("root".equals(uniqueNodeId)) { + return nodeDAO.getRootNode(); + } return nodeDAO.getNode(uniqueNodeId); } @@ -158,7 +161,7 @@ public List getChildNodes(@PathVariable final String uniqueNodeId) { public void deleteNodes(@RequestBody List nodeIds) { nodeDAO.deleteNodes(nodeIds); nodeIds.forEach(id -> - webSocketService.sendMessageToClients(new WebSocketMessage(SaveAndRestoreMessageType.NODE_REMOVED, id))); + webSocketService.sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.NODE_REMOVED, id))); } /** @@ -220,10 +223,19 @@ public Node updateNode(@RequestParam(value = "customTimeForMigration", required } nodeToUpdate.setUserName(principal.getName()); Node updatedNode = nodeDAO.updateNode(nodeToUpdate, Boolean.parseBoolean(customTimeForMigration)); - webSocketService.sendMessageToClients(new WebSocketMessage(SaveAndRestoreMessageType.NODE_UPDATED, updatedNode)); + webSocketService.sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.NODE_UPDATED, updatedNode)); return updatedNode; } + /** + * Endpoint for the sake of convenience, i.e. client need not know the unique id of the root {@link Node} + * @return The root {@link Node} + */ + @GetMapping("/root") + public Node getRoot() { + return nodeDAO.getRootNode(); + } + /** * Checks if a {@link Node} has a tag named "golden". If so, it must be of type {@link NodeType#SNAPSHOT}. * @@ -235,11 +247,7 @@ private boolean areTagsValid(Node node) { return true; } - if (!node.getNodeType().equals(NodeType.SNAPSHOT) && - node.getTags().stream().anyMatch(t -> t.getName().equalsIgnoreCase(Tag.GOLDEN))) { - return false; - } - - return true; + return node.getNodeType().equals(NodeType.SNAPSHOT) || + node.getTags().stream().noneMatch(t -> t.getName().equalsIgnoreCase(Tag.GOLDEN)); } } diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/persistence/dao/impl/DAOTestIT.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/persistence/dao/impl/DAOTestIT.java index cb35b26059..7e289d2122 100644 --- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/persistence/dao/impl/DAOTestIT.java +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/persistence/dao/impl/DAOTestIT.java @@ -719,7 +719,25 @@ public void testGetChildNodes() { List childNodes = nodeDAO.getChildNodes(rootNode.getUniqueId()); assertTrue(nodeDAO.getChildNodes(folder1.getUniqueId()).isEmpty()); + } + + @Test + public void testGetChildNodesFromRoot() { + Node rootNode = nodeDAO.getRootNode(); + Node folder1 = Node.builder().name("SomeFolder").build(); + + // Create folder1 in the root folder + folder1 = nodeDAO.createNode(rootNode.getUniqueId(), folder1); + + List childNodes = nodeDAO.getChildNodes("root"); + assertTrue(nodeDAO.getChildNodes(folder1.getUniqueId()).isEmpty()); + } + + @Test + public void testGetRootNode() { + Node rootNode = nodeDAO.getNode("root"); + assertEquals(Node.ROOT_FOLDER_UNIQUE_ID, rootNode.getUniqueId()); } @Test diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java index e0f4f8b6b2..6d78e9442b 100644 --- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java @@ -810,4 +810,23 @@ public void testCreateNodeWithValidTags2() throws Exception { mockMvc.perform(request).andExpect(status().isOk()); } + + @Test + public void testGetRootNode() throws Exception { + when(nodeDAO.getRootNode()).thenReturn(Node.builder().uniqueId(Node.ROOT_FOLDER_UNIQUE_ID).build()); + MockHttpServletRequestBuilder request = get("/root"); + MvcResult mvcResult = mockMvc.perform(request).andExpect(status().isOk()).andReturn(); + Node node = objectMapper.readValue(mvcResult.getResponse().getContentAsString(), Node.class); + assertEquals(Node.ROOT_FOLDER_UNIQUE_ID, node.getUniqueId()); + } + + @Test + public void testRootNodeChildNodes() throws Exception { + when(nodeDAO.getChildNodes("root")).thenReturn(List.of(Node.builder().uniqueId("foo").build())); + MockHttpServletRequestBuilder request = get("/node/root/children"); + MvcResult mvcResult = mockMvc.perform(request).andExpect(status().isOk()).andReturn(); + List nodes = objectMapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() { + }); + assertEquals(1, nodes.size()); + } } From ef333e3839be62c2a340f9a4644c02b14b6f2acf Mon Sep 17 00:00:00 2001 From: georgweiss Date: Wed, 3 Jun 2026 11:18:03 +0200 Subject: [PATCH 2/2] Updated doc --- services/save-and-restore/doc/index.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/services/save-and-restore/doc/index.rst b/services/save-and-restore/doc/index.rst index dab386c7d9..7e40d001f1 100644 --- a/services/save-and-restore/doc/index.rst +++ b/services/save-and-restore/doc/index.rst @@ -127,6 +127,14 @@ A special case is the root node as it has a fixed unique id: **.../node/44bef5de-e8e6-4014-af37-b8f6c8a939a2** +The root node may also be retrieved using: + +**.../root** + +or + +**.../node/root*** + Retrieve multiple nodes """"""""""""""""""""""" Method: GET @@ -265,6 +273,10 @@ The a list of all the children nodes of the node with id `{uniqueNodeId}` } ] +One may retrieve the child nodes of the root node using: + +**.../node/root/children** + .. _Get a configuration: Get a configuration