irods-http 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- irods_http-0.1.0/LICENSE +29 -0
- irods_http-0.1.0/PKG-INFO +78 -0
- irods_http-0.1.0/README.md +62 -0
- irods_http-0.1.0/irods_http/__init__.py +31 -0
- irods_http-0.1.0/irods_http/collections.py +307 -0
- irods_http-0.1.0/irods_http/common.py +107 -0
- irods_http-0.1.0/irods_http/data_objects.py +843 -0
- irods_http-0.1.0/irods_http/irods_http.py +104 -0
- irods_http-0.1.0/irods_http/queries.py +163 -0
- irods_http-0.1.0/irods_http/resources.py +236 -0
- irods_http-0.1.0/irods_http/rules.py +68 -0
- irods_http-0.1.0/irods_http/tickets.py +93 -0
- irods_http-0.1.0/irods_http/users_groups.py +325 -0
- irods_http-0.1.0/irods_http/zones.py +117 -0
- irods_http-0.1.0/irods_http.egg-info/PKG-INFO +78 -0
- irods_http-0.1.0/irods_http.egg-info/SOURCES.txt +22 -0
- irods_http-0.1.0/irods_http.egg-info/dependency_links.txt +1 -0
- irods_http-0.1.0/irods_http.egg-info/requires.txt +1 -0
- irods_http-0.1.0/irods_http.egg-info/top_level.txt +1 -0
- irods_http-0.1.0/pyproject.toml +504 -0
- irods_http-0.1.0/setup.cfg +4 -0
- irods_http-0.1.0/test/__init__.py +1 -0
- irods_http-0.1.0/test/config.py +54 -0
- irods_http-0.1.0/test/test_endpoint_operations.py +2187 -0
irods_http-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, The University of North Carolina at Chapel Hill
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: irods-http
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python wrapper for the iRODS HTTP API
|
|
5
|
+
Author-email: iRODS Consortium <info@irods.org>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Repository, https://github.com/irods/irods_client_http_python
|
|
8
|
+
Project-URL: Issues, https://github.com/irods/irods_client_http_python/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: requests
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# iRODS HTTP API Python Wrapper
|
|
18
|
+
|
|
19
|
+
This is a Python wrapper for the [iRODS HTTP API](https://github.com/irods/irods_client_http_api).
|
|
20
|
+
|
|
21
|
+
Documentation for the endpoint operations can be found [here](https://github.com/irods/irods_client_http_api/blob/main/API.md).
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
This wrapper is available via pip:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
pip install irods-http
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
To use the wrapper, follow the steps listed below.
|
|
33
|
+
|
|
34
|
+
```py
|
|
35
|
+
import irods_http
|
|
36
|
+
|
|
37
|
+
# Placeholder values needed for irods_http.authenticate()
|
|
38
|
+
url_base = "http://<host>:<port>/irods-http-api/<version>"
|
|
39
|
+
username = "<username>"
|
|
40
|
+
password = "<password>"
|
|
41
|
+
|
|
42
|
+
# Create an IRODSHTTPSession to an iRODS HTTP API server
|
|
43
|
+
session = irods_http.authenticate(url_base, username, password)
|
|
44
|
+
|
|
45
|
+
# Use the session for all other operations
|
|
46
|
+
response = irods_http.collections.create(session, '/<zone_name>/home/<username>/new_collection')
|
|
47
|
+
|
|
48
|
+
# Check the resopnse for errors
|
|
49
|
+
if response['status_code'] != 200:
|
|
50
|
+
# Handle HTTP error.
|
|
51
|
+
|
|
52
|
+
if response['data']['irods_response']['status_code'] < 0:
|
|
53
|
+
# Handle iRODS error.
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The response dict will have this format:
|
|
57
|
+
```py
|
|
58
|
+
{
|
|
59
|
+
'status_code': <integer>,
|
|
60
|
+
'data': <dict>
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
where `status_code` is the HTTP status code from the response, and `data` is the result of the iRODS operation.
|
|
64
|
+
|
|
65
|
+
`response['data']` will contain a dict named `irods_response`, which will contain the `status_code` returned by the iRODS Server as well as any other expected properties.
|
|
66
|
+
|
|
67
|
+
```py
|
|
68
|
+
{
|
|
69
|
+
'irods_response': {
|
|
70
|
+
'status_code': <integer>
|
|
71
|
+
# Other properties vary between endpoints
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
When calling `data_objects.read()`, the `response['data']` will contain the raw bytes instead of a dict.
|
|
77
|
+
|
|
78
|
+
More information regarding iRODS HTTP API response data is available [here](https://github.com/irods/irods_client_http_api/blob/main/API.md).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# iRODS HTTP API Python Wrapper
|
|
2
|
+
|
|
3
|
+
This is a Python wrapper for the [iRODS HTTP API](https://github.com/irods/irods_client_http_api).
|
|
4
|
+
|
|
5
|
+
Documentation for the endpoint operations can be found [here](https://github.com/irods/irods_client_http_api/blob/main/API.md).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
This wrapper is available via pip:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
pip install irods-http
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
To use the wrapper, follow the steps listed below.
|
|
17
|
+
|
|
18
|
+
```py
|
|
19
|
+
import irods_http
|
|
20
|
+
|
|
21
|
+
# Placeholder values needed for irods_http.authenticate()
|
|
22
|
+
url_base = "http://<host>:<port>/irods-http-api/<version>"
|
|
23
|
+
username = "<username>"
|
|
24
|
+
password = "<password>"
|
|
25
|
+
|
|
26
|
+
# Create an IRODSHTTPSession to an iRODS HTTP API server
|
|
27
|
+
session = irods_http.authenticate(url_base, username, password)
|
|
28
|
+
|
|
29
|
+
# Use the session for all other operations
|
|
30
|
+
response = irods_http.collections.create(session, '/<zone_name>/home/<username>/new_collection')
|
|
31
|
+
|
|
32
|
+
# Check the resopnse for errors
|
|
33
|
+
if response['status_code'] != 200:
|
|
34
|
+
# Handle HTTP error.
|
|
35
|
+
|
|
36
|
+
if response['data']['irods_response']['status_code'] < 0:
|
|
37
|
+
# Handle iRODS error.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The response dict will have this format:
|
|
41
|
+
```py
|
|
42
|
+
{
|
|
43
|
+
'status_code': <integer>,
|
|
44
|
+
'data': <dict>
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
where `status_code` is the HTTP status code from the response, and `data` is the result of the iRODS operation.
|
|
48
|
+
|
|
49
|
+
`response['data']` will contain a dict named `irods_response`, which will contain the `status_code` returned by the iRODS Server as well as any other expected properties.
|
|
50
|
+
|
|
51
|
+
```py
|
|
52
|
+
{
|
|
53
|
+
'irods_response': {
|
|
54
|
+
'status_code': <integer>
|
|
55
|
+
# Other properties vary between endpoints
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
When calling `data_objects.read()`, the `response['data']` will contain the raw bytes instead of a dict.
|
|
61
|
+
|
|
62
|
+
More information regarding iRODS HTTP API response data is available [here](https://github.com/irods/irods_client_http_api/blob/main/API.md).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""iRODS HTTP client library for Python."""
|
|
2
|
+
|
|
3
|
+
from . import (
|
|
4
|
+
collections,
|
|
5
|
+
data_objects,
|
|
6
|
+
queries,
|
|
7
|
+
resources,
|
|
8
|
+
rules,
|
|
9
|
+
tickets,
|
|
10
|
+
users_groups,
|
|
11
|
+
zones,
|
|
12
|
+
)
|
|
13
|
+
from .irods_http import (
|
|
14
|
+
IRODSHTTPSession,
|
|
15
|
+
authenticate,
|
|
16
|
+
get_server_info,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"IRODSHTTPSession",
|
|
21
|
+
"authenticate",
|
|
22
|
+
"collections",
|
|
23
|
+
"data_objects",
|
|
24
|
+
"get_server_info",
|
|
25
|
+
"queries",
|
|
26
|
+
"resources",
|
|
27
|
+
"rules",
|
|
28
|
+
"tickets",
|
|
29
|
+
"users_groups",
|
|
30
|
+
"zones",
|
|
31
|
+
]
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"""Collection operations for iRODS HTTP API."""
|
|
2
|
+
|
|
3
|
+
import builtins
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from . import common
|
|
9
|
+
from .irods_http import IRODSHTTPSession # noqa: TC001
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create(session: IRODSHTTPSession, lpath: str, create_intermediates: int = 0) -> dict:
|
|
13
|
+
"""
|
|
14
|
+
Create a new collection.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
session: IRODSHTTPSession object containing the base URL and authentication token.
|
|
18
|
+
lpath: The absolute logical path of the collection to be created.
|
|
19
|
+
create_intermediates: Set to 1 to create intermediates, otherwise set to 0. Defaults to 0.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
A dict containing the HTTP status code and iRODS response.
|
|
23
|
+
The iRODS response is only valid if no error occurred during HTTP communication.
|
|
24
|
+
"""
|
|
25
|
+
common.validate_not_none(session.token)
|
|
26
|
+
common.validate_instance(lpath, str)
|
|
27
|
+
common.validate_0_or_1(create_intermediates)
|
|
28
|
+
|
|
29
|
+
data = {
|
|
30
|
+
"op": "create",
|
|
31
|
+
"lpath": lpath,
|
|
32
|
+
"create-intermediates": create_intermediates,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
|
|
36
|
+
return common.process_response(r)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def remove(session: IRODSHTTPSession, lpath: str, recurse: int = 0, no_trash: int = 0) -> dict:
|
|
40
|
+
"""
|
|
41
|
+
Remove an existing collection.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
session: IRODSHTTPSession object containing the base URL and authentication token.
|
|
45
|
+
lpath: The absolute logical path of the collection to be removed.
|
|
46
|
+
recurse: Set to 1 to remove contents of the collection, otherwise set to 0. Defaults to 0.
|
|
47
|
+
no_trash: Set to 1 to permanently remove, 0 to move to trash. Defaults to 0.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
A dict containing the HTTP status code and iRODS response.
|
|
51
|
+
The iRODS response is only valid if no error occurred during HTTP communication.
|
|
52
|
+
"""
|
|
53
|
+
common.validate_not_none(session.token)
|
|
54
|
+
common.validate_instance(lpath, str)
|
|
55
|
+
common.validate_0_or_1(recurse)
|
|
56
|
+
common.validate_0_or_1(no_trash)
|
|
57
|
+
|
|
58
|
+
data = {
|
|
59
|
+
"op": "remove",
|
|
60
|
+
"lpath": lpath,
|
|
61
|
+
"recurse": recurse,
|
|
62
|
+
"no-trash": no_trash,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
|
|
66
|
+
return common.process_response(r)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def stat(session: IRODSHTTPSession, lpath: str, ticket: str = "") -> dict:
|
|
70
|
+
"""
|
|
71
|
+
Give information about a collection.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
session: IRODSHTTPSession object containing the base URL and authentication token.
|
|
75
|
+
lpath: The absolute logical path of the collection being accessed.
|
|
76
|
+
ticket: Ticket to be enabled before the operation. Defaults to an empty string.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
A dict containing the HTTP status code and iRODS response.
|
|
80
|
+
The iRODS response is only valid if no error occurred during HTTP communication.
|
|
81
|
+
"""
|
|
82
|
+
common.validate_not_none(session.token)
|
|
83
|
+
common.validate_instance(lpath, str)
|
|
84
|
+
common.validate_instance(ticket, str)
|
|
85
|
+
|
|
86
|
+
params = {"op": "stat", "lpath": lpath, "ticket": ticket}
|
|
87
|
+
|
|
88
|
+
r = requests.get(session.url_base + "/collections", params=params, headers=session.get_headers) # noqa: S113
|
|
89
|
+
return common.process_response(r)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def list(session: IRODSHTTPSession, lpath: str, recurse: int = 0, ticket: str = "") -> dict: # noqa: A001
|
|
93
|
+
"""
|
|
94
|
+
Show the contents of a collection.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
session: IRODSHTTPSession object containing the base URL and authentication token.
|
|
98
|
+
lpath: The absolute logical path of the collection to have its contents listed.
|
|
99
|
+
recurse: Set to 1 to list the contents of objects in the collection,
|
|
100
|
+
otherwise set to 0. Defaults to 0.
|
|
101
|
+
ticket: Ticket to be enabled before the operation. Defaults to an empty string.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
A dict containing the HTTP status code and iRODS response.
|
|
105
|
+
The iRODS response is only valid if no error occurred during HTTP communication.
|
|
106
|
+
"""
|
|
107
|
+
common.validate_not_none(session.token)
|
|
108
|
+
common.validate_instance(lpath, str)
|
|
109
|
+
common.validate_0_or_1(recurse)
|
|
110
|
+
common.validate_instance(ticket, str)
|
|
111
|
+
|
|
112
|
+
params = {"op": "list", "lpath": lpath, "recurse": recurse, "ticket": ticket}
|
|
113
|
+
|
|
114
|
+
r = requests.get(session.url_base + "/collections", params=params, headers=session.get_headers) # noqa: S113
|
|
115
|
+
return common.process_response(r)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def set_permission(
|
|
119
|
+
session: IRODSHTTPSession,
|
|
120
|
+
lpath: str,
|
|
121
|
+
entity_name: str,
|
|
122
|
+
permission: str,
|
|
123
|
+
admin: int = 0,
|
|
124
|
+
) -> dict:
|
|
125
|
+
"""
|
|
126
|
+
Set the permission of a user for a given collection.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
session: IRODSHTTPSession object containing the base URL and authentication token.
|
|
130
|
+
lpath: The absolute logical path of the collection to have a permission set.
|
|
131
|
+
entity_name: The name of the user or group having its permission set.
|
|
132
|
+
permission: The permission level being set. Either 'null', 'read', 'write', or 'own'.
|
|
133
|
+
admin: Set to 1 to run this operation as an admin, otherwise set to 0. Defaults to 0.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
A dict containing the HTTP status code and iRODS response.
|
|
137
|
+
The iRODS response is only valid if no error occurred during HTTP communication.
|
|
138
|
+
|
|
139
|
+
Raises:
|
|
140
|
+
ValueError: If permission is not 'null', 'read', 'write', or 'own'.
|
|
141
|
+
"""
|
|
142
|
+
common.validate_not_none(session.token)
|
|
143
|
+
common.validate_instance(lpath, str)
|
|
144
|
+
common.validate_instance(entity_name, str)
|
|
145
|
+
common.validate_instance(permission, str)
|
|
146
|
+
if permission not in ["null", "read", "write", "own"]:
|
|
147
|
+
raise ValueError("permission must be either 'null', 'read', 'write', or 'own'")
|
|
148
|
+
common.validate_0_or_1(admin)
|
|
149
|
+
|
|
150
|
+
data = {
|
|
151
|
+
"op": "set_permission",
|
|
152
|
+
"lpath": lpath,
|
|
153
|
+
"entity-name": entity_name,
|
|
154
|
+
"permission": permission,
|
|
155
|
+
"admin": admin,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
|
|
159
|
+
return common.process_response(r)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def set_inheritance(session: IRODSHTTPSession, lpath: str, enable: int, admin: int = 0) -> dict:
|
|
163
|
+
"""
|
|
164
|
+
Set the inheritance for a collection.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
session: IRODSHTTPSession object containing the base URL and authentication token.
|
|
168
|
+
lpath: The absolute logical path of the collection to have its inheritance set.
|
|
169
|
+
enable: Set to 1 to enable inheritance, or 0 to disable.
|
|
170
|
+
admin: Set to 1 to run this operation as an admin, otherwise set to 0. Defaults to 0.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
A dict containing the HTTP status code and iRODS response.
|
|
174
|
+
The iRODS response is only valid if no error occurred during HTTP communication.
|
|
175
|
+
"""
|
|
176
|
+
common.validate_not_none(session.token)
|
|
177
|
+
common.validate_instance(lpath, str)
|
|
178
|
+
common.validate_0_or_1(enable)
|
|
179
|
+
common.validate_0_or_1(admin)
|
|
180
|
+
|
|
181
|
+
data = {
|
|
182
|
+
"op": "set_inheritance",
|
|
183
|
+
"lpath": lpath,
|
|
184
|
+
"enable": enable,
|
|
185
|
+
"admin": admin,
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
|
|
189
|
+
return common.process_response(r)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def modify_permissions(session: IRODSHTTPSession, lpath: str, operations: dict, admin: int = 0) -> dict:
|
|
193
|
+
"""
|
|
194
|
+
Modify permissions for multiple users or groups for a collection.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
session: IRODSHTTPSession object containing the base URL and authentication token.
|
|
198
|
+
lpath: The absolute logical path of the collection to have its permissions modified.
|
|
199
|
+
operations: Dictionary containing the operations to carry out. Should contain names
|
|
200
|
+
and permissions for all operations.
|
|
201
|
+
admin: Set to 1 to run this operation as an admin, otherwise set to 0. Defaults to 0.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
A dict containing the HTTP status code and iRODS response.
|
|
205
|
+
The iRODS response is only valid if no error occurred during HTTP communication.
|
|
206
|
+
"""
|
|
207
|
+
common.validate_not_none(session.token)
|
|
208
|
+
common.validate_instance(lpath, str)
|
|
209
|
+
common.validate_instance(operations, builtins.list)
|
|
210
|
+
common.validate_instance(operations[0], dict)
|
|
211
|
+
common.validate_0_or_1(admin)
|
|
212
|
+
|
|
213
|
+
data = {
|
|
214
|
+
"op": "modify_permissions",
|
|
215
|
+
"lpath": lpath,
|
|
216
|
+
"operations": json.dumps(operations),
|
|
217
|
+
"admin": admin,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
|
|
221
|
+
return common.process_response(r)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def modify_metadata(session: IRODSHTTPSession, lpath: str, operations: dict, admin: int = 0) -> dict:
|
|
225
|
+
"""
|
|
226
|
+
Modify the metadata for a collection.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
session: IRODSHTTPSession object containing the base URL and authentication token.
|
|
230
|
+
lpath: The absolute logical path of the collection to have its metadata modified.
|
|
231
|
+
operations: Dictionary containing the operations to carry out. Should contain the
|
|
232
|
+
operation, attribute, value, and optionally units.
|
|
233
|
+
admin: Set to 1 to run this operation as an admin, otherwise set to 0. Defaults to 0.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
A dict containing the HTTP status code and iRODS response.
|
|
237
|
+
The iRODS response is only valid if no error occurred during HTTP communication.
|
|
238
|
+
"""
|
|
239
|
+
common.validate_not_none(session.token)
|
|
240
|
+
common.validate_instance(lpath, str)
|
|
241
|
+
common.validate_instance(operations, builtins.list)
|
|
242
|
+
common.validate_instance(operations[0], dict)
|
|
243
|
+
common.validate_0_or_1(admin)
|
|
244
|
+
|
|
245
|
+
data = {
|
|
246
|
+
"op": "modify_metadata",
|
|
247
|
+
"lpath": lpath,
|
|
248
|
+
"operations": json.dumps(operations),
|
|
249
|
+
"admin": admin,
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
|
|
253
|
+
return common.process_response(r)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def rename(session: IRODSHTTPSession, old_lpath: str, new_lpath: str) -> dict:
|
|
257
|
+
"""
|
|
258
|
+
Rename or move a collection.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
session: IRODSHTTPSession object containing the base URL and authentication token.
|
|
262
|
+
old_lpath: The current absolute logical path of the collection.
|
|
263
|
+
new_lpath: The absolute logical path of the destination for the collection.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
A dict containing the HTTP status code and iRODS response.
|
|
267
|
+
The iRODS response is only valid if no error occurred during HTTP communication.
|
|
268
|
+
"""
|
|
269
|
+
common.validate_not_none(session.token)
|
|
270
|
+
common.validate_instance(old_lpath, str)
|
|
271
|
+
common.validate_instance(new_lpath, str)
|
|
272
|
+
|
|
273
|
+
data = {"op": "rename", "old-lpath": old_lpath, "new-lpath": new_lpath}
|
|
274
|
+
|
|
275
|
+
r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
|
|
276
|
+
return common.process_response(r)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def touch(session: IRODSHTTPSession, lpath: str, seconds_since_epoch: int = -1, reference: str = "") -> dict:
|
|
280
|
+
"""
|
|
281
|
+
Update mtime for a collection.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
session: IRODSHTTPSession object containing the base URL and authentication token.
|
|
285
|
+
lpath: The absolute logical path of the collection being touched.
|
|
286
|
+
seconds_since_epoch: The value to set mtime to, defaults to -1 as a flag.
|
|
287
|
+
reference: The absolute logical path of the collection to use as a reference for mtime.
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
A dict containing the HTTP status code and iRODS response.
|
|
291
|
+
The iRODS response is only valid if no error occurred during HTTP communication.
|
|
292
|
+
"""
|
|
293
|
+
common.validate_not_none(session.token)
|
|
294
|
+
common.validate_instance(lpath, str)
|
|
295
|
+
common.validate_gte_minus1(seconds_since_epoch)
|
|
296
|
+
common.validate_instance(reference, str)
|
|
297
|
+
|
|
298
|
+
data = {"op": "touch", "lpath": lpath}
|
|
299
|
+
|
|
300
|
+
if seconds_since_epoch != -1:
|
|
301
|
+
data["seconds-since-epoch"] = seconds_since_epoch
|
|
302
|
+
|
|
303
|
+
if reference != "":
|
|
304
|
+
data["reference"] = reference
|
|
305
|
+
|
|
306
|
+
r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
|
|
307
|
+
return common.process_response(r)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Common utility functions for iRODS HTTP client operations."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def process_response(r):
|
|
5
|
+
"""
|
|
6
|
+
Process an HTTP response and return standardized response dict.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
r: The HTTP response object.
|
|
10
|
+
|
|
11
|
+
Returns:
|
|
12
|
+
A dict with 'status_code' and 'data' keys containing the HTTP status code
|
|
13
|
+
and parsed JSON response body (or None if response is empty).
|
|
14
|
+
"""
|
|
15
|
+
rdict = r.json() if r.text != "" else None
|
|
16
|
+
return {"status_code": r.status_code, "data": rdict}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def validate_not_none(x):
|
|
20
|
+
"""
|
|
21
|
+
Validate that a value is not None.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
x: The value to validate.
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
ValueError: If x is None
|
|
28
|
+
"""
|
|
29
|
+
if x is None:
|
|
30
|
+
raise ValueError
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def validate_instance(x, expected_type):
|
|
34
|
+
"""
|
|
35
|
+
Validate that a value is an instance of the expected type.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
x: The value to validate.
|
|
39
|
+
expected_type: The expected type.
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
TypeError: If x is not an instance of expected_type.
|
|
43
|
+
"""
|
|
44
|
+
if not isinstance(x, expected_type):
|
|
45
|
+
raise TypeError
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def validate_0_or_1(x):
|
|
49
|
+
"""
|
|
50
|
+
Validate that a value is either 0 or 1.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
x: The value to validate (must be an int).
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
TypeError: If x is not an integer.
|
|
57
|
+
ValueError: If x is not 0 or 1.
|
|
58
|
+
"""
|
|
59
|
+
validate_instance(x, int)
|
|
60
|
+
if x not in [0, 1]:
|
|
61
|
+
raise ValueError(f"{x} must be 0 or 1")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def validate_gte_zero(x):
|
|
65
|
+
"""
|
|
66
|
+
Validate that a value is greater than or equal to zero.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
x: The value to validate (must be an int).
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
TypeError: If x is not an integer.
|
|
73
|
+
ValueError: If x is less than 0.
|
|
74
|
+
"""
|
|
75
|
+
validate_instance(x, int)
|
|
76
|
+
if not x >= 0:
|
|
77
|
+
raise ValueError(f"{x} must be >= 0")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def validate_gte_minus1(x):
|
|
81
|
+
"""
|
|
82
|
+
Validate that a value is greater than or equal to -1.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
x: The value to validate (must be an int).
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
88
|
+
TypeError: If x is not an integer.
|
|
89
|
+
ValueError: If x is less than -1.
|
|
90
|
+
"""
|
|
91
|
+
validate_instance(x, int)
|
|
92
|
+
if not x >= -1:
|
|
93
|
+
raise ValueError(f"{x} must be >= 0, or flag value of -1")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def assert_success(cls, response):
|
|
97
|
+
"""
|
|
98
|
+
Validate HTTP and iRODS status codes are successes.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
cls: The unittest.TestCase class
|
|
102
|
+
response: The response from the iRODS HTTP API request
|
|
103
|
+
"""
|
|
104
|
+
# HTTP status code
|
|
105
|
+
cls.assertEqual(response["status_code"], 200)
|
|
106
|
+
# iRODS status code
|
|
107
|
+
cls.assertEqual(response["data"]["irods_response"]["status_code"], 0)
|