Repository: getmoto/moto

Issue:
Add support for IoT RegisterCertificate
IoT is missing support for RegisterCertificate.



Discussion:

Resolving patch:
diff --git a/moto/iot/models.py b/moto/iot/models.py
--- a/moto/iot/models.py
+++ b/moto/iot/models.py
@@ -96,7 +96,7 @@ def to_dict(self):
 
 
 class FakeCertificate(BaseModel):
-    def __init__(self, certificate_pem, status, region_name):
+    def __init__(self, certificate_pem, status, region_name, ca_certificate_pem=None):
         m = hashlib.sha256()
         m.update(str(uuid.uuid4()).encode('utf-8'))
         self.certificate_id = m.hexdigest()
@@ -109,12 +109,18 @@ def __init__(self, certificate_pem, status, region_name):
         self.transfer_data = {}
         self.creation_date = time.time()
         self.last_modified_date = self.creation_date
+
         self.ca_certificate_id = None
+        self.ca_certificate_pem = ca_certificate_pem
+        if ca_certificate_pem:
+            m.update(str(uuid.uuid4()).encode('utf-8'))
+            self.ca_certificate_id = m.hexdigest()
 
     def to_dict(self):
         return {
             'certificateArn': self.arn,
             'certificateId': self.certificate_id,
+            'caCertificateId': self.ca_certificate_id,
             'status': self.status,
             'creationDate': self.creation_date
         }
@@ -410,6 +416,12 @@ def describe_certificate(self, certificate_id):
     def list_certificates(self):
         return self.certificates.values()
 
+    def register_certificate(self, certificate_pem, ca_certificate_pem, set_as_active, status):
+        certificate = FakeCertificate(certificate_pem, 'ACTIVE' if set_as_active else status,
+                                      self.region_name, ca_certificate_pem)
+        self.certificates[certificate.certificate_id] = certificate
+        return certificate
+
     def update_certificate(self, certificate_id, new_status):
         cert = self.describe_certificate(certificate_id)
         # TODO: validate new_status
diff --git a/moto/iot/responses.py b/moto/iot/responses.py
--- a/moto/iot/responses.py
+++ b/moto/iot/responses.py
@@ -183,6 +183,20 @@ def list_certificates(self):
         # TODO: implement pagination in the future
         return json.dumps(dict(certificates=[_.to_dict() for _ in certificates]))
 
+    def register_certificate(self):
+        certificate_pem = self._get_param("certificatePem")
+        ca_certificate_pem = self._get_param("caCertificatePem")
+        set_as_active = self._get_bool_param("setAsActive")
+        status = self._get_param("status")
+
+        cert = self.iot_backend.register_certificate(
+            certificate_pem=certificate_pem,
+            ca_certificate_pem=ca_certificate_pem,
+            set_as_active=set_as_active,
+            status=status
+        )
+        return json.dumps(dict(certificateId=cert.certificate_id, certificateArn=cert.arn))
+
     def update_certificate(self):
         certificate_id = self._get_param("certificateId")
         new_status = self._get_param("newStatus")


Test patch:
diff --git a/tests/test_iot/test_iot.py b/tests/test_iot/test_iot.py
--- a/tests/test_iot/test_iot.py
+++ b/tests/test_iot/test_iot.py
@@ -228,7 +228,7 @@ def test_list_things_with_attribute_and_thing_type_filter_and_next_token():
 
 @mock_iot
 def test_certs():
-    client = boto3.client('iot', region_name='ap-northeast-1')
+    client = boto3.client('iot', region_name='us-east-1')
     cert = client.create_keys_and_certificate(setAsActive=True)
     cert.should.have.key('certificateArn').which.should_not.be.none
     cert.should.have.key('certificateId').which.should_not.be.none
@@ -245,6 +245,29 @@ def test_certs():
     cert_desc.should.have.key('certificateId').which.should_not.be.none
     cert_desc.should.have.key('certificatePem').which.should_not.be.none
     cert_desc.should.have.key('status').which.should.equal('ACTIVE')
+    cert_pem = cert_desc['certificatePem']
+
+    res = client.list_certificates()
+    for cert in res['certificates']:
+        cert.should.have.key('certificateArn').which.should_not.be.none
+        cert.should.have.key('certificateId').which.should_not.be.none
+        cert.should.have.key('status').which.should_not.be.none
+        cert.should.have.key('creationDate').which.should_not.be.none
+
+    client.update_certificate(certificateId=cert_id, newStatus='REVOKED')
+    cert = client.describe_certificate(certificateId=cert_id)
+    cert_desc = cert['certificateDescription']
+    cert_desc.should.have.key('status').which.should.equal('REVOKED')
+
+    client.delete_certificate(certificateId=cert_id)
+    res = client.list_certificates()
+    res.should.have.key('certificates')
+
+    # Test register_certificate flow
+    cert = client.register_certificate(certificatePem=cert_pem, setAsActive=True)
+    cert.should.have.key('certificateId').which.should_not.be.none
+    cert.should.have.key('certificateArn').which.should_not.be.none
+    cert_id = cert['certificateId']
 
     res = client.list_certificates()
     res.should.have.key('certificates').which.should.have.length_of(1)
@@ -256,11 +279,12 @@ def test_certs():
 
     client.update_certificate(certificateId=cert_id, newStatus='REVOKED')
     cert = client.describe_certificate(certificateId=cert_id)
-    cert_desc.should.have.key('status').which.should.equal('ACTIVE')
+    cert_desc = cert['certificateDescription']
+    cert_desc.should.have.key('status').which.should.equal('REVOKED')
 
     client.delete_certificate(certificateId=cert_id)
     res = client.list_certificates()
-    res.should.have.key('certificates').which.should.have.length_of(0)
+    res.should.have.key('certificates')
 
 
 @mock_iot