Skip to content

Commit

Permalink
Add CRUD support for Images Variants
Browse files Browse the repository at this point in the history
  • Loading branch information
hwrdprkns committed Feb 5, 2024
1 parent 6cf3006 commit 6f28535
Show file tree
Hide file tree
Showing 5 changed files with 364 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/1494.txt
@@ -0,0 +1,3 @@
```release-note:enhancement
images_variants: Add support for Images Variants CRUD operations
```
161 changes: 161 additions & 0 deletions images_variants.go
@@ -0,0 +1,161 @@
package cloudflare

import (
"context"
"fmt"
"net/http"

"github.com/goccy/go-json"
)

type ImagesVariant struct {
ID string `json:"id,omitempty"`
NeverRequireSignedURLs bool `json:"neverRequireSignedURLs,omitempty"`
Options ImagesVariantsOptions `json:"options,omitempty"`
}

type ImagesVariantsOptions struct {
Fit string `json:"fit,omitempty"`
Height int `json:"height,omitempty"`
Metadata string `json:"metadata,omitempty"`
Width int `json:"width,omitempty"`
}

type ListImageVariantsParams struct{}

type ListImagesVariantsResponse struct {
Result ListImageVariantsResult `json:"result,omitempty"`
Response
}

type ListImageVariantsResult struct {
ImagesVariants map[string]ImagesVariant `json:"variants,omitempty"`
}

type CreateImagesVariantParams struct {
ImagesVariant
}

type UpdateImagesVariantParams struct {
ID string `json:"-"`
NeverRequireSignedURLs bool `json:"neverRequireSignedURLs,omitempty"`
Options ImagesVariantsOptions `json:"options,omitempty"`
}

type ImagesVariantResult struct {
Variant ImagesVariant `json:"variant,omitempty"`
}

type ImagesVariantResponse struct {
Result ImagesVariantResult `json:"result,omitempty"`
Response
}

// Lists existing variants.
//
// API Reference: https://developers.cloudflare.com/api/operations/cloudflare-images-variants-list-variants
func (api *API) ListImagesVariants(ctx context.Context, rc *ResourceContainer, params ListImageVariantsParams) (map[string]ImagesVariant, error) {
if rc.Identifier == "" {
return map[string]ImagesVariant{}, ErrMissingAccountID
}

baseURL := fmt.Sprintf("/accounts/%s/images/v1/variants", rc.Identifier)
res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil)
if err != nil {
return map[string]ImagesVariant{}, err
}

var listImageVariantsResponse ListImagesVariantsResponse
err = json.Unmarshal(res, &listImageVariantsResponse)
if err != nil {
return map[string]ImagesVariant{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}

return listImageVariantsResponse.Result.ImagesVariants, nil
}

// Fetch details for a single variant.
//
// API Reference: https://developers.cloudflare.com/api/operations/cloudflare-images-variants-variant-details
func (api *API) GetImagesVariantDetails(ctx context.Context, rc *ResourceContainer, variantID string) (ImagesVariant, error) {
if rc.Identifier == "" {
return ImagesVariant{}, ErrMissingAccountID
}

baseURL := fmt.Sprintf("/accounts/%s/images/v1/variants/%s", rc.Identifier, variantID)
res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil)
if err != nil {
return ImagesVariant{}, err
}

var imagesVariantDetailResponse ImagesVariantResponse
err = json.Unmarshal(res, &imagesVariantDetailResponse)
if err != nil {
return ImagesVariant{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}

return imagesVariantDetailResponse.Result.Variant, nil
}

// Specify variants that allow you to resize images for different use cases.
//
// API Reference: https://developers.cloudflare.com/api/operations/cloudflare-images-variants-create-a-variant
func (api *API) CreateImagesVariant(ctx context.Context, rc *ResourceContainer, params CreateImagesVariantParams) (ImagesVariant, error) {
if rc.Identifier == "" {
return ImagesVariant{}, ErrMissingAccountID
}

baseURL := fmt.Sprintf("/accounts/%s/images/v1/variants", rc.Identifier)
res, err := api.makeRequestContext(ctx, http.MethodPost, baseURL, params.ImagesVariant)
if err != nil {
return ImagesVariant{}, err
}

var createImagesVariantResponse ImagesVariantResponse
err = json.Unmarshal(res, &createImagesVariantResponse)
if err != nil {
return ImagesVariant{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}

return createImagesVariantResponse.Result.Variant, nil
}

// Deleting a variant purges the cache for all images associated with the variant.
//
// API Reference: https://developers.cloudflare.com/api/operations/cloudflare-images-variants-variant-details
func (api *API) DeleteImagesVariant(ctx context.Context, rc *ResourceContainer, variantID string) error {
if rc.Identifier == "" {
return ErrMissingAccountID
}

baseURL := fmt.Sprintf("/accounts/%s/images/v1/variants/%s", rc.Identifier, variantID)
_, err := api.makeRequestContext(ctx, http.MethodDelete, baseURL, nil)
if err != nil {
return fmt.Errorf("%s: %w", errMakeRequestError, err)
}

return nil
}

// Updating a variant purges the cache for all images associated with the variant.
//
// API Reference: https://developers.cloudflare.com/api/operations/cloudflare-images-variants-variant-details
func (api *API) UpdateImagesVariant(ctx context.Context, rc *ResourceContainer, params UpdateImagesVariantParams) (ImagesVariant, error) {
if rc.Identifier == "" {
return ImagesVariant{}, ErrMissingAccountID
}

baseURL := fmt.Sprintf("/accounts/%s/images/v1/variants/%s", rc.Identifier, params.ID)
res, err := api.makeRequestContext(ctx, http.MethodPatch, baseURL, params)
if err != nil {
return ImagesVariant{}, err
}

var imagesVariantDetailResponse ImagesVariantResponse
err = json.Unmarshal(res, &imagesVariantDetailResponse)
if err != nil {
return ImagesVariant{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}

return imagesVariantDetailResponse.Result.Variant, nil
}
164 changes: 164 additions & 0 deletions images_variants_test.go
@@ -0,0 +1,164 @@
package cloudflare

import (
"context"
"fmt"
"net/http"
"testing"

"github.com/stretchr/testify/assert"
)

const (
testImagesVariantID = "hero"
)

func TestImageVariants_ListVariants(t *testing.T) {
setup()
defer teardown()

handler := func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method)
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, loadFixture("images_variants", "single_list"))
}

