|
1 | 1 | package errors |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "encoding/json" |
| 5 | + sysErrors "errors" |
4 | 6 | "fmt" |
5 | 7 | "strings" |
6 | 8 |
|
7 | 9 | "github.com/spf13/cobra" |
| 10 | + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" |
8 | 11 | ) |
9 | 12 |
|
10 | 13 | const ( |
@@ -548,3 +551,136 @@ type OneOfFlagsIsMissing struct { |
548 | 551 | func (e *OneOfFlagsIsMissing) Error() string { |
549 | 552 | return fmt.Sprintf(ONE_OF_THE_FLAGS_MUST_BE_PROVIDED_WHEN_ANOTHER_FLAG_IS_SET, e.MissingFlags, e.SetFlag) |
550 | 553 | } |
| 554 | + |
| 555 | +// ___FORMATTING_ERRORS_________________________________________________________ |
| 556 | + |
| 557 | +// InvalidFormatError indicates that an unsupported format was provided. |
| 558 | +type InvalidFormatError struct { |
| 559 | + Format string // The invalid format that was provided |
| 560 | +} |
| 561 | + |
| 562 | +func (e *InvalidFormatError) Error() string { |
| 563 | + if e.Format != "" { |
| 564 | + return fmt.Sprintf("unsupported format provided: %s", e.Format) |
| 565 | + } |
| 566 | + return "unsupported format provided" |
| 567 | +} |
| 568 | + |
| 569 | +// NewInvalidFormatError creates a new InvalidFormatError with the provided format. |
| 570 | +func NewInvalidFormatError(format string) *InvalidFormatError { |
| 571 | + return &InvalidFormatError{ |
| 572 | + Format: format, |
| 573 | + } |
| 574 | +} |
| 575 | + |
| 576 | +// ___BUILD_REQUEST_ERRORS______________________________________________________ |
| 577 | +// BuildRequestError indicates that a request could not be built. |
| 578 | +type BuildRequestError struct { |
| 579 | + Reason string // Optional: specific reason why the request failed to build |
| 580 | + Err error // Optional: underlying error |
| 581 | +} |
| 582 | + |
| 583 | +func (e *BuildRequestError) Error() string { |
| 584 | + if e.Reason != "" && e.Err != nil { |
| 585 | + return fmt.Sprintf("could not build request (%s): %v", e.Reason, e.Err) |
| 586 | + } |
| 587 | + if e.Reason != "" { |
| 588 | + return fmt.Sprintf("could not build request: %s", e.Reason) |
| 589 | + } |
| 590 | + if e.Err != nil { |
| 591 | + return fmt.Sprintf("could not build request: %v", e.Err) |
| 592 | + } |
| 593 | + return "could not build request" |
| 594 | +} |
| 595 | + |
| 596 | +func (e *BuildRequestError) Unwrap() error { |
| 597 | + return e.Err |
| 598 | +} |
| 599 | + |
| 600 | +// NewBuildRequestError creates a new BuildRequestError with optional reason and underlying error. |
| 601 | +func NewBuildRequestError(reason string, err error) *BuildRequestError { |
| 602 | + return &BuildRequestError{ |
| 603 | + Reason: reason, |
| 604 | + Err: err, |
| 605 | + } |
| 606 | +} |
| 607 | + |
| 608 | +// ___REQUESTS_ERRORS___________________________________________________________ |
| 609 | +// RequestFailedError indicates that an API request failed. |
| 610 | +// If the provided error is an OpenAPI error, the status code and message from the error body will be included in the error message. |
| 611 | +type RequestFailedError struct { |
| 612 | + Err error // Optional: underlying error |
| 613 | +} |
| 614 | + |
| 615 | +func (e *RequestFailedError) Error() string { |
| 616 | + var msg = "request failed" |
| 617 | + |
| 618 | + if e.Err != nil { |
| 619 | + var oApiErr *oapierror.GenericOpenAPIError |
| 620 | + if sysErrors.As(e.Err, &oApiErr) { |
| 621 | + // Extract status code from OpenAPI error header if it exists |
| 622 | + if oApiErr.StatusCode > 0 { |
| 623 | + msg += fmt.Sprintf(" (%d)", oApiErr.StatusCode) |
| 624 | + } |
| 625 | + |
| 626 | + // Try to extract message from OpenAPI error body |
| 627 | + if bodyMsg := extractOpenApiMessageFromBody(oApiErr.Body); bodyMsg != "" { |
| 628 | + msg += fmt.Sprintf(": %s", bodyMsg) |
| 629 | + } else if trimmedBody := strings.TrimSpace(string(oApiErr.Body)); trimmedBody != "" { |
| 630 | + msg += fmt.Sprintf(": %s", trimmedBody) |
| 631 | + } else { |
| 632 | + // Otherwise use the Go error |
| 633 | + msg += fmt.Sprintf(": %v", e.Err) |
| 634 | + } |
| 635 | + } else { |
| 636 | + // If this can't be cased into a OpenApi error use the Go error |
| 637 | + msg += fmt.Sprintf(": %v", e.Err) |
| 638 | + } |
| 639 | + } |
| 640 | + |
| 641 | + return msg |
| 642 | +} |
| 643 | + |
| 644 | +func (e *RequestFailedError) Unwrap() error { |
| 645 | + return e.Err |
| 646 | +} |
| 647 | + |
| 648 | +// NewRequestFailedError creates a new RequestFailedError with optional details. |
| 649 | +func NewRequestFailedError(err error) *RequestFailedError { |
| 650 | + return &RequestFailedError{ |
| 651 | + Err: err, |
| 652 | + } |
| 653 | +} |
| 654 | + |
| 655 | +// ___HELPERS___________________________________________________________________ |
| 656 | +// extractOpenApiMessageFromBody attempts to parse a JSON body and extract the "message" |
| 657 | +// field. It returns an empty string if parsing fails or if no message is found. |
| 658 | +func extractOpenApiMessageFromBody(body []byte) string { |
| 659 | + trimmedBody := strings.TrimSpace(string(body)) |
| 660 | + // Return early if empty. |
| 661 | + if trimmedBody == "" { |
| 662 | + return "" |
| 663 | + } |
| 664 | + |
| 665 | + // Try to unmarshal as a structured error first |
| 666 | + var errorBody struct { |
| 667 | + Message string `json:"message"` |
| 668 | + } |
| 669 | + if err := json.Unmarshal(body, &errorBody); err == nil && errorBody.Message != "" { |
| 670 | + if msg := strings.TrimSpace(errorBody.Message); msg != "" { |
| 671 | + return msg |
| 672 | + } |
| 673 | + } |
| 674 | + |
| 675 | + // If that fails, try to unmarshal as a plain string |
| 676 | + var plainBody string |
| 677 | + if err := json.Unmarshal(body, &plainBody); err == nil && plainBody != "" { |
| 678 | + if msg := strings.TrimSpace(plainBody); msg != "" { |
| 679 | + return msg |
| 680 | + } |
| 681 | + return "" |
| 682 | + } |
| 683 | + |
| 684 | + // All parsing attempts failed or yielded no message |
| 685 | + return "" |
| 686 | +} |
0 commit comments