Skip to content

Commit 629801c

Browse files
committed
ENH: implementations of 'move' and 'copy' API
1 parent 2076c23 commit 629801c

File tree

4 files changed

+195
-0
lines changed

4 files changed

+195
-0
lines changed

src/save_and_restore_api/_api_async.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,24 @@ async def compare(self, nodeId, *, tolerance=None, compareMode=None, skipReadbac
231231
)
232232
return await self.send_request(method, url, url_params=url_params)
233233

234+
# =============================================================================================
235+
# STRUCTURE-CONTROLLER API METHODS
236+
# =============================================================================================
237+
238+
async def structure_move(self, nodeIds, newParentNodeId, *, auth=None):
239+
# Reusing docstrings from the threaded version
240+
method, url, params, url_params = self._prepare_structure_move(
241+
nodeIds=nodeIds, newParentNodeId=newParentNodeId
242+
)
243+
return await self.send_request(method, url, params=params, url_params=url_params, auth=auth)
244+
245+
async def structure_copy(self, nodeIds, newParentNodeId, *, auth=None):
246+
# Reusing docstrings from the threaded version
247+
method, url, params, url_params = self._prepare_structure_copy(
248+
nodeIds=nodeIds, newParentNodeId=newParentNodeId
249+
)
250+
return await self.send_request(method, url, params=params, url_params=url_params, auth=auth)
251+
234252

