roll out vpc functionality

This commit is contained in:
Jason Heimann 2019-04-02 13:54:24 -07:00
parent 87f69d1b82
commit 6760694045
9 changed files with 492 additions and 9 deletions

View File

@ -60,6 +60,7 @@ type Droplet struct {
Kernel *Kernel `json:"kernel,omitempty"`
Tags []string `json:"tags,omitempty"`
VolumeIDs []string `json:"volume_ids"`
VPCUUID string `json:"vpc_uuid,omitempty"`
}
// PublicIPv4 returns the public IPv4 address for the Droplet.
@ -222,6 +223,7 @@ type DropletCreateRequest struct {
UserData string `json:"user_data,omitempty"`
Volumes []DropletCreateVolume `json:"volumes,omitempty"`
Tags []string `json:"tags"`
VPCUUID string `json:"vpc_uuid,omitempty"`
}
// DropletMultiCreateRequest is a request to create multiple Droplets.
@ -237,6 +239,7 @@ type DropletMultiCreateRequest struct {
Monitoring bool `json:"monitoring"`
UserData string `json:"user_data,omitempty"`
Tags []string `json:"tags"`
VPCUUID string `json:"vpc_uuid,omitempty"`
}
func (d DropletCreateRequest) String() string {

View File

@ -152,7 +152,8 @@ func TestDroplets_Create(t *testing.T) {
{ID: "hello-im-another-volume"},
{Name: "hello-im-still-a-volume", ID: "should be ignored due to Name"},
},
Tags: []string{"one", "two"},
Tags: []string{"one", "two"},
VPCUUID: "880b7f98-f062-404d-b33c-458d545696f6",
}
mux.HandleFunc("/v2/droplets", func(w http.ResponseWriter, r *http.Request) {
@ -171,8 +172,26 @@ func TestDroplets_Create(t *testing.T) {
map[string]interface{}{"id": "hello-im-another-volume"},
map[string]interface{}{"name": "hello-im-still-a-volume"},
},
"tags": []interface{}{"one", "two"},
"tags": []interface{}{"one", "two"},
"vpc_uuid": "880b7f98-f062-404d-b33c-458d545696f6",
}
jsonBlob := `
{
"droplet": {
"id": 1,
"vpc_uuid": "880b7f98-f062-404d-b33c-458d545696f6"
},
"links": {
"actions": [
{
"id": 1,
"href": "http://example.com",
"rel": "create"
}
]
}
}
`
var v map[string]interface{}
err := json.NewDecoder(r.Body).Decode(&v)
@ -184,7 +203,7 @@ func TestDroplets_Create(t *testing.T) {
t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected)
}
fmt.Fprintf(w, `{"droplet":{"id":1}, "links":{"actions": [{"id": 1, "href": "http://example.com", "rel": "create"}]}}`)
fmt.Fprintf(w, jsonBlob)
})
droplet, resp, err := client.Droplets.Create(ctx, createRequest)
@ -196,6 +215,11 @@ func TestDroplets_Create(t *testing.T) {
t.Errorf("expected id '%d', received '%d'", 1, id)
}
vpcid := "880b7f98-f062-404d-b33c-458d545696f6"
if id := droplet.VPCUUID; id != vpcid {
t.Errorf("expected VPC uuid '%s', received '%s'", vpcid, id)
}
if a := resp.Links.Actions[0]; a.ID != 1 {
t.Errorf("expected action id '%d', received '%d'", 1, a.ID)
}
@ -212,7 +236,8 @@ func TestDroplets_CreateMultiple(t *testing.T) {
Image: DropletCreateImage{
ID: 1,
},
Tags: []string{"one", "two"},
Tags: []string{"one", "two"},
VPCUUID: "880b7f98-f062-404d-b33c-458d545696f6",
}
mux.HandleFunc("/v2/droplets", func(w http.ResponseWriter, r *http.Request) {
@ -227,7 +252,31 @@ func TestDroplets_CreateMultiple(t *testing.T) {
"private_networking": false,
"monitoring": false,
"tags": []interface{}{"one", "two"},
"vpc_uuid": "880b7f98-f062-404d-b33c-458d545696f6",
}
jsonBlob := `
{
"droplets": [
{
"id": 1,
"vpc_uuid": "880b7f98-f062-404d-b33c-458d545696f6"
},
{
"id": 2,
"vpc_uuid": "880b7f98-f062-404d-b33c-458d545696f6"
}
],
"links": {
"actions": [
{
"id": 1,
"href": "http://example.com",
"rel": "multiple_create"
}
]
}
}
`
var v map[string]interface{}
err := json.NewDecoder(r.Body).Decode(&v)
@ -239,7 +288,7 @@ func TestDroplets_CreateMultiple(t *testing.T) {
t.Errorf("Request body = %#v, expected %#v", v, expected)
}
fmt.Fprintf(w, `{"droplets":[{"id":1},{"id":2}], "links":{"actions": [{"id": 1, "href": "http://example.com", "rel": "multiple_create"}]}}`)
fmt.Fprintf(w, jsonBlob)
})
droplets, resp, err := client.Droplets.CreateMultiple(ctx, createRequest)
@ -250,9 +299,16 @@ func TestDroplets_CreateMultiple(t *testing.T) {
if id := droplets[0].ID; id != 1 {
t.Errorf("expected id '%d', received '%d'", 1, id)
}
if id := droplets[1].ID; id != 2 {
t.Errorf("expected id '%d', received '%d'", 1, id)
t.Errorf("expected id '%d', received '%d'", 2, id)
}
vpcid := "880b7f98-f062-404d-b33c-458d545696f6"
if id := droplets[0].VPCUUID; id != vpcid {
t.Errorf("expected VPC uuid '%s', received '%s'", vpcid, id)
}
if id := droplets[1].VPCUUID; id != vpcid {
t.Errorf("expected VPC uuid '%s', received '%s'", vpcid, id)
}
if a := resp.Links.Actions[0]; a.ID != 1 {

View File

@ -66,6 +66,7 @@ type Client struct {
Projects ProjectsService
Kubernetes KubernetesService
Databases DatabasesService
VPCs VPCsService
// Optional function called after every successful request made to the DO APIs
onRequestCompleted RequestCompletionCallback
@ -181,6 +182,7 @@ func NewClient(httpClient *http.Client) *Client {
c.Tags = &TagsServiceOp{client: c}
c.Kubernetes = &KubernetesServiceOp{client: c}
c.Databases = &DatabasesServiceOp{client: c}
c.VPCs = &VPCsServiceOp{client: c}
return c
}

View File

@ -16,7 +16,7 @@ const (
kubernetesOptionsPath = kubernetesBasePath + "/options"
)
// KubernetesService is an interface for interfacing with the kubernetes endpoints
// KubernetesService is an interface for interfacing with the Kubernetes endpoints
// of the DigitalOcean API.
// See: https://developers.digitalocean.com/documentation/v2#kubernetes
type KubernetesService interface {
@ -50,6 +50,7 @@ type KubernetesClusterCreateRequest struct {
RegionSlug string `json:"region,omitempty"`
VersionSlug string `json:"version,omitempty"`
Tags []string `json:"tags,omitempty"`
VPCUUID string `json:"vpc_uuid,omitempty"`
NodePools []*KubernetesNodePoolCreateRequest `json:"node_pools,omitempty"`
}
@ -94,6 +95,7 @@ type KubernetesCluster struct {
IPv4 string `json:"ipv4,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
Tags []string `json:"tags,omitempty"`
VPCUUID string `json:"vpc_uuid,omitempty"`
NodePools []*KubernetesNodePool `json:"node_pools,omitempty"`

View File

@ -26,6 +26,7 @@ func TestKubernetesClusters_ListClusters(t *testing.T) {
ServiceSubnet: "10.245.0.0/16",
IPv4: "",
Tags: []string(nil),
VPCUUID: "880b7f98-f062-404d-b33c-458d545696f6",
Status: &KubernetesClusterStatus{
State: KubernetesClusterStatusRunning,
},
@ -64,6 +65,7 @@ func TestKubernetesClusters_ListClusters(t *testing.T) {
ClusterSubnet: "10.244.0.0/16",
ServiceSubnet: "10.245.0.0/16",
IPv4: "1.2.3.4",
VPCUUID: "880b7f98-f062-404d-b33c-458d545696f7",
Status: &KubernetesClusterStatus{
State: KubernetesClusterStatusRunning,
},
@ -107,6 +109,7 @@ func TestKubernetesClusters_ListClusters(t *testing.T) {
"service_subnet": "10.245.0.0/16",
"ipv4": "",
"tags": null,
"vpc_uuid": "880b7f98-f062-404d-b33c-458d545696f6",
"status": {
"state": "running"
},
@ -155,6 +158,7 @@ func TestKubernetesClusters_ListClusters(t *testing.T) {
"status": {
"state": "running"
},
"vpc_uuid": "880b7f98-f062-404d-b33c-458d545696f7",
"node_pools": [
{
"id": "deadbeef-dead-beef-dead-deadbeefb4b3",
@ -214,6 +218,7 @@ func TestKubernetesClusters_Get(t *testing.T) {
ClusterSubnet: "10.244.0.0/16",
ServiceSubnet: "10.245.0.0/16",
IPv4: "1.2.3.4",
VPCUUID: "880b7f98-f062-404d-b33c-458d545696f6",
Status: &KubernetesClusterStatus{
State: KubernetesClusterStatusRunning,
},
@ -255,6 +260,7 @@ func TestKubernetesClusters_Get(t *testing.T) {
"service_subnet": "10.245.0.0/16",
"ipv4": "1.2.3.4",
"tags": null,
"vpc_uuid": "880b7f98-f062-404d-b33c-458d545696f6",
"status": {
"state": "running"
},
@ -332,6 +338,7 @@ func TestKubernetesClusters_Create(t *testing.T) {
ClusterSubnet: "10.244.0.0/16",
ServiceSubnet: "10.245.0.0/16",
Tags: []string{"cluster-tag-1", "cluster-tag-2"},
VPCUUID: "880b7f98-f062-404d-b33c-458d545696f6",
NodePools: []*KubernetesNodePool{
&KubernetesNodePool{
ID: "8d91899c-0739-4a1a-acc5-deadbeefbb8a",
@ -347,6 +354,7 @@ func TestKubernetesClusters_Create(t *testing.T) {
RegionSlug: want.RegionSlug,
VersionSlug: want.VersionSlug,
Tags: want.Tags,
VPCUUID: want.VPCUUID,
NodePools: []*KubernetesNodePoolCreateRequest{
&KubernetesNodePoolCreateRequest{
Size: want.NodePools[0].Size,
@ -370,6 +378,7 @@ func TestKubernetesClusters_Create(t *testing.T) {
"cluster-tag-1",
"cluster-tag-2"
],
"vpc_uuid": "880b7f98-f062-404d-b33c-458d545696f6",
"node_pools": [
{
"id": "8d91899c-0739-4a1a-acc5-deadbeefbb8a",
@ -415,6 +424,7 @@ func TestKubernetesClusters_Update(t *testing.T) {
ClusterSubnet: "10.244.0.0/16",
ServiceSubnet: "10.245.0.0/16",
Tags: []string{"cluster-tag-1", "cluster-tag-2"},
VPCUUID: "880b7f98-f062-404d-b33c-458d545696f6",
NodePools: []*KubernetesNodePool{
&KubernetesNodePool{
ID: "8d91899c-0739-4a1a-acc5-deadbeefbb8a",
@ -443,6 +453,7 @@ func TestKubernetesClusters_Update(t *testing.T) {
"cluster-tag-1",
"cluster-tag-2"
],
"vpc_uuid": "880b7f98-f062-404d-b33c-458d545696f6",
"node_pools": [
{
"id": "8d91899c-0739-4a1a-acc5-deadbeefbb8a",

View File

@ -43,6 +43,7 @@ type LoadBalancer struct {
Tags []string `json:"tags,omitempty"`
RedirectHttpToHttps bool `json:"redirect_http_to_https,omitempty"`
EnableProxyProtocol bool `json:"enable_proxy_protocol,omitempty"`
VPCUUID string `json:"vpc_uuid,omitempty"`
}
// String creates a human-readable description of a LoadBalancer.
@ -66,6 +67,7 @@ func (l LoadBalancer) AsRequest() *LoadBalancerRequest {
RedirectHttpToHttps: l.RedirectHttpToHttps,
EnableProxyProtocol: l.EnableProxyProtocol,
HealthCheck: l.HealthCheck,
VPCUUID: l.VPCUUID,
}
if l.HealthCheck != nil {
@ -138,6 +140,7 @@ type LoadBalancerRequest struct {
Tags []string `json:"tags,omitempty"`
RedirectHttpToHttps bool `json:"redirect_http_to_https,omitempty"`
EnableProxyProtocol bool `json:"enable_proxy_protocol,omitempty"`
VPCUUID string `json:"vpc_uuid,omitempty"`
}
// String creates a human-readable description of a LoadBalancerRequest.

View File

@ -144,7 +144,8 @@ var lbCreateJSONResponse = `
2,
21
],
"redirect_http_to_https":true
"redirect_http_to_https":true,
"vpc_uuid":"880b7f98-f062-404d-b33c-458d545696f6"
}
}
`
@ -369,6 +370,7 @@ func TestLoadBalancers_Create(t *testing.T) {
Tags: []string{"my-tag"},
DropletIDs: []int{2, 21},
RedirectHttpToHttps: true,
VPCUUID: "880b7f98-f062-404d-b33c-458d545696f6",
}
path := "/v2/load_balancers"
@ -438,6 +440,7 @@ func TestLoadBalancers_Create(t *testing.T) {
Tags: []string{"my-tag"},
DropletIDs: []int{2, 21},
RedirectHttpToHttps: true,
VPCUUID: "880b7f98-f062-404d-b33c-458d545696f6",
}
assert.Equal(t, expected, loadBalancer)
@ -825,6 +828,7 @@ func TestLoadBalancers_AsRequest(t *testing.T) {
},
RedirectHttpToHttps: true,
EnableProxyProtocol: true,
VPCUUID: "880b7f98-f062-404d-b33c-458d545696f6",
}
lb.DropletIDs = make([]int, 1, 2)
lb.DropletIDs[0] = 12345
@ -863,6 +867,7 @@ func TestLoadBalancers_AsRequest(t *testing.T) {
DropletIDs: []int{12345},
RedirectHttpToHttps: true,
EnableProxyProtocol: true,
VPCUUID: "880b7f98-f062-404d-b33c-458d545696f6",
}
r := lb.AsRequest()

183
vpcs.go Normal file
View File

@ -0,0 +1,183 @@
package godo
import (
"context"
"net/http"
"time"
)
const vpcsBasePath = "/v2/vpcs"
// VPCsService is an interface for managing Virtual Private Cloud configurations with the
// DigitalOcean API.
// See: https://developers.digitalocean.com/documentation/v2#vpcs
type VPCsService interface {
Create(context.Context, *VPCCreateRequest) (*VPC, *Response, error)
Get(context.Context, string) (*VPC, *Response, error)
List(context.Context, *ListOptions) ([]*VPC, *Response, error)
Update(context.Context, string, *VPCUpdateRequest) (*VPC, *Response, error)
Set(context.Context, string, ...VPCSetField) (*VPC, *Response, error)
Delete(context.Context, string) (*Response, error)
}
var _ VPCsService = &VPCsServiceOp{}
// VPCsServiceOp interfaces with VPC endpoints in the DigitalOcean API.
type VPCsServiceOp struct {
client *Client
}
// VPCCreateRequest represents a request to create a Virtual Private Cloud.
type VPCCreateRequest struct {
Name string `json:"name,omitempty"`
RegionSlug string `json:"region,omitempty"`
}
// VPCUpdateRequest represents a request to update a Virtual Private Cloud.
type VPCUpdateRequest struct {
Name string `json:"name,omitempty"`
}
// VPCSetField allows one to set individual fields within a VPC configuration.
type VPCSetField interface {
vpcSetField(map[string]interface{})
}
// VPCSetName is used when one want to set the `name` field of a VPC.
// Ex.: VPCs.Set(..., VPCSetName("new-name"))
type VPCSetName string
// VPC represents a DigitalOcean Virtual Private Cloud configuration.
type VPC struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
RegionSlug string `json:"region,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"`
Default bool `json:"default,omitempty"`
}
type vpcRoot struct {
VPC *VPC `json:"vpc"`
}
type vpcsRoot struct {
VPCs []*VPC `json:"vpcs"`
Links *Links `json:"links"`
}
// Get returns the details of a Virtual Private Cloud.
func (v *VPCsServiceOp) Get(ctx context.Context, id string) (*VPC, *Response, error) {
path := vpcsBasePath + "/" + id
req, err := v.client.NewRequest(ctx, http.MethodGet, path, nil)
if err != nil {
return nil, nil, err
}
root := new(vpcRoot)
resp, err := v.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
return root.VPC, resp, nil
}
// Create creates a new Virtual Private Cloud.
func (v *VPCsServiceOp) Create(ctx context.Context, create *VPCCreateRequest) (*VPC, *Response, error) {
path := vpcsBasePath
req, err := v.client.NewRequest(ctx, http.MethodPost, path, create)
if err != nil {
return nil, nil, err
}
root := new(vpcRoot)
resp, err := v.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
return root.VPC, resp, nil
}
// List returns a list of the caller's VPCs, with optional pagination.
func (v *VPCsServiceOp) List(ctx context.Context, opt *ListOptions) ([]*VPC, *Response, error) {
path, err := addOptions(vpcsBasePath, opt)
if err != nil {
return nil, nil, err
}
req, err := v.client.NewRequest(ctx, http.MethodGet, path, nil)
if err != nil {
return nil, nil, err
}
root := new(vpcsRoot)
resp, err := v.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
if l := root.Links; l != nil {
resp.Links = l
}
return root.VPCs, resp, nil
}
// Update updates a Virtual Private Cloud's properties.
func (v *VPCsServiceOp) Update(ctx context.Context, id string, update *VPCUpdateRequest) (*VPC, *Response, error) {
path := vpcsBasePath + "/" + id
req, err := v.client.NewRequest(ctx, http.MethodPut, path, update)
if err != nil {
return nil, nil, err
}
root := new(vpcRoot)
resp, err := v.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
return root.VPC, resp, nil
}
func (n VPCSetName) vpcSetField(in map[string]interface{}) {
in["name"] = n
}
// Set updates specific properties of a Virtual Private Cloud.
func (v *VPCsServiceOp) Set(ctx context.Context, id string, fields ...VPCSetField) (*VPC, *Response, error) {
path := vpcsBasePath + "/" + id
update := make(map[string]interface{}, len(fields))
for _, field := range fields {
field.vpcSetField(update)
}
req, err := v.client.NewRequest(ctx, http.MethodPatch, path, update)
if err != nil {
return nil, nil, err
}
root := new(vpcRoot)
resp, err := v.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
return root.VPC, resp, nil
}
// Delete deletes a Virtual Private Cloud. There is no way to recover a VPC once it has been
// destroyed.
func (v *VPCsServiceOp) Delete(ctx context.Context, id string) (*Response, error) {
path := vpcsBasePath + "/" + id
req, err := v.client.NewRequest(ctx, http.MethodDelete, path, nil)
if err != nil {
return nil, err
}
resp, err := v.client.Do(ctx, req, nil)
if err != nil {
return resp, err
}
return resp, nil
}

218
vpcs_test.go Normal file
View File

@ -0,0 +1,218 @@
package godo
import (
"encoding/json"
"fmt"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/require"
)
var vTestObj = &VPC{
ID: "880b7f98-f062-404d-b33c-458d545696f6",
Name: "my-new-vpc",
RegionSlug: "s2r7",
CreatedAt: time.Date(2019, 2, 4, 21, 48, 40, 995304079, time.UTC),
Default: false,
}
var vTestJSON = `
{
"id":"880b7f98-f062-404d-b33c-458d545696f6",
"name":"my-new-vpc",
"region":"s2r7",
"created_at":"2019-02-04T21:48:40.995304079Z",
"default":false
}
`
func TestVPCs_Get(t *testing.T) {
setup()
defer teardown()
svc := client.VPCs
path := "/v2/vpcs"
want := vTestObj
id := "880b7f98-f062-404d-b33c-458d545696f6"
jsonBlob := `
{
"vpc":
` + vTestJSON + `
}
`
mux.HandleFunc(path+"/"+id, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodGet)
fmt.Fprint(w, jsonBlob)
})
got, _, err := svc.Get(ctx, id)
require.NoError(t, err)
require.Equal(t, want, got)
}
func TestVPCs_List(t *testing.T) {
setup()
defer teardown()
svc := client.VPCs
path := "/v2/vpcs"
want := []*VPC{
vTestObj,
}
links := &Links{
Pages: &Pages{
Last: "http://localhost/v2/vpcs?page=3&per_page=1",
Next: "http://localhost/v2/vpcs?page=2&per_page=1",
},
}
jsonBlob := `
{
"vpcs": [
` + vTestJSON + `
],
"links": {
"pages": {
"last": "http://localhost/v2/vpcs?page=3&per_page=1",
"next": "http://localhost/v2/vpcs?page=2&per_page=1"
}
},
"meta": {"total": 3}
}
`
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodGet)
fmt.Fprint(w, jsonBlob)
})
got, resp, err := svc.List(ctx, nil)
require.NoError(t, err)
require.Equal(t, want, got)
require.Equal(t, resp.Links, links)
}
func TestVPCs_Create(t *testing.T) {
setup()
defer teardown()
svc := client.VPCs
path := "/v2/vpcs"
want := vTestObj
req := &VPCCreateRequest{
Name: "my-new-vpc",
RegionSlug: "s2r7",
}
jsonBlob := `
{
"vpc":
` + vTestJSON + `
}
`
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
c := new(VPCCreateRequest)
err := json.NewDecoder(r.Body).Decode(c)
if err != nil {
t.Fatal(err)
}
testMethod(t, r, http.MethodPost)
require.Equal(t, c, req)
fmt.Fprint(w, jsonBlob)
})
got, _, err := svc.Create(ctx, req)
require.NoError(t, err)
require.Equal(t, want, got)
}
func TestVPCs_Update(t *testing.T) {
setup()
defer teardown()
svc := client.VPCs
path := "/v2/vpcs"
want := vTestObj
id := "880b7f98-f062-404d-b33c-458d545696f6"
req := &VPCUpdateRequest{
Name: "my-new-vpc",
}
jsonBlob := `
{
"vpc":
` + vTestJSON + `
}
`
mux.HandleFunc(path+"/"+id, func(w http.ResponseWriter, r *http.Request) {
c := new(VPCUpdateRequest)
err := json.NewDecoder(r.Body).Decode(c)
if err != nil {
t.Fatal(err)
}
testMethod(t, r, http.MethodPut)
require.Equal(t, c, req)
fmt.Fprint(w, jsonBlob)
})
got, _, err := svc.Update(ctx, id, req)
require.NoError(t, err)
require.Equal(t, want, got)
}
func TestVPCs_Set(t *testing.T) {
setup()
defer teardown()
type setRequest struct {
Name string `json:"name"`
}
svc := client.VPCs
path := "/v2/vpcs"
want := vTestObj
id := "880b7f98-f062-404d-b33c-458d545696f6"
name := "my-new-vpc"
req := &setRequest{Name: name}
jsonBlob := `
{
"vpc":
` + vTestJSON + `
}
`
mux.HandleFunc(path+"/"+id, func(w http.ResponseWriter, r *http.Request) {
c := new(setRequest)
err := json.NewDecoder(r.Body).Decode(c)
if err != nil {
t.Fatal(err)
}
testMethod(t, r, http.MethodPatch)
require.Equal(t, c, req)
fmt.Fprint(w, jsonBlob)
})
got, _, err := svc.Set(ctx, id, VPCSetName(name))
require.NoError(t, err)
require.Equal(t, want, got)
}
func TestVPCs_Delete(t *testing.T) {
setup()
defer teardown()
svc := client.VPCs
path := "/v2/vpcs"
id := "880b7f98-f062-404d-b33c-458d545696f6"
mux.HandleFunc(path+"/"+id, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodDelete)
})
_, err := svc.Delete(ctx, id)
require.NoError(t, err)
}