diff options
author | Nejc Habjan <nejc.habjan@siemens.com> | 2022-12-08 01:07:26 +0100 |
---|---|---|
committer | Nejc Habjan <hab.nejc@gmail.com> | 2022-12-19 22:23:02 +0100 |
commit | 043de2d265e0e5114d1cd901f82869c003413d9b (patch) | |
tree | 90349e0fe6b282686c231adaec53cfa084480ab9 | |
parent | 6fca6512a32e9e289f988900e1157dfe788f54be (diff) | |
download | gitlab-043de2d265e0e5114d1cd901f82869c003413d9b.tar.gz |
feat(api): add support for bulk imports API
-rw-r--r-- | docs/api-objects.rst | 1 | ||||
-rw-r--r-- | docs/gl_objects/bulk_imports.rst | 82 | ||||
-rw-r--r-- | docs/gl_objects/projects.rst | 2 | ||||
-rw-r--r-- | gitlab/client.py | 4 | ||||
-rw-r--r-- | gitlab/v4/objects/__init__.py | 1 | ||||
-rw-r--r-- | gitlab/v4/objects/bulk_imports.py | 54 | ||||
-rw-r--r-- | tests/functional/api/test_bulk_imports.py | 41 | ||||
-rw-r--r-- | tests/unit/conftest.py | 5 | ||||
-rw-r--r-- | tests/unit/objects/test_bulk_imports.py | 159 |
9 files changed, 349 insertions, 0 deletions
diff --git a/docs/api-objects.rst b/docs/api-objects.rst index ede2cf8..8c7bb19 100644 --- a/docs/api-objects.rst +++ b/docs/api-objects.rst @@ -11,6 +11,7 @@ API examples gl_objects/emojis gl_objects/badges gl_objects/branches + gl_objects/bulk_imports gl_objects/messages gl_objects/ci_lint gl_objects/commits diff --git a/docs/gl_objects/bulk_imports.rst b/docs/gl_objects/bulk_imports.rst new file mode 100644 index 0000000..fa386bd --- /dev/null +++ b/docs/gl_objects/bulk_imports.rst @@ -0,0 +1,82 @@ +######################### +Migrations (Bulk Imports) +######################### + +References +---------- + +* v4 API: + + + :class:`gitlab.v4.objects.BulkImport` + + :class:`gitlab.v4.objects.BulkImportManager` + + :attr:`gitlab.Gitlab.bulk_imports` + + :class:`gitlab.v4.objects.BulkImportAllEntity` + + :class:`gitlab.v4.objects.BulkImportAllEntityManager` + + :attr:`gitlab.Gitlab.bulk_import_entities` + + :class:`gitlab.v4.objects.BulkImportEntity` + + :class:`gitlab.v4.objects.BulkImportEntityManager` + + :attr:`gitlab.v4.objects.BulkImport.entities` + +* GitLab API: https://docs.gitlab.com/ee/api/bulk_imports.html + +Examples +-------- + +.. note:: + + Like the project/group imports and exports, this is an asynchronous operation and you + will need to refresh the state from the server to get an accurate migration status. See + :ref:`project_import_export` in the import/export section for more details and examples. + +Start a bulk import/migration of a group and wait for completion:: + + # Create the migration + configuration = { + "url": "https://gitlab.example.com", + "access_token": private_token, + } + entity = { + "source_full_path": "source_group", + "source_type": "group_entity", + "destination_slug": "imported-group", + "destination_namespace": "imported-namespace", + } + migration = gl.bulk_imports.create( + { + "configuration": configuration, + "entities": [entity], + } + ) + + # Wait for the 'finished' status + while migration.status != "finished": + time.sleep(1) + migration.refresh() + +List all migrations:: + + gl.bulk_imports.list() + +List the entities of all migrations:: + + gl.bulk_import_entities.list() + +Get a single migration by ID:: + + migration = gl.bulk_imports.get(123) + +List the entities of a single migration:: + + entities = migration.entities.list() + +Get a single entity of a migration by ID:: + + entity = migration.entities.get(123) + +Refresh the state of a migration or entity from the server:: + + migration.refresh() + entity.refresh() + + print(migration.status) + print(entity.status) diff --git a/docs/gl_objects/projects.rst b/docs/gl_objects/projects.rst index 7fc0ab9..b80df0d 100644 --- a/docs/gl_objects/projects.rst +++ b/docs/gl_objects/projects.rst @@ -275,6 +275,8 @@ Reference * GitLab API: https://docs.gitlab.com/ce/api/project_import_export.html +.. _project_import_export: + Examples -------- diff --git a/gitlab/client.py b/gitlab/client.py index ebb7852..f558291 100644 --- a/gitlab/client.py +++ b/gitlab/client.py @@ -121,6 +121,10 @@ class Gitlab: self.broadcastmessages = objects.BroadcastMessageManager(self) """See :class:`~gitlab.v4.objects.BroadcastMessageManager`""" + self.bulk_imports = objects.BulkImportManager(self) + """See :class:`~gitlab.v4.objects.BulkImportManager`""" + self.bulk_import_entities = objects.BulkImportAllEntityManager(self) + """See :class:`~gitlab.v4.objects.BulkImportAllEntityManager`""" self.ci_lint = objects.CiLintManager(self) """See :class:`~gitlab.v4.objects.CiLintManager`""" self.deploykeys = objects.DeployKeyManager(self) diff --git a/gitlab/v4/objects/__init__.py b/gitlab/v4/objects/__init__.py index b6f7afc..bd3ae82 100644 --- a/gitlab/v4/objects/__init__.py +++ b/gitlab/v4/objects/__init__.py @@ -8,6 +8,7 @@ from .badges import * from .boards import * from .branches import * from .broadcast_messages import * +from .bulk_imports import * from .ci_lint import * from .clusters import * from .commits import * diff --git a/gitlab/v4/objects/bulk_imports.py b/gitlab/v4/objects/bulk_imports.py new file mode 100644 index 0000000..e8ef74f --- /dev/null +++ b/gitlab/v4/objects/bulk_imports.py @@ -0,0 +1,54 @@ +from typing import Any, cast, Union + +from gitlab.base import RESTManager, RESTObject +from gitlab.mixins import CreateMixin, ListMixin, RefreshMixin, RetrieveMixin +from gitlab.types import RequiredOptional + +__all__ = [ + "BulkImport", + "BulkImportManager", + "BulkImportAllEntity", + "BulkImportAllEntityManager", + "BulkImportEntity", + "BulkImportEntityManager", +] + + +class BulkImport(RefreshMixin, RESTObject): + entities: "BulkImportEntityManager" + + +class BulkImportManager(CreateMixin, RetrieveMixin, RESTManager): + _path = "/bulk_imports" + _obj_cls = BulkImport + _create_attrs = RequiredOptional(required=("configuration", "entities")) + _list_filters = ("sort", "status") + + def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> BulkImport: + return cast(BulkImport, super().get(id=id, lazy=lazy, **kwargs)) + + +class BulkImportEntity(RefreshMixin, RESTObject): + pass + + +class BulkImportEntityManager(RetrieveMixin, RESTManager): + _path = "/bulk_imports/{bulk_import_id}/entities" + _obj_cls = BulkImportEntity + _from_parent_attrs = {"bulk_import_id": "id"} + _list_filters = ("sort", "status") + + def get( + self, id: Union[str, int], lazy: bool = False, **kwargs: Any + ) -> BulkImportEntity: + return cast(BulkImportEntity, super().get(id=id, lazy=lazy, **kwargs)) + + +class BulkImportAllEntity(RESTObject): + pass + + +class BulkImportAllEntityManager(ListMixin, RESTManager): + _path = "/bulk_imports/entities" + _obj_cls = BulkImportAllEntity + _list_filters = ("sort", "status") diff --git a/tests/functional/api/test_bulk_imports.py b/tests/functional/api/test_bulk_imports.py new file mode 100644 index 0000000..899d358 --- /dev/null +++ b/tests/functional/api/test_bulk_imports.py @@ -0,0 +1,41 @@ +def test_bulk_imports(gl, group): + destination = f"{group.full_path}-import" + configuration = { + "url": gl.url, + "access_token": gl.private_token, + } + migration_entity = { + "source_full_path": group.full_path, + "source_type": "group_entity", + "destination_slug": destination, + "destination_namespace": destination, + } + created_migration = gl.bulk_imports.create( + { + "configuration": configuration, + "entities": [migration_entity], + } + ) + + assert created_migration.source_type == "gitlab" + assert created_migration.status == "created" + + migration = gl.bulk_imports.get(created_migration.id) + assert migration == created_migration + + migration.refresh() + assert migration == created_migration + + migrations = gl.bulk_imports.list() + assert migration in migrations + + all_entities = gl.bulk_import_entities.list() + entities = migration.entities.list() + assert isinstance(entities, list) + assert entities[0] in all_entities + + entity = migration.entities.get(entities[0].id) + assert entity == entities[0] + + entity.refresh() + assert entity.created_at == entities[0].created_at diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 1b52d86..476e41c 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -97,3 +97,8 @@ def schedule(project): @pytest.fixture def user(gl): return gl.users.get(1, lazy=True) + + +@pytest.fixture +def migration(gl): + return gl.bulk_imports.get(1, lazy=True) diff --git a/tests/unit/objects/test_bulk_imports.py b/tests/unit/objects/test_bulk_imports.py new file mode 100644 index 0000000..5effcdc --- /dev/null +++ b/tests/unit/objects/test_bulk_imports.py @@ -0,0 +1,159 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/bulk_imports.html +""" + +import pytest +import responses + +from gitlab.v4.objects import BulkImport, BulkImportAllEntity, BulkImportEntity + +migration_content = { + "id": 1, + "status": "finished", + "source_type": "gitlab", + "created_at": "2021-06-18T09:45:55.358Z", + "updated_at": "2021-06-18T09:46:27.003Z", +} +entity_content = { + "id": 1, + "bulk_import_id": 1, + "status": "finished", + "source_full_path": "source_group", + "destination_slug": "destination_slug", + "destination_namespace": "destination_path", + "parent_id": None, + "namespace_id": 1, + "project_id": None, + "created_at": "2021-06-18T09:47:37.390Z", + "updated_at": "2021-06-18T09:47:51.867Z", + "failures": [], +} + + +@pytest.fixture +def resp_create_bulk_import(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/bulk_imports", + json=migration_content, + content_type="application/json", + status=201, + ) + yield rsps + + +@pytest.fixture +def resp_list_bulk_imports(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/bulk_imports", + json=[migration_content], + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_get_bulk_import(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/bulk_imports/1", + json=migration_content, + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_list_all_bulk_import_entities(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/bulk_imports/entities", + json=[entity_content], + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_list_bulk_import_entities(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/bulk_imports/1/entities", + json=[entity_content], + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_get_bulk_import_entity(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/bulk_imports/1/entities/1", + json=entity_content, + content_type="application/json", + status=200, + ) + yield rsps + + +def test_create_bulk_import(gl, resp_create_bulk_import): + configuration = { + "url": gl.url, + "access_token": "test-token", + } + migration_entity = { + "source_full_path": "source", + "source_type": "group_entity", + "destination_slug": "destination", + "destination_namespace": "destination", + } + migration = gl.bulk_imports.create( + { + "configuration": configuration, + "entities": [migration_entity], + } + ) + assert isinstance(migration, BulkImport) + assert migration.status == "finished" + + +def test_list_bulk_imports(gl, resp_list_bulk_imports): + migrations = gl.bulk_imports.list() + assert isinstance(migrations[0], BulkImport) + assert migrations[0].status == "finished" + + +def test_get_bulk_import(gl, resp_get_bulk_import): + migration = gl.bulk_imports.get(1) + assert isinstance(migration, BulkImport) + assert migration.status == "finished" + + +def test_list_all_bulk_import_entities(gl, resp_list_all_bulk_import_entities): + entities = gl.bulk_import_entities.list() + assert isinstance(entities[0], BulkImportAllEntity) + assert entities[0].bulk_import_id == 1 + + +def test_list_bulk_import_entities(gl, migration, resp_list_bulk_import_entities): + entities = migration.entities.list() + assert isinstance(entities[0], BulkImportEntity) + assert entities[0].bulk_import_id == 1 + + +def test_get_bulk_import_entity(gl, migration, resp_get_bulk_import_entity): + entity = migration.entities.get(1) + assert isinstance(entity, BulkImportEntity) + assert entity.bulk_import_id == 1 |