-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(webhooks): consolidated resource and datasource logic (#332)
* Add base api payloads and client for webhooks * Temporary directive * Add first iteration of webhook resource and datasource implementation * use shared http helper * fix schema attrs * final * datasource example * test * Generate Terraform Docs --------- Co-authored-by: armalite <[email protected]> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
- Loading branch information
1 parent
c05870b
commit da29a47
Showing
12 changed files
with
1,087 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
--- | ||
# generated by https://github.com/hashicorp/terraform-plugin-docs | ||
page_title: "prefect_webhook Data Source - prefect" | ||
subcategory: "" | ||
description: |- | ||
Get information about an existing Webhook, by name or ID. | ||
Use this data source to obtain webhook-level attributes, such as ID, Name, Template, and more. | ||
--- | ||
|
||
# prefect_webhook (Data Source) | ||
|
||
Get information about an existing Webhook, by name or ID. | ||
<br> | ||
Use this data source to obtain webhook-level attributes, such as ID, Name, Template, and more. | ||
|
||
## Example Usage | ||
|
||
```terraform | ||
# Query by ID | ||
data "prefect_webhook" "example_by_id" { | ||
id = "00000000-0000-0000-0000-000000000000" | ||
} | ||
# Query by name | ||
data "prefect_webhook" "example_by_name" { | ||
name = "my-webhook" | ||
} | ||
``` | ||
|
||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Optional | ||
|
||
- `account_id` (String) Account ID (UUID), defaults to the account set in the provider | ||
- `id` (String) Webhook ID (UUID) | ||
- `name` (String) Name of the webhook | ||
- `workspace_id` (String) Workspace ID (UUID), defaults to the workspace set in the provider | ||
|
||
### Read-Only | ||
|
||
- `created` (String) Timestamp of when the resource was created (RFC3339) | ||
- `description` (String) Description of the webhook | ||
- `enabled` (Boolean) Whether the webhook is enabled | ||
- `slug` (String) Slug of the webhook | ||
- `template` (String) Template used by the webhook | ||
- `updated` (String) Timestamp of when the resource was updated (RFC3339) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
--- | ||
# generated by https://github.com/hashicorp/terraform-plugin-docs | ||
page_title: "prefect_webhook Resource - prefect" | ||
subcategory: "" | ||
description: |- | ||
The resource webhook represents a Prefect Cloud Webhook. Webhooks allow external services to trigger events in Prefect. | ||
--- | ||
|
||
# prefect_webhook (Resource) | ||
|
||
The resource `webhook` represents a Prefect Cloud Webhook. Webhooks allow external services to trigger events in Prefect. | ||
|
||
## Example Usage | ||
|
||
```terraform | ||
resource "prefect_webhook" "example" { | ||
name = "my-webhook" | ||
description = "This is a webhook" | ||
enabled = true | ||
template = jsonencode({ | ||
event = "model.refreshed" | ||
resource = { | ||
"prefect.resource.id" = "product.models.{{ body.model }}" | ||
"prefect.resource.name" = "{{ body.friendly_name }}" | ||
"producing-team" = "Data Science" | ||
} | ||
}) | ||
} | ||
# Use a JSON file to load a more complex template | ||
resource "prefect_webhook" "example_with_file" { | ||
name = "my-webhook" | ||
description = "This is a webhook" | ||
enabled = true | ||
template = file("./webhook-template.json") | ||
} | ||
# Access the endpoint of the webhook. | ||
output "endpoint" { | ||
value = prefect_webhook.example_with_file.endpoint | ||
} | ||
``` | ||
|
||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Required | ||
|
||
- `name` (String) Name of the webhook | ||
- `template` (String) Template used by the webhook | ||
|
||
### Optional | ||
|
||
- `account_id` (String) Account ID (UUID), defaults to the account set in the provider | ||
- `description` (String) Description of the webhook | ||
- `enabled` (Boolean) Whether the webhook is enabled | ||
- `workspace_id` (String) Workspace ID (UUID), defaults to the workspace set in the provider | ||
|
||
### Read-Only | ||
|
||
- `created` (String) Timestamp of when the resource was created (RFC3339) | ||
- `endpoint` (String) The fully-formed webhook endpoint, eg. https://api.prefect.cloud/SLUG | ||
- `id` (String) Webhook ID (UUID) | ||
- `updated` (String) Timestamp of when the resource was updated (RFC3339) | ||
|
||
## Import | ||
|
||
Import is supported using the following syntax: | ||
|
||
```shell | ||
# Prefect Webhooks can be imported using the format `workspace_id,id` | ||
terraform import prefect_webhook.example 11111111-1111-1111-1111-111111111111,00000000-0000-0000-0000-000000000000 | ||
|
||
# You can also import by id only if you have a workspace_id set in your provider | ||
terraform import prefect_webhook.example 00000000-0000-0000-0000-000000000000 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Query by ID | ||
data "prefect_webhook" "example_by_id" { | ||
id = "00000000-0000-0000-0000-000000000000" | ||
} | ||
|
||
# Query by name | ||
data "prefect_webhook" "example_by_name" { | ||
name = "my-webhook" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Prefect Webhooks can be imported using the format `workspace_id,id` | ||
terraform import prefect_webhook.example 11111111-1111-1111-1111-111111111111,00000000-0000-0000-0000-000000000000 | ||
|
||
# You can also import by id only if you have a workspace_id set in your provider | ||
terraform import prefect_webhook.example 00000000-0000-0000-0000-000000000000 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
resource "prefect_webhook" "example" { | ||
name = "my-webhook" | ||
description = "This is a webhook" | ||
enabled = true | ||
template = jsonencode({ | ||
event = "model.refreshed" | ||
resource = { | ||
"prefect.resource.id" = "product.models.{{ body.model }}" | ||
"prefect.resource.name" = "{{ body.friendly_name }}" | ||
"producing-team" = "Data Science" | ||
} | ||
}) | ||
} | ||
|
||
# Use a JSON file to load a more complex template | ||
resource "prefect_webhook" "example_with_file" { | ||
name = "my-webhook" | ||
description = "This is a webhook" | ||
enabled = true | ||
template = file("./webhook-template.json") | ||
} | ||
|
||
# Access the endpoint of the webhook. | ||
output "endpoint" { | ||
value = prefect_webhook.example_with_file.endpoint | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package api | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
"github.com/google/uuid" | ||
) | ||
|
||
type WebhooksClient interface { | ||
Create(ctx context.Context, request WebhookCreateRequest) (*Webhook, error) | ||
Get(ctx context.Context, webhookID string) (*Webhook, error) | ||
List(ctx context.Context, names []string) ([]*Webhook, error) | ||
Update(ctx context.Context, webhookID string, request WebhookUpdateRequest) error | ||
Delete(ctx context.Context, webhookID string) error | ||
} | ||
|
||
/*** REQUEST DATA STRUCTS ***/ | ||
|
||
type WebhookCreateRequest struct { | ||
Name string `json:"name"` | ||
Description string `json:"description,omitempty"` | ||
Enabled bool `json:"enabled"` | ||
Template string `json:"template"` | ||
} | ||
|
||
type WebhookUpdateRequest struct { | ||
Name string `json:"name"` | ||
Description string `json:"description,omitempty"` | ||
Enabled bool `json:"enabled"` | ||
Template string `json:"template"` | ||
} | ||
|
||
/*** RESPONSE DATA STRUCTS ***/ | ||
|
||
type Webhook struct { | ||
ID uuid.UUID `json:"id"` | ||
Name string `json:"name"` | ||
Description string `json:"description,omitempty"` | ||
Enabled bool `json:"enabled"` | ||
Template string `json:"template"` | ||
Created time.Time `json:"created"` | ||
Updated time.Time `json:"updated"` | ||
AccountID uuid.UUID `json:"account"` | ||
WorkspaceID uuid.UUID `json:"workspace"` | ||
Slug string `json:"slug"` | ||
} | ||
|
||
type ErrorResponse struct { | ||
Detail []ErrorDetail `json:"detail"` | ||
} | ||
|
||
type ErrorDetail struct { | ||
Loc []string `json:"loc"` | ||
Msg string `json:"msg"` | ||
Type string `json:"type"` | ||
} | ||
|
||
// WebhookFilter defines filters when searching for webhooks. | ||
type WebhookFilter struct { | ||
Webhooks struct { | ||
Name struct { | ||
Any []string `json:"any_"` | ||
} `json:"name,omitempty"` | ||
} `json:"webhooks"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package client | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/google/uuid" | ||
"github.com/prefecthq/terraform-provider-prefect/internal/api" | ||
) | ||
|
||
var _ = api.WebhooksClient(&WebhooksClient{}) | ||
|
||
// WebhooksClient is a client for working with webhooks. | ||
type WebhooksClient struct { | ||
hc *http.Client | ||
apiKey string | ||
routePrefix string | ||
} | ||
|
||
// Webhooks returns a WebhooksClient. | ||
// | ||
//nolint:ireturn // required to support PrefectClient mocking | ||
func (c *Client) Webhooks(accountID, workspaceID uuid.UUID) (api.WebhooksClient, error) { | ||
if accountID == uuid.Nil { | ||
accountID = c.defaultAccountID | ||
} | ||
|
||
if workspaceID == uuid.Nil { | ||
workspaceID = c.defaultWorkspaceID | ||
} | ||
|
||
if err := validateCloudEndpoint(c.endpoint, accountID, workspaceID); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &WebhooksClient{ | ||
hc: c.hc, | ||
apiKey: c.apiKey, | ||
routePrefix: getWorkspaceScopedURL(c.endpoint, accountID, workspaceID, "webhooks"), | ||
}, nil | ||
} | ||
|
||
// Create creates a new webhook. | ||
func (c *WebhooksClient) Create(ctx context.Context, createPayload api.WebhookCreateRequest) (*api.Webhook, error) { | ||
cfg := requestConfig{ | ||
method: http.MethodPost, | ||
url: c.routePrefix + "/", | ||
body: &createPayload, | ||
successCodes: successCodesStatusCreated, | ||
apiKey: c.apiKey, | ||
} | ||
|
||
var webhook api.Webhook | ||
if err := requestWithDecodeResponse(ctx, c.hc, cfg, &webhook); err != nil { | ||
return nil, fmt.Errorf("failed to create webhook: %w", err) | ||
} | ||
|
||
return &webhook, nil | ||
} | ||
|
||
// Get returns details for a webhook by ID. | ||
func (c *WebhooksClient) Get(ctx context.Context, webhookID string) (*api.Webhook, error) { | ||
cfg := requestConfig{ | ||
method: http.MethodGet, | ||
url: c.routePrefix + "/" + webhookID, | ||
successCodes: successCodesStatusOK, | ||
body: http.NoBody, | ||
apiKey: c.apiKey, | ||
} | ||
|
||
var webhook api.Webhook | ||
if err := requestWithDecodeResponse(ctx, c.hc, cfg, &webhook); err != nil { | ||
return nil, fmt.Errorf("failed to get webhook: %w", err) | ||
} | ||
|
||
return &webhook, nil | ||
} | ||
|
||
// Update modifies an existing webhook by ID. | ||
func (c *WebhooksClient) Update(ctx context.Context, webhookID string, updatePayload api.WebhookUpdateRequest) error { | ||
cfg := requestConfig{ | ||
method: http.MethodPut, | ||
url: c.routePrefix + "/" + webhookID, | ||
body: &updatePayload, | ||
successCodes: successCodesStatusOKOrNoContent, | ||
apiKey: c.apiKey, | ||
} | ||
|
||
resp, err := request(ctx, c.hc, cfg) | ||
if err != nil { | ||
return fmt.Errorf("failed to update webhook: %w", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
return nil | ||
} | ||
|
||
// Delete removes a webhook by ID. | ||
func (c *WebhooksClient) Delete(ctx context.Context, webhookID string) error { | ||
cfg := requestConfig{ | ||
method: http.MethodDelete, | ||
url: c.routePrefix + "/" + webhookID, | ||
successCodes: successCodesStatusOKOrNoContent, | ||
body: http.NoBody, | ||
apiKey: c.apiKey, | ||
} | ||
|
||
resp, err := request(ctx, c.hc, cfg) | ||
if err != nil { | ||
return fmt.Errorf("failed to delete webhook: %w", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
return nil | ||
} | ||
|
||
// List returns a list of webhooks matching filter criteria. | ||
func (c *WebhooksClient) List(ctx context.Context, names []string) ([]*api.Webhook, error) { | ||
filter := api.WebhookFilter{} | ||
filter.Webhooks.Name.Any = names | ||
|
||
cfg := requestConfig{ | ||
method: http.MethodGet, | ||
url: c.routePrefix + "/", | ||
body: http.NoBody, | ||
successCodes: successCodesStatusOK, | ||
apiKey: c.apiKey, | ||
} | ||
|
||
var webhooks []*api.Webhook | ||
if err := requestWithDecodeResponse(ctx, c.hc, cfg, &webhooks); err != nil { | ||
return nil, fmt.Errorf("failed to list webhooks: %w", err) | ||
} | ||
|
||
return webhooks, nil | ||
} |
Oops, something went wrong.