dspace-api-toolkit 0.1.1__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.
Files changed (72) hide show
  1. dspace_api_toolkit-0.1.1/LICENSE +28 -0
  2. dspace_api_toolkit-0.1.1/PKG-INFO +211 -0
  3. dspace_api_toolkit-0.1.1/README.md +165 -0
  4. dspace_api_toolkit-0.1.1/dspace_api/__init__.py +75 -0
  5. dspace_api_toolkit-0.1.1/dspace_api/_base.py +407 -0
  6. dspace_api_toolkit-0.1.1/dspace_api/_containers.py +217 -0
  7. dspace_api_toolkit-0.1.1/dspace_api/_version.py +4 -0
  8. dspace_api_toolkit-0.1.1/dspace_api/auth.py +467 -0
  9. dspace_api_toolkit-0.1.1/dspace_api/authorizations.py +486 -0
  10. dspace_api_toolkit-0.1.1/dspace_api/bundles.py +1193 -0
  11. dspace_api_toolkit-0.1.1/dspace_api/collections.py +1069 -0
  12. dspace_api_toolkit-0.1.1/dspace_api/communities.py +363 -0
  13. dspace_api_toolkit-0.1.1/dspace_api/config.py +254 -0
  14. dspace_api_toolkit-0.1.1/dspace_api/discovery.py +920 -0
  15. dspace_api_toolkit-0.1.1/dspace_api/entity_types.py +127 -0
  16. dspace_api_toolkit-0.1.1/dspace_api/epersons.py +647 -0
  17. dspace_api_toolkit-0.1.1/dspace_api/exceptions.py +110 -0
  18. dspace_api_toolkit-0.1.1/dspace_api/groups.py +599 -0
  19. dspace_api_toolkit-0.1.1/dspace_api/items.py +549 -0
  20. dspace_api_toolkit-0.1.1/dspace_api/metadata.py +742 -0
  21. dspace_api_toolkit-0.1.1/dspace_api/permissions.py +1052 -0
  22. dspace_api_toolkit-0.1.1/dspace_api/projection.py +292 -0
  23. dspace_api_toolkit-0.1.1/dspace_api/relationships.py +694 -0
  24. dspace_api_toolkit-0.1.1/dspace_api/session.py +538 -0
  25. dspace_api_toolkit-0.1.1/dspace_api/site.py +98 -0
  26. dspace_api_toolkit-0.1.1/dspace_api/utils.py +519 -0
  27. dspace_api_toolkit-0.1.1/dspace_api/versions.py +292 -0
  28. dspace_api_toolkit-0.1.1/dspace_api/vocabulary.py +365 -0
  29. dspace_api_toolkit-0.1.1/dspace_api/workflow.py +893 -0
  30. dspace_api_toolkit-0.1.1/dspace_api/workspaces.py +1404 -0
  31. dspace_api_toolkit-0.1.1/dspace_api_toolkit.egg-info/PKG-INFO +211 -0
  32. dspace_api_toolkit-0.1.1/dspace_api_toolkit.egg-info/SOURCES.txt +70 -0
  33. dspace_api_toolkit-0.1.1/dspace_api_toolkit.egg-info/dependency_links.txt +1 -0
  34. dspace_api_toolkit-0.1.1/dspace_api_toolkit.egg-info/requires.txt +17 -0
  35. dspace_api_toolkit-0.1.1/dspace_api_toolkit.egg-info/top_level.txt +1 -0
  36. dspace_api_toolkit-0.1.1/pyproject.toml +65 -0
  37. dspace_api_toolkit-0.1.1/setup.cfg +4 -0
  38. dspace_api_toolkit-0.1.1/tests/test_all_languages.py +311 -0
  39. dspace_api_toolkit-0.1.1/tests/test_auth.py +415 -0
  40. dspace_api_toolkit-0.1.1/tests/test_authorizations.py +210 -0
  41. dspace_api_toolkit-0.1.1/tests/test_base.py +167 -0
  42. dspace_api_toolkit-0.1.1/tests/test_bundles.py +549 -0
  43. dspace_api_toolkit-0.1.1/tests/test_client_and_config.py +115 -0
  44. dspace_api_toolkit-0.1.1/tests/test_collection_items.py +99 -0
  45. dspace_api_toolkit-0.1.1/tests/test_collections.py +385 -0
  46. dspace_api_toolkit-0.1.1/tests/test_communities.py +114 -0
  47. dspace_api_toolkit-0.1.1/tests/test_containers.py +308 -0
  48. dspace_api_toolkit-0.1.1/tests/test_contract_coverage_tool.py +132 -0
  49. dspace_api_toolkit-0.1.1/tests/test_discovery.py +677 -0
  50. dspace_api_toolkit-0.1.1/tests/test_entity_types.py +43 -0
  51. dspace_api_toolkit-0.1.1/tests/test_env_file.py +45 -0
  52. dspace_api_toolkit-0.1.1/tests/test_epersons.py +273 -0
  53. dspace_api_toolkit-0.1.1/tests/test_exceptions.py +38 -0
  54. dspace_api_toolkit-0.1.1/tests/test_groups.py +244 -0
  55. dspace_api_toolkit-0.1.1/tests/test_integration_tooling.py +517 -0
  56. dspace_api_toolkit-0.1.1/tests/test_items.py +403 -0
  57. dspace_api_toolkit-0.1.1/tests/test_large_uploads.py +263 -0
  58. dspace_api_toolkit-0.1.1/tests/test_metadata.py +305 -0
  59. dspace_api_toolkit-0.1.1/tests/test_permissions.py +572 -0
  60. dspace_api_toolkit-0.1.1/tests/test_projection.py +242 -0
  61. dspace_api_toolkit-0.1.1/tests/test_relationships.py +354 -0
  62. dspace_api_toolkit-0.1.1/tests/test_search_browse.py +248 -0
  63. dspace_api_toolkit-0.1.1/tests/test_server_compat.py +318 -0
  64. dspace_api_toolkit-0.1.1/tests/test_session.py +418 -0
  65. dspace_api_toolkit-0.1.1/tests/test_site.py +82 -0
  66. dspace_api_toolkit-0.1.1/tests/test_utils.py +700 -0
  67. dspace_api_toolkit-0.1.1/tests/test_versions.py +161 -0
  68. dspace_api_toolkit-0.1.1/tests/test_vocabulary.py +18 -0
  69. dspace_api_toolkit-0.1.1/tests/test_vocabulary_manager.py +160 -0
  70. dspace_api_toolkit-0.1.1/tests/test_workflow.py +380 -0
  71. dspace_api_toolkit-0.1.1/tests/test_workspace_submission.py +576 -0
  72. dspace_api_toolkit-0.1.1/tests/test_workspaces.py +497 -0
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Stefan Szepe and contributors
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,211 @@
1
+ Metadata-Version: 2.4
2
+ Name: dspace-api-toolkit
3
+ Version: 0.1.1
4
+ Summary: Python toolkit for the DSpace 7+ and DSpace-CRIS REST API: items, submissions, bitstreams (streamed large uploads), access control, communities, collections, versioning
5
+ Author: Stefan Szepe
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/sszepe/dspace-api-toolkit
8
+ Project-URL: Documentation, https://github.com/sszepe/dspace-api-toolkit#readme
9
+ Project-URL: Changelog, https://github.com/sszepe/dspace-api-toolkit/blob/main/CHANGELOG.md
10
+ Project-URL: Issues, https://github.com/sszepe/dspace-api-toolkit/issues
11
+ Project-URL: Compatibility matrix, https://github.com/sszepe/dspace-api-toolkit/actions/workflows/integration-matrix.yml
12
+ Keywords: dspace,dspace-cris,repository,rest-api,institutional-repository,open-access
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Topic :: Internet :: WWW/HTTP
26
+ Classifier: Topic :: Scientific/Engineering
27
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
28
+ Requires-Python: >=3.10
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE
31
+ Requires-Dist: requests>=2.31
32
+ Requires-Dist: python-dotenv>=1.0
33
+ Provides-Extra: test
34
+ Requires-Dist: pytest>=7.4; extra == "test"
35
+ Requires-Dist: pytest-cov>=4.1; extra == "test"
36
+ Requires-Dist: responses>=0.23; extra == "test"
37
+ Provides-Extra: integration
38
+ Requires-Dist: pytest>=7.4; extra == "integration"
39
+ Requires-Dist: responses>=0.23; extra == "integration"
40
+ Requires-Dist: pyyaml>=6.0; extra == "integration"
41
+ Provides-Extra: dev
42
+ Requires-Dist: dspace-api-toolkit[integration,test]; extra == "dev"
43
+ Requires-Dist: build>=1.2; extra == "dev"
44
+ Requires-Dist: twine>=5.1; extra == "dev"
45
+ Dynamic: license-file
46
+
47
+ # dspace-api-toolkit
48
+
49
+ [![CI](https://github.com/sszepe/dspace-api-toolkit/actions/workflows/ci.yml/badge.svg)](https://github.com/sszepe/dspace-api-toolkit/actions/workflows/ci.yml)
50
+ [![Compatibility matrix](https://github.com/sszepe/dspace-api-toolkit/actions/workflows/integration-matrix.yml/badge.svg)](https://github.com/sszepe/dspace-api-toolkit/actions/workflows/integration-matrix.yml)
51
+ [![PyPI](https://img.shields.io/pypi/v/dspace-api-toolkit.svg)](https://pypi.org/project/dspace-api-toolkit/)
52
+ [![Python](https://img.shields.io/pypi/pyversions/dspace-api-toolkit.svg)](https://pypi.org/project/dspace-api-toolkit/)
53
+ [![License](https://img.shields.io/pypi/l/dspace-api-toolkit.svg)](LICENSE)
54
+
55
+ A Python toolkit for the REST API of **DSpace 7+** and **DSpace-CRIS**, built
56
+ for repository administrators and integration scripts: batch imports,
57
+ submissions, files of any size, access control, collection setup, versioning -
58
+ and long unattended runs that survive token expiry, rate limits and restarts.
59
+
60
+ Every feature is tested against mocked HTTP with pytest with a target
61
+ 100% coverage **and** against real DSpace / DSpace-CRIS installations started
62
+ with Docker Compose; the results form a [compatibility matrix](#compatibility).
63
+
64
+ ```bash
65
+ pip install dspace-api-toolkit
66
+ ```
67
+
68
+ Requires Python 3.10+. The import name of the dspace-api-toolkit in python code
69
+ using it is `dspace_api`.
70
+
71
+ ## Quick start
72
+
73
+ ```python
74
+ from dspace_api import DSpaceClient, MetadataPatch
75
+
76
+ client = DSpaceClient("https://repo.example.org/server/api", "admin@example.org", "secret")
77
+
78
+ # Submit an item through the submission process, with an embargoed file
79
+ result = client.workspace.submit(
80
+ collection_uuid,
81
+ {"dc.title": "My thesis", "dc.contributor.author": ["Doe, Jane"], "dc.date.issued": "2026"},
82
+ files=["thesis.pdf"],
83
+ file_access_conditions=[{"name": "embargo", "startDate": "2027-01-01"}],
84
+ )
85
+ item_uuid = result["item_uuid"]
86
+
87
+ # Upload a large file - streamed, constant memory, MD5 verified
88
+ bundle = client.bundles.get_or_create_bundle(item_uuid, "ORIGINAL")
89
+ client.bundles.upload_bitstream(bundle["uuid"], "dataset.zip",
90
+ progress=lambda done, total: print(f"{done / total:.0%}"))
91
+
92
+ # Precise metadata edits
93
+ client.metadata.patch_metadata("item", item_uuid,
94
+ MetadataPatch().append("dc.subject", "music").move("dc.contributor.author", 1, 0))
95
+
96
+ # Check permissions before acting
97
+ if client.authorizations.can(("item", item_uuid), "withdrawItem"):
98
+ ...
99
+
100
+ client.close()
101
+ ```
102
+
103
+ More scripts will be added in the [`examples/`](examples/) folder.
104
+
105
+ ## What's covered
106
+
107
+ | Area | Manager | Highlights |
108
+ |---|---|---|
109
+ | Items | `client.items` | create, read, update, delete; `upsert_metadata`, position-aware upserts, move, mapped collections |
110
+ | Metadata | `client.metadata` | `MetadataPatch` builder (archived items, submission sections, files), PATCH any object, schema/field registry, `ensure_fields()` |
111
+ | Submissions | `client.workspace` | create (empty / external source / BibTeX-RIS file), fill forms by field, upload, file & item access conditions, license, CC license, deposit, one-call `submit()` with validation errors |
112
+ | Bundles & files | `client.bundles` | bundles, streamed uploads of any size, replace, download, primary bitstream, move, format registry |
113
+ | Access control | `client.permissions` | resource policies, remove / grant / restore group access with CSV reports, bulk access control script |
114
+ | Permission checks | `client.authorizations` | `can`, `features_on`, `require`, batch checks, admin scope |
115
+ | Versioning | `client.versions` | create versions, `new_version()` flow, histories, drafts |
116
+ | Communities | `client.communities` | create, hierarchy and tree walk, admin group, logo, *Authorized searches |
117
+ | Collections | `client.collections` | role groups (admin, submitters, item/bitstream read, workflow), read restrictions, submission policies, license, item template, mapped items, harvesting, logo |
118
+ | People & groups | `client.epersons`, `client.groups` | users, registration, end-user agreement, groups and members |
119
+ | Site & config | `client.site`, `client.entity_types` | Terms of Use per language, entity types |
120
+ | Search & browse | `client.discovery` | search configuration, `f.<filter>` filters, paged `iter_search`, `count`, facets with all values (`facet_counts`), browse indexes, entries and items |
121
+ | Review workflow | `client.workflow` | workflow items, `pending_tasks`, claim / approve / reject / unclaim, `review()` in one call, `approve_all_steps`, workflow definitions, steps and actions, supervision orders |
122
+ | Relationships | `client.relationships` | configurable entities (plain DSpace): relationship types, `relate` / `unrelate` by label, `related_items` in order, `reorder`, name variants, `ensure_related` |
123
+ | Vocabularies | `client.vocabulary` | vocabularies and authorities, exact `lookup`, `invalid_values` for import validation, form-field vocabularies, hierarchical trees, `to_metadata_value` |
124
+
125
+ Every read accepts `all_languages=`, `projection=` and `embed=` (including
126
+ nested embeds with sizes) to shape the response.
127
+
128
+ ## Configuration
129
+
130
+ Arguments win over environment variables, which win over a `.env` file in the
131
+ working directory (see [`.env.example`](.env.example)):
132
+
133
+ | Variable | Default | |
134
+ |---|---|---|
135
+ | `DSPACE_URL` | `http://localhost:8080/server/api` | REST API root |
136
+ | `DSPACE_TIMEOUT` | `30` | seconds per request |
137
+ | `DSPACE_MAX_RETRIES` | `3` | retries for 429 / 502 / 503 / 504 and connection errors (idempotent requests; 429 for all) |
138
+ | `DSPACE_RETRY_BACKOFF`, `DSPACE_RETRY_MAX_WAIT` | `1.0`, `30` | exponential backoff with jitter, honours `Retry-After` |
139
+ | `DSPACE_TOKEN_REFRESH_MARGIN` | `60` | refresh the login token this many seconds before it expires |
140
+ | `DSPACE_REAUTHENTICATE` | `true` | log in again after a 401 |
141
+ | `DSPACE_ACCEPT_USER_AGREEMENT` | `false` | DSpace-CRIS: accept the end-user agreement for the logged-in user when the server demands it |
142
+ | `DSPACE_ALL_LANGUAGES` | `false` | default for `projection=allLanguages` (DSpace-CRIS, DSpace 10; left out automatically on DSpace 8/9) |
143
+
144
+ `DSpaceConfig(env_file=False)` ignores `.env`; `env_file="prod.env"` reads another file.
145
+
146
+ ## Large files
147
+
148
+ Uploads (`upload_bitstream`, `replace_bitstream`, `workspace.upload_file`)
149
+ stream the multipart body from disk with an exact `Content-Length`: memory
150
+ use stays at one chunk (8 MiB) regardless of file size, the MD5 is computed
151
+ while sending and compared with DSpace's checksum afterwards, and the read
152
+ timeout grows with the file size. The server must allow large requests too:
153
+
154
+ - DSpace `config/application.properties`: `spring.servlet.multipart.max-file-size`
155
+ and `max-request-size` (default 512MB)
156
+ - reverse proxy: nginx `client_max_body_size`, `proxy_request_buffering off`, `proxy_read_timeout`
157
+ - Tomcat connector timeout
158
+
159
+ ## Compatibility
160
+
161
+ The integration suite runs against these Docker Compose stacks (upstream
162
+ compose files of each branch):
163
+
164
+ | Target | |
165
+ |---|---|
166
+ | DSpace 8.x, 9.x, 10.x | `dspace/dspace` images |
167
+ | DSpace-CRIS 2024.02, 2025.02 | `4science/dspace-cris` images |
168
+
169
+ You can run the same suite against your own **test** installation, from a
170
+ clone of the repository:
171
+
172
+ ```bash
173
+ git clone https://github.com/sszepe/dspace-api-toolkit && cd dspace-api-toolkit
174
+ pip install -e ".[integration]"
175
+ DSPACE_IT_PASSWORD=... python -m integration.run_matrix --existing \
176
+ --url https://dspace-test.example.org/server/api --user admin@example.org --name my-test-server
177
+ ```
178
+
179
+ The integration tests require an administrator account. They create and delete test
180
+ data.
181
+
182
+ WARNING: never point the integration matrix to a production instancce!
183
+
184
+ ## Development
185
+
186
+ ```bash
187
+ git clone https://github.com/sszepe/dspace-api-toolkit && cd dspace-api-toolkit
188
+ python -m venv .venv && . .venv/bin/activate
189
+ pip install -e ".[dev]"
190
+ pytest # unit tests, 100% coverage gate
191
+ python -m integration.run_matrix -t dspace-9 # regression tests against a Docker stack
192
+ ```
193
+
194
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the regression test workflow and
195
+ [integration/README.md](integration/README.md) for the matrix details.
196
+
197
+ ## Related projects
198
+
199
+ - [dspace-rest-client](https://pypi.org/project/dspace-rest-client/) - the
200
+ DSpace community's general-purpose Python client.
201
+
202
+ We are very grateful to the team behind dspace-rest-client. Their work inspired this
203
+ project, and we learned a great deal from their approach and implementation. As our
204
+ requirements and goals evolved, we eventually reached a point where we needed to take
205
+ a different direction and develop our own solution. We sincerely appreciate the
206
+ foundation they provided and the work they continue to contribute to the DSpace
207
+ community.
208
+
209
+ ## License
210
+
211
+ BSD 3-Clause, see [LICENSE](LICENSE).
@@ -0,0 +1,165 @@
1
+ # dspace-api-toolkit
2
+
3
+ [![CI](https://github.com/sszepe/dspace-api-toolkit/actions/workflows/ci.yml/badge.svg)](https://github.com/sszepe/dspace-api-toolkit/actions/workflows/ci.yml)
4
+ [![Compatibility matrix](https://github.com/sszepe/dspace-api-toolkit/actions/workflows/integration-matrix.yml/badge.svg)](https://github.com/sszepe/dspace-api-toolkit/actions/workflows/integration-matrix.yml)
5
+ [![PyPI](https://img.shields.io/pypi/v/dspace-api-toolkit.svg)](https://pypi.org/project/dspace-api-toolkit/)
6
+ [![Python](https://img.shields.io/pypi/pyversions/dspace-api-toolkit.svg)](https://pypi.org/project/dspace-api-toolkit/)
7
+ [![License](https://img.shields.io/pypi/l/dspace-api-toolkit.svg)](LICENSE)
8
+
9
+ A Python toolkit for the REST API of **DSpace 7+** and **DSpace-CRIS**, built
10
+ for repository administrators and integration scripts: batch imports,
11
+ submissions, files of any size, access control, collection setup, versioning -
12
+ and long unattended runs that survive token expiry, rate limits and restarts.
13
+
14
+ Every feature is tested against mocked HTTP with pytest with a target
15
+ 100% coverage **and** against real DSpace / DSpace-CRIS installations started
16
+ with Docker Compose; the results form a [compatibility matrix](#compatibility).
17
+
18
+ ```bash
19
+ pip install dspace-api-toolkit
20
+ ```
21
+
22
+ Requires Python 3.10+. The import name of the dspace-api-toolkit in python code
23
+ using it is `dspace_api`.
24
+
25
+ ## Quick start
26
+
27
+ ```python
28
+ from dspace_api import DSpaceClient, MetadataPatch
29
+
30
+ client = DSpaceClient("https://repo.example.org/server/api", "admin@example.org", "secret")
31
+
32
+ # Submit an item through the submission process, with an embargoed file
33
+ result = client.workspace.submit(
34
+ collection_uuid,
35
+ {"dc.title": "My thesis", "dc.contributor.author": ["Doe, Jane"], "dc.date.issued": "2026"},
36
+ files=["thesis.pdf"],
37
+ file_access_conditions=[{"name": "embargo", "startDate": "2027-01-01"}],
38
+ )
39
+ item_uuid = result["item_uuid"]
40
+
41
+ # Upload a large file - streamed, constant memory, MD5 verified
42
+ bundle = client.bundles.get_or_create_bundle(item_uuid, "ORIGINAL")
43
+ client.bundles.upload_bitstream(bundle["uuid"], "dataset.zip",
44
+ progress=lambda done, total: print(f"{done / total:.0%}"))
45
+
46
+ # Precise metadata edits
47
+ client.metadata.patch_metadata("item", item_uuid,
48
+ MetadataPatch().append("dc.subject", "music").move("dc.contributor.author", 1, 0))
49
+
50
+ # Check permissions before acting
51
+ if client.authorizations.can(("item", item_uuid), "withdrawItem"):
52
+ ...
53
+
54
+ client.close()
55
+ ```
56
+
57
+ More scripts will be added in the [`examples/`](examples/) folder.
58
+
59
+ ## What's covered
60
+
61
+ | Area | Manager | Highlights |
62
+ |---|---|---|
63
+ | Items | `client.items` | create, read, update, delete; `upsert_metadata`, position-aware upserts, move, mapped collections |
64
+ | Metadata | `client.metadata` | `MetadataPatch` builder (archived items, submission sections, files), PATCH any object, schema/field registry, `ensure_fields()` |
65
+ | Submissions | `client.workspace` | create (empty / external source / BibTeX-RIS file), fill forms by field, upload, file & item access conditions, license, CC license, deposit, one-call `submit()` with validation errors |
66
+ | Bundles & files | `client.bundles` | bundles, streamed uploads of any size, replace, download, primary bitstream, move, format registry |
67
+ | Access control | `client.permissions` | resource policies, remove / grant / restore group access with CSV reports, bulk access control script |
68
+ | Permission checks | `client.authorizations` | `can`, `features_on`, `require`, batch checks, admin scope |
69
+ | Versioning | `client.versions` | create versions, `new_version()` flow, histories, drafts |
70
+ | Communities | `client.communities` | create, hierarchy and tree walk, admin group, logo, *Authorized searches |
71
+ | Collections | `client.collections` | role groups (admin, submitters, item/bitstream read, workflow), read restrictions, submission policies, license, item template, mapped items, harvesting, logo |
72
+ | People & groups | `client.epersons`, `client.groups` | users, registration, end-user agreement, groups and members |
73
+ | Site & config | `client.site`, `client.entity_types` | Terms of Use per language, entity types |
74
+ | Search & browse | `client.discovery` | search configuration, `f.<filter>` filters, paged `iter_search`, `count`, facets with all values (`facet_counts`), browse indexes, entries and items |
75
+ | Review workflow | `client.workflow` | workflow items, `pending_tasks`, claim / approve / reject / unclaim, `review()` in one call, `approve_all_steps`, workflow definitions, steps and actions, supervision orders |
76
+ | Relationships | `client.relationships` | configurable entities (plain DSpace): relationship types, `relate` / `unrelate` by label, `related_items` in order, `reorder`, name variants, `ensure_related` |
77
+ | Vocabularies | `client.vocabulary` | vocabularies and authorities, exact `lookup`, `invalid_values` for import validation, form-field vocabularies, hierarchical trees, `to_metadata_value` |
78
+
79
+ Every read accepts `all_languages=`, `projection=` and `embed=` (including
80
+ nested embeds with sizes) to shape the response.
81
+
82
+ ## Configuration
83
+
84
+ Arguments win over environment variables, which win over a `.env` file in the
85
+ working directory (see [`.env.example`](.env.example)):
86
+
87
+ | Variable | Default | |
88
+ |---|---|---|
89
+ | `DSPACE_URL` | `http://localhost:8080/server/api` | REST API root |
90
+ | `DSPACE_TIMEOUT` | `30` | seconds per request |
91
+ | `DSPACE_MAX_RETRIES` | `3` | retries for 429 / 502 / 503 / 504 and connection errors (idempotent requests; 429 for all) |
92
+ | `DSPACE_RETRY_BACKOFF`, `DSPACE_RETRY_MAX_WAIT` | `1.0`, `30` | exponential backoff with jitter, honours `Retry-After` |
93
+ | `DSPACE_TOKEN_REFRESH_MARGIN` | `60` | refresh the login token this many seconds before it expires |
94
+ | `DSPACE_REAUTHENTICATE` | `true` | log in again after a 401 |
95
+ | `DSPACE_ACCEPT_USER_AGREEMENT` | `false` | DSpace-CRIS: accept the end-user agreement for the logged-in user when the server demands it |
96
+ | `DSPACE_ALL_LANGUAGES` | `false` | default for `projection=allLanguages` (DSpace-CRIS, DSpace 10; left out automatically on DSpace 8/9) |
97
+
98
+ `DSpaceConfig(env_file=False)` ignores `.env`; `env_file="prod.env"` reads another file.
99
+
100
+ ## Large files
101
+
102
+ Uploads (`upload_bitstream`, `replace_bitstream`, `workspace.upload_file`)
103
+ stream the multipart body from disk with an exact `Content-Length`: memory
104
+ use stays at one chunk (8 MiB) regardless of file size, the MD5 is computed
105
+ while sending and compared with DSpace's checksum afterwards, and the read
106
+ timeout grows with the file size. The server must allow large requests too:
107
+
108
+ - DSpace `config/application.properties`: `spring.servlet.multipart.max-file-size`
109
+ and `max-request-size` (default 512MB)
110
+ - reverse proxy: nginx `client_max_body_size`, `proxy_request_buffering off`, `proxy_read_timeout`
111
+ - Tomcat connector timeout
112
+
113
+ ## Compatibility
114
+
115
+ The integration suite runs against these Docker Compose stacks (upstream
116
+ compose files of each branch):
117
+
118
+ | Target | |
119
+ |---|---|
120
+ | DSpace 8.x, 9.x, 10.x | `dspace/dspace` images |
121
+ | DSpace-CRIS 2024.02, 2025.02 | `4science/dspace-cris` images |
122
+
123
+ You can run the same suite against your own **test** installation, from a
124
+ clone of the repository:
125
+
126
+ ```bash
127
+ git clone https://github.com/sszepe/dspace-api-toolkit && cd dspace-api-toolkit
128
+ pip install -e ".[integration]"
129
+ DSPACE_IT_PASSWORD=... python -m integration.run_matrix --existing \
130
+ --url https://dspace-test.example.org/server/api --user admin@example.org --name my-test-server
131
+ ```
132
+
133
+ The integration tests require an administrator account. They create and delete test
134
+ data.
135
+
136
+ WARNING: never point the integration matrix to a production instancce!
137
+
138
+ ## Development
139
+
140
+ ```bash
141
+ git clone https://github.com/sszepe/dspace-api-toolkit && cd dspace-api-toolkit
142
+ python -m venv .venv && . .venv/bin/activate
143
+ pip install -e ".[dev]"
144
+ pytest # unit tests, 100% coverage gate
145
+ python -m integration.run_matrix -t dspace-9 # regression tests against a Docker stack
146
+ ```
147
+
148
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the regression test workflow and
149
+ [integration/README.md](integration/README.md) for the matrix details.
150
+
151
+ ## Related projects
152
+
153
+ - [dspace-rest-client](https://pypi.org/project/dspace-rest-client/) - the
154
+ DSpace community's general-purpose Python client.
155
+
156
+ We are very grateful to the team behind dspace-rest-client. Their work inspired this
157
+ project, and we learned a great deal from their approach and implementation. As our
158
+ requirements and goals evolved, we eventually reached a point where we needed to take
159
+ a different direction and develop our own solution. We sincerely appreciate the
160
+ foundation they provided and the work they continue to contribute to the DSpace
161
+ community.
162
+
163
+ ## License
164
+
165
+ BSD 3-Clause, see [LICENSE](LICENSE).
@@ -0,0 +1,75 @@
1
+ # -*- coding: UTF-8 -*-
2
+ """DSpace API toolkit"""
3
+ from ._version import __version__
4
+ from .config import DSpaceConfig
5
+ from .auth import DSpaceAuth
6
+ from .items import ItemManager
7
+ from .workspaces import WorkspaceManager
8
+ from .bundles import BundleManager
9
+ from .collections import CollectionManager
10
+ from .discovery import DiscoveryManager
11
+ from .vocabulary import VocabularyManager
12
+ from .permissions import PermissionManager
13
+ from .communities import CommunityManager
14
+ from .groups import GroupManager
15
+ from .epersons import EPersonManager
16
+ from .site import SiteManager
17
+ from .entity_types import EntityTypeManager
18
+ from .metadata import MetadataManager, MetadataPatch
19
+ from .authorizations import AuthorizationManager, Feature
20
+ from .versions import VersionManager
21
+ from .relationships import RelationshipManager
22
+ from .workflow import WorkflowError, WorkflowManager
23
+ from .workspaces import SubmissionError
24
+
25
+
26
+ class DSpaceClient:
27
+ """Client for interacting with the DSpace REST API."""
28
+ def __init__(self, dspace_url=None, username=None, password=None, version=8, all_languages=None):
29
+ """Initialize the DSpace client with optional URL, credentials, API version, and language settings.
30
+
31
+ Parameters:
32
+ dspace_url: The base URL of the DSpace instance.
33
+ username: The username for authentication.
34
+ password: The password for authentication.
35
+ version: The API version to use (default is 8).
36
+ all_languages: Optional flag for handling all languages in metadata.
37
+
38
+ """
39
+ # all_languages: default for projection=allLanguages on reads
40
+ # (None -> DSPACE_ALL_LANGUAGES env var, default off)
41
+ self.config = DSpaceConfig(dspace_url, version, all_languages=all_languages)
42
+ # Initialize authentication manager with the given configuration.
43
+ self.auth = DSpaceAuth(self.config)
44
+ # Log in if username and password are provided.
45
+ if username and password:
46
+ self.auth.login(username, password)
47
+ # Initialize managers for various DSpace API components.
48
+ self.items = ItemManager(self.auth)
49
+ self.workspace = WorkspaceManager(self.auth)
50
+ self.bundles = BundleManager(self.auth)
51
+ self.collections = CollectionManager(self.auth)
52
+ self.discovery = DiscoveryManager(self.auth)
53
+ self.vocabulary = VocabularyManager(self.auth)
54
+ self.permissions = PermissionManager(self.auth)
55
+ self.communities = CommunityManager(self.auth)
56
+ self.groups = GroupManager(self.auth)
57
+ self.epersons = EPersonManager(self.auth)
58
+ self.site = SiteManager(self.auth)
59
+ self.entity_types = EntityTypeManager(self.auth)
60
+ self.metadata = MetadataManager(self.auth)
61
+ self.authorizations = AuthorizationManager(self.auth)
62
+ self.versions = VersionManager(self.auth)
63
+ self.relationships = RelationshipManager(self.auth)
64
+ self.workflow = WorkflowManager(self.auth)
65
+
66
+ def close(self):
67
+ """Close the client and log out from the DSpace API.
68
+
69
+ Parameters:
70
+ None
71
+
72
+ Returns:
73
+ None
74
+ """
75
+ self.auth.logout()