Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Workers for Platforms APIs #1508

Merged
merged 1 commit into from Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changelog/1508.txt
@@ -0,0 +1,7 @@
```release-note:enhancement
workers_for_platforms: Add ability to list Workers for Platforms namespaces, get a namespace, create a new namespace or delete a namespace.
```

```release-note:enhancement
workers: Add Workers for Platforms support for getting a Worker, content and bindings
```
13 changes: 12 additions & 1 deletion workers.go
Expand Up @@ -262,6 +262,14 @@ func (api *API) DeleteWorker(ctx context.Context, rc *ResourceContainer, params
//
// API reference: https://developers.cloudflare.com/api/operations/worker-script-download-worker
func (api *API) GetWorker(ctx context.Context, rc *ResourceContainer, scriptName string) (WorkerScriptResponse, error) {
return api.GetWorkerWithDispatchNamespace(ctx, rc, scriptName, "")
}

// GetWorker fetch raw script content for your worker returns string containing
// worker code js.
//
// API reference: https://developers.cloudflare.com/api/operations/worker-script-download-worker
func (api *API) GetWorkerWithDispatchNamespace(ctx context.Context, rc *ResourceContainer, scriptName string, dispatchNamespace string) (WorkerScriptResponse, error) {
if rc.Level != AccountRouteLevel {
return WorkerScriptResponse{}, ErrRequiredAccountLevelResourceContainer
}
Expand All @@ -271,6 +279,9 @@ func (api *API) GetWorker(ctx context.Context, rc *ResourceContainer, scriptName
}

uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s", rc.Identifier, scriptName)
if dispatchNamespace != "" {
uri = fmt.Sprintf("/accounts/%s/workers/dispatch/namespaces/%s/scripts/%s/content", rc.Identifier, dispatchNamespace, scriptName)
}
res, err := api.makeRequestContextWithHeadersComplete(ctx, http.MethodGet, uri, nil, nil)
var r WorkerScriptResponse
if err != nil {
Expand Down Expand Up @@ -354,7 +365,7 @@ func (api *API) UploadWorker(ctx context.Context, rc *ResourceContainer, params
}

uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s", rc.Identifier, params.ScriptName)
if params.DispatchNamespaceName != nil {
if params.DispatchNamespaceName != nil && *params.DispatchNamespaceName != "" {
uri = fmt.Sprintf("/accounts/%s/workers/dispatch/namespaces/%s/scripts/%s", rc.Identifier, *params.DispatchNamespaceName, params.ScriptName)
}

Expand Down
12 changes: 8 additions & 4 deletions workers_bindings.go
Expand Up @@ -49,7 +49,8 @@ const (
)

type ListWorkerBindingsParams struct {
ScriptName string
ScriptName string
DispatchNamespace *string
}

// WorkerBindingListItem a struct representing an individual binding in a list of bindings.
Expand Down Expand Up @@ -426,9 +427,9 @@ func (b WorkerD1DatabaseBinding) serialize(bindingName string) (workerBindingMet
}

return workerBindingMeta{
"name": bindingName,
"type": b.Type(),
"id": b.DatabaseID,
"name": bindingName,
"type": b.Type(),
"id": b.DatabaseID,
}, nil, nil
}

Expand Down Expand Up @@ -468,6 +469,9 @@ func (api *API) ListWorkerBindings(ctx context.Context, rc *ResourceContainer, p
}

uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/bindings", rc.Identifier, params.ScriptName)
if params.DispatchNamespace != nil && *params.DispatchNamespace != "" {
uri = fmt.Sprintf("/accounts/%s/workers/dispatch/namespaces/%s/scripts/%s/bindings", rc.Identifier, *params.DispatchNamespace, params.ScriptName)
}

var jsonRes struct {
Response
Expand Down
85 changes: 85 additions & 0 deletions workers_bindings_test.go
Expand Up @@ -108,6 +108,91 @@ func TestListWorkerBindings(t *testing.T) {
assert.Equal(t, WorkerD1DataseBindingType, res.BindingList[8].Binding.Type())
}

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

mux.HandleFunc("/accounts/"+testAccountID+"/workers/dispatch/namespaces/my-namespace/scripts/my-script/bindings", 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, listBindingsResponseData)
})

res, err := client.ListWorkerBindings(context.Background(), AccountIdentifier(testAccountID), ListWorkerBindingsParams{
ScriptName: "my-script",
DispatchNamespace: &[]string{"my-namespace"}[0],
})
assert.NoError(t, err)

assert.Equal(t, successResponse, res.Response)
assert.Equal(t, 9, len(res.BindingList))

assert.Equal(t, res.BindingList[0], WorkerBindingListItem{
Name: "MY_KV",
Binding: WorkerKvNamespaceBinding{
NamespaceID: "89f5f8fd93f94cb98473f6f421aa3b65",
},
})
assert.Equal(t, WorkerKvNamespaceBindingType, res.BindingList[0].Binding.Type())

// WASM binding - No binding content endpoint exists for WfP

assert.Equal(t, res.BindingList[2], WorkerBindingListItem{
Name: "MY_PLAIN_TEXT",
Binding: WorkerPlainTextBinding{
Text: "text",
},
})
assert.Equal(t, WorkerPlainTextBindingType, res.BindingList[2].Binding.Type())

assert.Equal(t, res.BindingList[3], WorkerBindingListItem{
Name: "MY_SECRET_TEXT",
Binding: WorkerSecretTextBinding{},
})
assert.Equal(t, WorkerSecretTextBindingType, res.BindingList[3].Binding.Type())

environment := "MY_ENVIRONMENT"
assert.Equal(t, res.BindingList[4], WorkerBindingListItem{
Name: "MY_SERVICE_BINDING",
Binding: WorkerServiceBinding{
Service: "MY_SERVICE",
Environment: &environment,
},
})
assert.Equal(t, WorkerServiceBindingType, res.BindingList[4].Binding.Type())

assert.Equal(t, res.BindingList[5], WorkerBindingListItem{
Name: "MY_NEW_BINDING",
Binding: WorkerInheritBinding{},
})
assert.Equal(t, WorkerInheritBindingType, res.BindingList[5].Binding.Type())

assert.Equal(t, res.BindingList[6], WorkerBindingListItem{
Name: "MY_BUCKET",
Binding: WorkerR2BucketBinding{
BucketName: "bucket",
},
})
assert.Equal(t, WorkerR2BucketBindingType, res.BindingList[6].Binding.Type())

assert.Equal(t, res.BindingList[7], WorkerBindingListItem{
Name: "MY_DATASET",
Binding: WorkerAnalyticsEngineBinding{
Dataset: "my_dataset",
},
})

assert.Equal(t, WorkerAnalyticsEngineBindingType, res.BindingList[7].Binding.Type())

assert.Equal(t, res.BindingList[8], WorkerBindingListItem{
Name: "MY_DATABASE",
Binding: WorkerD1DatabaseBinding{
DatabaseID: "cef5331f-e5c7-4c8a-a415-7908ae45f92a",
},
})
assert.Equal(t, WorkerD1DataseBindingType, res.BindingList[8].Binding.Type())
}

func ExampleUnsafeBinding() {
pretty := func(meta workerBindingMeta) string {
buf := bytes.NewBufferString("")
Expand Down
139 changes: 139 additions & 0 deletions workers_for_platforms.go
@@ -0,0 +1,139 @@
package cloudflare

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

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

type WorkersForPlatformsDispatchNamespace struct {
NamespaceId string `json:"namespace_id"`
NamespaceName string `json:"namespace_name"`
CreatedOn *time.Time `json:"created_on,omitempty"`
CreatedBy string `json:"created_by"`
ModifiedOn *time.Time `json:"modified_on,omitempty"`
ModifiedBy string `json:"modified_by"`
}

type ListWorkersForPlatformsDispatchNamespaceResponse struct {
Response
Result []WorkersForPlatformsDispatchNamespace `json:"result"`
}

type GetWorkersForPlatformsDispatchNamespaceResponse struct {
Response
Result WorkersForPlatformsDispatchNamespace `json:"result"`
}

type CreateWorkersForPlatformsDispatchNamespaceParams struct {
Name string `json:"name"`
}

// ListWorkersForPlatformsDispatchNamespaces lists the dispatch namespaces.
//
// API reference: https://developers.cloudflare.com/api/operations/namespace-worker-list
func (api *API) ListWorkersForPlatformsDispatchNamespaces(ctx context.Context, rc *ResourceContainer) (*ListWorkersForPlatformsDispatchNamespaceResponse, error) {
if rc.Level != AccountRouteLevel {
return nil, ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return nil, ErrMissingAccountID
}

uri := fmt.Sprintf("/accounts/%s/workers/dispatch/namespaces", rc.Identifier)
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)

var r ListWorkersForPlatformsDispatchNamespaceResponse
if err != nil {
return nil, err
}

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

return &r, nil
}

// GetWorkersForPlatformsDispatchNamespace gets a specific dispatch namespace.
//
// API reference: https://developers.cloudflare.com/api/operations/namespace-worker-get-namespace
func (api *API) GetWorkersForPlatformsDispatchNamespace(ctx context.Context, rc *ResourceContainer, name string) (*GetWorkersForPlatformsDispatchNamespaceResponse, error) {
if rc.Level != AccountRouteLevel {
return nil, ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return nil, ErrMissingAccountID
}

uri := fmt.Sprintf("/accounts/%s/workers/dispatch/namespaces/%s", rc.Identifier, name)
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)

var r GetWorkersForPlatformsDispatchNamespaceResponse
if err != nil {
return nil, err
}

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

return &r, nil
}

// CreateWorkersForPlatformsDispatchNamespace creates a new dispatch namespace.
//
// API reference: https://developers.cloudflare.com/api/operations/namespace-worker-create
func (api *API) CreateWorkersForPlatformsDispatchNamespace(ctx context.Context, rc *ResourceContainer, params CreateWorkersForPlatformsDispatchNamespaceParams) (*GetWorkersForPlatformsDispatchNamespaceResponse, error) {
if rc.Level != AccountRouteLevel {
return nil, ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return nil, ErrMissingAccountID
}

uri := fmt.Sprintf("/accounts/%s/workers/dispatch/namespaces", rc.Identifier)
res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params)

var r GetWorkersForPlatformsDispatchNamespaceResponse
if err != nil {
return nil, err
}

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

return &r, nil
}

// DeleteWorkersForPlatformsDispatchNamespace deletes a dispatch namespace.
//
// API reference: https://developers.cloudflare.com/api/operations/namespace-worker-delete-namespace
func (api *API) DeleteWorkersForPlatformsDispatchNamespace(ctx context.Context, rc *ResourceContainer, name string) error {
if rc.Level != AccountRouteLevel {
return ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return ErrMissingAccountID
}

uri := fmt.Sprintf("/accounts/%s/workers/dispatch/namespaces/%s", rc.Identifier, name)
_, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)

if err != nil {
return err
}

return nil
}