From 1db6d8438810c6daab6f49f8f3b1a6f49d5ee2cf Mon Sep 17 00:00:00 2001 From: Sergei Kolyshkin Date: Tue, 8 Jan 2019 19:07:32 +0300 Subject: [PATCH] Resell V2 - add cross-region subnets List method Add crossregionsubnets List method and ListOpts structure. Provide tests and doc example. Also this commit fixes fields order for Get method fixtures. --- .../resell/v2/crossregionsubnets/doc.go | 10 ++ .../resell/v2/crossregionsubnets/requests.go | 32 +++++ .../v2/crossregionsubnets/requests_opts.go | 6 + .../v2/crossregionsubnets/testing/fixtures.go | 124 ++++++++++++++++-- .../testing/requests_test.go | 110 ++++++++++++++++ 5 files changed, 274 insertions(+), 8 deletions(-) create mode 100644 selvpcclient/resell/v2/crossregionsubnets/requests_opts.go diff --git a/selvpcclient/resell/v2/crossregionsubnets/doc.go b/selvpcclient/resell/v2/crossregionsubnets/doc.go index 7faa24c..5d90f83 100644 --- a/selvpcclient/resell/v2/crossregionsubnets/doc.go +++ b/selvpcclient/resell/v2/crossregionsubnets/doc.go @@ -9,5 +9,15 @@ Example of getting a single cross-region subnet referenced by its id log.Fatal(err) } fmt.Println(crossRegionSubnet) + +Example of getting all cross-region subnets + + allCrossRegionSubnets, _, err := crossregionsubnets.List(ctx, resellClient, crossregionsubnets.ListOpts{}) + if err != nil { + log.Fatal(err) + } + for _, crossRegionSubnet := range allCrossRegionSubnets { + fmt.Println(crossRegionSubnet) + } */ package crossregionsubnets diff --git a/selvpcclient/resell/v2/crossregionsubnets/requests.go b/selvpcclient/resell/v2/crossregionsubnets/requests.go index 2c58380..0dba1a7 100644 --- a/selvpcclient/resell/v2/crossregionsubnets/requests.go +++ b/selvpcclient/resell/v2/crossregionsubnets/requests.go @@ -32,3 +32,35 @@ func Get(ctx context.Context, client *selvpcclient.ServiceClient, id string) (*C return result.CrossRegionSubnet, responseResult, nil } + +// List gets a list of cross-region subnets in the current domain. +func List(ctx context.Context, client *selvpcclient.ServiceClient, opts ListOpts) ([]*CrossRegionSubnet, *selvpcclient.ResponseResult, error) { + url := strings.Join([]string{client.Endpoint, resourceURL}, "/") + + queryParams, err := selvpcclient.BuildQueryParameters(opts) + if err != nil { + return nil, nil, err + } + if queryParams != "" { + url = strings.Join([]string{url, queryParams}, "?") + } + + responseResult, err := client.DoRequest(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, nil, err + } + if responseResult.Err != nil { + return nil, responseResult, responseResult.Err + } + + // Extract cross-region subnets from the response body. + var result struct { + CrossRegionSubnets []*CrossRegionSubnet `json:"cross_region_subnets"` + } + err = responseResult.ExtractResult(&result) + if err != nil { + return nil, responseResult, err + } + + return result.CrossRegionSubnets, responseResult, nil +} diff --git a/selvpcclient/resell/v2/crossregionsubnets/requests_opts.go b/selvpcclient/resell/v2/crossregionsubnets/requests_opts.go new file mode 100644 index 0000000..994830e --- /dev/null +++ b/selvpcclient/resell/v2/crossregionsubnets/requests_opts.go @@ -0,0 +1,6 @@ +package crossregionsubnets + +// ListOpts represents options for the List request. +type ListOpts struct { + Detailed bool `param:"detailed"` +} diff --git a/selvpcclient/resell/v2/crossregionsubnets/testing/fixtures.go b/selvpcclient/resell/v2/crossregionsubnets/testing/fixtures.go index 9ef8cd3..586b364 100644 --- a/selvpcclient/resell/v2/crossregionsubnets/testing/fixtures.go +++ b/selvpcclient/resell/v2/crossregionsubnets/testing/fixtures.go @@ -66,42 +66,150 @@ var TestGetCrossRegionSubnetResponse = &crossregionsubnets.CrossRegionSubnet{ Status: "ACTIVE", Servers: []servers.Server{ { - Status: "ACTIVE", - Updated: crossregionSubnetServerTimeStamp, ID: "22170dcf-2e58-49b7-9115-951b84d366f6", Name: "Node01", - }, - { Status: "ACTIVE", Updated: crossregionSubnetServerTimeStamp, + }, + { ID: "df842202-fdcc-490e-b92a-6e252e5577c7", Name: "Node02", + Status: "ACTIVE", + Updated: crossregionSubnetServerTimeStamp, }, }, Subnets: []subnets.Subnet{ { ID: 10, + Region: "ru-1", CIDR: "192.168.200.0/24", - ProjectID: "b63ab68796e34858befb8fa2a8b1e12a", NetworkID: "78c1cbe1-c34d-4685-be2d-a877a1b1dec4", SubnetID: "7db1255f-2545-4b8a-9446-22608c0f6cb8", - Region: "ru-1", + ProjectID: "b63ab68796e34858befb8fa2a8b1e12a", VLANID: 1003, VTEPIPAddress: "10.10.0.101", }, { ID: 20, + Region: "ru-3", CIDR: "192.168.200.0/24", - ProjectID: "b63ab68796e34858befb8fa2a8b1e12a", NetworkID: "67f7ab15-9424-4b50-999a-1c4de12372ec", SubnetID: "66ee047b-c699-4d62-9b64-363d2d77f021", - Region: "ru-3", + ProjectID: "b63ab68796e34858befb8fa2a8b1e12a", VLANID: 1003, VTEPIPAddress: "10.10.0.201", }, }, } +// TestListCrossRegionSubnetsResponseRaw represents a raw response from the List request. +const TestListCrossRegionSubnetsResponseRaw = ` +{ + "cross_region_subnets": [ + { + "id": 12, + "cidr": "192.168.200.0/24", + "vlan_id": 1003, + "status": "ACTIVE", + "servers": [ + { + "status": "ACTIVE", + "updated": "2019-01-04T08:09:43Z", + "id": "22170dcf-2e58-49b7-9115-951b84d366f6", + "name": "Node01" + }, + { + "status": "ACTIVE", + "updated": "2019-01-04T08:09:43Z", + "id": "df842202-fdcc-490e-b92a-6e252e5577c7", + "name": "Node02" + } + ], + "subnets": [ + { + "id": 10, + "vlan_id": 1003, + "cidr": "192.168.200.0/24", + "project_id": "b63ab68796e34858befb8fa2a8b1e12a", + "network_id": "78c1cbe1-c34d-4685-be2d-a877a1b1dec4", + "subnet_id": "7db1255f-2545-4b8a-9446-22608c0f6cb8", + "region": "ru-1", + "vtep_ip_address": "10.10.0.101" + }, + { + "id": 20, + "vlan_id": 1003, + "cidr": "192.168.200.0/24", + "project_id": "b63ab68796e34858befb8fa2a8b1e12a", + "network_id": "67f7ab15-9424-4b50-999a-1c4de12372ec", + "subnet_id": "66ee047b-c699-4d62-9b64-363d2d77f021", + "region": "ru-3", + "vtep_ip_address": "10.10.0.201" + } + ] + } + ] +} +` + +// TestListCrossRegionSubnetsResponse represents an unmarshalled TestListCrossRegionSubnetsResponseRaw +var TestListCrossRegionSubnetsResponse = []*crossregionsubnets.CrossRegionSubnet{ + { + ID: 12, + CIDR: "192.168.200.0/24", + VLANID: 1003, + Status: "ACTIVE", + Servers: []servers.Server{ + { + ID: "22170dcf-2e58-49b7-9115-951b84d366f6", + Name: "Node01", + Status: "ACTIVE", + Updated: crossregionSubnetServerTimeStamp, + }, + { + ID: "df842202-fdcc-490e-b92a-6e252e5577c7", + Name: "Node02", + Status: "ACTIVE", + Updated: crossregionSubnetServerTimeStamp, + }, + }, + Subnets: []subnets.Subnet{ + { + ID: 10, + Region: "ru-1", + CIDR: "192.168.200.0/24", + NetworkID: "78c1cbe1-c34d-4685-be2d-a877a1b1dec4", + SubnetID: "7db1255f-2545-4b8a-9446-22608c0f6cb8", + ProjectID: "b63ab68796e34858befb8fa2a8b1e12a", + VLANID: 1003, + VTEPIPAddress: "10.10.0.101", + }, + { + ID: 20, + Region: "ru-3", + CIDR: "192.168.200.0/24", + NetworkID: "67f7ab15-9424-4b50-999a-1c4de12372ec", + SubnetID: "66ee047b-c699-4d62-9b64-363d2d77f021", + ProjectID: "b63ab68796e34858befb8fa2a8b1e12a", + VLANID: 1003, + VTEPIPAddress: "10.10.0.201", + }, + }, + }, +} + +// TestManyCrossRegionSubnetsInvalidResponseRaw represents a raw invalid response with +// several cross-region subnets. +const TestManyCrossRegionSubnetsInvalidResponseRaw = ` +{ + "cross_region_subnets": [ + { + "id": "b63ab68796e34858befb8fa2a8b1e12a" + } + ] +} +` + // TestSingleCrossRegionSubnetInvalidResponseRaw represents a raw invalid response with // a single cross-region subnet. const TestSingleCrossRegionSubnetInvalidResponseRaw = ` diff --git a/selvpcclient/resell/v2/crossregionsubnets/testing/requests_test.go b/selvpcclient/resell/v2/crossregionsubnets/testing/requests_test.go index 897f009..09a4709 100644 --- a/selvpcclient/resell/v2/crossregionsubnets/testing/requests_test.go +++ b/selvpcclient/resell/v2/crossregionsubnets/testing/requests_test.go @@ -119,3 +119,113 @@ func TestGetCrossRegionSubnetUnmarshalError(t *testing.T) { t.Fatal("expected error from the Get method") } } + +func TestListCrossRegionSubnets(t *testing.T) { + endpointCalled := false + + testEnv := testutils.SetupTestEnv() + defer testEnv.TearDownTestEnv() + testEnv.NewTestResellV2Client() + testutils.HandleReqWithoutBody(t, &testutils.HandleReqOpts{ + Mux: testEnv.Mux, + URL: "/resell/v2/cross_region_subnets", + RawResponse: TestListCrossRegionSubnetsResponseRaw, + Method: http.MethodGet, + Status: http.StatusOK, + CallFlag: &endpointCalled, + }) + + ctx := context.Background() + actual, _, err := crossregionsubnets.List(ctx, testEnv.Client, crossregionsubnets.ListOpts{}) + if err != nil { + t.Fatal(err) + } + + expected := TestListCrossRegionSubnetsResponse + + if !endpointCalled { + t.Fatal("endpoint wasn't called") + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("expected %#v, but got %#v", expected, actual) + } +} + +func TestListCrossRegionSubnetsHTTPError(t *testing.T) { + endpointCalled := false + + testEnv := testutils.SetupTestEnv() + defer testEnv.TearDownTestEnv() + testEnv.NewTestResellV2Client() + testutils.HandleReqWithoutBody(t, &testutils.HandleReqOpts{ + Mux: testEnv.Mux, + URL: "/resell/v2/cross_region_subnets", + RawResponse: TestListCrossRegionSubnetsResponseRaw, + Method: http.MethodGet, + Status: http.StatusBadGateway, + CallFlag: &endpointCalled, + }) + + ctx := context.Background() + allCrossRegionSubnets, httpResponse, err := crossregionsubnets.List(ctx, testEnv.Client, crossregionsubnets.ListOpts{}) + + if !endpointCalled { + t.Fatal("endpoint wasn't called") + } + if allCrossRegionSubnets != nil { + t.Fatal("expected no cross-region subnets from the List method") + } + if err == nil { + t.Fatal("expected error from the List method") + } + if httpResponse.StatusCode != http.StatusBadGateway { + t.Fatalf("expected %d status in the HTTP response, but got %d", + http.StatusBadGateway, httpResponse.StatusCode) + } +} + +func TestListCrossRegionSubnetsTimeoutError(t *testing.T) { + testEnv := testutils.SetupTestEnv() + testEnv.Server.Close() + defer testEnv.TearDownTestEnv() + testEnv.NewTestResellV2Client() + + ctx := context.Background() + allCrossRegionSubnets, _, err := crossregionsubnets.List(ctx, testEnv.Client, crossregionsubnets.ListOpts{}) + + if allCrossRegionSubnets != nil { + t.Fatal("expected no cross-region subnets from the List method") + } + if err == nil { + t.Fatal("expected error from the List method") + } +} + +func TestListCrossRegionSubnetsUnmarshalError(t *testing.T) { + endpointCalled := false + + testEnv := testutils.SetupTestEnv() + defer testEnv.TearDownTestEnv() + testEnv.NewTestResellV2Client() + testutils.HandleReqWithoutBody(t, &testutils.HandleReqOpts{ + Mux: testEnv.Mux, + URL: "/resell/v2/cross_region_subnets", + RawResponse: TestManyCrossRegionSubnetsInvalidResponseRaw, + Method: http.MethodGet, + Status: http.StatusOK, + CallFlag: &endpointCalled, + }) + + ctx := context.Background() + allCrossRegionSubnets, _, err := crossregionsubnets.List(ctx, testEnv.Client, crossregionsubnets.ListOpts{}) + + if !endpointCalled { + t.Fatal("endpoint wasn't called") + } + if allCrossRegionSubnets != nil { + t.Fatal("expected no cross-region subnets from the List method") + } + if err == nil { + t.Fatal("expected error from the List method") + } +}