-
Notifications
You must be signed in to change notification settings - Fork 17
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
feat(blocks): add update resource operation #198
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,6 +106,9 @@ func (r *BlockResource) Schema(_ context.Context, _ resource.SchemaRequest, resp | |
"type_slug": schema.StringAttribute{ | ||
Required: true, | ||
Description: "Block Type slug, which determines the schema of the `data` JSON attribute. Use `prefect block types ls` to view all available Block type slugs.", | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
"data": schema.StringAttribute{ | ||
Required: true, | ||
|
@@ -256,6 +259,8 @@ func (r *BlockResource) Read(ctx context.Context, req resource.ReadRequest, resp | |
"Error creating block client", | ||
fmt.Sprintf("Could not create block client, unexpected error: %s. This is a bug in the provider, please report this to the maintainers.", err.Error()), | ||
) | ||
|
||
return | ||
} | ||
|
||
var blockID uuid.UUID | ||
|
@@ -306,9 +311,121 @@ func (r *BlockResource) Read(ctx context.Context, req resource.ReadRequest, resp | |
} | ||
|
||
// Update updates the resource and sets the updated Terraform state on success. | ||
// | ||
//nolint:revive // TODO: remove this comment when method is implemented | ||
func (r *BlockResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { | ||
var plan BlockResourceModel | ||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
blockTypeClient, err := r.client.BlockTypes(plan.AccountID.ValueUUID(), plan.WorkspaceID.ValueUUID()) | ||
if err != nil { | ||
resp.Diagnostics.Append(helpers.CreateClientErrorDiagnostic("Block Types", err)) | ||
|
||
return | ||
} | ||
|
||
blockSchemaClient, err := r.client.BlockSchemas(plan.AccountID.ValueUUID(), plan.WorkspaceID.ValueUUID()) | ||
if err != nil { | ||
resp.Diagnostics.Append(helpers.CreateClientErrorDiagnostic("Block Schema", err)) | ||
|
||
return | ||
} | ||
|
||
blockDocumentClient, err := r.client.BlockDocuments(plan.AccountID.ValueUUID(), plan.WorkspaceID.ValueUUID()) | ||
if err != nil { | ||
resp.Diagnostics.Append(helpers.CreateClientErrorDiagnostic("Block Document", err)) | ||
|
||
return | ||
} | ||
Comment on lines
+321
to
+340
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wonder if we just make a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I started abstracting a helper method to get the clients but then the PR started getting bigger since I was modifying other methods. Let me make an issue for that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
blockType, err := blockTypeClient.GetBySlug(ctx, plan.TypeSlug.ValueString()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok here's an interesting thing that came up - so the PATCH endpoint accepts a block_schema_id (meaning it's a modifiable attribute). block_schemas are a "version" of a type but what if the type_slug is modified in the TF resource? then we'll get a failure like so - Terraform will perform the following actions:
# prefect_block.lambda will be updated in-place
~ resource "prefect_block" "lambda" {
id = "adf48aa0-318d-40d1-aca0-c897f05cc22b"
name = "lambda"
~ type_slug = "lambda-function" -> "secret"
~ updated = "2024-06-06T23:40:59Z" -> (known after apply)
# (3 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
prefect_block.lambda: Modifying... [id=adf48aa0-318d-40d1-aca0-c897f05cc22b]
╷
│ Error: Error during update Block Document
│
│ with prefect_block.lambda,
│ on main.tf line 19, in resource "prefect_block" "lambda":
│ 19: resource "prefect_block" "lambda" {
│
│ Could not update Block Document, unexpected error: status code 400 Bad Request, error={"detail":"Must migrate block document to a block schema of the same
│ block type."} we can find out, but im guessing the intent of the endpoint was to allow changing schema versions of the same block type, but we shouldnt' allow changing the type itself so there's two things here:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@parkedwards good call-out. I started looking into this but haven't yet found a way to work with I found a reference to DiffSuppressFunc, which isn't what we want, but does allow you to work with I'll keep looking but if you know of a way off the top of your head just lemme know 👍🏼 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe RequiresReplaceIf could help here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. take a peek at the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That change results in:
What do you think of that flow @parkedwards? Or would you rather reject the change entirely? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh i completely missed your comment about using the planmodifier. yeah i like it! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
🤦🏼 I tried that and my editor didn't want to autocomplete The planmodifier approach feels more "correct" here based on some googling but I'm also open to just grabbing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Fair question, probably not for now if the UI doesn't expose that. Although maybe it's worth a debug log of the block schema's metadata 🤔 |
||
if err != nil { | ||
resp.Diagnostics.Append(helpers.ResourceClientErrorDiagnostic("Block Type", "get_by_slug", err)) | ||
|
||
return | ||
} | ||
|
||
blockSchemas, err := blockSchemaClient.List(ctx, []uuid.UUID{blockType.ID}) | ||
if err != nil { | ||
resp.Diagnostics.Append(helpers.ResourceClientErrorDiagnostic("Block Schema", "list", err)) | ||
|
||
return | ||
} | ||
|
||
if len(blockSchemas) == 0 { | ||
resp.Diagnostics.AddError( | ||
"No block schemas found", | ||
fmt.Sprintf("No block schemas found for %s block type slug", plan.TypeSlug.ValueString()), | ||
) | ||
|
||
return | ||
} | ||
|
||
latestBlockSchema := blockSchemas[0] | ||
|
||
blockID, err := uuid.Parse(plan.ID.ValueString()) | ||
if err != nil { | ||
resp.Diagnostics.AddAttributeError( | ||
path.Root("id"), | ||
"Error parsing block ID", | ||
fmt.Sprintf("Could not parse block ID to UUID, unexpected error: %s", err.Error()), | ||
) | ||
|
||
return | ||
} | ||
|
||
var data map[string]interface{} | ||
resp.Diagnostics.Append(plan.Data.Unmarshal(&data)...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
err = blockDocumentClient.Update(ctx, blockID, api.BlockDocumentUpdate{ | ||
BlockSchemaID: latestBlockSchema.ID, | ||
Data: data, | ||
MergeExistingData: true, | ||
}) | ||
|
||
if err != nil { | ||
resp.Diagnostics.Append(helpers.ResourceClientErrorDiagnostic("Block Document", "update", err)) | ||
|
||
return | ||
} | ||
|
||
block, err := blockDocumentClient.Get(ctx, blockID) | ||
if err != nil { | ||
resp.Diagnostics.AddError( | ||
"Error refreshing block state", | ||
fmt.Sprintf("Could not read block, unexpected error: %s", err.Error()), | ||
) | ||
|
||
return | ||
} | ||
|
||
diags := copyBlockToModel(block, &plan) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
byteSlice, err := json.Marshal(block.Data) | ||
if err != nil { | ||
diags.AddAttributeError( | ||
path.Root("data"), | ||
"Failed to serialize Block Data", | ||
fmt.Sprintf("Could not serialize Block Data as JSON string: %s", err.Error()), | ||
) | ||
|
||
return | ||
} | ||
plan.Data = jsontypes.NewNormalizedValue(string(byteSlice)) | ||
|
||
diags = resp.State.Set(ctx, &plan) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
} | ||
|
||
// Delete deletes the resource and removes the Terraform state on success. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice