Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions services/save-and-restore/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -910,15 +910,15 @@ the list is empty, all PVs in the snapshot were restored correctly.
}
]

Restore from snapshot node
""""""""""""""""""""""""""
Restore from snapshot or composite snapshot node
""""""""""""""""""""""""""""""""""""""""""""""""

**.../restore/node?nodeId=<snapshot node id>**
**.../restore/node?nodeId=<node id>**

Method: POST

This is the same as the endpoint to restore from snapshot items, however it uses snapshot items
from an existing node rather than providing them explicitly. It returns the same result.
from an existing snapshot node or composite snapshot rather than providing them explicitly. It returns the same result.

Compare Endpoint
----------------
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
/**
* Copyright (C) 2018 European Spallation Source ERIC.
* <p>
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* Copyright (C) 2026 European Spallation Source ERIC.
*/
package org.phoebus.service.saveandrestore.web.controllers;

import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.RestoreResult;
import org.phoebus.applications.saveandrestore.model.SnapshotItem;
import org.phoebus.saveandrestore.util.SnapshotUtil;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
Expand All @@ -49,19 +34,40 @@ public class SnapshotRestoreController extends BaseController {

private static final Logger LOG = Logger.getLogger(SnapshotRestoreController.class.getName());

/**
* Restores a snapshot or composite snapshot
*
* @param snapshotItems List of {@link SnapshotItem}s subject to a restore operation. Callee will need to
* retrieve this list from a snapshot or composite snapshot.
* @return The result of the operation
*/
@PostMapping(value = "/restore/items", produces = JSON)
public List<RestoreResult> restoreFromSnapshotItems(
@RequestBody List<SnapshotItem> snapshotItems) {
return snapshotUtil.restore(snapshotItems, connectionTimeout);
}

/**
* Restores a snapshot or composite snapshot
*
* @param nodeId Unique id of a snapshot or composite snapshot
* @return The result of the operation
*/
@PostMapping(value = "/restore/node", produces = JSON)
public List<RestoreResult> restoreFromSnapshotNode(
@RequestParam(value = "nodeId") String nodeId){
@RequestParam(value = "nodeId") String nodeId) {
Node snapshotNode = nodeDAO.getNode(nodeId);
LOG.log(Level.INFO, "Restore requested for snapshot '" + snapshotNode.getName() + "'");
var snapshot = nodeDAO.getSnapshotData(nodeId);
return snapshotUtil.restore(snapshot.getSnapshotItems(), connectionTimeout);
List<SnapshotItem> snapshotItems;
if (snapshotNode.getNodeType().equals(NodeType.SNAPSHOT)) {
snapshotItems = nodeDAO.getSnapshotData(nodeId).getSnapshotItems();
} else if(snapshotNode.getNodeType().equals(NodeType.COMPOSITE_SNAPSHOT)){
snapshotItems = nodeDAO.getSnapshotItemsFromCompositeSnapshot(nodeId);
}
else{
throw new IllegalArgumentException("Node " + snapshotNode + " is not a snapshot or composite snapshot");
}
return snapshotUtil.restore(snapshotItems, connectionTimeout);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.phoebus.applications.saveandrestore.model.ConfigPv;
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.RestoreResult;
import org.phoebus.applications.saveandrestore.model.SnapshotData;
import org.phoebus.applications.saveandrestore.model.SnapshotItem;
Expand Down Expand Up @@ -64,7 +65,7 @@ public void testRestoreFromSnapshotNode() throws Exception {
item.setConfigPv(configPv);
snapshotData.setSnapshotItems(List.of(item));

when(nodeDAO.getNode("uniqueId")).thenReturn(Node.builder().name("name").uniqueId("uniqueId").build());
when(nodeDAO.getNode("uniqueId")).thenReturn(Node.builder().name("name").nodeType(NodeType.SNAPSHOT).uniqueId("uniqueId").build());
when(nodeDAO.getSnapshotData("uniqueId")).thenReturn(snapshotData);

MockHttpServletRequestBuilder request = post("/restore/node?nodeId=uniqueId")
Expand All @@ -80,6 +81,39 @@ public void testRestoreFromSnapshotNode() throws Exception {
});
}

@Test
public void testRestoreFromCompositeSnapshotNode() throws Exception {

SnapshotItem item = new SnapshotItem();
ConfigPv configPv = new ConfigPv();
configPv.setPvName("loc://x");
item.setValue(VFloat.of(1.0, Alarm.none(), Time.now(), Display.none()));
item.setConfigPv(configPv);

when(nodeDAO.getNode("uniqueId")).thenReturn(Node.builder().name("name").nodeType(NodeType.COMPOSITE_SNAPSHOT).uniqueId("uniqueId").build());
when(nodeDAO.getSnapshotItemsFromCompositeSnapshot("uniqueId")).thenReturn(List.of(item));

MockHttpServletRequestBuilder request = post("/restore/node?nodeId=uniqueId")
.header(HttpHeaders.AUTHORIZATION, userAuthorization);

MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON))
.andReturn();

// Make sure response is in the Restore Result json format
objectMapper.readValue(
result.getResponse().getContentAsString(),
new TypeReference<List<RestoreResult>>() {
});
}

@Test
public void testRestoreFromInvalidNodeType() throws Exception {
when(nodeDAO.getNode("uniqueId")).thenReturn(Node.builder().name("name").uniqueId("uniqueId").build());
MockHttpServletRequestBuilder request = post("/restore/node?nodeId=uniqueId")
.header(HttpHeaders.AUTHORIZATION, userAuthorization);
mockMvc.perform(request).andExpect(status().isBadRequest());
}

@Test
public void testRestoreFromSnapshotItems() throws Exception {

Expand Down
Loading