Repository: python-gitlab/python-gitlab

Issue:
Add support for remote-import API Endpoint
## Description of the problem, including code/CLI snippet

Hello Together,

GitLab supports the import from a remote storage, additionally to the export to a remote storage. This is really helpful, if you migrate a lot of big repositories to another GitLab Instance, it's increase the speed and also reduce the API calls and scripts complexity.

https://docs.gitlab.com/ee/api/project_import_export.html#import-a-file-from-a-remote-object-storage

## Expected Behavior

It would be great if gitlab-python would support this endpoint, so additionally to the import_project function, we could also use the import_project_from_remote function.

## Actual Behavior

At the moment, it is not possible to import a repository from a remote storage via import_project function. 

## Specifications

  - python-gitlab version: 3.8.1
  - API version you are using (v3/v4): v4
  - Gitlab server version (or gitlab.com): 15.2.3



Discussion:
@nejch @JohnVillalovos trying to implement this. Can you please assign this to me? 
I am thinking to create an FileImportMixin which will be used by
* [import_project](https://docs.gitlab.com/ee/api/project_import_export.html#import-a-file) 
* [import_project_from_remote](https://docs.gitlab.com/ee/api/project_import_export.html#import-a-file-from-a-remote-object-storage)
* [import_project_from_s3](https://docs.gitlab.com/ee/api/project_import_export.html#import-a-file-from-aws-s3)

I can add the unit tests too. Just worried about functionally testing these esp S3 one. If you have any ideas on how to test, please let me know. Will most probably create a mock server to mock file responses.


Resolving patch:
diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py
--- a/gitlab/v4/objects/projects.py
+++ b/gitlab/v4/objects/projects.py
@@ -822,6 +822,8 @@ def import_project(
         Args:
             file: Data or file object containing the project
             path: Name and path for the new project
+            name: The name of the project to import. If not provided,
+                defaults to the path of the project.
             namespace: The ID or path of the namespace that the project
                 will be imported to
             overwrite: If True overwrite an existing project with the
@@ -849,6 +851,49 @@ def import_project(
             "/projects/import", post_data=data, files=files, **kwargs
         )
 
+    def remote_import(
+        self,
+        url: str,
+        path: str,
+        name: Optional[str] = None,
+        namespace: Optional[str] = None,
+        overwrite: bool = False,
+        override_params: Optional[Dict[str, Any]] = None,
+        **kwargs: Any,
+    ) -> Union[Dict[str, Any], requests.Response]:
+        """Import a project from an archive file stored on a remote URL.
+
+        Args:
+            url: URL for the file containing the project data to import
+            path: Name and path for the new project
+            name: The name of the project to import. If not provided,
+                defaults to the path of the project.
+            namespace: The ID or path of the namespace that the project
+                will be imported to
+            overwrite: If True overwrite an existing project with the
+                same path
+            override_params: Set the specific settings for the project
+            **kwargs: Extra options to send to the server (e.g. sudo)
+
+        Raises:
+            GitlabAuthenticationError: If authentication is not correct
+            GitlabListError: If the server failed to perform the request
+
+        Returns:
+            A representation of the import status.
+        """
+        data = {"path": path, "overwrite": str(overwrite), "url": url}
+        if override_params:
+            for k, v in override_params.items():
+                data[f"override_params[{k}]"] = v
+        if name is not None:
+            data["name"] = name
+        if namespace:
+            data["namespace"] = namespace
+        return self.gitlab.http_post(
+            "/projects/remote-import", post_data=data, **kwargs
+        )
+
     def import_bitbucket_server(
         self,
         bitbucket_server_url: str,


Test patch:
diff --git a/tests/functional/api/test_import_export.py b/tests/functional/api/test_import_export.py
--- a/tests/functional/api/test_import_export.py
+++ b/tests/functional/api/test_import_export.py
@@ -1,5 +1,7 @@
 import time
 
+import pytest
+
 import gitlab
 
 
@@ -64,3 +66,15 @@ def test_project_import_export(gl, project, temp_dir):
         count += 1
         if count == 15:
             raise Exception("Project import taking too much time")
+
+
+def test_project_remote_import(gl):
+    with pytest.raises(gitlab.exceptions.GitlabHttpError) as err_info:
+        gl.projects.remote_import(
+            "ftp://whatever.com/url", "remote-project", "remote-project", "root"
+        )
+    assert err_info.value.response_code == 400
+    assert (
+        "File url is blocked: Only allowed schemes are https"
+        in err_info.value.error_message
+    )
diff --git a/tests/unit/objects/test_project_import_export.py b/tests/unit/objects/test_project_import_export.py
--- a/tests/unit/objects/test_project_import_export.py
+++ b/tests/unit/objects/test_project_import_export.py
@@ -29,6 +29,30 @@ def resp_import_project():
         yield rsps
 
 
+@pytest.fixture
+def resp_remote_import():
+    content = {
+        "id": 1,
+        "description": None,
+        "name": "remote-project",
+        "name_with_namespace": "Administrator / remote-project",
+        "path": "remote-project",
+        "path_with_namespace": "root/remote-project",
+        "created_at": "2018-02-13T09:05:58.023Z",
+        "import_status": "scheduled",
+    }
+
+    with responses.RequestsMock() as rsps:
+        rsps.add(
+            method=responses.POST,
+            url="http://localhost/api/v4/projects/remote-import",
+            json=content,
+            content_type="application/json",
+            status=200,
+        )
+        yield rsps
+
+
 @pytest.fixture
 def resp_import_status():
     content = {
@@ -99,6 +123,13 @@ def test_import_project(gl, resp_import_project):
     assert project_import["import_status"] == "scheduled"
 
 
+def test_remote_import(gl, resp_remote_import):
+    project_import = gl.projects.remote_import(
+        "https://whatever.com/url", "remote-project", "remote-project", "root"
+    )
+    assert project_import["import_status"] == "scheduled"
+
+
 def test_import_project_with_override_params(gl, resp_import_project):
     project_import = gl.projects.import_project(
         "file", "api-project", override_params={"visibility": "private"}