From 14ebf29a61119649b00469ab6400948d603cb0e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomak?= Date: Thu, 6 Feb 2020 11:49:41 +0100 Subject: [PATCH 1/2] Add UpdateOrganizationalUnit endpoint to Organizations API --- moto/organizations/models.py | 5 +++++ moto/organizations/responses.py | 5 +++++ .../test_organizations_boto3.py | 17 +++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/moto/organizations/models.py b/moto/organizations/models.py index 42e4dd00a..9be129fa7 100644 --- a/moto/organizations/models.py +++ b/moto/organizations/models.py @@ -222,6 +222,11 @@ class OrganizationsBackend(BaseBackend): self.attach_policy(PolicyId=utils.DEFAULT_POLICY_ID, TargetId=new_ou.id) return new_ou.describe() + def update_organizational_unit(self, **kwargs): + ou = self.get_organizational_unit_by_id(kwargs["OrganizationalUnitId"]) + ou.name = kwargs["Name"] + return ou.describe() + def get_organizational_unit_by_id(self, ou_id): ou = next((ou for ou in self.ou if ou.id == ou_id), None) if ou is None: diff --git a/moto/organizations/responses.py b/moto/organizations/responses.py index 7c42eb4ec..ba7dd4453 100644 --- a/moto/organizations/responses.py +++ b/moto/organizations/responses.py @@ -36,6 +36,11 @@ class OrganizationsResponse(BaseResponse): self.organizations_backend.create_organizational_unit(**self.request_params) ) + def update_organizational_unit(self): + return json.dumps( + self.organizations_backend.update_organizational_unit(**self.request_params) + ) + def describe_organizational_unit(self): return json.dumps( self.organizations_backend.describe_organizational_unit( diff --git a/tests/test_organizations/test_organizations_boto3.py b/tests/test_organizations/test_organizations_boto3.py index dd79ae787..ab3ddf671 100644 --- a/tests/test_organizations/test_organizations_boto3.py +++ b/tests/test_organizations/test_organizations_boto3.py @@ -713,3 +713,20 @@ def test_untag_resource_errors(): ex.response["Error"]["Message"].should.equal( "You provided a value that does not match the required pattern." ) + + +@mock_organizations +def test_update_organizational_unit(): + client = boto3.client("organizations", region_name="us-east-1") + org = client.create_organization(FeatureSet="ALL")["Organization"] + root_id = client.list_roots()["Roots"][0]["Id"] + ou_name = "ou01" + response = client.create_organizational_unit(ParentId=root_id, Name=ou_name) + validate_organizational_unit(org, response) + response["OrganizationalUnit"]["Name"].should.equal(ou_name) + new_ou_name = "ou02" + response = client.update_organizational_unit( + OrganizationalUnitId=response["OrganizationalUnit"]["Id"], Name=new_ou_name + ) + validate_organizational_unit(org, response) + response["OrganizationalUnit"]["Name"].should.equal(new_ou_name) From fc9eab25919eea0759e1b3146ad111532d1ddfa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomak?= Date: Thu, 6 Feb 2020 12:38:37 +0100 Subject: [PATCH 2/2] Raise DuplicateOrganizationalUnitException Calling UpdateOrganizationalUnit with name that already exists should raise proper error. --- moto/organizations/exceptions.py | 10 +++++++++ moto/organizations/models.py | 8 ++++++- .../test_organizations_boto3.py | 21 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/moto/organizations/exceptions.py b/moto/organizations/exceptions.py index 01b98da7e..b40908862 100644 --- a/moto/organizations/exceptions.py +++ b/moto/organizations/exceptions.py @@ -10,3 +10,13 @@ class InvalidInputException(JsonRESTError): "InvalidInputException", "You provided a value that does not match the required pattern.", ) + + +class DuplicateOrganizationalUnitException(JsonRESTError): + code = 400 + + def __init__(self): + super(DuplicateOrganizationalUnitException, self).__init__( + "DuplicateOrganizationalUnitException", + "An OU with the same name already exists.", + ) diff --git a/moto/organizations/models.py b/moto/organizations/models.py index 9be129fa7..0db069f9a 100644 --- a/moto/organizations/models.py +++ b/moto/organizations/models.py @@ -8,7 +8,10 @@ from moto.core import BaseBackend, BaseModel from moto.core.exceptions import RESTError from moto.core.utils import unix_time from moto.organizations import utils -from moto.organizations.exceptions import InvalidInputException +from moto.organizations.exceptions import ( + InvalidInputException, + DuplicateOrganizationalUnitException, +) class FakeOrganization(BaseModel): @@ -223,6 +226,9 @@ class OrganizationsBackend(BaseBackend): return new_ou.describe() def update_organizational_unit(self, **kwargs): + for ou in self.ou: + if ou.name == kwargs["Name"]: + raise DuplicateOrganizationalUnitException ou = self.get_organizational_unit_by_id(kwargs["OrganizationalUnitId"]) ou.name = kwargs["Name"] return ou.describe() diff --git a/tests/test_organizations/test_organizations_boto3.py b/tests/test_organizations/test_organizations_boto3.py index ab3ddf671..876e83712 100644 --- a/tests/test_organizations/test_organizations_boto3.py +++ b/tests/test_organizations/test_organizations_boto3.py @@ -730,3 +730,24 @@ def test_update_organizational_unit(): ) validate_organizational_unit(org, response) response["OrganizationalUnit"]["Name"].should.equal(new_ou_name) + + +@mock_organizations +def test_update_organizational_unit_duplicate_error(): + client = boto3.client("organizations", region_name="us-east-1") + org = client.create_organization(FeatureSet="ALL")["Organization"] + root_id = client.list_roots()["Roots"][0]["Id"] + ou_name = "ou01" + response = client.create_organizational_unit(ParentId=root_id, Name=ou_name) + validate_organizational_unit(org, response) + response["OrganizationalUnit"]["Name"].should.equal(ou_name) + with assert_raises(ClientError) as e: + client.update_organizational_unit( + OrganizationalUnitId=response["OrganizationalUnit"]["Id"], Name=ou_name + ) + exc = e.exception + exc.operation_name.should.equal("UpdateOrganizationalUnit") + exc.response["Error"]["Code"].should.contain("DuplicateOrganizationalUnitException") + exc.response["Error"]["Message"].should.equal( + "An OU with the same name already exists." + )