From ca256a07a2cdaf77a5c20e307d334b82fd0fe861 Mon Sep 17 00:00:00 2001
From: Max Wittig <max.wittig@siemens.com>
Date: Thu, 24 Oct 2019 08:50:39 +0200
Subject: [PATCH] feat: add deployment creation

Added in GitLab 12.4

Fixes #917
---
 docs/gl_objects/deployments.rst | 16 ++++++++++++
 gitlab/tests/test_gitlab.py     | 44 +++++++++++++++++++++++++++++++++
 gitlab/v4/objects.py            |  5 ++--
 3 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/docs/gl_objects/deployments.rst b/docs/gl_objects/deployments.rst
index 333d497ed..d6b4cfae8 100644
--- a/docs/gl_objects/deployments.rst
+++ b/docs/gl_objects/deployments.rst
@@ -23,3 +23,19 @@ List deployments for a project::
 Get a single deployment::
 
     deployment = project.deployments.get(deployment_id)
+
+Create a new deployment::
+
+    deployment = project.deployments.create({
+        "environment": "Test",
+        "sha": "1agf4gs",
+        "ref": "master",
+        "tag": False,
+        "status": "created",
+    })
+
+Update a deployment::
+
+    deployment = project.deployments.get(42)
+    deployment.status = "failed"
+    deployment.save()
diff --git a/gitlab/tests/test_gitlab.py b/gitlab/tests/test_gitlab.py
index 665810c9e..c208b313c 100644
--- a/gitlab/tests/test_gitlab.py
+++ b/gitlab/tests/test_gitlab.py
@@ -651,6 +651,50 @@ def resp_mark_all_as_done(url, request):
         with HTTMock(resp_mark_all_as_done):
             self.gl.todos.mark_all_as_done()
 
+    def test_deployment(self):
+        content = '{"id": 42, "status": "success", "ref": "master"}'
+        json_content = json.loads(content)
+
+        @urlmatch(
+            scheme="http",
+            netloc="localhost",
+            path="/api/v4/projects/1/deployments",
+            method="post",
+        )
+        def resp_deployment_create(url, request):
+            headers = {"content-type": "application/json"}
+            return response(200, json_content, headers, None, 5, request)
+
+        @urlmatch(
+            scheme="http",
+            netloc="localhost",
+            path="/api/v4/projects/1/deployments/42",
+            method="put",
+        )
+        def resp_deployment_update(url, request):
+            headers = {"content-type": "application/json"}
+            return response(200, json_content, headers, None, 5, request)
+
+        with HTTMock(resp_deployment_create):
+            deployment = self.gl.projects.get(1, lazy=True).deployments.create(
+                {
+                    "environment": "Test",
+                    "sha": "1agf4gs",
+                    "ref": "master",
+                    "tag": False,
+                    "status": "created",
+                }
+            )
+            self.assertEqual(deployment.id, 42)
+            self.assertEqual(deployment.status, "success")
+            self.assertEqual(deployment.ref, "master")
+
+        with HTTMock(resp_deployment_update):
+            json_content["status"] = "failed"
+            deployment.status = "failed"
+            deployment.save()
+            self.assertEqual(deployment.status, "failed")
+
     def test_update_submodule(self):
         @urlmatch(
             scheme="http", netloc="localhost", path="/api/v4/projects/1$", method="get"
diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py
index 44188c7c3..474cc2b37 100644
--- a/gitlab/v4/objects.py
+++ b/gitlab/v4/objects.py
@@ -3735,15 +3735,16 @@ def set_approvers(self, approver_ids=None, approver_group_ids=None, **kwargs):
         self.gitlab.http_put(path, post_data=data, **kwargs)
 
 
-class ProjectDeployment(RESTObject):
+class ProjectDeployment(RESTObject, SaveMixin):
     pass
 
 
-class ProjectDeploymentManager(RetrieveMixin, RESTManager):
+class ProjectDeploymentManager(RetrieveMixin, CreateMixin, UpdateMixin, RESTManager):
     _path = "/projects/%(project_id)s/deployments"
     _obj_cls = ProjectDeployment
     _from_parent_attrs = {"project_id": "id"}
     _list_filters = ("order_by", "sort")
+    _create_attrs = (("sha", "ref", "tag", "status", "environment"), tuple())
 
 
 class ProjectProtectedBranch(ObjectDeleteMixin, RESTObject):