Skip to content
This repository has been archived by the owner on Jan 2, 2024. It is now read-only.

Commit

Permalink
api: report 400 if plan or feature not found (#212)
Browse files Browse the repository at this point in the history
Previously, the API would return a 500 if the user supplied a
non-existent feature or plan.

There is more work to do on error reporting, but this commit provides
clarity to the user with a clear error message.

Fixes #211
  • Loading branch information
bmizerany committed Jan 9, 2023
1 parent e4a653d commit 59a04f8
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 9 deletions.
40 changes: 32 additions & 8 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package api
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"time"
Expand All @@ -17,39 +18,62 @@ import (
"tier.run/values"
)

func init() {
// check that all error codes are unique
seen := map[string]bool{}
for _, e := range errorLookup {
if seen[e.Code] {
panic("duplicate error message")
}
if seen[e.Code] {
panic(fmt.Sprintf("duplicate error code %q", e.Code))
}
seen[e.Code] = true
}
}

// HTTP Errors
var errorLookup = map[error]error{
control.ErrOrgNotFound: &trweb.HTTPError{
//
// TERR1000: invalid requests
// TERR1010: org or features problem
// TERR2000: internal errors
var errorLookup = map[error]*trweb.HTTPError{
control.ErrOrgNotFound: {
Status: 400,
Code: "org_not_found",
Message: "org not found",
},
control.ErrFeatureNotFound: &trweb.HTTPError{
control.ErrFeatureNotFound: {
Status: 400,
Code: "feature_not_found",
Message: "feature not found",
},
control.ErrUnexpectedMissingOrg: &trweb.HTTPError{
control.ErrNoFeatures: {
Status: 400,
Code: "TERR1020",
Message: "feature or plan not found",
},
control.ErrUnexpectedMissingOrg: {
Status: 500,
Code: "TERR1050",
Message: "Stripe reported a customer was created and then reported it did not exist. This might mean you purged your Test Mode and need to reset TIER_PREFIX_KEY=<randomString>.",
},
control.ErrFeatureNotMetered: &trweb.HTTPError{ // TODO(bmizerany): this may be relaxed if we decide to log and accept
control.ErrFeatureNotMetered: { // TODO(bmizerany): this may be relaxed if we decide to log and accept
Status: 400,
Code: "invalid_request",
Message: "feature not reportable",
},
control.ErrInvalidEmail: &trweb.HTTPError{
control.ErrInvalidEmail: {
Status: 400,
Code: "invalid_email",
Message: "invalid email",
},
control.ErrInvalidMetadata: &trweb.HTTPError{
control.ErrInvalidMetadata: {
Status: 400,
Code: "invalid_metadata",
Message: "metadata keys must not use reserved prefix ('tier.')",
},
stripe.ErrInvalidAPIKey: &trweb.HTTPError{
stripe.ErrInvalidAPIKey: {
Status: 401,
Code: "invalid_api_key",
Message: "invalid api key",
Expand Down
6 changes: 6 additions & 0 deletions api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ func TestAPISubscribe(t *testing.T) {
Code: "feature_not_found",
Message: "feature not found",
})

sub("org:test", []string{"plan:nope@0"}, &apitypes.Error{
Status: 400,
Code: "TERR1020",
Message: "feature or plan not found",
})
}

func TestPhaseBadOrg(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion control/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
var (
ErrFeatureExists = errors.New("feature already exists")
ErrFeatureNotFound = errors.New("feature not found")
ErrNoFeatures = errors.New("no features")
ErrFeatureNotMetered = errors.New("feature is not metered")
ErrPlanExists = errors.New("plan already exists")
ErrInvalidEmail = errors.New("invalid email")
Expand Down Expand Up @@ -413,7 +414,7 @@ func Expand(fs []Feature, names ...string) ([]refs.FeaturePlan, error) {
}
}
if len(out) == n {
return nil, fmt.Errorf("no features found for plan %q", p)
return nil, fmt.Errorf("%w found for plan %q", ErrNoFeatures, p)
}
} else {
out = append(out, fp)
Expand Down

0 comments on commit 59a04f8

Please sign in to comment.