Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 043de2d

Browse filesBrowse files
committed
feat(api): add support for bulk imports API
1 parent 6fca651 commit 043de2d
Copy full SHA for 043de2d

File tree

Expand file treeCollapse file tree

9 files changed

+349
-0
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

9 files changed

+349
-0
lines changed
Open diff view settings
Collapse file

‎docs/api-objects.rst‎

Copy file name to clipboardExpand all lines: docs/api-objects.rst
+1Lines changed: 1 addition & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ API examples
1111
gl_objects/emojis
1212
gl_objects/badges
1313
gl_objects/branches
14+
gl_objects/bulk_imports
1415
gl_objects/messages
1516
gl_objects/ci_lint
1617
gl_objects/commits
Collapse file

‎docs/gl_objects/bulk_imports.rst‎

Copy file name to clipboard
+82Lines changed: 82 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#########################
2+
Migrations (Bulk Imports)
3+
#########################
4+
5+
References
6+
----------
7+
8+
* v4 API:
9+
10+
+ :class:`gitlab.v4.objects.BulkImport`
11+
+ :class:`gitlab.v4.objects.BulkImportManager`
12+
+ :attr:`gitlab.Gitlab.bulk_imports`
13+
+ :class:`gitlab.v4.objects.BulkImportAllEntity`
14+
+ :class:`gitlab.v4.objects.BulkImportAllEntityManager`
15+
+ :attr:`gitlab.Gitlab.bulk_import_entities`
16+
+ :class:`gitlab.v4.objects.BulkImportEntity`
17+
+ :class:`gitlab.v4.objects.BulkImportEntityManager`
18+
+ :attr:`gitlab.v4.objects.BulkImport.entities`
19+
20+
* GitLab API: https://docs.gitlab.com/ee/api/bulk_imports.html
21+
22+
Examples
23+
--------
24+
25+
.. note::
26+
27+
Like the project/group imports and exports, this is an asynchronous operation and you
28+
will need to refresh the state from the server to get an accurate migration status. See
29+
:ref:`project_import_export` in the import/export section for more details and examples.
30+
31+
Start a bulk import/migration of a group and wait for completion::
32+
33+
# Create the migration
34+
configuration = {
35+
"url": "https://gitlab.example.com",
36+
"access_token": private_token,
37+
}
38+
entity = {
39+
"source_full_path": "source_group",
40+
"source_type": "group_entity",
41+
"destination_slug": "imported-group",
42+
"destination_namespace": "imported-namespace",
43+
}
44+
migration = gl.bulk_imports.create(
45+
{
46+
"configuration": configuration,
47+
"entities": [entity],
48+
}
49+
)
50+
51+
# Wait for the 'finished' status
52+
while migration.status != "finished":
53+
time.sleep(1)
54+
migration.refresh()
55+
56+
List all migrations::
57+
58+
gl.bulk_imports.list()
59+
60+
List the entities of all migrations::
61+
62+
gl.bulk_import_entities.list()
63+
64+
Get a single migration by ID::
65+
66+
migration = gl.bulk_imports.get(123)
67+
68+
List the entities of a single migration::
69+
70+
entities = migration.entities.list()
71+
72+
Get a single entity of a migration by ID::
73+
74+
entity = migration.entities.get(123)
75+
76+
Refresh the state of a migration or entity from the server::
77+
78+
migration.refresh()
79+
entity.refresh()
80+
81+
print(migration.status)
82+
print(entity.status)
Collapse file

‎docs/gl_objects/projects.rst‎

Copy file name to clipboardExpand all lines: docs/gl_objects/projects.rst
+2Lines changed: 2 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ Reference
275275

276276
* GitLab API: https://docs.gitlab.com/ce/api/project_import_export.html
277277

278+
.. _project_import_export:
279+
278280
Examples
279281
--------
280282

Collapse file

‎gitlab/client.py‎

