Skip to content

feat: Allow partial success results#400

Open
nurof3n wants to merge 3 commits into
prod-stagingfrom
nurof3n/partial-results
Open

feat: Allow partial success results#400
nurof3n wants to merge 3 commits into
prod-stagingfrom
nurof3n/partial-results

Conversation

@nurof3n

@nurof3n nurof3n commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

To be merged after #388
Closes TOOL-1048

@nurof3n nurof3n requested a review from jedevc July 6, 2026 12:50
@nurof3n nurof3n force-pushed the nurof3n/partial-results branch from 91aa4ef to e0a2d5b Compare July 6, 2026 12:52
@nurof3n nurof3n changed the title fix(volume): Allow GET to show more than expected results feat: Allow partial success results Jul 6, 2026
@nurof3n nurof3n force-pushed the nurof3n/partial-results branch 3 times, most recently from 67276b3 to 1d4c4d4 Compare July 6, 2026 13:07
@jedevc

jedevc commented Jul 6, 2026

Copy link
Copy Markdown
Member

Ho hum. Can I get an explanation of what this one is for? 🤔 It's not immediately obvious to me sorry!

@nurof3n

nurof3n commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Ho hum. Can I get an explanation of what this one is for? 🤔 It's not immediately obvious to me sorry!

@jedevc Yup, so the router can return a 207 on listing or bulk requests when some of the metros returned errors, and it would contain both the results for successful calls and the errors for the others.
And I needed a way to show the error in the CLI and also show the listing output.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the CLI’s multi-metro list/get/delete operations to allow returning partial results when the API returns both a response payload and an error (or when only some items succeed). It introduces shared helpers in internal/cmd/util.go and wires them into several resource commands (instances, volumes, templates, certificates, service groups), aligning with TOOL-1048 and the follow-up to PR #388.

Changes:

  • Add shared partial-success helpers and a PartialResult error type to preserve successful items while still surfacing failures.
  • Update list/get handlers across multiple resources to continue processing response payloads even when an API error is present (when data exists).
  • Update delete handlers to detect partial deletions and return structured partial failures when possible.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
internal/cmd/util.go Adds partial-success helpers (listGetOpError, deleteOpError), PartialResult, and error-combining utilities.
internal/cmd/instances.go Uses partial-success logic for list/get and returns partial delete results when some instances delete successfully.
internal/cmd/instance_templates.go Uses partial-success logic for template instance list/get/delete operations.
internal/cmd/volumes.go Uses partial-success logic for volume list/get and returns partial delete results when possible.
internal/cmd/volume_templates.go Uses partial-success logic for template volume list/get/delete operations.
internal/cmd/certificates.go Uses partial-success logic for certificate list/get and returns partial delete results when possible.
internal/cmd/services.go Uses partial-success logic for service group list/get; delete path attempts partial handling but currently has dead/unreachable logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/cmd/services.go
Comment on lines +371 to +374
var deleted []group.Ref
if err == nil {
deleted = refs
}
Comment thread internal/cmd/instances.go Outdated
Comment on lines +731 to +742
var deleted []group.Ref
if instances != nil {
for _, instance := range instances.Data.Instances {
if instance.Status == platform.ResponseStatusSuccess {
deleted = append(deleted, group.Ref{
Metro: c.Metro.Name,
Name: instance.Name,
UUID: instance.Uuid,
})
}
}
}
Comment thread internal/cmd/instances.go Outdated
Comment on lines +754 to +756
if instances != nil {
for _, instance := range instances.Data.Instances {
if instance.Status == platform.ResponseStatusSuccess {
Comment thread internal/cmd/util.go
Comment on lines +252 to +258
for ref, reason := range pr.Failed {
key := ref.Name
if key == "" {
key = ref.UUID
}
failedItems = append(failedItems, fmt.Sprintf("%s (%s): %s", key, ref.Metro, reason))
}
Comment thread internal/cmd/util.go Outdated
Comment on lines +299 to +305
// If there are other errors and we had partial results, convert to combined error
if len(otherErrs) > 0 {
otherErr := errors.Join(otherErrs...)
return fmt.Errorf("%w (and %d operation(s) partially succeeded)", otherErr, merged.SuccessCount())
}
return merged
}
Signed-off-by: Alex-Andrei Cioc <[email protected]>
@nurof3n nurof3n force-pushed the nurof3n/partial-results branch from 48a0314 to 7aece91 Compare July 8, 2026 10:13
@jedevc

jedevc commented Jul 8, 2026

Copy link
Copy Markdown
Member

Ho hum. Can I get an explanation of what this one is for? 🤔 It's not immediately obvious to me sorry!

@jedevc Yup, so the router can return a 207 on listing or bulk requests when some of the metros returned errors, and it would contain both the results for successful calls and the errors for the others. And I needed a way to show the error in the CLI and also show the listing output.

Makes sense.

The bit that I don't get is the new helpers in utils.go. Why listGetOpError and similar? IMO, if the response has a partial-success (i.e. resp.Status == "partial_success" and status is 207), we should never abort processing. We should return both all the errors and all the successful responses. It feels like it's a bit overcomplicated but maybe I'm missing something.

Comment thread internal/cmd/instances.go
return deleted, nil
// If there were deletions despite the error, report partial success
if len(deleted) > 0 && err != nil && !platform.ErrorContainsOnly(err, platform.APIHTTPErrorNotFound) {
pr := NewPartialResult()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should match how Get already handles this? And just use errors.Join.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants