Skip to content

Commit eb16940

Browse files
committed
Save&restpre service: compare endpoint completed
1 parent 2d926bb commit eb16940

File tree

12 files changed

+458
-19
lines changed

12 files changed

+458
-19
lines changed

services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ComparisonController.java

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import org.phoebus.applications.saveandrestore.model.ComparisonResult;
2121
import org.phoebus.applications.saveandrestore.model.CompositeSnapshotData;
22+
import org.phoebus.applications.saveandrestore.model.ConfigPv;
2223
import org.phoebus.applications.saveandrestore.model.Node;
2324
import org.phoebus.applications.saveandrestore.model.ComparisonMode;
2425
import org.phoebus.applications.saveandrestore.model.SnapshotItem;
@@ -71,26 +72,51 @@ public List<ComparisonResult> compare(@PathVariable String nodeId,
7172
if (node == null) {
7273
throw new NodeNotFoundException("Node " + nodeId + " does not exist");
7374
}
75+
List<ConfigPv> configPvs = new ArrayList<>();
76+
List<SnapshotItem> snapshotItems = new ArrayList<>();
7477
switch (node.getNodeType()) {
75-
case SNAPSHOT:
76-
return snapshotUtil.comparePvs(getSnapshotItems(node.getUniqueId()), tolerance, compareMode, skipReadback);
77-
case COMPOSITE_SNAPSHOT:
78-
return snapshotUtil.comparePvs(getCompositeSnapshotItems(node.getUniqueId()), tolerance, compareMode, skipReadback);
78+
case SNAPSHOT: {
79+
getSnapshotItemsAndConfig(node.getUniqueId(), snapshotItems, configPvs);
80+
return snapshotUtil.comparePvs(snapshotItems, configPvs, tolerance, compareMode, skipReadback);
81+
}
82+
case COMPOSITE_SNAPSHOT: {
83+
getCompositeSnapshotItemsAndConfig(node.getUniqueId(), snapshotItems, configPvs);
84+
return snapshotUtil.comparePvs(snapshotItems, configPvs, tolerance, compareMode, skipReadback);
85+
}
7986
default:
8087
throw new IllegalArgumentException("Node type" + node.getNodeType() + " cannot be compared");
8188
}
8289
}
8390

84-
private List<SnapshotItem> getSnapshotItems(String nodeId) {
85-
return nodeDAO.getSnapshotData(nodeId).getSnapshotItems();
91+
/**
92+
* Collects {@link SnapshotItem}s from the snapshot and {@link ConfigPv}s from the associated configuration.
93+
* @param snapshotNodeId The snapshot's unique id.
94+
* @param snapshotItems {@link List} into which {@link SnapshotItem}s are added.
95+
* @param configPvs {@link List} into which {@link ConfigPv}s are added.
96+
*/
97+
private void getSnapshotItemsAndConfig(String snapshotNodeId, List<SnapshotItem> snapshotItems, List<ConfigPv> configPvs){
98+
snapshotItems.addAll(nodeDAO.getSnapshotData(snapshotNodeId).getSnapshotItems());
99+
Node configNode = nodeDAO.getParentNode(snapshotNodeId);
100+
configPvs.addAll(nodeDAO.getConfigurationData(configNode.getUniqueId()).getPvList());
86101
}
87102

88-
private List<SnapshotItem> getCompositeSnapshotItems(String nodeId) {
89-
CompositeSnapshotData compositeSnapshotData = nodeDAO.getCompositeSnapshotData(nodeId);
103+
/**
104+
* Collects {@link SnapshotItem}s from the composite snapshot and {@link ConfigPv}s from the associated configuration.
105+
* Recursive calls are done when composite snapshots contains composite snapshots.
106+
* @param compositeSnapshotNodeId The composite snapshot's unique id.
107+
* @param snapshotItems {@link List} into which {@link SnapshotItem}s are added.
108+
* @param configPvs {@link List} into which {@link ConfigPv}s are added.
109+
*/
110+
protected void getCompositeSnapshotItemsAndConfig(String compositeSnapshotNodeId, List<SnapshotItem> snapshotItems, List<ConfigPv> configPvs){
111+
CompositeSnapshotData compositeSnapshotData = nodeDAO.getCompositeSnapshotData(compositeSnapshotNodeId);
90112
List<String> referencedSnapshots = compositeSnapshotData.getReferencedSnapshotNodes();
91-
List<SnapshotItem> snapshotItems = new ArrayList<>();
92-
referencedSnapshots.forEach(id -> snapshotItems.addAll(getSnapshotItems(id)));
93-
return snapshotItems;
113+
referencedSnapshots.forEach(id -> {
114+
Node node = nodeDAO.getNode(id);
115+
switch (node.getNodeType()){
116+
case SNAPSHOT -> getSnapshotItemsAndConfig(id, snapshotItems, configPvs);
117+
case COMPOSITE_SNAPSHOT -> getCompositeSnapshotItemsAndConfig(id, snapshotItems, configPvs);
118+
}
119+
});
94120
}
95121
}
96122

services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ComparisonControllerTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.phoebus.applications.saveandrestore.model.ComparisonResult;
1616
import org.phoebus.applications.saveandrestore.model.CompositeSnapshotData;
1717
import org.phoebus.applications.saveandrestore.model.ConfigPv;
18+
import org.phoebus.applications.saveandrestore.model.ConfigurationData;
1819
import org.phoebus.applications.saveandrestore.model.Node;
1920
import org.phoebus.applications.saveandrestore.model.NodeType;
2021
import org.phoebus.applications.saveandrestore.model.SnapshotData;
@@ -91,7 +92,16 @@ public void testSingleSnapshot() throws Exception {
9192
ConfigPv configPv2 = new ConfigPv();
9293
configPv2.setPvName("loc://y(771.0)");
9394

95+
when(nodeDAO.getParentNode("nodeId")).thenReturn(Node.builder().nodeType(NodeType.CONFIGURATION).uniqueId("configId").build());
96+
97+
ConfigurationData configurationData = new ConfigurationData();
98+
configurationData.setUniqueId("configId");
99+
configurationData.setPvList(List.of(configPv1, configPv2));
100+
101+
when(nodeDAO.getConfigurationData("configId")).thenReturn(configurationData);
102+
94103
SnapshotData snapshotData = new SnapshotData();
104+
//snapshotData.setUniqueId("uniqueId");
95105
SnapshotItem snapshotItem1 = new SnapshotItem();
96106
snapshotItem1.setConfigPv(configPv1);
97107
snapshotItem1.setValue(VDouble.of(42.0, Alarm.none(),
@@ -148,6 +158,21 @@ public void testCompositeSnapshot() throws Exception{
148158
when(nodeDAO.getSnapshotData("id1")).thenReturn(snapshotData1);
149159
when(nodeDAO.getSnapshotData("id2")).thenReturn(snapshotData2);
150160

161+
when(nodeDAO.getNode("id1")).thenReturn(Node.builder().name("id1").uniqueId("id1").nodeType(NodeType.SNAPSHOT).build());
162+
when(nodeDAO.getNode("id2")).thenReturn(Node.builder().name("id2").uniqueId("id2").nodeType(NodeType.SNAPSHOT).build());
163+
when(nodeDAO.getParentNode("id1")).thenReturn(Node.builder().nodeType(NodeType.CONFIGURATION).name("id1parent").uniqueId("id1parent").build());
164+
when(nodeDAO.getParentNode("id2")).thenReturn(Node.builder().nodeType(NodeType.CONFIGURATION).name("id2parent").uniqueId("id2parent").build());
165+
166+
ConfigurationData configurationData1 = new ConfigurationData();
167+
configurationData1.setPvList(List.of(configPv1, configPv2));
168+
169+
when(nodeDAO.getConfigurationData("id1parent")).thenReturn(configurationData1);
170+
171+
ConfigurationData configurationData2 = new ConfigurationData();
172+
configurationData2.setPvList(List.of(configPv1, configPv2));
173+
174+
when(nodeDAO.getConfigurationData("id2parent")).thenReturn(configurationData2);
175+
151176
MockHttpServletRequestBuilder request = get("/compare/nodeId");
152177

153178
MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON))
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright (C) 2025 European Spallation Source ERIC.
3+
*/
4+
5+
package org.phoebus.service.saveandrestore.web.controllers;
6+
7+
import com.fasterxml.jackson.databind.ObjectMapper;
8+
import org.junit.jupiter.api.Test;
9+
import org.junit.jupiter.api.TestInstance;
10+
import org.phoebus.applications.saveandrestore.model.CompositeSnapshot;
11+
import org.phoebus.applications.saveandrestore.model.CompositeSnapshotData;
12+
import org.phoebus.applications.saveandrestore.model.ConfigPv;
13+
import org.phoebus.applications.saveandrestore.model.Configuration;
14+
import org.phoebus.applications.saveandrestore.model.Node;
15+
import org.phoebus.applications.saveandrestore.model.NodeType;
16+
import org.phoebus.applications.saveandrestore.model.Snapshot;
17+
import org.phoebus.applications.saveandrestore.model.SnapshotItem;
18+
import org.phoebus.service.saveandrestore.persistence.config.ElasticConfig;
19+
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
20+
import org.springframework.beans.factory.annotation.Autowired;
21+
import org.springframework.boot.test.context.SpringBootTest;
22+
import org.springframework.context.annotation.Profile;
23+
import org.springframework.test.context.ContextConfiguration;
24+
import org.springframework.test.context.TestPropertySource;
25+
26+
import java.util.ArrayList;
27+
import java.util.List;
28+
import java.util.UUID;
29+
30+
import static org.junit.jupiter.api.Assertions.*;
31+
32+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
33+
@SpringBootTest
34+
@ContextConfiguration(classes = ElasticConfig.class)
35+
@TestPropertySource(locations = "classpath:test_application.properties")
36+
@Profile("IT")
37+
public class ComparisonControllerTestIT {
38+
39+
@Autowired
40+
private ComparisonController comparisonController;
41+
42+
@Autowired
43+
private NodeDAO nodeDAO;
44+
45+
private ObjectMapper objectMapper = new ObjectMapper();
46+
47+
@Test
48+
public void testGetSnapshotItemsAndConfig() throws Exception{
49+
Node topLevelFolder = Node.builder().nodeType(NodeType.FOLDER).name(UUID.randomUUID().toString())
50+
.build();
51+
topLevelFolder = nodeDAO.createNode(Node.ROOT_FOLDER_UNIQUE_ID, topLevelFolder);
52+
53+
CompositeSnapshot compositeSnapshot1 = null;
54+
CompositeSnapshot compositeSnapshot2 = null;
55+
56+
try {
57+
58+
Configuration configuration1 = objectMapper.readValue(getClass().getResourceAsStream("/IT-test1-config.json"), Configuration.class);
59+
configuration1 = nodeDAO.createConfiguration(topLevelFolder.getUniqueId(), configuration1);
60+
61+
Snapshot snapshot1 = objectMapper.readValue(getClass().getResourceAsStream("/IT-test1-snapshot.json"), Snapshot.class);
62+
snapshot1 = nodeDAO.createSnapshot(configuration1.getConfigurationNode().getUniqueId(), snapshot1);
63+
64+
Configuration configuration2 = objectMapper.readValue(getClass().getResourceAsStream("/IT-test2-config.json"), Configuration.class);
65+
configuration2 = nodeDAO.createConfiguration(topLevelFolder.getUniqueId(), configuration2);
66+
67+
Snapshot snapshot2 = objectMapper.readValue(getClass().getResourceAsStream("/IT-test2-snapshot.json"), Snapshot.class);
68+
snapshot2 = nodeDAO.createSnapshot(configuration2.getConfigurationNode().getUniqueId(), snapshot2);
69+
70+
Configuration configuration3 = objectMapper.readValue(getClass().getResourceAsStream("/IT-test3-config.json"), Configuration.class);
71+
configuration3 = nodeDAO.createConfiguration(topLevelFolder.getUniqueId(), configuration3);
72+
73+
Snapshot snapshot3 = objectMapper.readValue(getClass().getResourceAsStream("/IT-test3-snapshot.json"), Snapshot.class);
74+
snapshot3 = nodeDAO.createSnapshot(configuration3.getConfigurationNode().getUniqueId(), snapshot3);
75+
76+
Node compositeSnapshotSimple = Node.builder().name("CompositeSimple").nodeType(NodeType.COMPOSITE_SNAPSHOT).build();
77+
CompositeSnapshotData compositeSnapshotData1 = new CompositeSnapshotData();
78+
compositeSnapshotData1.setReferencedSnapshotNodes(List.of(snapshot1.getSnapshotNode().getUniqueId(), snapshot2.getSnapshotNode().getUniqueId()));
79+
compositeSnapshot1 = new CompositeSnapshot();
80+
compositeSnapshot1.setCompositeSnapshotNode(compositeSnapshotSimple);
81+
compositeSnapshot1.setCompositeSnapshotData(compositeSnapshotData1);
82+
83+
compositeSnapshot1 = nodeDAO.createCompositeSnapshot(topLevelFolder.getUniqueId(), compositeSnapshot1);
84+
85+
Node compositeSnapshotComposite = Node.builder().name("CompositeComposite").nodeType(NodeType.COMPOSITE_SNAPSHOT).build();
86+
CompositeSnapshotData compositeSnapshotData2 = new CompositeSnapshotData();
87+
compositeSnapshotData2.setReferencedSnapshotNodes(List.of(compositeSnapshot1.getCompositeSnapshotNode().getUniqueId(), snapshot3.getSnapshotNode().getUniqueId()));
88+
compositeSnapshot2 = new CompositeSnapshot();
89+
compositeSnapshot2.setCompositeSnapshotNode(compositeSnapshotComposite);
90+
compositeSnapshot2.setCompositeSnapshotData(compositeSnapshotData2);
91+
92+
compositeSnapshot2 = nodeDAO.createCompositeSnapshot(topLevelFolder.getUniqueId(), compositeSnapshot2);
93+
94+
List<SnapshotItem> snapshotItems = new ArrayList<>();
95+
List<ConfigPv> configPvs = new ArrayList<>();
96+
97+
comparisonController.getCompositeSnapshotItemsAndConfig(compositeSnapshot1.getCompositeSnapshotNode().getUniqueId(), snapshotItems, configPvs);
98+
99+
assertEquals(snapshotItems.size(), configPvs.size());
100+
101+
snapshotItems.clear();
102+
configPvs.clear();
103+
104+
comparisonController.getCompositeSnapshotItemsAndConfig(compositeSnapshot2.getCompositeSnapshotNode().getUniqueId(), snapshotItems, configPvs);
105+
106+
assertEquals(snapshotItems.size(), configPvs.size());
107+
108+
} finally {
109+
if(compositeSnapshot1 != null && compositeSnapshot1.getCompositeSnapshotNode() != null && compositeSnapshot1.getCompositeSnapshotNode().getUniqueId() != null){
110+
nodeDAO.deleteNode(compositeSnapshot1.getCompositeSnapshotNode().getUniqueId());
111+
}
112+
if(compositeSnapshot2 != null && compositeSnapshot2.getCompositeSnapshotNode() != null && compositeSnapshot2.getCompositeSnapshotNode().getUniqueId() != null){
113+
nodeDAO.deleteNode(compositeSnapshot2.getCompositeSnapshotNode().getUniqueId());
114+
}
115+
nodeDAO.deleteNode(topLevelFolder.getUniqueId());
116+
}
117+
}
118+
}

services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.fasterxml.jackson.databind.ObjectMapper;
2323
import org.junit.jupiter.api.Test;
2424
import org.junit.jupiter.api.extension.ExtendWith;
25+
import org.phoebus.applications.saveandrestore.model.Comparison;
2526
import org.phoebus.applications.saveandrestore.model.ConfigPv;
2627
import org.phoebus.applications.saveandrestore.model.Configuration;
2728
import org.phoebus.applications.saveandrestore.model.ConfigurationData;
@@ -176,15 +177,15 @@ public void testCreateInvalidConfiguration() throws Exception {
176177
ConfigurationData configurationData = new ConfigurationData();
177178
configuration.setConfigurationData(configurationData);
178179
configurationData.setPvList(List.of(ConfigPv.builder().pvName("foo").build(),
179-
ConfigPv.builder().pvName("fooo").tolerance(1.0).build()));
180+
ConfigPv.builder().pvName("fooo").comparison(new Comparison(null, 0.1)).build()));
180181
MockHttpServletRequestBuilder request = put("/config?parentNodeId=a")
181182
.header(HttpHeaders.AUTHORIZATION, adminAuthorization)
182183
.contentType(JSON).content(objectMapper.writeValueAsString(configuration));
183184

184185
mockMvc.perform(request).andExpect(status().isBadRequest());
185186

186187
configurationData.setPvList(List.of(
187-
ConfigPv.builder().pvName("fooo").readbackPvName("bar").tolerance(1.0).build()));
188+
ConfigPv.builder().pvName("fooo").readbackPvName("bar").comparison(new Comparison(null, 0.1)).build()));
188189