Copy file name to clipboardExpand all lines: gitlab/client.py
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ def __init__(
121121

122122
self.broadcastmessages = objects.BroadcastMessageManager(self)
123123
"""See :class:`~gitlab.v4.objects.BroadcastMessageManager`"""
124+
self.bulk_imports = objects.BulkImportManager(self)
125+
"""See :class:`~gitlab.v4.objects.BulkImportManager`"""
126+
self.bulk_import_entities = objects.BulkImportAllEntityManager(self)
127+
"""See :class:`~gitlab.v4.objects.BulkImportAllEntityManager`"""
124128
self.ci_lint = objects.CiLintManager(self)
125129
"""See :class:`~gitlab.v4.objects.CiLintManager`"""
126130
self.deploykeys = objects.DeployKeyManager(self)
Collapse file

‎gitlab/v4/objects/__init__.py‎

Copy file name to clipboardExpand all lines: gitlab/v4/objects/__init__.py
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .boards import *
99
from .branches import *
1010
from .broadcast_messages import *
11+
from .bulk_imports import *
1112
from .ci_lint import *
1213
from .clusters import *
1314
from .commits import *
Collapse file

‎gitlab/v4/objects/bulk_imports.py‎

Copy file name to clipboard
+54Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from typing import Any, cast, Union
2+
3+
from gitlab.base import RESTManager, RESTObject
4+
from gitlab.mixins import CreateMixin, ListMixin, RefreshMixin, RetrieveMixin
5+
from gitlab.types import RequiredOptional
6+
7+
__all__ = [
8+
"BulkImport",
9+
"BulkImportManager",
10+
"BulkImportAllEntity",
11+
"BulkImportAllEntityManager",
12+
"BulkImportEntity",
13+
"BulkImportEntityManager",
14+
]
15+
16+
17+
class BulkImport(RefreshMixin, RESTObject):
18+
entities: "BulkImportEntityManager"
19+
20+
21+
class BulkImportManager(CreateMixin, RetrieveMixin, RESTManager):
22+
_path = "/bulk_imports"
23+
_obj_cls = BulkImport
24+
_create_attrs = RequiredOptional(required=("configuration", "entities"))
25+
_list_filters = ("sort", "status")
26+
27+
def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> BulkImport:
28+
return cast(BulkImport, super().get(id=id, lazy=lazy, **kwargs))
29+
30+
31+
class BulkImportEntity(RefreshMixin, RESTObject):
32+
pass
33+
34+
35+
class BulkImportEntityManager(RetrieveMixin, RESTManager):
36+
_path = "/bulk_imports/{bulk_import_id}/entities"
37+
_obj_cls = BulkImportEntity
38+
_from_parent_attrs = {"bulk_import_id": "id"}
39+
_list_filters = ("sort", "status")
40+
41+
def get(
42+
self, id: Union[str, int], lazy: bool = False, **kwargs: Any
43+
) -> BulkImportEntity:
44+
return cast(BulkImportEntity, super().get(id=id, lazy=lazy, **kwargs))
45+
46+
47+
class BulkImportAllEntity(RESTObject):
48+
pass
49+
50+
51+
class BulkImportAllEntityManager(ListMixin, RESTManager):
52+
_path = "/bulk_imports/entities"
53+
_obj_cls = BulkImportAllEntity
54+
_list_filters = ("sort", "status")
Collapse file
+41Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
def test_bulk_imports(gl, group):
2+
destination = f"{group.full_path}-import"
3+
configuration = {
4+
"url": gl.url,
5+
"access_token": gl.private_token,
6+
}
7+
migration_entity = {
8+
"source_full_path": group.full_path,
9+
"source_type": "group_entity",
10+
"destination_slug": destination,
11+
"destination_namespace": destination,
12+
}
13+
created_migration = gl.bulk_imports.create(
14+
{
15+
"configuration": configuration,
16+
"entities": [migration_entity],
17+
}
18+
)
19+
20+
assert created_migration.source_type == "gitlab"
21+
assert created_migration.status == "created"
22+
23+
migration = gl.bulk_imports.get(created_migration.id)
24+
assert migration == created_migration
25+
26+
migration.refresh()
27+
assert migration == created_migration
28+
29+
migrations = gl.bulk_imports.list()
30+
assert migration in migrations
31+
32+
all_entities = gl.bulk_import_entities.list()
33+
entities = migration.entities.list()
34+
assert isinstance(entities, list)
35+
assert entities[0] in all_entities
36+
37+
entity = migration.entities.get(entities[0].id)
38+
assert entity == entities[0]
39+
40+
entity.refresh()
41+
assert entity.created_at == entities[0].created_at
Collapse file

‎tests/unit/conftest.py‎

Copy file name to clipboardExpand all lines: tests/unit/conftest.py
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,8 @@ def schedule(project):
9797
@pytest.fixture
9898
def user(gl):
9999
return gl.users.get(1, lazy=True)
100+
101+
102+
@pytest.fixture
103+
def migration(gl):
104+
return gl.bulk_imports.get(1, lazy=True)
Collapse file
+159Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
"""
2+
GitLab API: https://docs.gitlab.com/ce/api/bulk_imports.html
3+
"""
4+
5+
import pytest
6+
import responses
7+
8+
from gitlab.v4.objects import BulkImport, BulkImportAllEntity, BulkImportEntity
9+
10+
migration_content = {
11+
"id": 1,
12+
"status": "finished",
13+
"source_type": "gitlab",
14+
"created_at": "2021-06-18T09:45:55.358Z",
15+
"updated_at": "2021-06-18T09:46:27.003Z",
16+
}
17+
entity_content = {
18+
"id": 1,
19+
"bulk_import_id": 1,
20+
"status": "finished",
21+
"source_full_path": "source_group",
22+
"destination_slug": "destination_slug",
23+
"destination_namespace": "destination_path",
24+
"parent_id": None,
25+
"namespace_id": 1,
26+
"project_id": None,
27+
"created_at": "2021-06-18T09:47:37.390Z",
28+
"updated_at": "2021-06-18T09:47:51.867Z",
29+
"failures": [],
30+
}
31+
32+
33+
@pytest.fixture
34+
def resp_create_bulk_import():
35+
with responses.RequestsMock() as rsps:
36+
rsps.add(
37+
method=responses.POST,
38+
url="http://localhost/api/v4/bulk_imports",
39+
json=migration_content,
40+
content_type="application/json",
41+
status=201,
42+
)
43+
yield rsps
44+
45+
46+
@pytest.fixture
47+
def resp_list_bulk_imports():
48+
with responses.RequestsMock() as rsps:
49+
rsps.add(
50+
method=responses.GET,
51+
url="http://localhost/api/v4/bulk_imports",
52+
json=[migration_content],
53+
content_type="application/json",
54+
status=200,
55+
)
56+
yield rsps
57+
58+
59+
@pytest.fixture
60+
def resp_get_bulk_import():
61+
with responses.RequestsMock() as rsps:
62+
rsps.add(
63+
method=responses.GET,
64+
url="http://localhost/api/v4/bulk_imports/1",
65+
json=migration_content,
66+
content_type="application/json",
67+
status=200,
68+
)
69+
yield rsps
70+
71+
72+
@pytest.fixture
73+
def resp_list_all_bulk_import_entities():
74+
with responses.RequestsMock() as rsps:
75+
rsps.add(
76+
method=responses.GET,
77+
url="http://localhost/api/v4/bulk_imports/entities",
78+
json=[entity_content],
79+
content_type="application/json",
80+
status=200,
81+
)
82+
yield rsps
83+
84+
85+
@pytest.fixture
86+
def resp_list_bulk_import_entities():
87+
with responses.RequestsMock() as rsps:
88+
rsps.add(
89+
method=responses.GET,
90+
url="http://localhost/api/v4/bulk_imports/1/entities",
91+
json=[entity_content],
92+
content_type="application/json",
93+
status=200,
94+
)
95+
yield rsps
96+
97+
98+
@pytest.fixture
99+
def resp_get_bulk_import_entity():
100+
with responses.RequestsMock() as rsps:
101+
rsps.add(
102+
method=responses.GET,
103+
url="http://localhost/api/v4/bulk_imports/1/entities/1",
104+
json=entity_content,
105+
content_type="application/json",
106+
status=200,
107+
)
108+
yield rsps
109+
110+
111+
def test_create_bulk_import(gl, resp_create_bulk_import):
112+
configuration = {
113+
"url": gl.url,
114+
"access_token": "test-token",
115+
}
116+
migration_entity = {
117+
"source_full_path": "source",
118+
"source_type": "group_entity",
119+
"destination_slug": "destination",
120+
"destination_namespace": "destination",
121+
}
122+
migration = gl.bulk_imports.create(
123+
{
124+
"configuration": configuration,
125+
"entities": [migration_entity],
126+
}
127+
)
128+
assert isinstance(migration, BulkImport)
129+
assert migration.status == "finished"
130+
131+
132+
def test_list_bulk_imports(gl, resp_list_bulk_imports):
133+
migrations = gl.bulk_imports.list()
134+
assert isinstance(migrations[0], BulkImport)
135+
assert migrations[0].status == "finished"
136+
137+
138+
def test_get_bulk_import(gl, resp_get_bulk_import):
139+
migration = gl.bulk_imports.get(1)
140+
assert isinstance(migration, BulkImport)
141+
assert migration.status == "finished"
142+
143+
144+
def test_list_all_bulk_import_entities(gl, resp_list_all_bulk_import_entities):
145+
entities = gl.bulk_import_entities.list()
146+
assert isinstance(entities[0], BulkImportAllEntity)
147+
assert entities[0].bulk_import_id == 1
148+
149+
150+
def test_list_bulk_import_entities(gl, migration, resp_list_bulk_import_entities):
151+
entities = migration.entities.list()
152+
assert isinstance(entities[0], BulkImportEntity)
153+
assert entities[0].bulk_import_id == 1
154+
155+
156+
def test_get_bulk_import_entity(gl, migration, resp_get_bulk_import_entity):
157+
entity = migration.entities.get(1)
158+
assert isinstance(entity, BulkImportEntity)
159+
assert entity.bulk_import_id == 1

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.