From 5ec814a6042b73f000b62d3baa3fd74afc27c992 Mon Sep 17 00:00:00 2001 From: James Belleau Date: Wed, 6 May 2020 21:12:48 -0500 Subject: [PATCH] Fixes and additional tests --- moto/backends.py | 1 + moto/managedblockchain/models.py | 77 ++++++---- moto/managedblockchain/responses.py | 41 ++++-- moto/managedblockchain/utils.py | 6 + .../test_managedblockchain_networks.py | 132 ++++++++++++++---- 5 files changed, 195 insertions(+), 62 deletions(-) diff --git a/moto/backends.py b/moto/backends.py index bb71429eb..3934afa67 100644 --- a/moto/backends.py +++ b/moto/backends.py @@ -39,6 +39,7 @@ BACKENDS = { "kms": ("kms", "kms_backends"), "lambda": ("awslambda", "lambda_backends"), "logs": ("logs", "logs_backends"), + "managedblockchain": ("managedblockchain", "managedblockchain_backends"), "moto_api": ("core", "moto_api_backends"), "opsworks": ("opsworks", "opsworks_backends"), "organizations": ("organizations", "organizations_backends"), diff --git a/moto/managedblockchain/models.py b/moto/managedblockchain/models.py index 32e9ebbb5..475a19bbd 100644 --- a/moto/managedblockchain/models.py +++ b/moto/managedblockchain/models.py @@ -8,7 +8,7 @@ from moto.core import BaseBackend, BaseModel from .exceptions import BadRequestException -from .utils import get_network_id +from .utils import get_network_id, get_member_id FRAMEWORKS = [ "HYPERLEDGER_FABRIC", @@ -37,7 +37,7 @@ class ManagedBlockchainNetwork(BaseModel): region, description=None, ): - self.st = datetime.datetime.now(datetime.timezone.utc) + self.creationdate = datetime.datetime.utcnow() self.id = id self.name = name self.description = description @@ -49,19 +49,34 @@ class ManagedBlockchainNetwork(BaseModel): self.region = region def to_dict(self): + # Format for list_networks + d = { + "Id": self.id, + "Name": self.name, + "Framework": self.framework, + "FrameworkVersion": self.frameworkversion, + "Status": "AVAILABLE", + "CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"), + } + if self.description is not None: + d["Description"] = self.description + return d + + def get_format(self): + # Format for get_networks frameworkattributes = { "Fabric": { "OrderingServiceEndpoint": "orderer.{0}.managedblockchain.{1}.amazonaws.com:30001".format( - self.id, self.region + self.id.lower(), self.region ), "Edition": self.frameworkconfiguration["Fabric"]["Edition"], } } vpcendpointname = "com.amazonaws.{0}.managedblockchain.{1}".format( - self.region, self.id + self.region, self.id.lower() ) - # Use iso_8601_datetime_with_milliseconds ? + d = { "Id": self.id, "Name": self.name, @@ -71,7 +86,7 @@ class ManagedBlockchainNetwork(BaseModel): "VpcEndpointServiceName": vpcendpointname, "VotingPolicy": self.voting_policy, "Status": "AVAILABLE", - "CreationDate": self.st.strftime("%Y-%m-%dT%H:%M:%S.%f%z"), + "CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"), } if self.description is not None: d["Description"] = self.description @@ -90,14 +105,21 @@ class ManagedBlockchainBackend(BaseBackend): def create_network( self, - json_body, + name, + framework, + frameworkversion, + frameworkconfiguration, + voting_policy, + member_configuration, + description=None, ): - name = json_body["Name"] - framework = json_body["Framework"] - frameworkversion = json_body["FrameworkVersion"] - frameworkconfiguration = json_body["FrameworkConfiguration"] - voting_policy = json_body["VotingPolicy"] - member_configuration = json_body["MemberConfiguration"] + self.name = name + self.framework = framework + self.frameworkversion = frameworkversion + self.frameworkconfiguration = frameworkconfiguration + self.voting_policy = voting_policy + self.member_configuration = member_configuration + self.description = description # Check framework if framework not in FRAMEWORKS: @@ -119,33 +141,32 @@ class ManagedBlockchainBackend(BaseBackend): ## Generate network ID network_id = get_network_id() + ## Generate memberid ID - will need to actually create member + member_id = get_member_id() + self.networks[network_id] = ManagedBlockchainNetwork( id=network_id, name=name, - framework=framework, - frameworkversion=frameworkversion, - frameworkconfiguration=frameworkconfiguration, - voting_policy=voting_policy, - member_configuration=member_configuration, + framework=self.framework, + frameworkversion=self.frameworkversion, + frameworkconfiguration=self.frameworkconfiguration, + voting_policy=self.voting_policy, + member_configuration=self.member_configuration, region=self.region_name, + description=self.description, ) + # Return the network and member ID + d = {"NetworkId": network_id, "MemberId": member_id} + return d + def list_networks(self): return self.networks.values() def get_network(self, network_id): - return self.networks[network_id] - + return self.networks.get(network_id) managedblockchain_backends = {} for region in Session().get_available_regions("managedblockchain"): managedblockchain_backends[region] = ManagedBlockchainBackend(region) -for region in Session().get_available_regions( - "managedblockchain", partition_name="aws-us-gov" -): - managedblockchain_backends[region] = ManagedBlockchainBackend(region) -for region in Session().get_available_regions( - "managedblockchain", partition_name="aws-cn" -): - managedblockchain_backends[region] = ManagedBlockchainBackend(region) diff --git a/moto/managedblockchain/responses.py b/moto/managedblockchain/responses.py index ff7c5ff5c..93084581d 100644 --- a/moto/managedblockchain/responses.py +++ b/moto/managedblockchain/responses.py @@ -5,7 +5,10 @@ from six.moves.urllib.parse import urlparse, parse_qs from moto.core.responses import BaseResponse from .models import managedblockchain_backends -from .utils import region_from_managedblckchain_url, networkid_from_managedblockchain_url +from .utils import ( + region_from_managedblckchain_url, + networkid_from_managedblockchain_url, +) class ManagedBlockchainResponse(BaseResponse): @@ -16,7 +19,9 @@ class ManagedBlockchainResponse(BaseResponse): @classmethod def network_response(clazz, request, full_url, headers): region_name = region_from_managedblckchain_url(full_url) - response_instance = ManagedBlockchainResponse(managedblockchain_backends[region_name]) + response_instance = ManagedBlockchainResponse( + managedblockchain_backends[region_name] + ) return response_instance._network_response(request, full_url, headers) def _network_response(self, request, full_url, headers): @@ -42,13 +47,35 @@ class ManagedBlockchainResponse(BaseResponse): return 200, headers, response def _network_response_post(self, json_body, querystring, headers): - self.backend.create_network(json_body) - return 201, headers, "" + name = json_body["Name"] + framework = json_body["Framework"] + frameworkversion = json_body["FrameworkVersion"] + frameworkconfiguration = json_body["FrameworkConfiguration"] + voting_policy = json_body["VotingPolicy"] + member_configuration = json_body["MemberConfiguration"] + + # Optional + description = None + if "Description" in json_body: + description = json_body["Description"] + + response = self.backend.create_network( + name, + framework, + frameworkversion, + frameworkconfiguration, + voting_policy, + member_configuration, + description, + ) + return 201, headers, json.dumps(response) @classmethod def networkid_response(clazz, request, full_url, headers): region_name = region_from_managedblckchain_url(full_url) - response_instance = ManagedBlockchainResponse(managedblockchain_backends[region_name]) + response_instance = ManagedBlockchainResponse( + managedblockchain_backends[region_name] + ) return response_instance._networkid_response(request, full_url, headers) def _networkid_response(self, request, full_url, headers): @@ -60,8 +87,6 @@ class ManagedBlockchainResponse(BaseResponse): def _networkid_response_get(self, network_id, headers): mbcnetwork = self.backend.get_network(network_id) - response = json.dumps( - {"Network": mbcnetwork.to_dict()} - ) + response = json.dumps({"Network": mbcnetwork.get_format()}) headers["content-type"] = "application/json" return 200, headers, response diff --git a/moto/managedblockchain/utils.py b/moto/managedblockchain/utils.py index 687b7990b..2a93d93f4 100644 --- a/moto/managedblockchain/utils.py +++ b/moto/managedblockchain/utils.py @@ -21,3 +21,9 @@ def get_network_id(): return "n-" + "".join( random.choice(string.ascii_uppercase + string.digits) for _ in range(26) ) + + +def get_member_id(): + return "m-" + "".join( + random.choice(string.ascii_uppercase + string.digits) for _ in range(26) + ) diff --git a/tests/test_managedblockchain/test_managedblockchain_networks.py b/tests/test_managedblockchain/test_managedblockchain_networks.py index 7bdc0ec59..f9c98676e 100644 --- a/tests/test_managedblockchain/test_managedblockchain_networks.py +++ b/tests/test_managedblockchain/test_managedblockchain_networks.py @@ -3,43 +3,46 @@ from __future__ import unicode_literals import boto3 import sure # noqa +from moto.managedblockchain.exceptions import BadRequestException from moto import mock_managedblockchain +default_frameworkconfiguration = {"Fabric": {"Edition": "STARTER"}} + +default_votingpolicy = { + "ApprovalThresholdPolicy": { + "ThresholdPercentage": 50, + "ProposalDurationInHours": 24, + "ThresholdComparator": "GREATER_THAN_OR_EQUAL_TO", + } +} + +default_memberconfiguration = { + "Name": "testmember1", + "Description": "Test Member 1", + "FrameworkConfiguration": { + "Fabric": {"AdminUsername": "admin", "AdminPassword": "Admin12345"} + }, + "LogPublishingConfiguration": { + "Fabric": {"CaLogs": {"Cloudwatch": {"Enabled": False}}} + }, +} + + @mock_managedblockchain def test_create_network(): conn = boto3.client("managedblockchain", region_name="us-east-1") - frameworkconfiguration = {"Fabric": {"Edition": "STARTER"}} - - votingpolicy = { - "ApprovalThresholdPolicy": { - "ThresholdPercentage": 50, - "ProposalDurationInHours": 24, - "ThresholdComparator": "GREATER_THAN_OR_EQUAL_TO", - } - } - - memberconfiguration = { - "Name": "testmember1", - "Description": "Test Member 1", - "FrameworkConfiguration": { - "Fabric": {"AdminUsername": "admin", "AdminPassword": "Admin12345"} - }, - "LogPublishingConfiguration": { - "Fabric": {"CaLogs": {"Cloudwatch": {"Enabled": False}}} - }, - } - - conn.create_network( + response = conn.create_network( Name="testnetwork1", - Description="Test Network 1", Framework="HYPERLEDGER_FABRIC", FrameworkVersion="1.2", - FrameworkConfiguration=frameworkconfiguration, - VotingPolicy=votingpolicy, - MemberConfiguration=memberconfiguration, + FrameworkConfiguration=default_frameworkconfiguration, + VotingPolicy=default_votingpolicy, + MemberConfiguration=default_memberconfiguration, ) + response["NetworkId"].should.match("n-[A-Z0-9]{26}") + response["MemberId"].should.match("m-[A-Z0-9]{26}") # Find in full list response = conn.list_networks() @@ -51,3 +54,80 @@ def test_create_network(): network_id = mbcnetworks[0]["Id"] response = conn.get_network(NetworkId=network_id) response["Network"]["Name"].should.equal("testnetwork1") + + +@mock_managedblockchain +def test_create_network_withopts(): + conn = boto3.client("managedblockchain", region_name="us-east-1") + + response = conn.create_network( + Name="testnetwork1", + Description="Test Network 1", + Framework="HYPERLEDGER_FABRIC", + FrameworkVersion="1.2", + FrameworkConfiguration=default_frameworkconfiguration, + VotingPolicy=default_votingpolicy, + MemberConfiguration=default_memberconfiguration, + ) + response["NetworkId"].should.match("n-[A-Z0-9]{26}") + response["MemberId"].should.match("m-[A-Z0-9]{26}") + + # Find in full list + response = conn.list_networks() + mbcnetworks = response["Networks"] + mbcnetworks.should.have.length_of(1) + mbcnetworks[0]["Description"].should.equal("Test Network 1") + + # Get network details + network_id = mbcnetworks[0]["Id"] + response = conn.get_network(NetworkId=network_id) + response["Network"]["Description"].should.equal("Test Network 1") + + +@mock_managedblockchain +def test_create_network_noframework(): + conn = boto3.client("managedblockchain", region_name="us-east-1") + + response = conn.create_network.when.called_with( + Name="testnetwork1", + Description="Test Network 1", + Framework="HYPERLEDGER_VINYL", + FrameworkVersion="1.2", + FrameworkConfiguration=default_frameworkconfiguration, + VotingPolicy=default_votingpolicy, + MemberConfiguration=default_memberconfiguration, + ).should.throw(Exception, "Invalid request body") + + +@mock_managedblockchain +def test_create_network_badframeworkver(): + conn = boto3.client("managedblockchain", region_name="us-east-1") + + response = conn.create_network.when.called_with( + Name="testnetwork1", + Description="Test Network 1", + Framework="HYPERLEDGER_FABRIC", + FrameworkVersion="1.X", + FrameworkConfiguration=default_frameworkconfiguration, + VotingPolicy=default_votingpolicy, + MemberConfiguration=default_memberconfiguration, + ).should.throw( + Exception, "Invalid version 1.X requested for framework HYPERLEDGER_FABRIC" + ) + + +@mock_managedblockchain +def test_create_network_badedition(): + conn = boto3.client("managedblockchain", region_name="us-east-1") + + frameworkconfiguration = {"Fabric": {"Edition": "SUPER"}} + + response = conn.create_network.when.called_with( + Name="testnetwork1", + Description="Test Network 1", + Framework="HYPERLEDGER_FABRIC", + FrameworkVersion="1.2", + FrameworkConfiguration=frameworkconfiguration, + VotingPolicy=default_votingpolicy, + MemberConfiguration=default_memberconfiguration, + ).should.throw(Exception, "Invalid request body")