mux.HandleFunc("/accounts/"+testAccountID+"/images/v1/variants", handler)

want := map[string]ImagesVariant{
"hero": {
ID: "hero",
NeverRequireSignedURLs: true,
Options: ImagesVariantsOptions{
Fit: "scale-down",
Height: 768,
Width: 1366,
Metadata: "none",
},
},
}

got, err := client.ListImagesVariants(context.Background(), AccountIdentifier(testAccountID), ListImageVariantsParams{})
if assert.NoError(t, err) {
assert.Equal(t, want, got)
}
}

func TestImageVariants_Delete(t *testing.T) {
setup()
defer teardown()

handler := func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodDelete, r.Method, "Expected method '%s', got %s", http.MethodDelete, r.Method)
w.Header().Set("content-type", "application/json")
fmt.Fprint(w, `{
"success": true,
"errors": [],
"messages": [],
"result": {}
}`)
}

url := fmt.Sprintf("/accounts/%s/images/v1/variants/%s", testAccountID, testImagesVariantID)
mux.HandleFunc(url, handler)

err := client.DeleteImagesVariant(context.Background(), AccountIdentifier(testAccountID), testImagesVariantID)
assert.NoError(t, err)
}

func TestImagesVariants_GetDetails(t *testing.T) {
setup()
defer teardown()

handler := func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodGet, r.Method, "Expected method '%s', got %s", http.MethodGet, r.Method)
w.Header().Set("content-type", "application/json")
fmt.Fprint(w, loadFixture("images_variants", "single_full"))
}

url := fmt.Sprintf("/accounts/%s/images/v1/variants/%s", testAccountID, testImagesVariantID)
mux.HandleFunc(url, handler)

want := ImagesVariant{
ID: "hero",
NeverRequireSignedURLs: true,
Options: ImagesVariantsOptions{
Fit: "scale-down",
Height: 768,
Width: 1366,
Metadata: "none",
},
}

got, err := client.GetImagesVariantDetails(context.Background(), AccountIdentifier(testAccountID), testImagesVariantID)
if assert.NoError(t, err) {
assert.Equal(t, want, got)
}
}

func TestImagesVariants_CreateVariant(t *testing.T) {
setup()
defer teardown()

handler := func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodPost, r.Method, "Expected method '%s', got %s", http.MethodPost, r.Method)
w.Header().Set("content-type", "application/json")
fmt.Fprint(w, loadFixture("images_variants", "single_full"))
}

url := fmt.Sprintf("/accounts/%s/images/v1/variants", testAccountID)
mux.HandleFunc(url, handler)

want := ImagesVariant{
ID: "hero",
NeverRequireSignedURLs: true,
Options: ImagesVariantsOptions{
Fit: "scale-down",
Height: 768,
Width: 1366,
Metadata: "none",
},
}

got, err := client.CreateImagesVariant(context.Background(), AccountIdentifier(testAccountID), CreateImagesVariantParams{
ImagesVariant: want,
})
if assert.NoError(t, err) {
assert.Equal(t, want, got)
}
}

func TestImagesVariants_UpdateVariant(t *testing.T) {
setup()
defer teardown()

handler := func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodPatch, r.Method, "Expected method '%s', got %s", http.MethodPatch, r.Method)
w.Header().Set("content-type", "application/json")
fmt.Fprint(w, loadFixture("images_variants", "single_full"))
}

url := fmt.Sprintf("/accounts/%s/images/v1/variants/%s", testAccountID, testImagesVariantID)
mux.HandleFunc(url, handler)

want := ImagesVariant{
ID: "hero",
NeverRequireSignedURLs: true,
Options: ImagesVariantsOptions{
Fit: "scale-down",
Height: 768,
Width: 1366,
Metadata: "none",
},
}

got, err := client.UpdateImagesVariant(context.Background(), AccountIdentifier(testAccountID), UpdateImagesVariantParams{
ID: want.ID,
NeverRequireSignedURLs: want.NeverRequireSignedURLs,
Options: want.Options,
})

if assert.NoError(t, err) {
assert.Equal(t, want, got)
}
}
17 changes: 17 additions & 0 deletions testdata/fixtures/images_variants/single_full.json
@@ -0,0 +1,17 @@
{
"errors": [],
"messages": [],
"result": {
"variant": {
"id": "hero",
"neverRequireSignedURLs": true,
"options": {
"fit": "scale-down",
"height": 768,
"metadata": "none",
"width": 1366
}
}
},
"success": true
}
19 changes: 19 additions & 0 deletions testdata/fixtures/images_variants/single_list.json
@@ -0,0 +1,19 @@
{
"errors": [],
"messages": [],
"result": {
"variants": {
"hero": {
"id": "hero",
"neverRequireSignedURLs": true,
"options": {
"fit": "scale-down",
"height": 768,
"metadata": "none",
"width": 1366
}
}
}
},
"success": true
}

0 comments on commit 6f28535

Please sign in to comment.