235253
SaveRestoreAPI.info_get.__doc__ = _SaveRestoreAPI_Threads.info_get.__doc__
236254
SaveRestoreAPI.version_get.__doc__ = _SaveRestoreAPI_Threads.version_get.__doc__
@@ -258,3 +276,5 @@ async def compare(self, nodeId, *, tolerance=None, compareMode=None, skipReadbac
258276
SaveRestoreAPI.restore_node.__doc__ = _SaveRestoreAPI_Threads.restore_node.__doc__
259277
SaveRestoreAPI.restore_items.__doc__ = _SaveRestoreAPI_Threads.restore_items.__doc__
260278
SaveRestoreAPI.compare.__doc__ = _SaveRestoreAPI_Threads.compare.__doc__
279+
SaveRestoreAPI.structure_move.__doc__ = _SaveRestoreAPI_Threads.structure_move.__doc__
280+
SaveRestoreAPI.structure_copy.__doc__ = _SaveRestoreAPI_Threads.structure_copy.__doc__

src/save_and_restore_api/_api_base.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,22 @@ def _prepare_compare(self, *, nodeId, tolerance, compareMode, skipReadback):
345345
url_params = None
346346
return method, url, url_params
347347

348+
# =============================================================================================
349+
# STRUCTURE-CONTROLLER API METHODS
350+
# =============================================================================================
351+
352+
def _prepare_structure_move(self, *, nodeIds, newParentNodeId):
353+
method, url = "POST", "/move"
354+
url_params = {"to": newParentNodeId}
355+
params = nodeIds
356+
return method, url, params, url_params
357+
358+
def _prepare_structure_copy(self, *, nodeIds, newParentNodeId):
359+
method, url = "POST", "/copy"
360+
url_params = {"to": newParentNodeId}
361+
params = nodeIds
362+
return method, url, params, url_params
363+
348364
# =============================================================================================
349365

350366
# def create_config(self, parent_node_uid, name, pv_list):

src/save_and_restore_api/_api_threads.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,3 +375,31 @@ def compare(self, nodeId, *, tolerance=None, compareMode=None, skipReadback=None
375375
nodeId=nodeId, tolerance=tolerance, compareMode=compareMode, skipReadback=skipReadback
376376
)
377377
return self.send_request(method, url, url_params=url_params)
378+
379+
# =============================================================================================
380+
# STRUCTURE-CONTROLLER API METHODS
381+
# =============================================================================================
382+
383+
def structure_move(self, nodeIds, *, newParentNodeId, auth=None):
384+
"""
385+
Move nodes specified by a list of UIDs ``nodeIds`` to a new parent node specified
386+
by ``newParentNodeId``. The API requires 'admin' priviledges.
387+
388+
API: POST /structure/move
389+
"""
390+
method, url, params, url_params = self._prepare_structure_move(
391+
nodeIds=nodeIds, newParentNodeId=newParentNodeId
392+
)
393+
return self.send_request(method, url, params=params, url_params=url_params, auth=auth)
394+
395+
def structure_copy(self, nodeIds, *, newParentNodeId, auth=None):
396+
"""
397+
Copy nodes specified by a list of UIDs ``nodeIds`` to a new parent node specified
398+
by ``newParentNodeId``. The API requires 'admin' priviledges.
399+
400+
API: POST /structure/copy
401+
"""
402+
method, url, params, url_params = self._prepare_structure_copy(
403+
nodeIds=nodeIds, newParentNodeId=newParentNodeId
404+
)
405+
return self.send_request(method, url, params=params, url_params=url_params, auth=auth)

tests/test_structure_control.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
from __future__ import annotations
2+
3+
import asyncio
4+
5+
import pytest
6+
7+
from save_and_restore_api import SaveRestoreAPI as SaveRestoreAPI_Threads
8+
from save_and_restore_api.aio import SaveRestoreAPI as SaveRestoreAPI_Async
9+
10+
from .common import (
11+
_is_async,
12+
_select_auth,
13+
base_url,
14+
clear_sar, # noqa: F401
15+
create_root_folder,
16+
)
17+
18+
# =============================================================================================
19+
# TESTS FOR COMPARISON-CONTROLLER API METHODS
20+
# =============================================================================================
21+
22+
23+
# fmt: off
24+
@pytest.mark.parametrize("usemove", [True, False])
25+
@pytest.mark.parametrize("usesetauth", [True, False])
26+
@pytest.mark.parametrize("library", ["THREADS", "ASYNC"])
27+
# fmt: on
28+
def test_structure_move_01(clear_sar, library, usesetauth, usemove): # noqa: F811
29+
"""
30+
Basic tests for the 'move' and 'copy' API.
31+
"""
32+
root_folder_uid = create_root_folder()
33+
34+
if not _is_async(library):
35+
with SaveRestoreAPI_Threads(base_url=base_url, timeout=10) as SR:
36+
auth = _select_auth(SR=SR, usesetauth=usesetauth)
37+
auth_admin = {"auth": SR.auth_gen(username="admin", password="adminPass")}
38+
39+
# Create two folders
40+
response = SR.node_add(root_folder_uid, name="Folder1", nodeType="FOLDER", **auth)
41+
folder1_uid = response["uniqueId"]
42+
response = SR.node_add(root_folder_uid, name="Folder2", nodeType="FOLDER", **auth)
43+
folder2_uid = response["uniqueId"]
44+
45+
# Create two configurations in folder1
46+
response = SR.config_create(
47+
folder1_uid,
48+
configurationNode={"name": "Config1"},
49+
configurationData={"pvList": []},
50+
**auth
51+
)
52+
config1_uid = response["configurationNode"]["uniqueId"]
53+
response = SR.config_create(
54+
folder1_uid,
55+
configurationNode={"name": "Config2"},
56+
configurationData={"pvList": []},
57+
**auth
58+
)
59+
config2_uid = response["configurationNode"]["uniqueId"]
60+
61+
response = SR.node_get_children(folder1_uid)
62+
assert len(response) == 2
63+
response = SR.node_get_children(folder2_uid)
64+
assert len(response) == 0
65+
66+
if usemove:
67+
response = SR.structure_move([config1_uid, config2_uid], newParentNodeId=folder2_uid, **auth_admin)
68+
assert response["uniqueId"] == folder2_uid
69+
n_folder1, n_folder2 = 0, 2
70+
else:
71+
response = SR.structure_copy([config1_uid, config2_uid], newParentNodeId=folder2_uid, **auth_admin)
72+
assert response["uniqueId"] == folder2_uid
73+
n_folder1, n_folder2 = 2, 2
74+
75+
response = SR.node_get_children(folder1_uid)
76+
assert len(response) == n_folder1
77+
response = SR.node_get_children(folder2_uid)
78+
assert len(response) == n_folder2
79+
80+
else:
81+
async def testing():
82+
async with SaveRestoreAPI_Async(base_url=base_url, timeout=2) as SR:
83+
auth = _select_auth(SR=SR, usesetauth=usesetauth)
84+
auth_admin = {"auth": SR.auth_gen(username="admin", password="adminPass")}
85+
86+
# Create two folders
87+
response = await SR.node_add(root_folder_uid, name="Folder1", nodeType="FOLDER", **auth)
88+
folder1_uid = response["uniqueId"]
89+
response = await SR.node_add(root_folder_uid, name="Folder2", nodeType="FOLDER", **auth)
90+
folder2_uid = response["uniqueId"]
91+
92+
# Create two configurations in folder1
93+
response = await SR.config_create(
94+
folder1_uid,
95+
configurationNode={"name": "Config1"},
96+
configurationData={"pvList": []},
97+
**auth
98+
)
99+
config1_uid = response["configurationNode"]["uniqueId"]
100+
response = await SR.config_create(
101+
folder1_uid,
102+
configurationNode={"name": "Config2"},
103+
configurationData={"pvList": []},
104+
**auth
105+
)
106+
config2_uid = response["configurationNode"]["uniqueId"]
107+
108+
response = await SR.node_get_children(folder1_uid)
109+
assert len(response) == 2
110+
response = await SR.node_get_children(folder2_uid)
111+
assert len(response) == 0
112+
113+
if usemove:
114+
response = await SR.structure_move(
115+
[config1_uid, config2_uid], newParentNodeId=folder2_uid, **auth_admin
116+
)
117+
assert response["uniqueId"] == folder2_uid
118+
n_folder1, n_folder2 = 0, 2
119+
else:
120+
response = await SR.structure_copy(
121+
[config1_uid, config2_uid], newParentNodeId=folder2_uid, **auth_admin
122+
)
123+
assert response["uniqueId"] == folder2_uid
124+
n_folder1, n_folder2 = 2, 2
125+
126+
response = await SR.node_get_children(folder1_uid)
127+
assert len(response) == n_folder1
128+
response = await SR.node_get_children(folder2_uid)
129+
assert len(response) == n_folder2
130+
131+
asyncio.run(testing())

0 commit comments

Comments
 (0)