Repository: getmoto/moto

Issue:
Support Secrets Manager restore-secret
Implement support for `aws secretsmanager restore-secret`.


Discussion:

Resolving patch:
diff --git a/moto/secretsmanager/models.py b/moto/secretsmanager/models.py
--- a/moto/secretsmanager/models.py
+++ b/moto/secretsmanager/models.py
@@ -271,6 +271,20 @@ def delete_secret(self, secret_id, recovery_window_in_days, force_delete_without
 
         return arn, name, self._unix_time_secs(deletion_date)
 
+    def restore_secret(self, secret_id):
+
+        if not self._is_valid_identifier(secret_id):
+            raise ResourceNotFoundException
+
+        self.secrets[secret_id].pop('deleted_date', None)
+
+        secret = self.secrets[secret_id]
+
+        arn = secret_arn(self.region, secret['secret_id'])
+        name = secret['name']
+
+        return arn, name
+
 
 available_regions = (
     boto3.session.Session().get_available_regions("secretsmanager")
diff --git a/moto/secretsmanager/responses.py b/moto/secretsmanager/responses.py
--- a/moto/secretsmanager/responses.py
+++ b/moto/secretsmanager/responses.py
@@ -86,3 +86,10 @@ def delete_secret(self):
             force_delete_without_recovery=force_delete_without_recovery,
         )
         return json.dumps(dict(ARN=arn, Name=name, DeletionDate=deletion_date))
+
+    def restore_secret(self):
+        secret_id = self._get_param("SecretId")
+        arn, name = secretsmanager_backends[self.region].restore_secret(
+            secret_id=secret_id,
+        )
+        return json.dumps(dict(ARN=arn, Name=name))


Test patch:
diff --git a/tests/test_secretsmanager/test_secretsmanager.py b/tests/test_secretsmanager/test_secretsmanager.py
--- a/tests/test_secretsmanager/test_secretsmanager.py
+++ b/tests/test_secretsmanager/test_secretsmanager.py
@@ -347,6 +347,46 @@ def test_list_secrets():
     }]
 
 
+@mock_secretsmanager
+def test_restore_secret():
+    conn = boto3.client('secretsmanager', region_name='us-west-2')
+
+    conn.create_secret(Name='test-secret',
+                       SecretString='foosecret')
+
+    conn.delete_secret(SecretId='test-secret')
+
+    described_secret_before = conn.describe_secret(SecretId='test-secret')
+    assert described_secret_before['DeletedDate'] > datetime.fromtimestamp(1, pytz.utc)
+
+    restored_secret = conn.restore_secret(SecretId='test-secret')
+    assert restored_secret['ARN']
+    assert restored_secret['Name'] == 'test-secret'
+
+    described_secret_after = conn.describe_secret(SecretId='test-secret')
+    assert 'DeletedDate' not in described_secret_after
+
+
+@mock_secretsmanager
+def test_restore_secret_that_is_not_deleted():
+    conn = boto3.client('secretsmanager', region_name='us-west-2')
+
+    conn.create_secret(Name='test-secret',
+                       SecretString='foosecret')
+
+    restored_secret = conn.restore_secret(SecretId='test-secret')
+    assert restored_secret['ARN']
+    assert restored_secret['Name'] == 'test-secret'
+
+
+@mock_secretsmanager
+def test_restore_secret_that_does_not_exist():
+    conn = boto3.client('secretsmanager', region_name='us-west-2')
+
+    with assert_raises(ClientError):
+        result = conn.restore_secret(SecretId='i-dont-exist')
+
+
 @mock_secretsmanager
 def test_rotate_secret():
     secret_name = 'test-secret'