From b2b646eb0bb0fca90a129c04867e32cebabdfbaa Mon Sep 17 00:00:00 2001 From: Mark McDonnell Date: Fri, 17 Jan 2025 18:16:38 +0000 Subject: [PATCH 1/2] feat(domains): add support for domains/v1 endpoints (#577) * feat(domainsv1): add udm endpoints * refactor(domains): rename files * tests: fix * domains_v1: implement UDM endpoints * refactor(domainsV1): sort DomainsV1Data fields * refactor(domains): update annotation Co-authored-by: Kevin P. Fleming * refactor(domains): update annotation Co-authored-by: Kevin P. Fleming * refactor(domains): update annotation Co-authored-by: Kevin P. Fleming * refactor(domains): update annotation Co-authored-by: Kevin P. Fleming * refactor(domains): update annotation Co-authored-by: Kevin P. Fleming * refactor(domains): update annotation Co-authored-by: Kevin P. Fleming * refactor(domains): update annotation Co-authored-by: Kevin P. Fleming * refactor(domains): update annotation Co-authored-by: Kevin P. Fleming * refactor(domains): update annotation Co-authored-by: Kevin P. Fleming * refactor(domains): update annotation Co-authored-by: Kevin P. Fleming * refactor(domains): update annotation * refactor(domains): make singular * refactor(design): domains_v1 * refactor(domains): rename test functions * remove(domains): cleanup fixture/test --------- Co-authored-by: Kevin P. Fleming --- fastly/domains/doc.go | 3 + fastly/domains/v1/api_create.go | 33 +++++++ fastly/domains/v1/api_delete.go | 35 ++++++++ fastly/domains/v1/api_get.go | 37 ++++++++ fastly/domains/v1/api_list.go | 61 +++++++++++++ fastly/domains/v1/api_response.go | 43 +++++++++ fastly/domains/v1/api_test.go | 116 +++++++++++++++++++++++++ fastly/domains/v1/api_update.go | 39 +++++++++ fastly/domains/v1/doc.go | 3 + fastly/domains/v1/fixtures/create.yaml | 49 +++++++++++ fastly/domains/v1/fixtures/delete.yaml | 41 +++++++++ fastly/domains/v1/fixtures/get.yaml | 45 ++++++++++ fastly/domains/v1/fixtures/list.yaml | 45 ++++++++++ fastly/domains/v1/fixtures/update.yaml | 49 +++++++++++ fastly/errors.go | 4 + fastly/fastly_test_utils.go | 4 +- 16 files changed, 605 insertions(+), 2 deletions(-) create mode 100644 fastly/domains/doc.go create mode 100644 fastly/domains/v1/api_create.go create mode 100644 fastly/domains/v1/api_delete.go create mode 100644 fastly/domains/v1/api_get.go create mode 100644 fastly/domains/v1/api_list.go create mode 100644 fastly/domains/v1/api_response.go create mode 100644 fastly/domains/v1/api_test.go create mode 100644 fastly/domains/v1/api_update.go create mode 100644 fastly/domains/v1/doc.go create mode 100644 fastly/domains/v1/fixtures/create.yaml create mode 100644 fastly/domains/v1/fixtures/delete.yaml create mode 100644 fastly/domains/v1/fixtures/get.yaml create mode 100644 fastly/domains/v1/fixtures/list.yaml create mode 100644 fastly/domains/v1/fixtures/update.yaml diff --git a/fastly/domains/doc.go b/fastly/domains/doc.go new file mode 100644 index 00000000..d46ef937 --- /dev/null +++ b/fastly/domains/doc.go @@ -0,0 +1,3 @@ +// Package domains contains subpackages which offer various operations to +// enable, disable, and configure Fastly domains +package domains diff --git a/fastly/domains/v1/api_create.go b/fastly/domains/v1/api_create.go new file mode 100644 index 00000000..f809c6da --- /dev/null +++ b/fastly/domains/v1/api_create.go @@ -0,0 +1,33 @@ +package v1 + +import ( + "encoding/json" + "fmt" + + "github.com/fastly/go-fastly/v9/fastly" +) + +// CreateInput specifies the information needed for the Create() function to +// perform the operation. +type CreateInput struct { + // FQDN is the fully-qualified domain name of the domain (required). + FQDN *string `json:"fqdn"` + // ServiceID is the service_id associated with the domain or nil if there + // is no association (optional) + ServiceID *string `json:"service_id"` +} + +// Create creates a new domain. +func Create(c *fastly.Client, i *CreateInput) (*Data, error) { + resp, err := c.PostJSON("/domains/v1", i, nil) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var d *Data + if err := json.NewDecoder(resp.Body).Decode(&d); err != nil { + return nil, fmt.Errorf("failed to decode json response: %w", err) + } + return d, nil +} diff --git a/fastly/domains/v1/api_delete.go b/fastly/domains/v1/api_delete.go new file mode 100644 index 00000000..ce21e280 --- /dev/null +++ b/fastly/domains/v1/api_delete.go @@ -0,0 +1,35 @@ +package v1 + +import ( + "net/http" + + "github.com/fastly/go-fastly/v9/fastly" +) + +// DeleteInput specifies the information needed for the Delete() function to +// perform the operation. +type DeleteInput struct { + // DomainID of the domain to delete (required). + DomainID *string +} + +// Delete deletes the specified domain. +func Delete(c *fastly.Client, i *DeleteInput) error { + if i.DomainID == nil { + return fastly.ErrMissingDomainID + } + + path := fastly.ToSafeURL("domains", "v1", *i.DomainID) + + resp, err := c.Delete(path, nil) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusNoContent { + return fastly.NewHTTPError(resp) + } + + return nil +} diff --git a/fastly/domains/v1/api_get.go b/fastly/domains/v1/api_get.go new file mode 100644 index 00000000..2e5651eb --- /dev/null +++ b/fastly/domains/v1/api_get.go @@ -0,0 +1,37 @@ +package v1 + +import ( + "encoding/json" + "fmt" + + "github.com/fastly/go-fastly/v9/fastly" +) + +// GetInput specifies the information needed for the Get() function to perform +// the operation. +type GetInput struct { + // DomainID is the domain identifier (required). + DomainID *string +} + +// Get retrieves a specified domain. +func Get(c *fastly.Client, i *GetInput) (*Data, error) { + if i.DomainID == nil { + return nil, fastly.ErrMissingDomainID + } + + path := fastly.ToSafeURL("domains", "v1", *i.DomainID) + + resp, err := c.Get(path, nil) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var d *Data + if err := json.NewDecoder(resp.Body).Decode(&d); err != nil { + return nil, fmt.Errorf("failed to decode json response: %w", err) + } + + return d, nil +} diff --git a/fastly/domains/v1/api_list.go b/fastly/domains/v1/api_list.go new file mode 100644 index 00000000..fdb81f14 --- /dev/null +++ b/fastly/domains/v1/api_list.go @@ -0,0 +1,61 @@ +package v1 + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/fastly/go-fastly/v9/fastly" +) + +// ListInput specifies the information needed for the List() function to perform +// the operation. +type ListInput struct { + // Cursor is the cursor value from the next_cursor field of a previous + // response, used to retrieve the next page. To request the first page, this + // should be an empty string or nil. + Cursor *string + // FQDN filters results by the FQDN using a fuzzy/partial match (optional). + FQDN *string + // Limit is the maximum number of results to return (optional). + Limit *int + // ServiceID filter results based on a service_id (optional). + ServiceID *string + // Sort is the order in which to list the results (optional). + Sort *string +} + +// List retrieves a list of domains, with optional filtering and pagination. +func List(c *fastly.Client, i *ListInput) (*Collection, error) { + ro := &fastly.RequestOptions{ + Params: map[string]string{}, + } + if i.Cursor != nil { + ro.Params["cursor"] = *i.Cursor + } + if i.Limit != nil { + ro.Params["limit"] = strconv.Itoa(*i.Limit) + } + if i.FQDN != nil { + ro.Params["fqdn"] = *i.FQDN + } + if i.ServiceID != nil { + ro.Params["service_id"] = *i.ServiceID + } + if i.Sort != nil { + ro.Params["sort"] = *i.Sort + } + + resp, err := c.Get("/domains/v1", ro) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var cl *Collection + if err := json.NewDecoder(resp.Body).Decode(&cl); err != nil { + return nil, fmt.Errorf("failed to decode json response: %w", err) + } + + return cl, nil +} diff --git a/fastly/domains/v1/api_response.go b/fastly/domains/v1/api_response.go new file mode 100644 index 00000000..ddda7bf7 --- /dev/null +++ b/fastly/domains/v1/api_response.go @@ -0,0 +1,43 @@ +package v1 + +import ( + "time" +) + +// Collection is the API response structure for the list operation. +type Collection struct { + // Data contains the API data. + Data []Data `json:"data"` + // Meta contains metadata related to paginating the full dataset. + Meta Meta `json:"meta"` +} + +// Data is a subset of the API response structure containing the specific API +// data itself. +type Data struct { + // CreatedAt is the date and time in ISO 8601 format. + CreatedAt time.Time `json:"created_at"` + // ID is the domain identifier (UUID). + DomainID string `json:"id"` + // FQDN is the fully-qualified domain name of the domain. Read-only + // after creation. + FQDN string `json:"fqdn"` + // ServiceID is the service_id associated with the domain or nil if there + // is no association. + ServiceID *string `json:"service_id"` + // UpdatedAt is the date and time in ISO 8601 format. + UpdatedAt time.Time `json:"updated_at"` +} + +// Meta is a subset of the API response structure containing metadata related to +// paginating the full dataset. +type Meta struct { + // Limit is how many results are included in this response. + Limit int `json:"limit"` + // NextCursor is the cursor value used to retrieve the next page. + NextCursor string `json:"next_cursor"` + // Sort is the field used to order the response by. + Sort string `json:"sort"` + // Total is the total number of results. + Total int `json:"total"` +} diff --git a/fastly/domains/v1/api_test.go b/fastly/domains/v1/api_test.go new file mode 100644 index 00000000..ed3fc8be --- /dev/null +++ b/fastly/domains/v1/api_test.go @@ -0,0 +1,116 @@ +package v1 + +import ( + "errors" + "testing" + + "github.com/fastly/go-fastly/v9/fastly" +) + +func TestClient_Domain(t *testing.T) { + t.Parallel() + + var err error + fqdn := "fastly-sdk-gofastly-testing.com" + + // Create + var d *Data + fastly.Record(t, "create", func(c *fastly.Client) { + d, err = Create(c, &CreateInput{ + FQDN: fastly.ToPointer(fqdn), + }) + }) + if err != nil { + t.Fatal(err) + } + if d.FQDN != fqdn { + t.Errorf("bad fqdn: %v", d.FQDN) + } + if d.ServiceID != nil { + t.Errorf("bad service_id: %v", d.ServiceID) + } + + // List Definitions + var cl *Collection + fastly.Record(t, "list", func(c *fastly.Client) { + cl, err = List(c, &ListInput{ + Limit: fastly.ToPointer(10), + FQDN: fastly.ToPointer(d.FQDN), + Sort: fastly.ToPointer("fqdn"), + }) + }) + if err != nil { + t.Fatal(err) + } + if len(cl.Data) < 1 { + t.Errorf("bad domains list: %v", cl) + } + + // Get + var gd *Data + fastly.Record(t, "get", func(c *fastly.Client) { + gd, err = Get(c, &GetInput{ + DomainID: &d.DomainID, + }) + }) + if err != nil { + t.Fatal(err) + } + if d.FQDN != gd.FQDN { + t.Errorf("bad fqdn: %q (%q)", d.FQDN, gd.FQDN) + } + + // Update + var ud *Data + fastly.Record(t, "update", func(c *fastly.Client) { + ud, err = Update(c, &UpdateInput{ + DomainID: fastly.ToPointer(d.DomainID), + ServiceID: fastly.ToPointer(fastly.DefaultDeliveryTestServiceID), + }) + }) + if err != nil { + t.Fatal(err) + } + if ud.ServiceID == nil || *ud.ServiceID != fastly.DefaultDeliveryTestServiceID { + t.Errorf("bad service id: %v", ud.ServiceID) + } + + // Delete + fastly.Record(t, "delete", func(c *fastly.Client) { + err = Delete(c, &DeleteInput{ + DomainID: &d.DomainID, + }) + }) + if err != nil { + t.Fatal(err) + } +} + +func TestClient_GetDomain_validation(t *testing.T) { + var err error + _, err = Get(fastly.TestClient, &GetInput{ + DomainID: nil, + }) + if !errors.Is(err, fastly.ErrMissingDomainID) { + t.Errorf("bad error: %s", err) + } +} + +func TestClient_UpdateDomain_validation(t *testing.T) { + var err error + _, err = Update(fastly.TestClient, &UpdateInput{ + DomainID: nil, + }) + if !errors.Is(err, fastly.ErrMissingDomainID) { + t.Errorf("bad error: %s", err) + } +} + +func TestClient_DeleteDomain_validation(t *testing.T) { + err := Delete(fastly.TestClient, &DeleteInput{ + DomainID: nil, + }) + if !errors.Is(err, fastly.ErrMissingDomainID) { + t.Errorf("bad error: %s", err) + } +} diff --git a/fastly/domains/v1/api_update.go b/fastly/domains/v1/api_update.go new file mode 100644 index 00000000..8730da70 --- /dev/null +++ b/fastly/domains/v1/api_update.go @@ -0,0 +1,39 @@ +package v1 + +import ( + "encoding/json" + "fmt" + + "github.com/fastly/go-fastly/v9/fastly" +) + +// UpdateInput specifies the information needed for the Update() function to +// perform the operation. +type UpdateInput struct { + // DomainID is the domain identifier (required). + DomainID *string `json:"-"` + // ServiceID is the service_id associated with the domain or nil if there + // is no association (optional) + ServiceID *string `json:"service_id"` +} + +// Update updates the specified domain. +func Update(c *fastly.Client, i *UpdateInput) (*Data, error) { + if i.DomainID == nil { + return nil, fastly.ErrMissingDomainID + } + + path := fastly.ToSafeURL("domains", "v1", *i.DomainID) + + resp, err := c.PatchJSON(path, i, nil) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var d *Data + if err := json.NewDecoder(resp.Body).Decode(&d); err != nil { + return nil, fmt.Errorf("failed to decode json response: %w", err) + } + return d, nil +} diff --git a/fastly/domains/v1/doc.go b/fastly/domains/v1/doc.go new file mode 100644 index 00000000..e3dfd1d8 --- /dev/null +++ b/fastly/domains/v1/doc.go @@ -0,0 +1,3 @@ +// Package v1 contains functionality which offer various operations to enable, +// disable, and configure Fastly domains using its v1 implementation. +package v1 diff --git a/fastly/domains/v1/fixtures/create.yaml b/fastly/domains/v1/fixtures/create.yaml new file mode 100644 index 00000000..c5842817 --- /dev/null +++ b/fastly/domains/v1/fixtures/create.yaml @@ -0,0 +1,49 @@ +--- +version: 1 +interactions: +- request: + body: '{"fqdn":"fastly-sdk-gofastly-testing.com","service_id":null}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/9.11.0 (+github.com/fastly/go-fastly; go1.20.14) + url: https://api.fastly.com/domains/v1 + method: POST + response: + body: '{"id":"hbRABkI8yvSnUbCLRiqUbQ","created_at":"2025-01-14T14:21:09.000Z","fqdn":"fastly-sdk-gofastly-testing.com","updated_at":"2025-01-14T14:21:09.000Z","service_id":null}' + headers: + Accept-Ranges: + - bytes + Cache-Control: + - no-store + Content-Length: + - "170" + Content-Type: + - application/json + Date: + - Tue, 14 Jan 2025 14:21:09 GMT + Pragma: + - no-cache + Server: + - fastly control-gateway + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish, 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-chi-kigq8000095-CHI, cache-lon420121-LON + X-Timer: + - S1736864469.862284,VS0,VE211 + status: 201 Created + code: 201 + duration: "" diff --git a/fastly/domains/v1/fixtures/delete.yaml b/fastly/domains/v1/fixtures/delete.yaml new file mode 100644 index 00000000..1933eac7 --- /dev/null +++ b/fastly/domains/v1/fixtures/delete.yaml @@ -0,0 +1,41 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - FastlyGo/9.11.0 (+github.com/fastly/go-fastly; go1.20.14) + url: https://api.fastly.com/domains/v1/hbRABkI8yvSnUbCLRiqUbQ + method: DELETE + response: + body: "" + headers: + Accept-Ranges: + - bytes + Cache-Control: + - no-store + Date: + - Tue, 14 Jan 2025 14:21:10 GMT + Pragma: + - no-cache + Server: + - fastly control-gateway + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish, 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-chi-kigq8000030-CHI, cache-lon420121-LON + X-Timer: + - S1736864470.818474,VS0,VE211 + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/domains/v1/fixtures/get.yaml b/fastly/domains/v1/fixtures/get.yaml new file mode 100644 index 00000000..6e363deb --- /dev/null +++ b/fastly/domains/v1/fixtures/get.yaml @@ -0,0 +1,45 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - FastlyGo/9.11.0 (+github.com/fastly/go-fastly; go1.20.14) + url: https://api.fastly.com/domains/v1/hbRABkI8yvSnUbCLRiqUbQ + method: GET + response: + body: '{"id":"hbRABkI8yvSnUbCLRiqUbQ","created_at":"2025-01-14T14:21:09.000Z","fqdn":"fastly-sdk-gofastly-testing.com","updated_at":"2025-01-14T14:21:09.000Z","service_id":null}' + headers: + Accept-Ranges: + - bytes + Cache-Control: + - no-store + Content-Length: + - "170" + Content-Type: + - application/json + Date: + - Tue, 14 Jan 2025 14:21:09 GMT + Pragma: + - no-cache + Server: + - fastly control-gateway + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish, 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-chi-kigq8000030-CHI, cache-lon420121-LON + X-Timer: + - S1736864469.304457,VS0,VE192 + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/domains/v1/fixtures/list.yaml b/fastly/domains/v1/fixtures/list.yaml new file mode 100644 index 00000000..5c3dfef7 --- /dev/null +++ b/fastly/domains/v1/fixtures/list.yaml @@ -0,0 +1,45 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - FastlyGo/9.11.0 (+github.com/fastly/go-fastly; go1.20.14) + url: https://api.fastly.com/domains/v1?fqdn=fastly-sdk-gofastly-testing.com&limit=10&sort=fqdn + method: GET + response: + body: '{"data":[{"id":"hbRABkI8yvSnUbCLRiqUbQ","created_at":"2025-01-14T14:21:09.000Z","fqdn":"fastly-sdk-gofastly-testing.com","updated_at":"2025-01-14T14:21:09.000Z","service_id":null}],"meta":{"total":1,"limit":10}}' + headers: + Accept-Ranges: + - bytes + Cache-Control: + - no-store + Content-Length: + - "211" + Content-Type: + - application/json + Date: + - Tue, 14 Jan 2025 14:21:09 GMT + Pragma: + - no-cache + Server: + - fastly control-gateway + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish, 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-chi-kigq8000022-CHI, cache-lon420121-LON + X-Timer: + - S1736864469.103643,VS0,VE174 + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/domains/v1/fixtures/update.yaml b/fastly/domains/v1/fixtures/update.yaml new file mode 100644 index 00000000..ca40e16a --- /dev/null +++ b/fastly/domains/v1/fixtures/update.yaml @@ -0,0 +1,49 @@ +--- +version: 1 +interactions: +- request: + body: '{"service_id":"kKJb5bOFI47uHeBVluGfX1"}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/9.11.0 (+github.com/fastly/go-fastly; go1.20.14) + url: https://api.fastly.com/domains/v1/hbRABkI8yvSnUbCLRiqUbQ + method: PATCH + response: + body: '{"id":"hbRABkI8yvSnUbCLRiqUbQ","created_at":"2025-01-14T14:21:09.000Z","fqdn":"fastly-sdk-gofastly-testing.com","updated_at":"2025-01-14T14:21:09.000Z","service_id":"kKJb5bOFI47uHeBVluGfX1"}' + headers: + Accept-Ranges: + - bytes + Cache-Control: + - no-store + Content-Length: + - "190" + Content-Type: + - application/json + Date: + - Tue, 14 Jan 2025 14:21:09 GMT + Pragma: + - no-cache + Server: + - fastly control-gateway + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish, 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-chi-kigq8000030-CHI, cache-lon420121-LON + X-Timer: + - S1736864470.527836,VS0,VE260 + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/errors.go b/fastly/errors.go index 66ba83f7..604d5886 100644 --- a/fastly/errors.go +++ b/fastly/errors.go @@ -125,6 +125,10 @@ var ErrMissingTokenID = errors.New("missing required field 'TokenID'") // requires a "ID" key, but one was not set. var ErrMissingID = NewFieldError("ID") +// ErrMissingDomainID is an error that is returned when an input struct +// requires a "DomainID" key, but one was not set. +var ErrMissingDomainID = NewFieldError("DomainID") + // ErrMissingEntryID is an error that is returned when an input struct // requires a "EntryID" key, but one was not set. var ErrMissingEntryID = NewFieldError("EntryID") diff --git a/fastly/fastly_test_utils.go b/fastly/fastly_test_utils.go index 1d8fb873..fedf9b53 100644 --- a/fastly/fastly_test_utils.go +++ b/fastly/fastly_test_utils.go @@ -26,7 +26,7 @@ var TestComputeServiceID = computeServiceIDForTest() var TestNGWAFWorkspaceID = ngwafWorkspaceIDForTest() // ID of the default Delivery service for testing. -var defaultDeliveryTestServiceID = "kKJb5bOFI47uHeBVluGfX1" +var DefaultDeliveryTestServiceID = "kKJb5bOFI47uHeBVluGfX1" // ID of the default Compute service for testing. var defaultComputeTestServiceID = "XsjdElScZGjmfCcTwsYRC1" @@ -50,7 +50,7 @@ func deliveryServiceIDForTest() string { return tsid } - return defaultDeliveryTestServiceID + return DefaultDeliveryTestServiceID } func computeServiceIDForTest() string { From ebabc697ca5a5dace90307a24b9f64367e716bf1 Mon Sep 17 00:00:00 2001 From: Mark McDonnell Date: Tue, 21 Jan 2025 14:34:12 +0000 Subject: [PATCH 2/2] fix(domains): parse error response correctly (#579) --- fastly/domains/v1/api_test.go | 26 +++++++++- fastly/domains/v1/fixtures/create.yaml | 10 ++-- .../domains/v1/fixtures/create_duplicate.yaml | 50 +++++++++++++++++++ fastly/domains/v1/fixtures/delete.yaml | 8 +-- fastly/domains/v1/fixtures/get.yaml | 12 ++--- fastly/domains/v1/fixtures/list.yaml | 10 ++-- fastly/domains/v1/fixtures/update.yaml | 12 ++--- fastly/errors.go | 10 +++- 8 files changed, 110 insertions(+), 28 deletions(-) create mode 100644 fastly/domains/v1/fixtures/create_duplicate.yaml diff --git a/fastly/domains/v1/api_test.go b/fastly/domains/v1/api_test.go index ed3fc8be..eacb9d4c 100644 --- a/fastly/domains/v1/api_test.go +++ b/fastly/domains/v1/api_test.go @@ -30,6 +30,30 @@ func TestClient_Domain(t *testing.T) { t.Errorf("bad service_id: %v", d.ServiceID) } + fastly.Record(t, "create_duplicate", func(c *fastly.Client) { + _, err = Create(c, &CreateInput{ + FQDN: fastly.ToPointer(fqdn), + }) + }) + if err == nil { + t.Fatal("expected an error and got nil") + } + var httpError *fastly.HTTPError + if !errors.As(err, &httpError) { + t.Fatalf("unexpected error type: %T", err) + } else { + var okErr bool + for _, he := range httpError.Errors { + if he.Detail == "fqdn has already been taken" { + okErr = true + break + } + } + if !okErr { + t.Errorf("bad error: %v", err) + } + } + // List Definitions var cl *Collection fastly.Record(t, "list", func(c *fastly.Client) { @@ -72,7 +96,7 @@ func TestClient_Domain(t *testing.T) { t.Fatal(err) } if ud.ServiceID == nil || *ud.ServiceID != fastly.DefaultDeliveryTestServiceID { - t.Errorf("bad service id: %v", ud.ServiceID) + t.Errorf("bad service id: %v", *ud.ServiceID) } // Delete diff --git a/fastly/domains/v1/fixtures/create.yaml b/fastly/domains/v1/fixtures/create.yaml index c5842817..b228f315 100644 --- a/fastly/domains/v1/fixtures/create.yaml +++ b/fastly/domains/v1/fixtures/create.yaml @@ -14,18 +14,18 @@ interactions: url: https://api.fastly.com/domains/v1 method: POST response: - body: '{"id":"hbRABkI8yvSnUbCLRiqUbQ","created_at":"2025-01-14T14:21:09.000Z","fqdn":"fastly-sdk-gofastly-testing.com","updated_at":"2025-01-14T14:21:09.000Z","service_id":null}' + body: '{"id":"bN7Iz1AyGJAZs3q9KFiVYg","created_at":"2025-01-20T18:11:45.000Z","fqdn":"fastly-sdk-gofastly-testing.com","updated_at":"2025-01-20T18:11:45.000Z","service_id":null}' headers: Accept-Ranges: - bytes Cache-Control: - no-store Content-Length: - - "170" + - "171" Content-Type: - application/json Date: - - Tue, 14 Jan 2025 14:21:09 GMT + - Mon, 20 Jan 2025 18:11:45 GMT Pragma: - no-cache Server: @@ -41,9 +41,9 @@ interactions: X-Content-Type-Options: - nosniff X-Served-By: - - cache-chi-kigq8000095-CHI, cache-lon420121-LON + - cache-chi-kigq8000095-CHI, cache-lon420104-LON X-Timer: - - S1736864469.862284,VS0,VE211 + - S1737396705.078453,VS0,VE201 status: 201 Created code: 201 duration: "" diff --git a/fastly/domains/v1/fixtures/create_duplicate.yaml b/fastly/domains/v1/fixtures/create_duplicate.yaml new file mode 100644 index 00000000..b3697333 --- /dev/null +++ b/fastly/domains/v1/fixtures/create_duplicate.yaml @@ -0,0 +1,50 @@ +--- +version: 1 +interactions: +- request: + body: '{"fqdn":"fastly-sdk-gofastly-testing.com","service_id":null}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/9.11.0 (+github.com/fastly/go-fastly; go1.20.14) + url: https://api.fastly.com/domains/v1 + method: POST + response: + body: '{"errors":[{"title":"Invalid value for fqdn","detail":"fqdn has already + been taken"}]}' + headers: + Accept-Ranges: + - bytes + Cache-Control: + - no-store + Content-Length: + - "86" + Content-Type: + - application/json + Date: + - Mon, 20 Jan 2025 18:11:45 GMT + Pragma: + - no-cache + Server: + - fastly control-gateway + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish, 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-chi-kigq8000095-CHI, cache-lon420104-LON + X-Timer: + - S1737396705.304617,VS0,VE193 + status: 400 Bad Request + code: 400 + duration: "" diff --git a/fastly/domains/v1/fixtures/delete.yaml b/fastly/domains/v1/fixtures/delete.yaml index 1933eac7..87b4a2c3 100644 --- a/fastly/domains/v1/fixtures/delete.yaml +++ b/fastly/domains/v1/fixtures/delete.yaml @@ -7,7 +7,7 @@ interactions: headers: User-Agent: - FastlyGo/9.11.0 (+github.com/fastly/go-fastly; go1.20.14) - url: https://api.fastly.com/domains/v1/hbRABkI8yvSnUbCLRiqUbQ + url: https://api.fastly.com/domains/v1/bN7Iz1AyGJAZs3q9KFiVYg method: DELETE response: body: "" @@ -17,7 +17,7 @@ interactions: Cache-Control: - no-store Date: - - Tue, 14 Jan 2025 14:21:10 GMT + - Mon, 20 Jan 2025 18:11:46 GMT Pragma: - no-cache Server: @@ -33,9 +33,9 @@ interactions: X-Content-Type-Options: - nosniff X-Served-By: - - cache-chi-kigq8000030-CHI, cache-lon420121-LON + - cache-chi-kigq8000149-CHI, cache-lon420104-LON X-Timer: - - S1736864470.818474,VS0,VE211 + - S1737396706.248403,VS0,VE254 status: 204 No Content code: 204 duration: "" diff --git a/fastly/domains/v1/fixtures/get.yaml b/fastly/domains/v1/fixtures/get.yaml index 6e363deb..7cf9eded 100644 --- a/fastly/domains/v1/fixtures/get.yaml +++ b/fastly/domains/v1/fixtures/get.yaml @@ -7,21 +7,21 @@ interactions: headers: User-Agent: - FastlyGo/9.11.0 (+github.com/fastly/go-fastly; go1.20.14) - url: https://api.fastly.com/domains/v1/hbRABkI8yvSnUbCLRiqUbQ + url: https://api.fastly.com/domains/v1/bN7Iz1AyGJAZs3q9KFiVYg method: GET response: - body: '{"id":"hbRABkI8yvSnUbCLRiqUbQ","created_at":"2025-01-14T14:21:09.000Z","fqdn":"fastly-sdk-gofastly-testing.com","updated_at":"2025-01-14T14:21:09.000Z","service_id":null}' + body: '{"id":"bN7Iz1AyGJAZs3q9KFiVYg","created_at":"2025-01-20T18:11:45.000Z","fqdn":"fastly-sdk-gofastly-testing.com","updated_at":"2025-01-20T18:11:45.000Z","service_id":null}' headers: Accept-Ranges: - bytes Cache-Control: - no-store Content-Length: - - "170" + - "171" Content-Type: - application/json Date: - - Tue, 14 Jan 2025 14:21:09 GMT + - Mon, 20 Jan 2025 18:11:45 GMT Pragma: - no-cache Server: @@ -37,9 +37,9 @@ interactions: X-Content-Type-Options: - nosniff X-Served-By: - - cache-chi-kigq8000030-CHI, cache-lon420121-LON + - cache-chi-kigq8000149-CHI, cache-lon420104-LON X-Timer: - - S1736864469.304457,VS0,VE192 + - S1737396706.730595,VS0,VE205 status: 200 OK code: 200 duration: "" diff --git a/fastly/domains/v1/fixtures/list.yaml b/fastly/domains/v1/fixtures/list.yaml index 5c3dfef7..418d7613 100644 --- a/fastly/domains/v1/fixtures/list.yaml +++ b/fastly/domains/v1/fixtures/list.yaml @@ -10,18 +10,18 @@ interactions: url: https://api.fastly.com/domains/v1?fqdn=fastly-sdk-gofastly-testing.com&limit=10&sort=fqdn method: GET response: - body: '{"data":[{"id":"hbRABkI8yvSnUbCLRiqUbQ","created_at":"2025-01-14T14:21:09.000Z","fqdn":"fastly-sdk-gofastly-testing.com","updated_at":"2025-01-14T14:21:09.000Z","service_id":null}],"meta":{"total":1,"limit":10}}' + body: '{"data":[{"id":"bN7Iz1AyGJAZs3q9KFiVYg","created_at":"2025-01-20T18:11:45.000Z","fqdn":"fastly-sdk-gofastly-testing.com","updated_at":"2025-01-20T18:11:45.000Z","service_id":null}],"meta":{"total":1,"limit":10}}' headers: Accept-Ranges: - bytes Cache-Control: - no-store Content-Length: - - "211" + - "212" Content-Type: - application/json Date: - - Tue, 14 Jan 2025 14:21:09 GMT + - Mon, 20 Jan 2025 18:11:45 GMT Pragma: - no-cache Server: @@ -37,9 +37,9 @@ interactions: X-Content-Type-Options: - nosniff X-Served-By: - - cache-chi-kigq8000022-CHI, cache-lon420121-LON + - cache-chi-klot8100046-CHI, cache-lon420104-LON X-Timer: - - S1736864469.103643,VS0,VE174 + - S1737396706.523168,VS0,VE179 status: 200 OK code: 200 duration: "" diff --git a/fastly/domains/v1/fixtures/update.yaml b/fastly/domains/v1/fixtures/update.yaml index ca40e16a..4c37a76d 100644 --- a/fastly/domains/v1/fixtures/update.yaml +++ b/fastly/domains/v1/fixtures/update.yaml @@ -11,21 +11,21 @@ interactions: - application/json User-Agent: - FastlyGo/9.11.0 (+github.com/fastly/go-fastly; go1.20.14) - url: https://api.fastly.com/domains/v1/hbRABkI8yvSnUbCLRiqUbQ + url: https://api.fastly.com/domains/v1/bN7Iz1AyGJAZs3q9KFiVYg method: PATCH response: - body: '{"id":"hbRABkI8yvSnUbCLRiqUbQ","created_at":"2025-01-14T14:21:09.000Z","fqdn":"fastly-sdk-gofastly-testing.com","updated_at":"2025-01-14T14:21:09.000Z","service_id":"kKJb5bOFI47uHeBVluGfX1"}' + body: '{"id":"bN7Iz1AyGJAZs3q9KFiVYg","created_at":"2025-01-20T18:11:45.000Z","fqdn":"fastly-sdk-gofastly-testing.com","updated_at":"2025-01-20T18:11:46.000Z","service_id":"kKJb5bOFI47uHeBVluGfX1"}' headers: Accept-Ranges: - bytes Cache-Control: - no-store Content-Length: - - "190" + - "191" Content-Type: - application/json Date: - - Tue, 14 Jan 2025 14:21:09 GMT + - Mon, 20 Jan 2025 18:11:46 GMT Pragma: - no-cache Server: @@ -41,9 +41,9 @@ interactions: X-Content-Type-Options: - nosniff X-Served-By: - - cache-chi-kigq8000030-CHI, cache-lon420121-LON + - cache-chi-kigq8000149-CHI, cache-lon420104-LON X-Timer: - - S1736864470.527836,VS0,VE260 + - S1737396706.961460,VS0,VE263 status: 200 OK code: 200 duration: "" diff --git a/fastly/errors.go b/fastly/errors.go index 604d5886..7b694c0f 100644 --- a/fastly/errors.go +++ b/fastly/errors.go @@ -458,13 +458,21 @@ func NewHTTPError(resp *http.Response) *HTTPError { if r, ok := le["reason"]; ok { detail, _ = r.(string) } + if d, ok := le["detail"]; ok { + detail, _ = d.(string) + } + var title string if i, ok := le["index"]; ok { index, _ = i.(float64) + title = fmt.Sprintf("error at index: %v", index) + } + if t, ok := le["title"]; ok { + title, _ = t.(string) } e.Errors = append(e.Errors, &ErrorObject{ Code: code, Detail: detail, - Title: fmt.Sprintf("error at index: %v", index), + Title: title, }) } } else {