From 61975e20db23861f906b5ec2d5654eaa16f5c7ce Mon Sep 17 00:00:00 2001 From: Jacob Spizziri Date: Tue, 31 May 2022 10:21:41 -0500 Subject: [PATCH 1/4] build(go): upgrade go from 1.17 to 1.18 --- .github/workflows/ci.yml | 6 +++--- go.mod | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 350cd1b..a69d076 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.17 + go-version: 1.18 - name: build run: go build -v ./... @@ -30,7 +30,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.17 + go-version: 1.18 - name: Test run: go test -v -race ./... @@ -41,4 +41,4 @@ jobs: - uses: actions/checkout@v2 - name: golangci-lint - run: docker run -v $GITHUB_WORKSPACE:/repo -w /repo golangci/golangci-lint:v1.42 golangci-lint run + run: docker run -v $GITHUB_WORKSPACE:/repo -w /repo golangci/golangci-lint:v1.46 golangci-lint run diff --git a/go.mod b/go.mod index ecdb5be..85138f8 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/simplesurance/bunny-go -go 1.17 +go 1.18 require ( github.com/google/go-querystring v1.1.0 From 61d3d821d8c630683e4bbe5c7b9299c83c3d6aed Mon Sep 17 00:00:00 2001 From: Jacob Spizziri Date: Tue, 31 May 2022 10:23:53 -0500 Subject: [PATCH 2/4] feat(storagezone): add the ability to get, list, add, update, and delete storage zones --- README.md | 9 ++++- client.go | 12 +++++-- integrationtest_test.go | 34 ++++++++++++++++++ resource_delete.go | 17 +++++++++ resource_get.go | 22 ++++++++++++ resource_list.go | 76 +++++++++++++++++++++++++++++++++++++++++ resource_post.go | 33 ++++++++++++++++++ storagezone.go | 8 +++++ storagezone_add.go | 32 +++++++++++++++++ storagezone_delete.go | 14 ++++++++ storagezone_get.go | 33 ++++++++++++++++++ storagezone_list.go | 20 +++++++++++ storagezone_test.go | 75 ++++++++++++++++++++++++++++++++++++++++ storagezone_update.go | 27 +++++++++++++++ 14 files changed, 409 insertions(+), 3 deletions(-) create mode 100644 resource_delete.go create mode 100644 resource_get.go create mode 100644 resource_list.go create mode 100644 resource_post.go create mode 100644 storagezone.go create mode 100644 storagezone_add.go create mode 100644 storagezone_delete.go create mode 100644 storagezone_get.go create mode 100644 storagezone_list.go create mode 100644 storagezone_test.go create mode 100644 storagezone_update.go diff --git a/README.md b/README.md index c16a23e..63669d6 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,14 @@ Endpoints](https://docs.bunny.net/reference/bunnynet-api-overview) are supported - [ ] Remove Blocked IP - [ ] Purge - [ ] Statistics - - [ ] Storage Zone + - [ ] [Storage Zone](https://docs.bunny.net/reference/storagezonepublic_index) + - [x] List Storage Zones + - [x] Add Storage Zone + - [x] Get Storage Zone + - [x] Update Storage Zone + - [x] Delete Storage Zone + - [ ] Reset Password + - [ ] Reset Read-Only Password - [ ] User - [ ] Edge Storage API - [ ] Stream API diff --git a/client.go b/client.go index 53fa380..286cdff 100644 --- a/client.go +++ b/client.go @@ -11,6 +11,7 @@ import ( "net/http" "net/http/httputil" "net/url" + "reflect" "github.com/google/go-querystring/query" "github.com/google/uuid" @@ -44,9 +45,13 @@ type Client struct { logf Logf userAgent string - PullZone *PullZoneService + PullZone *PullZoneService + StorageZone *StorageZoneService } +// NoContentResponse is a special struct that should be used for requests that return a 204 response +type NoContentResponse struct{} + var discardLogF = func(string, ...interface{}) {} // NewClient returns a new bunny.net API client. @@ -65,6 +70,7 @@ func NewClient(APIKey string, opts ...Option) *Client { } clt.PullZone = &PullZoneService{client: &clt} + clt.StorageZone = &StorageZoneService{client: &clt} for _, opt := range opts { opt(&clt) @@ -301,7 +307,9 @@ func (c *Client) unmarshalHTTPJSONBody(resp *http.Response, reqURL string, resul } if len(body) == 0 { - if result != nil { + var nc *NoContentResponse + isNoContent := reflect.TypeOf(result) == reflect.TypeOf(nc) + if result != nil && !isNoContent { return &HTTPError{ RequestURL: reqURL, StatusCode: resp.StatusCode, diff --git a/integrationtest_test.go b/integrationtest_test.go index 3c37da5..cf4a788 100644 --- a/integrationtest_test.go +++ b/integrationtest_test.go @@ -18,6 +18,9 @@ const envVarApiKeyName = "BUNNY_API_KEY" // pullzoneNamePrefix is the prefix for all pullzones created by the integrationtests. const pullzoneNamePrefix = "bunny-go-test-" +// pullzoneNamePrefix is the prefix for all pullzones created by the integrationtests. +const storagezoneNamePrefix = "bunny-go-test-storage-" + func newClient(t *testing.T) *bunny.Client { t.Helper() @@ -34,6 +37,10 @@ func randomPullZoneName() string { return pullzoneNamePrefix + uuid.New().String() } +func randomStorageZoneName() string { + return storagezoneNamePrefix + uuid.New().String() +} + // createPullZone creates a Pull Zone via the bunny client and registers a // testing cleanup function to remove it when the test terminates. // If creating the Pull Zone fails, t.Fatal is called. @@ -59,3 +66,30 @@ func createPullZone(t *testing.T, clt *bunny.Client, opts *bunny.PullZoneAddOpti return pz } + + +// createStorageZone creates a Storage Zone via the bunny client and registers a +// testing cleanup function to remove it when the test terminates. +// If creating the Storage Zone fails, t.Fatal is called. +func createStorageZone(t *testing.T, clt *bunny.Client, opts *bunny.StorageZoneAddOptions) *bunny.StorageZone { + t.Helper() + + pz, err := clt.StorageZone.Add(context.Background(), opts) + require.NoError(t, err, "creating storage zone failed") + require.NotNil(t, pz.ID, "add returned storage zone with nil id") + require.NotNil(t, pz.Name, "add returned storage zone with nil name") + + t.Logf("created storage zone: %q, id: %d", *pz.Name, *pz.ID) + + t.Cleanup(func() { + err := clt.StorageZone.Delete(context.Background(), *pz.ID) + if err != nil { + t.Errorf("could not delete storage zone (id: %d, name: %q) on test cleanup: %s", *pz.ID, *pz.Name, err) + return + + } + t.Logf("cleanup: deleted storage zone: %q, id: %d", *pz.Name, *pz.ID) + }) + + return pz +} diff --git a/resource_delete.go b/resource_delete.go new file mode 100644 index 0000000..4d525c8 --- /dev/null +++ b/resource_delete.go @@ -0,0 +1,17 @@ +package bunny + +import "context" + +func resourceDelete( + ctx context.Context, + client *Client, + path string, + opts any, +) error { + req, err := client.newDeleteRequest(path, opts) + if err != nil { + return err + } + + return client.sendRequest(ctx, req, nil) +} diff --git a/resource_get.go b/resource_get.go new file mode 100644 index 0000000..ffd4d87 --- /dev/null +++ b/resource_get.go @@ -0,0 +1,22 @@ +package bunny + +import "context" + +func resourceGet[Resp any]( + ctx context.Context, + client *Client, + path string, +) (*Resp, error) { + var res Resp + + req, err := client.newGetRequest(path, nil) + if err != nil { + return nil, err + } + + if err := client.sendRequest(ctx, req, &res); err != nil { + return nil, err + } + + return &res, err +} diff --git a/resource_list.go b/resource_list.go new file mode 100644 index 0000000..5058314 --- /dev/null +++ b/resource_list.go @@ -0,0 +1,76 @@ +package bunny + +import "context" + +const ( + // DefaultPaginationPage is the default value that is used for + // PaginationOptions.Page if it is unset. + DefaultPaginationPage = 1 + // DefaultPaginationPerPage is the default value that is used for + // PaginationOptions.PerPage if it is unset. + DefaultPaginationPerPage = 1000 +) + +// PaginationOptions specifies optional parameters for List APIs. +type PaginationOptions struct { + // Page the page to return + Page int32 `url:"page,omitempty"` + // PerPage how many entries to return per page + PerPage int32 `url:"per_page,omitempty"` +} + +// PaginationReply represents the pagination information contained in a +// List API endpoint response. +// +// Ex. Bunny.net API docs: +// - https://docs.bunny.net/reference/pullzonepublic_index +// - https://docs.bunny.net/reference/storagezonepublic_index +type PaginationReply[Item any] struct { + Items []*Item `json:"Items,omitempty"` + CurrentPage *int32 `json:"CurrentPage"` + TotalItems *int32 `json:"TotalItems"` + HasMoreItems *bool `json:"HasMoreItems"` +} + +func (p *PaginationOptions) ensureConstraints() { + if p.Page < 1 { + p.Page = DefaultPaginationPage + } + + if p.PerPage < 1 { + p.PerPage = DefaultPaginationPerPage + } +} + +func resourceList[Resp any]( + ctx context.Context, + client *Client, + path string, + opts *PaginationOptions, +) (*Resp, error) { + var res Resp + + // Ensure that opts.Page is >=1, if it isn't bunny.net will send a + // different response JSON object, that contains only a single Object, + // without items and paginations fields. Enforcing opts.page =>1 ensures + // that we always unmarshall into the same struct. + if opts == nil { + opts = &PaginationOptions{ + Page: DefaultPaginationPage, + PerPage: DefaultPaginationPerPage, + } + } else { + opts.ensureConstraints() + } + + req, err := client.newGetRequest(path, opts) + if err != nil { + return nil, err + } + + if err := client.sendRequest(ctx, req, &res); err != nil { + return nil, err + } + + return &res, nil +} diff --git a/resource_post.go b/resource_post.go new file mode 100644 index 0000000..25313a3 --- /dev/null +++ b/resource_post.go @@ -0,0 +1,33 @@ +package bunny + +import "context" + +func resourcePost[Resp any]( + ctx context.Context, + client *Client, + path string, + opts any, +) (*Resp, error) { + var res Resp + + req, err := client.newPostRequest(path, opts) + if err != nil { + return nil, err + } + + if err := client.sendRequest(ctx, req, &res); err != nil { + return nil, err + } + + return &res, nil +} + +func resourcePostWith204Response( + ctx context.Context, + client *Client, + path string, + opts any, +) error { + _, err := resourcePost[NoContentResponse](ctx, client, path, opts) + return err +} diff --git a/storagezone.go b/storagezone.go new file mode 100644 index 0000000..b1baa9a --- /dev/null +++ b/storagezone.go @@ -0,0 +1,8 @@ +package bunny + +// StorageZoneService communicates with the /storagezone API endpoint. +// +// Bunny.net API docs: https://docs.bunny.net/reference/storagezonepublic_index +type StorageZoneService struct { + client *Client +} diff --git a/storagezone_add.go b/storagezone_add.go new file mode 100644 index 0000000..ef96ccd --- /dev/null +++ b/storagezone_add.go @@ -0,0 +1,32 @@ +package bunny + +import "context" + +// StorageZoneAddOptions are the request parameters for the Get Storage Zone API endpoint. +// +// Bunny.net API docs: https://docs.bunny.net/reference/storagezonepublic_add +type StorageZoneAddOptions struct { + // The name of the storage zone + Name *string `json:"Name,omitempty"` + // The ID of the storage zone that the storage zone is linked to. + Region *string `json:"Region,omitempty"` + + // The origin URL of the storage zone where the files are fetched from (Optional) + OriginURL *string `json:"OriginUrl,omitempty"` + // The code of the main storage zone region (Optional) + ReplicationRegions []string `json:"ReplicationRegions,omitempty"` +} + +// Add creates a new Storage Zone. +// opts and the non-optional parameters in the struct must be specified for a successful request. +// On success the created StorageZone is returned. +// +// Bunny.net API docs: https://docs.bunny.net/reference/storagezonepublic_add +func (s *StorageZoneService) Add(ctx context.Context, opts *StorageZoneAddOptions) (*StorageZone, error) { + return resourcePost[StorageZone]( + ctx, + s.client, + "/storagezone", + opts, + ) +} diff --git a/storagezone_delete.go b/storagezone_delete.go new file mode 100644 index 0000000..677648d --- /dev/null +++ b/storagezone_delete.go @@ -0,0 +1,14 @@ +package bunny + +import ( + "context" + "fmt" +) + +// Delete removes the Pull Zone with the given id. +// +// Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_delete +func (s *StorageZoneService) Delete(ctx context.Context, id int64) error { + path := fmt.Sprintf("storagezone/%d", id) + return resourceDelete(ctx, s.client, path, nil) +} diff --git a/storagezone_get.go b/storagezone_get.go new file mode 100644 index 0000000..328640c --- /dev/null +++ b/storagezone_get.go @@ -0,0 +1,33 @@ +package bunny + +import ( + "context" + "fmt" +) + +// StorageZone represents the response of the the List and Get Storage Zone API endpoint. +// +// Bunny.net API docs: https://docs.bunny.net/reference/storagezonepublic_index2 https://docs.bunny.net/reference/storagezonepublic_index +type StorageZone struct { + ID *int64 `json:"Id,omitempty"` + + UserID *string `json:"UserId,omitempty"` + Name *string `json:"Name,omitempty"` + Password *string `json:"Password,omitempty"` + DateModified *string `json:"DateModified,omitempty"` + Deleted *bool `json:"Deleted,omitempty"` + StorageUsed *int64 `json:"StorageUsed,omitempty"` + FilesStored *int64 `json:"FilesStored,omitempty"` + Region *string `json:"Region,omitempty"` + ReplicationRegions []string `json:"ReplicationRegions,omitempty"` + PullZones []*PullZone `json:"PullZones,omitempty"` + ReadOnlyPassword *string `json:"ReadOnlyPassword,omitempty"` +} + +// Get retrieves the Storage Zone with the given id. +// +// Bunny.net API docs: https://docs.bunny.net/reference/storagezonepublic_index2 +func (s *StorageZoneService) Get(ctx context.Context, id int64) (*StorageZone, error) { + path := fmt.Sprintf("storagezone/%d", id) + return resourceGet[StorageZone](ctx, s.client, path) +} diff --git a/storagezone_list.go b/storagezone_list.go new file mode 100644 index 0000000..b967a41 --- /dev/null +++ b/storagezone_list.go @@ -0,0 +1,20 @@ +package bunny + +import "context" + +// StorageZones represents the response of the List Storage Zone API endpoint. +// +// Bunny.net API docs: https://docs.bunny.net/reference/storagezonepublic_index +type StorageZones PaginationReply[StorageZone] + +// List retrieves the Storage Zones. +// If opts is nil, DefaultPaginationPerPage and DefaultPaginationPage will be used. +// if opts.Page or or opts.PerPage is < 1, the related DefaultPagination values are used. +// +// Bunny.net API docs: https://docs.bunny.net/reference/storagezonepublic_index +func (s *StorageZoneService) List( + ctx context.Context, + opts *PaginationOptions, +) (*StorageZones, error) { + return resourceList[StorageZones](ctx, s.client, "/storagezone", opts) +} diff --git a/storagezone_test.go b/storagezone_test.go new file mode 100644 index 0000000..7ed1dbb --- /dev/null +++ b/storagezone_test.go @@ -0,0 +1,75 @@ +//go:build integrationtest +// +build integrationtest + +package bunny_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" + + bunny "github.com/simplesurance/bunny-go" +) + +func TestStorageZoneCRUD(t *testing.T) { + clt := newClient(t) + + szName := randomStorageZoneName() + szOrigin := "http://bunny.net" + szRegion := "NY" + szAddopts := bunny.StorageZoneAddOptions{ + Name: &szName, + OriginURL: &szOrigin, + Region: &szRegion, + ReplicationRegions: []string{"DE"}, + } + + listSzBefore, err := clt.StorageZone.List(context.Background(), nil) + require.NoError(t, err, "storage zone list failed before add") + + sz := createStorageZone(t, clt, &szAddopts) + + // get the newly created storage zone + getSz, err := clt.StorageZone.Get(context.Background(), *sz.ID) + require.NoError(t, err, "storage zone get failed after adding") + assert.NotNil(t, getSz.ID) + assert.Equal( + t, + getSz.ReplicationRegions[0], + "DE", + "storage zone replication region should be set correctly", + ) + + // update the storage zone + szUpdateOrigin := szOrigin + "/updated" + szUpdateRewrite404To200 := true + updateOpts := bunny.StorageZoneUpdateOptions{ + OriginURL: &szUpdateOrigin, + Rewrite404To200: &szUpdateRewrite404To200, + ReplicationRegions: []string{"LA"}, + } + updateErr := clt.StorageZone.Update(context.Background(), *sz.ID, &updateOpts) + assert.Nil(t, updateErr) + + // get the updated storage zone and validate updated properties + getUpdatedSz, err := clt.StorageZone.Get(context.Background(), *sz.ID) + assert.NotNil(t, getUpdatedSz.ID) + assert.Equal( + t, + "LA", + getUpdatedSz.ReplicationRegions[len(getUpdatedSz.ReplicationRegions) - 1], + "storage zone replication region should be updated correctly", + ) + + // check the total number of storage zones is the expected amount + listSzAfter, err := clt.StorageZone.List(context.Background(), nil) + require.NoError(t, err, "storage zone list failed after add") + assert.Equal( + t, + *listSzBefore.TotalItems + 1, + *listSzAfter.TotalItems, + "storage zones total items should increase by exactly 1", + ) +} diff --git a/storagezone_update.go b/storagezone_update.go new file mode 100644 index 0000000..ee062d1 --- /dev/null +++ b/storagezone_update.go @@ -0,0 +1,27 @@ +package bunny + +import ( + "context" + "fmt" +) + +// StorageZoneUpdateOptions represents the request parameters for the Update Storage +// Zone API endpoint. +// +// Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_updatepullzone +type StorageZoneUpdateOptions struct { + // NOTE: the naming in the Bunny API for this property is inconsistent. + // In the update call its `ReplicationZones` but everywhere else its + // referred to as `ReplicationRegions`. + ReplicationRegions []string `json:"ReplicationZones,omitempty"` + OriginURL *string `json:"OriginUrl,omitempty"` + Custom404FilePath *string `json:"Custom404FilePath,omitempty"` + Rewrite404To200 *bool `json:"Rewrite404To200,omitempty"` +} + +// Update changes the configuration the Storage-Zone with the given ID. +// Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_updatepullzone +func (s *StorageZoneService) Update(ctx context.Context, id int64, opts *StorageZoneUpdateOptions) error { + path := fmt.Sprintf("storagezone/%d", id) + return resourcePostWith204Response(ctx, s.client, path, opts) +} From a2d87539aeae1aa811305ea394ab05ed0cddca5b Mon Sep 17 00:00:00 2001 From: Jacob Spizziri Date: Fri, 3 Jun 2022 07:56:47 -0500 Subject: [PATCH 3/4] test(integrationtest): fix typo in integration test --- integrationtest_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrationtest_test.go b/integrationtest_test.go index cf4a788..8e7fb03 100644 --- a/integrationtest_test.go +++ b/integrationtest_test.go @@ -57,7 +57,7 @@ func createPullZone(t *testing.T, clt *bunny.Client, opts *bunny.PullZoneAddOpti t.Cleanup(func() { err := clt.PullZone.Delete(context.Background(), *pz.ID) if err != nil { - t.Errorf("coult not delete pull zone (id: %d, name: %q) on test cleanup: %s", *pz.ID, *pz.Name, err) + t.Errorf("could not delete pull zone (id: %d, name: %q) on test cleanup: %s", *pz.ID, *pz.Name, err) return } From 5d45067d83e41ec43cdcfc7ddfedf1877c5d20a4 Mon Sep 17 00:00:00 2001 From: Jacob Spizziri Date: Mon, 6 Jun 2022 12:02:33 -0500 Subject: [PATCH 4/4] feat(pullzone): upgrade pullzone to take advantaged of the abstract resource functions upgrade pullzone to take advantaged of the abstract resource functions. a breaking change has been introduced in this version by removing PullZone specific pagination structs in favor of more abstract ones. BREAKING CHANGE: `PullZonePaginationOptions` and `PullZonePaginationReply` have been replaced with `PaginationOptions` and `PaginationReply` --- pullzone_add.go | 18 +++----- pullzone_add_custom_certificate.go | 10 ++-- pullzone_add_custom_hostname.go | 8 +--- pullzone_delete.go | 8 +--- pullzone_edgerule_add_update.go | 8 +--- pullzone_edgerule_delete.go | 8 +--- pullzone_edgerule_set_enabled.go | 8 +--- pullzone_get.go | 14 +----- pullzone_list.go | 74 +++--------------------------- pullzone_remove_certificate.go | 10 ++-- pullzone_remove_custom_hostname.go | 8 +--- pullzone_set_force_ssl.go | 8 +--- pullzone_update.go | 21 ++++----- 13 files changed, 42 insertions(+), 161 deletions(-) diff --git a/pullzone_add.go b/pullzone_add.go index 8ce107c..8b90255 100644 --- a/pullzone_add.go +++ b/pullzone_add.go @@ -23,16 +23,10 @@ type PullZoneAddOptions struct { // // Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_add func (s *PullZoneService) Add(ctx context.Context, opts *PullZoneAddOptions) (*PullZone, error) { - var res PullZone - - req, err := s.client.newPostRequest("/pullzone", opts) - if err != nil { - return nil, err - } - - if err := s.client.sendRequest(ctx, req, &res); err != nil { - return nil, err - } - - return &res, nil + return resourcePost[PullZone]( + ctx, + s.client, + "/pullzone", + opts, + ) } diff --git a/pullzone_add_custom_certificate.go b/pullzone_add_custom_certificate.go index f6b3a2b..4a49518 100644 --- a/pullzone_add_custom_certificate.go +++ b/pullzone_add_custom_certificate.go @@ -17,11 +17,7 @@ type PullZoneAddCustomCertificateOptions struct { // AddCustomCertificate represents the Add Custom Certificate API Endpoint. // // Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_addcertificate -func (s *PullZoneService) AddCustomCertificate(ctx context.Context, pullZoneID int64, options *PullZoneAddCustomCertificateOptions) error { - req, err := s.client.newPostRequest(fmt.Sprintf("/pullzone/%d/addCertificate", pullZoneID), options) - if err != nil { - return err - } - - return s.client.sendRequest(ctx, req, nil) +func (s *PullZoneService) AddCustomCertificate(ctx context.Context, pullZoneID int64, opts *PullZoneAddCustomCertificateOptions) error { + path := fmt.Sprintf("/pullzone/%d/addCertificate", pullZoneID) + return resourcePostWith204Response(ctx, s.client, path, opts) } diff --git a/pullzone_add_custom_hostname.go b/pullzone_add_custom_hostname.go index 64661fc..d9066b7 100644 --- a/pullzone_add_custom_hostname.go +++ b/pullzone_add_custom_hostname.go @@ -18,10 +18,6 @@ type AddCustomHostnameOptions struct { // // Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_addhostname func (s *PullZoneService) AddCustomHostname(ctx context.Context, pullZoneID int64, opts *AddCustomHostnameOptions) error { - req, err := s.client.newPostRequest(fmt.Sprintf("pullzone/%d/addHostname", pullZoneID), opts) - if err != nil { - return err - } - - return s.client.sendRequest(ctx, req, nil) + path := fmt.Sprintf("pullzone/%d/addHostname", pullZoneID) + return resourcePostWith204Response(ctx, s.client, path, opts) } diff --git a/pullzone_delete.go b/pullzone_delete.go index a5ea4e3..bdb410e 100644 --- a/pullzone_delete.go +++ b/pullzone_delete.go @@ -9,10 +9,6 @@ import ( // // Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_delete func (s *PullZoneService) Delete(ctx context.Context, id int64) error { - req, err := s.client.newDeleteRequest(fmt.Sprintf("pullzone/%d", id), nil) - if err != nil { - return err - } - - return s.client.sendRequest(ctx, req, nil) + path := fmt.Sprintf("pullzone/%d", id) + return resourceDelete(ctx, s.client, path, nil) } diff --git a/pullzone_edgerule_add_update.go b/pullzone_edgerule_add_update.go index a7bd3f9..e0e737e 100644 --- a/pullzone_edgerule_add_update.go +++ b/pullzone_edgerule_add_update.go @@ -26,10 +26,6 @@ type AddOrUpdateEdgeRuleOptions struct { // // Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_addedgerule func (s *PullZoneService) AddOrUpdateEdgeRule(ctx context.Context, pullZoneID int64, opts *AddOrUpdateEdgeRuleOptions) error { - req, err := s.client.newPostRequest(fmt.Sprintf("pullzone/%d/edgerules/addOrUpdate", pullZoneID), opts) - if err != nil { - return err - } - - return s.client.sendRequest(ctx, req, nil) + path := fmt.Sprintf("pullzone/%d/edgerules/addOrUpdate", pullZoneID) + return resourcePostWith204Response(ctx, s.client, path, opts) } diff --git a/pullzone_edgerule_delete.go b/pullzone_edgerule_delete.go index 0169e73..a5aea1c 100644 --- a/pullzone_edgerule_delete.go +++ b/pullzone_edgerule_delete.go @@ -11,10 +11,6 @@ import ( // // Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_deleteedgerule func (s *PullZoneService) DeleteEdgeRule(ctx context.Context, pullZoneID int64, edgeRuleGUID string) error { - req, err := s.client.newDeleteRequest(fmt.Sprintf("pullzone/%d/edgerules/%s", pullZoneID, edgeRuleGUID), nil) - if err != nil { - return err - } - - return s.client.sendRequest(ctx, req, nil) + path := fmt.Sprintf("pullzone/%d/edgerules/%s", pullZoneID, edgeRuleGUID) + return resourceDelete(ctx, s.client, path, nil) } diff --git a/pullzone_edgerule_set_enabled.go b/pullzone_edgerule_set_enabled.go index fbd0c7e..b46eebf 100644 --- a/pullzone_edgerule_set_enabled.go +++ b/pullzone_edgerule_set_enabled.go @@ -28,10 +28,6 @@ func (s *PullZoneService) SetEdgeRuleEnabled(ctx context.Context, pullZoneID int } } - req, err := s.client.newPostRequest(fmt.Sprintf("pullzone/%d/edgerules/%s/setEdgeRuleEnabled", pullZoneID, edgeRuleGUID), opts) - if err != nil { - return err - } - - return s.client.sendRequest(ctx, req, nil) + path := fmt.Sprintf("pullzone/%d/edgerules/%s/setEdgeRuleEnabled", pullZoneID, edgeRuleGUID) + return resourcePostWith204Response(ctx, s.client, path, opts) } diff --git a/pullzone_get.go b/pullzone_get.go index 2347523..0c715e1 100644 --- a/pullzone_get.go +++ b/pullzone_get.go @@ -163,16 +163,6 @@ type EdgeRule struct { // // Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_index2 func (s *PullZoneService) Get(ctx context.Context, id int64) (*PullZone, error) { - var res PullZone - - req, err := s.client.newGetRequest(fmt.Sprintf("pullzone/%d", id), nil) - if err != nil { - return nil, err - } - - if err := s.client.sendRequest(ctx, req, &res); err != nil { - return nil, err - } - - return &res, err + path := fmt.Sprintf("pullzone/%d", id) + return resourceGet[PullZone](ctx, s.client, path) } diff --git a/pullzone_list.go b/pullzone_list.go index dd340da..12ea830 100644 --- a/pullzone_list.go +++ b/pullzone_list.go @@ -2,81 +2,19 @@ package bunny import "context" -const ( - // DefaultPaginationPage is the default value that is used for - // PullZonePaginationOptions.Page if it is unset. - DefaultPaginationPage = 1 - // DefaultPaginationPerPage is the default value that is used for - // PullZonePaginationOptions.PerPage if it is unset. - DefaultPaginationPerPage = 1000 -) - // PullZones represents the response of the List Pull Zone API endpoint. // // Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_index -type PullZones struct { - Items []*PullZone `json:"Items,omitempty"` - PullZonePaginationReply -} - -// PullZonePaginationReply represents the pagination information contained in a -// Pull Zone List API endpoint response. -// -// Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_index -type PullZonePaginationReply struct { - CurrentPage *int32 `json:"CurrentPage"` - TotalItems *int32 `json:"TotalItems"` - HasMoreItems *bool `json:"HasMoreItems"` -} - -// PullZonePaginationOptions specifies optional parameters for List APIs. -type PullZonePaginationOptions struct { - // Page the page to return - Page int32 `url:"page,omitempty"` - // PerPage how many entries to return per page - PerPage int32 `url:"per_page,omitempty"` -} +type PullZones PaginationReply[PullZone] // List retrieves the Pull Zones. // If opts is nil, DefaultPaginationPerPage and DefaultPaginationPage will be used. // if opts.Page or or opts.PerPage is < 1, the related DefaultPagination values are used. // // Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_index -func (s *PullZoneService) List(ctx context.Context, opts *PullZonePaginationOptions) (*PullZones, error) { - var res PullZones - - // Ensure that opts.Page is >=1, if it isn't bunny.net will send a - // different response JSON object, that contains only a single - // PullZone, without items and paginations fields. - // Enforcing opts.page =>1 ensures that we always unmarshal into the - // same struct. - if opts == nil { - opts = &PullZonePaginationOptions{ - Page: DefaultPaginationPage, - PerPage: DefaultPaginationPerPage, - } - } else { - opts.ensureConstraints() - } - - req, err := s.client.newGetRequest("/pullzone", opts) - if err != nil { - return nil, err - } - - if err := s.client.sendRequest(ctx, req, &res); err != nil { - return nil, err - } - - return &res, nil -} - -func (p *PullZonePaginationOptions) ensureConstraints() { - if p.Page < 1 { - p.Page = DefaultPaginationPage - } - - if p.PerPage < 1 { - p.PerPage = DefaultPaginationPerPage - } +func (s *PullZoneService) List( + ctx context.Context, + opts *PaginationOptions, +) (*PullZones, error) { + return resourceList[PullZones](ctx, s.client, "/pullzone", opts) } diff --git a/pullzone_remove_certificate.go b/pullzone_remove_certificate.go index dfecb2c..500c0ab 100644 --- a/pullzone_remove_certificate.go +++ b/pullzone_remove_certificate.go @@ -16,11 +16,7 @@ type RemoveCertificateOptions struct { // RemoveCertificate represents the Remove Certificate API Endpoint. // // Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_removecertificate -func (s *PullZoneService) RemoveCertificate(ctx context.Context, pullZoneID int64, options *RemoveCertificateOptions) error { - req, err := s.client.newDeleteRequest(fmt.Sprintf("/pullzone/%d/removeCertificate", pullZoneID), options) - if err != nil { - return err - } - - return s.client.sendRequest(ctx, req, nil) +func (s *PullZoneService) RemoveCertificate(ctx context.Context, pullZoneID int64, opts *RemoveCertificateOptions) error { + path := fmt.Sprintf("/pullzone/%d/removeCertificate", pullZoneID) + return resourceDelete(ctx, s.client, path, opts) } diff --git a/pullzone_remove_custom_hostname.go b/pullzone_remove_custom_hostname.go index 2d382d8..1fd51f1 100644 --- a/pullzone_remove_custom_hostname.go +++ b/pullzone_remove_custom_hostname.go @@ -18,10 +18,6 @@ type RemoveCustomHostnameOptions struct { // // Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_removehostname func (s *PullZoneService) RemoveCustomHostname(ctx context.Context, pullZoneID int64, opts *RemoveCustomHostnameOptions) error { - req, err := s.client.newDeleteRequest(fmt.Sprintf("pullzone/%d/removeHostname", pullZoneID), opts) - if err != nil { - return err - } - - return s.client.sendRequest(ctx, req, nil) + path := fmt.Sprintf("pullzone/%d/removeHostname", pullZoneID) + return resourceDelete(ctx, s.client, path, opts) } diff --git a/pullzone_set_force_ssl.go b/pullzone_set_force_ssl.go index 22072c7..c57d5b8 100644 --- a/pullzone_set_force_ssl.go +++ b/pullzone_set_force_ssl.go @@ -17,10 +17,6 @@ type SetForceSSLOptions struct { // // Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_setforcessl func (s *PullZoneService) SetForceSSL(ctx context.Context, pullzoneID int64, opts *SetForceSSLOptions) error { - req, err := s.client.newPostRequest(fmt.Sprintf("pullzone/%d/setForceSSL", pullzoneID), opts) - if err != nil { - return err - } - - return s.client.sendRequest(ctx, req, nil) + path := fmt.Sprintf("pullzone/%d/setForceSSL", pullzoneID) + return resourcePostWith204Response(ctx, s.client, path, opts) } diff --git a/pullzone_update.go b/pullzone_update.go index 5706f23..a9966a6 100644 --- a/pullzone_update.go +++ b/pullzone_update.go @@ -107,17 +107,12 @@ type PullZoneUpdateOptions struct { // Update changes the configuration the Pull-Zone with the given ID. // The updated Pull Zone is returned. // Bunny.net API docs: https://docs.bunny.net/reference/pullzonepublic_updatepullzone -func (s *PullZoneService) Update(ctx context.Context, id int64, pullZone *PullZoneUpdateOptions) (*PullZone, error) { - var res PullZone - - req, err := s.client.newPostRequest(fmt.Sprintf("pullzone/%d", id), pullZone) - if err != nil { - return nil, err - } - - if err := s.client.sendRequest(ctx, req, &res); err != nil { - return nil, err - } - - return &res, err +func (s *PullZoneService) Update(ctx context.Context, id int64, opts *PullZoneUpdateOptions) (*PullZone, error) { + path := fmt.Sprintf("pullzone/%d", id) + return resourcePost[PullZone]( + ctx, + s.client, + path, + opts, + ) }