189190
configuration.setConfigurationData(configurationData);
190191

@@ -195,8 +196,8 @@ public void testCreateInvalidConfiguration() throws Exception {
195196
mockMvc.perform(request).andExpect(status().isBadRequest());
196197

197198
configurationData.setPvList(List.of(
198-
ConfigPv.builder().pvName("fooo").readbackPvName("bar").compareMode(ComparisonMode.RELATIVE)
199-
.tolerance(-0.1).build()));
199+
ConfigPv.builder().pvName("fooo").readbackPvName("bar").comparison(new Comparison(ComparisonMode.RELATIVE, -0.1))
200+
.build()));
200201

201202
configuration.setConfigurationData(configurationData);
202203

services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.junit.jupiter.api.extension.ExtendWith;
2626
import org.mockito.Mockito;
2727
import org.mockito.stubbing.Answer;
28+
import org.phoebus.applications.saveandrestore.model.Comparison;
2829
import org.phoebus.applications.saveandrestore.model.ComparisonMode;
2930
import org.phoebus.applications.saveandrestore.model.ConfigPv;
3031
import org.phoebus.applications.saveandrestore.model.Configuration;
@@ -198,8 +199,7 @@ public void testCreateConfigWithToleranceData() throws Exception {
198199
ConfigurationData configurationData = new ConfigurationData();
199200
ConfigPv configPv1 = new ConfigPv();
200201
configPv1.setPvName("name");
201-
configPv1.setCompareMode(ComparisonMode.ABSOLUTE);
202-
configPv1.setTolerance(1.0);
202+
configPv1.setComparison(new Comparison(ComparisonMode.ABSOLUTE, 1.0));
203203
configurationData.setPvList(List.of(configPv1));
204204
configuration.setConfigurationData(configurationData);
205205

@@ -230,7 +230,7 @@ public void testCreateConfigWithBadToleranceData1() throws Exception {
230230
ConfigurationData configurationData = new ConfigurationData();
231231
ConfigPv configPv1 = new ConfigPv();
232232
configPv1.setPvName("name");
233-
configPv1.setCompareMode(ComparisonMode.ABSOLUTE);
233+
configPv1.setComparison(new Comparison(ComparisonMode.ABSOLUTE, null));
234234
configurationData.setPvList(List.of(configPv1));
235235
configuration.setConfigurationData(configurationData);
236236

@@ -257,7 +257,7 @@ public void testCreateConfigWithBadToleranceData2() throws Exception {
257257
ConfigurationData configurationData = new ConfigurationData();
258258
ConfigPv configPv1 = new ConfigPv();
259259
configPv1.setPvName("name");
260-
configPv1.setTolerance(1.0);
260+
configPv1.setComparison(new Comparison(null, 0.1));
261261
configurationData.setPvList(List.of(configPv1));
262262
configuration.setConfigurationData(configurationData);
263263

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"configurationNode": {
3+
"name": "IT-test1",
4+
"description": "IT-test1",
5+
"nodeType": "CONFIGURATION"
6+
},
7+
"configurationData": {
8+
"pvList": [
9+
{
10+
"pvName": "ao1",
11+
"readOnly": false,
12+
"comparison": {
13+
"comparisonMode": "ABSOLUTE",
14+
"tolerance": 1
15+
}
16+
},
17+
{
18+
"pvName": "ao2",
19+
"readOnly": false
20+
}
21+
]
22+
}
23+
}

0 commit comments

Comments
 (0)