From 57bf676c1c8353473cf3e6a055b1828ffd56bc92 Mon Sep 17 00:00:00 2001 From: Charlie Cruzan Date: Wed, 21 Aug 2024 10:55:19 -1000 Subject: [PATCH 1/9] new trigger for billing --- pkg/cmd/resource/operation.go | 4 +- pkg/fixtures/fixtures.go | 74 +++++++++- pkg/fixtures/triggers.go | 1 + .../triggers/billing.meter.thing.json | 75 ++++++++++ pkg/parsers/parsers.go | 134 ++++++++++++++++-- pkg/parsers/parsers_test.go | 20 +-- pkg/requests/base.go | 25 +++- pkg/requests/base_test.go | 6 +- pkg/requests/plugin.go | 2 +- pkg/requests/webhook_endpoints.go | 6 +- pkg/rpcservice/events_resend.go | 2 +- 11 files changed, 308 insertions(+), 41 deletions(-) create mode 100644 pkg/fixtures/triggers/billing.meter.thing.json diff --git a/pkg/cmd/resource/operation.go b/pkg/cmd/resource/operation.go index 37c15ffed..30c1a04c5 100644 --- a/pkg/cmd/resource/operation.go +++ b/pkg/cmd/resource/operation.go @@ -117,12 +117,12 @@ func (oc *OperationCmd) runOperationCmd(cmd *cobra.Command, args []string) error } // if confirmation is provided, make the request - _, err = oc.MakeRequest(cmd.Context(), apiKey, path, &oc.Parameters, false) + _, err = oc.MakeRequest(cmd.Context(), apiKey, path, &oc.Parameters, false, nil) return err } // else - _, err = oc.MakeRequest(cmd.Context(), apiKey, path, &oc.Parameters, false) + _, err = oc.MakeRequest(cmd.Context(), apiKey, path, &oc.Parameters, false, nil) return err } diff --git a/pkg/fixtures/fixtures.go b/pkg/fixtures/fixtures.go index 6ed738b1f..956b5cc3d 100644 --- a/pkg/fixtures/fixtures.go +++ b/pkg/fixtures/fixtures.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "net/http" "os" "path/filepath" "strings" @@ -45,6 +46,8 @@ type FixtureRequest struct { Params map[string]interface{} `json:"params"` IdempotencyKey string `json:"idempotency_key,omitempty"` Context string `json:"context,omitempty"` + APIBase string `json:"api_base,omitempty"` + Headers map[string]string `json:"headers,omitempty"` } // Fixture contains a mapping of an individual fixtures responses for querying @@ -310,6 +313,9 @@ func (fxt *Fixture) Execute(ctx context.Context, apiVersion string) ([]string, e } func errWasExpected(err error, expectedErrorType string) bool { + if expectedErrorType == "" { + return false + } if rerr, ok := err.(requests.RequestError); ok { return rerr.ErrorType == expectedErrorType } @@ -326,15 +332,43 @@ func (fxt *Fixture) UpdateEnv() error { return nil } +func (fxt *Fixture) getAPIBase(request FixtureRequest) string { + if request.APIBase != "" { + return request.APIBase + } + return fxt.BaseURL +} + +func (fxt *Fixture) unsupportedAPIKey(path string) bool { + return strings.HasPrefix(path, "/v2/") && !strings.HasPrefix(fxt.APIKey, "sk_") +} + +func (fxt *Fixture) addCustomHeaders(headers map[string]string) func(req *http.Request) error { + return func(req *http.Request) error { + for k, v := range headers { + value, err := parsers.ParseQuery(v, fxt.Responses) + if err != nil { + return fmt.Errorf("error parsing %s field: %w", k, err) + } + req.Header.Set(k, value) + } + return nil + } +} + func (fxt *Fixture) makeRequest(ctx context.Context, data FixtureRequest, apiVersion string) ([]byte, error) { req := requests.Base{ Method: strings.ToUpper(data.Method), SuppressOutput: true, - APIBaseURL: fxt.BaseURL, + APIBaseURL: fxt.getAPIBase(data), } path, err := parsers.ParsePath(data.Path, fxt.Responses) + if fxt.unsupportedAPIKey(path) { + return make([]byte, 0), fmt.Errorf("this trigger must be run with a secret API key (starts with 'sk_')") + } + if err != nil { return make([]byte, 0), err } @@ -352,12 +386,29 @@ func (fxt *Fixture) makeRequest(ctx context.Context, data FixtureRequest, apiVer params.SetIdempotency(idempotencyKey) } - return req.MakeRequest(ctx, fxt.APIKey, path, params, true) + var additionalConfigure func(req *http.Request) error + if data.Headers != nil { + additionalConfigure = fxt.addCustomHeaders(data.Headers) + } + + if strings.HasPrefix(path, "/v2/") { + jsonPayload := "" + if strings.ToLower(data.Method) == "post" { + jsonPayload, err = fxt.createJSONPayload(data.Params) + if err != nil { + return make([]byte, 0), err + } + } + + return req.MakeV2Request(ctx, fxt.APIKey, path, params, true, additionalConfigure, jsonPayload) + } + + return req.MakeRequest(ctx, fxt.APIKey, path, params, true, additionalConfigure) } func (fxt *Fixture) createParams(params interface{}, apiVersion string) (*requests.RequestParameters, error) { requestParams := requests.RequestParameters{} - parsed, err := parsers.ParseInterface(params, fxt.Responses) + parsed, err := parsers.ParseToFormData(params, fxt.Responses) if err != nil { return &requestParams, err } @@ -372,6 +423,23 @@ func (fxt *Fixture) createParams(params interface{}, apiVersion string) (*reques return &requestParams, nil } +func (fxt *Fixture) createJSONPayload(params interface{}) (string, error) { + if params == nil { + return "{}", nil + } + parsedJSON, err := parsers.ParseToApplicationJSON(params, fxt.Responses) + if err != nil { + return "", err + } + + jsonParams, err := json.Marshal(parsedJSON) + if err != nil { + return "", err + } + + return string(jsonParams), nil +} + func (fxt *Fixture) updateEnv(env map[string]string) error { dir, err := os.Getwd() if err != nil { diff --git a/pkg/fixtures/triggers.go b/pkg/fixtures/triggers.go index 5ba20b851..5b872d24e 100644 --- a/pkg/fixtures/triggers.go +++ b/pkg/fixtures/triggers.go @@ -126,6 +126,7 @@ var Events = map[string]string{ "transfer.created": "triggers/transfer.created.json", "transfer.reversed": "triggers/transfer.reversed.json", "transfer.updated": "triggers/transfer.updated.json", + "billing.meter.thing": "triggers/billing.meter.thing.json", } // BuildFromFixtureFile creates a new fixture struct for a file diff --git a/pkg/fixtures/triggers/billing.meter.thing.json b/pkg/fixtures/triggers/billing.meter.thing.json new file mode 100644 index 000000000..9a25142f6 --- /dev/null +++ b/pkg/fixtures/triggers/billing.meter.thing.json @@ -0,0 +1,75 @@ +{ + "_meta": { + "template_version": 0 + }, + "fixtures": [ + { + "name": "create_customer", + "path": "/v1/customers", + "method": "post", + "params": { + "description": "(created by Stripe CLI)" + } + }, + { + "name": "list_billing_meters", + "path": "/v1/billing/meters", + "method": "get", + "params": { + "status": "active" + } + }, + { + "name": "billing_meter", + "path": "/v1/billing/meters", + "method": "post", + "params": { + "display_name": "Stripe CLI Billing Meter", + "event_name": "stripe_cli_billing_meter_for_fixture", + "default_aggregation": { + "formula": "sum" + } + }, + "expected_error_type": "invalid_request_error" + }, + { + "name": "list_billing_meters_after_creation", + "path": "/v1/billing/meters", + "method": "get", + "params": { + "status": "active" + } + }, + { + "name": "billing_meter_event_session", + "path": "/v2/billing/meter_event_session", + "method": "post", + "headers": { + "Content-Type": "application/json", + "Stripe-Version": "unsafe-development" + }, + "params": {} + }, + { + "name": "billing_meter_event_stream", + "path": "/v2/billing/meter_event_stream", + "method": "post", + "api_base": "https://events.stripe.com", + "headers": { + "Content-Type": "application/json", + "Stripe-Version": "unsafe-development", + "Authorization": "STRIPE-V2-ACCESS-TOKEN ${billing_meter_event_session:authentication_token}" + }, + "params": { + "as_of": "${time-now}", + "event_name": "${list_billing_meters_after_creation:data.0.event_name}", + "external_identifier":"4242424242424242", + "timestamp":"${time-now}", + "payload": { + "value":"1", + "stripe_customer_id":"${create_customer:id}" + } + } + } + ] +} diff --git a/pkg/parsers/parsers.go b/pkg/parsers/parsers.go index 7e62c2cd2..9005b185b 100644 --- a/pkg/parsers/parsers.go +++ b/pkg/parsers/parsers.go @@ -1,6 +1,7 @@ package parsers import ( + "encoding/json" "fmt" "os" "path" @@ -8,6 +9,7 @@ import ( "regexp" "strconv" "strings" + "time" "github.com/joho/godotenv" "github.com/tidwall/gjson" @@ -85,14 +87,14 @@ func ParsePath(httpPath string, queryRespMap map[string]gjson.Result) (string, e return httpPath, nil } -// ParseInterface is the primary entrypoint into building the request +// ParseToFormData is the primary entrypoint into building the request // data for fixtures. The data will always be provided as an // interface{} and this will need to use reflection to determine how // to proceed. There are two primary paths here, `parseMap` and // `ParseArray`, which will recursively traverse and convert the data // // This returns an array of clean form data to make the request. -func ParseInterface(params interface{}, queryRespMap map[string]gjson.Result) ([]string, error) { +func ParseToFormData(params interface{}, queryRespMap map[string]gjson.Result) ([]string, error) { var data []string var cleanData []string @@ -100,14 +102,14 @@ func ParseInterface(params interface{}, queryRespMap map[string]gjson.Result) ([ switch v := reflect.ValueOf(params); v.Kind() { case reflect.Map: m := params.(map[string]interface{}) - parsed, err := ParseMap(m, "", -1, queryRespMap) + parsed, err := ParseMapForFormData(m, "", -1, queryRespMap) if err != nil { return make([]string, 0), err } data = append(data, parsed...) case reflect.Array: a := params.([]interface{}) - parsed, err := ParseArray(a, "", queryRespMap) + parsed, err := ParseArrayForFormData(a, "", queryRespMap) if err != nil { return make([]string, 0), err } @@ -124,10 +126,10 @@ func ParseInterface(params interface{}, queryRespMap map[string]gjson.Result) ([ return cleanData, nil } -// ParseMap recursively parses a map of string => interface{} until +// ParseMapForFormData recursively parses a map of string => interface{} until // each leaf node has a terminal type (String, Int, etc) that can no // longer be recursively traversed. -func ParseMap(params map[string]interface{}, parent string, index int, queryRespMap map[string]gjson.Result) ([]string, error) { +func ParseMapForFormData(params map[string]interface{}, parent string, index int, queryRespMap map[string]gjson.Result) ([]string, error) { data := make([]string, len(params)) var keyname string @@ -187,7 +189,7 @@ func ParseMap(params map[string]interface{}, parent string, index int, queryResp case reflect.Map: m := value.(map[string]interface{}) - result, err := ParseMap(m, keyname, -1, queryRespMap) + result, err := ParseMapForFormData(m, keyname, -1, queryRespMap) if err != nil { return make([]string, 0), err @@ -197,7 +199,7 @@ func ParseMap(params map[string]interface{}, parent string, index int, queryResp case reflect.Array, reflect.Slice: a := value.([]interface{}) - result, err := ParseArray(a, keyname, queryRespMap) + result, err := ParseArrayForFormData(a, keyname, queryRespMap) if err != nil { return make([]string, 0), err @@ -213,11 +215,11 @@ func ParseMap(params map[string]interface{}, parent string, index int, queryResp return data, nil } -// ParseArray is similar to parseMap but doesn't have to build the +// ParseArrayForFormData is similar to parseMap but doesn't have to build the // multi-depth keys. Form data arrays contain brackets with nothing // inside the bracket to designate an array instead of a key value // pair. -func ParseArray(params []interface{}, parent string, queryRespMap map[string]gjson.Result) ([]string, error) { +func ParseArrayForFormData(params []interface{}, parent string, queryRespMap map[string]gjson.Result) ([]string, error) { data := make([]string, len(params)) // The index is only used for arrays of maps @@ -238,14 +240,14 @@ func ParseArray(params []interface{}, parent string, queryRespMap map[string]gjs // When we parse arrays of maps, we want to track the index of the element for the request // ex: lines[0][id] = "id_0000", lines[1][id] = "id_1234", etc. index++ - parsed, err := ParseMap(m, parent, index, queryRespMap) + parsed, err := ParseMapForFormData(m, parent, index, queryRespMap) if err != nil { return make([]string, 0), err } data = append(data, parsed...) case reflect.Array, reflect.Slice: a := value.([]interface{}) - parsed, err := ParseArray(a, parent, queryRespMap) + parsed, err := ParseArrayForFormData(a, parent, queryRespMap) if err != nil { return make([]string, 0), err } @@ -285,6 +287,10 @@ func findSimilarQueryNames(queryRespMap map[string]gjson.Result, name string) ([ func ParseQuery(queryString string, queryRespMap map[string]gjson.Result) (string, error) { value := queryString + if queryString == "${time-now}" { + return time.Now().Format(time.RFC3339), nil + } + if query, isQuery := ToFixtureQuery(queryString); isQuery { name := query.Name @@ -334,7 +340,7 @@ func ParseQuery(queryString string, queryRespMap map[string]gjson.Result) (strin result := queryRespMap[name].Get(query.Query) if len(result.String()) != 0 { - return result.String(), nil + value = strings.ReplaceAll(queryString, query.Match, result.String()) } return value, nil @@ -396,3 +402,105 @@ func getEnvVar(key string) (string, error) { return envValue, nil } + +// ParseToApplicationJSON is the primary entrypoint into building the request +// data for v2 fixtures. The data will always be provided as an +// interface{} and this will need to use reflection to determine how +// to proceed. There are two primary paths here, `parseMap` and +// `ParseArray`, which will recursively traverse and convert the data +// +// This returns an interface of clean application/json data to make the request. +func ParseToApplicationJSON(params interface{}, queryRespMap map[string]gjson.Result) (interface{}, error) { + var parsed interface{} + var err error + + switch v := reflect.ValueOf(params); v.Kind() { + case reflect.Map: + parsed, err = parseMapForApplicationJSON(params.(map[string]interface{}), queryRespMap) + if err != nil { + return make(map[string]interface{}), err + } + case reflect.Array, reflect.Slice: + parsed, err = parseArrayForApplicationJSON(params.([]interface{}), queryRespMap) + if err != nil { + return make([]string, 0), err + } + default: + } + + return parsed, nil +} + +func parseMapForApplicationJSON(params map[string]interface{}, responses map[string]gjson.Result) (map[string]interface{}, error) { + result := params + + for key, value := range params { + switch v := reflect.ValueOf(value); v.Kind() { + case reflect.String: + parsed, err := ParseQuery(v.String(), responses) + if err != nil { + return make(map[string]interface{}), err + } + + switch { + case parsed[0:1] == "[" && parsed[len(parsed)-1:] == "]": + paramArray := make([]interface{}, 0) + json.Unmarshal([]byte(parsed), ¶mArray) + result[key] = paramArray + case parsed[0:1] == "{" && parsed[len(parsed)-1:] == "}": + paramMap := make(map[string]interface{}) + json.Unmarshal([]byte(parsed), ¶mMap) + result[key] = paramMap + default: + result[key] = parsed + } + case reflect.Map: + parsed, err := parseMapForApplicationJSON(value.(map[string]interface{}), responses) + if err != nil { + return make(map[string]interface{}), err + } + result[key] = parsed + case reflect.Array, reflect.Slice: + parsed, err := parseArrayForApplicationJSON(value.([]interface{}), responses) + if err != nil { + return make(map[string]interface{}), err + } + result[key] = parsed + default: + result[key] = value + } + } + + return result, nil +} + +func parseArrayForApplicationJSON(params []interface{}, responses map[string]gjson.Result) ([]interface{}, error) { + data := make([]interface{}, 0) + + for _, value := range params { + switch v := reflect.ValueOf(value); v.Kind() { + case reflect.String: + parsed, err := ParseQuery(v.String(), responses) + if err != nil { + return make([]interface{}, 0), err + } + data = append(data, parsed) + case reflect.Map: + parsed, err := parseMapForApplicationJSON(value.(map[string]interface{}), responses) + if err != nil { + return make([]interface{}, 0), err + } + data = append(data, parsed) + case reflect.Array, reflect.Slice: + parsed, err := parseArrayForApplicationJSON(value.([]interface{}), responses) + if err != nil { + return make([]interface{}, 0), err + } + data = append(data, parsed) + default: + data = append(data, value) + } + } + + return data, nil +} diff --git a/pkg/parsers/parsers_test.go b/pkg/parsers/parsers_test.go index f3e295e44..76ddb77ee 100644 --- a/pkg/parsers/parsers_test.go +++ b/pkg/parsers/parsers_test.go @@ -137,7 +137,7 @@ func TestParseInterfaceFromRaw(t *testing.T) { parsedFixtureData := make(map[string]interface{}) json.Unmarshal(rawFixtureData, &parsedFixtureData) - output, _ := ParseInterface(parsedFixtureData, make(map[string]gjson.Result)) + output, _ := ParseToFormData(parsedFixtureData, make(map[string]gjson.Result)) sort.Strings(output) require.Equal(t, len(output), 2) @@ -159,7 +159,7 @@ func TestParseInterfaceDeeplyNested(t *testing.T) { data := make(map[string]interface{}) data["custom_fields"] = customFields - output, _ := ParseInterface(data, make(map[string]gjson.Result)) + output, _ := ParseToFormData(data, make(map[string]gjson.Result)) sort.Strings(output) require.Equal(t, 2, len(output)) @@ -189,7 +189,7 @@ func TestParseInterface(t *testing.T) { data["address"] = address data["tax_id_data"] = taxIDData - output, _ := ParseInterface(data, make(map[string]gjson.Result)) + output, _ := ParseToFormData(data, make(map[string]gjson.Result)) sort.Strings(output) require.Equal(t, len(output), 8) @@ -214,7 +214,7 @@ func TestParseWithQueryIgnoreDefault(t *testing.T) { data["amount"] = "100" data["currency"] = "${cust_bender:currency|usd}" - output, _ := ParseInterface(data, queryRespMap) + output, _ := ParseToFormData(data, queryRespMap) sort.Strings(output) require.Equal(t, len(output), 4) @@ -232,7 +232,7 @@ func TestParseWithQueryDefaultValue(t *testing.T) { data := make(map[string]interface{}) data["currency"] = "${cust_bender:currency|usd}" - output, _ := ParseInterface(data, queryRespMap) + output, _ := ParseToFormData(data, queryRespMap) require.Equal(t, len(output), 1) require.Equal(t, "currency=usd", output[0]) @@ -242,7 +242,7 @@ func TestParseNoEnv(t *testing.T) { data := make(map[string]interface{}) data["phone"] = "${.env:PHONE_NOT_SET|+1234567890}" - output, _ := ParseInterface(data, make(map[string]gjson.Result)) + output, _ := ParseToFormData(data, make(map[string]gjson.Result)) require.Equal(t, len(output), 1) require.Equal(t, "phone=+1234567890", output[0]) @@ -262,7 +262,7 @@ func TestParseWithLocalEnv(t *testing.T) { path, _ := ParsePath(httpPath, queryRespMap) assert.Equal(t, "/v1/customers/cust_12345", path) - output, _ := ParseInterface(data, queryRespMap) + output, _ := ParseToFormData(data, queryRespMap) require.Equal(t, len(output), 1) require.Equal(t, "phone=+1234", output[0]) @@ -278,7 +278,7 @@ func TestParseWithEnvFile(t *testing.T) { data := make(map[string]interface{}) data["phone"] = "${.env:PHONE_FILE|+1234567890}" - output, _ := ParseInterface(data, make(map[string]gjson.Result)) + output, _ := ParseToFormData(data, make(map[string]gjson.Result)) require.Equal(t, len(output), 1) require.Equal(t, "phone=+1234", output[0]) @@ -294,7 +294,7 @@ func TestParseWithEnvSubstring(t *testing.T) { data := make(map[string]interface{}) data["url"] = "${.env:BASE_API_URL}/hook/stripe" - output, _ := ParseInterface(data, make(map[string]gjson.Result)) + output, _ := ParseToFormData(data, make(map[string]gjson.Result)) require.Equal(t, len(output), 1) require.Equal(t, "url=https://myexample.com/hook/stripe", output[0]) @@ -314,7 +314,7 @@ func TestParseArray(t *testing.T) { data["second_timezone"] = "${cust_bender:timezones.1}" data["third_timezone"] = "${cust_bender:timezones.2|notimezonefound}" - output, _ := ParseInterface(data, queryRespMap) + output, _ := ParseToFormData(data, queryRespMap) sort.Strings(output) require.Equal(t, len(output), 5) diff --git a/pkg/requests/base.go b/pkg/requests/base.go index 0849090c9..605ce18f8 100644 --- a/pkg/requests/base.go +++ b/pkg/requests/base.go @@ -135,7 +135,7 @@ func (rb *Base) RunRequestsCmd(cmd *cobra.Command, args []string) error { return err } - _, err = rb.MakeRequest(cmd.Context(), apiKey, path, &rb.Parameters, false) + _, err = rb.MakeRequest(cmd.Context(), apiKey, path, &rb.Parameters, false, nil) return err } @@ -203,8 +203,23 @@ func (rb *Base) MakeMultiPartRequest(ctx context.Context, apiKey, path string, p return rb.performRequest(ctx, client, path, params, reqBody.String(), errOnStatus, configure) } +func (rb *Base) MakeV2Request(ctx context.Context, apiKey, path string, params *RequestParameters, errOnStatus bool, additionalConfigure func(req *http.Request) error, jsonPayload string) ([]byte, error) { + parsedBaseURL, err := url.Parse(rb.APIBaseURL) + if err != nil { + return []byte{}, err + } + + client := &stripe.Client{ + BaseURL: parsedBaseURL, + APIKey: apiKey, + Verbose: rb.showHeaders, + } + + return rb.performRequest(ctx, client, path, params, jsonPayload, errOnStatus, additionalConfigure) +} + // MakeRequest will make a request to the Stripe API with the specific variables given to it -func (rb *Base) MakeRequest(ctx context.Context, apiKey, path string, params *RequestParameters, errOnStatus bool) ([]byte, error) { +func (rb *Base) MakeRequest(ctx context.Context, apiKey, path string, params *RequestParameters, errOnStatus bool, additionalConfigure func(req *http.Request) error) ([]byte, error) { parsedBaseURL, err := url.Parse(rb.APIBaseURL) if err != nil { return []byte{}, err @@ -216,18 +231,18 @@ func (rb *Base) MakeRequest(ctx context.Context, apiKey, path string, params *Re Verbose: rb.showHeaders, } - return rb.MakeRequestWithClient(ctx, client, path, params, errOnStatus) + return rb.MakeRequestWithClient(ctx, client, path, params, errOnStatus, additionalConfigure) } // MakeRequestWithClient will make a request to the Stripe API with the specific // variables given to it using the provided client. -func (rb *Base) MakeRequestWithClient(ctx context.Context, client stripe.RequestPerformer, path string, params *RequestParameters, errOnStatus bool) ([]byte, error) { +func (rb *Base) MakeRequestWithClient(ctx context.Context, client stripe.RequestPerformer, path string, params *RequestParameters, errOnStatus bool, additionalConfigure func(req *http.Request) error) ([]byte, error) { data, err := rb.BuildDataForRequest(params) if err != nil { return []byte{}, err } - return rb.performRequest(ctx, client, path, params, data, errOnStatus, nil) + return rb.performRequest(ctx, client, path, params, data, errOnStatus, additionalConfigure) } func (rb *Base) performRequest(ctx context.Context, client stripe.RequestPerformer, path string, params *RequestParameters, data string, errOnStatus bool, additionalConfigure func(req *http.Request) error) ([]byte, error) { diff --git a/pkg/requests/base_test.go b/pkg/requests/base_test.go index 56004ba92..930c0e981 100644 --- a/pkg/requests/base_test.go +++ b/pkg/requests/base_test.go @@ -111,7 +111,7 @@ func TestMakeRequest(t *testing.T) { expand: []string{"futurama.employees", "futurama.ships"}, } - _, err := rb.MakeRequest(context.Background(), "sk_test_1234", "/foo/bar", params, true) + _, err := rb.MakeRequest(context.Background(), "sk_test_1234", "/foo/bar", params, true, nil) require.NoError(t, err) } @@ -127,7 +127,7 @@ func TestMakeRequest_ErrOnStatus(t *testing.T) { params := &RequestParameters{} - _, err := rb.MakeRequest(context.Background(), "sk_test_1234", "/foo/bar", params, true) + _, err := rb.MakeRequest(context.Background(), "sk_test_1234", "/foo/bar", params, true, nil) require.Error(t, err) require.Equal(t, "Request failed, status=500, body=:(", err.Error()) } @@ -153,7 +153,7 @@ func TestMakeRequest_ErrOnAPIKeyExpired(t *testing.T) { params := &RequestParameters{} - _, err := rb.MakeRequest(context.Background(), "sk_test_1234", "/foo/bar", params, false) + _, err := rb.MakeRequest(context.Background(), "sk_test_1234", "/foo/bar", params, false, nil) require.Error(t, err) require.Contains(t, err.Error(), "Request failed, status=401, body=") } diff --git a/pkg/requests/plugin.go b/pkg/requests/plugin.go index a279b7d0d..0eed3a70a 100644 --- a/pkg/requests/plugin.go +++ b/pkg/requests/plugin.go @@ -28,7 +28,7 @@ func GetPluginData(ctx context.Context, baseURL, apiVersion, apiKey string, prof APIBaseURL: baseURL, } // /v1/stripecli/get-plugin-url - resp, err := base.MakeRequest(ctx, apiKey, "/v1/stripecli/get-plugin-url", params, true) + resp, err := base.MakeRequest(ctx, apiKey, "/v1/stripecli/get-plugin-url", params, true, nil) if err != nil { return PluginData{}, err } diff --git a/pkg/requests/webhook_endpoints.go b/pkg/requests/webhook_endpoints.go index 8c086a39a..96a89c29c 100644 --- a/pkg/requests/webhook_endpoints.go +++ b/pkg/requests/webhook_endpoints.go @@ -37,7 +37,7 @@ func WebhookEndpointsList(ctx context.Context, baseURL, apiVersion, apiKey strin SuppressOutput: true, APIBaseURL: baseURL, } - resp, _ := base.MakeRequest(ctx, apiKey, "/v1/webhook_endpoints", params, true) + resp, _ := base.MakeRequest(ctx, apiKey, "/v1/webhook_endpoints", params, true, nil) data := WebhookEndpointList{} json.Unmarshal(resp, &data) @@ -56,7 +56,7 @@ func WebhookEndpointsListWithClient(ctx context.Context, client stripe.RequestPe Method: http.MethodGet, SuppressOutput: true, } - resp, _ := base.MakeRequestWithClient(ctx, client, "/v1/webhook_endpoints", params, true) + resp, _ := base.MakeRequestWithClient(ctx, client, "/v1/webhook_endpoints", params, true, nil) data := WebhookEndpointList{} json.Unmarshal(resp, &data) @@ -91,7 +91,7 @@ func WebhookEndpointCreate(ctx context.Context, baseURL, apiVersion, apiKey, url SuppressOutput: true, APIBaseURL: baseURL, } - _, err := base.MakeRequest(ctx, apiKey, "/v1/webhook_endpoints", params, true) + _, err := base.MakeRequest(ctx, apiKey, "/v1/webhook_endpoints", params, true, nil) if err != nil { return err } diff --git a/pkg/rpcservice/events_resend.go b/pkg/rpcservice/events_resend.go index e704b20d2..0b5eec318 100644 --- a/pkg/rpcservice/events_resend.go +++ b/pkg/rpcservice/events_resend.go @@ -45,7 +45,7 @@ func (srv *RPCService) EventsResend(ctx context.Context, req *rpc.EventsResendRe params := getParamsFromReq(req) - stripeResp, err := stripeReq.MakeRequest(ctx, apiKey, path, params, true) + stripeResp, err := stripeReq.MakeRequest(ctx, apiKey, path, params, true, nil) if err != nil { return nil, status.Error(codes.FailedPrecondition, err.Error()) } From a3a08938317971094d3a202672be48c6bf5fd895 Mon Sep 17 00:00:00 2001 From: Charlie Cruzan Date: Wed, 21 Aug 2024 11:50:28 -1000 Subject: [PATCH 2/9] use expected name --- pkg/fixtures/triggers.go | 2 +- ...ter.thing.json => billing.meter.error_report_triggered.json} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename pkg/fixtures/triggers/{billing.meter.thing.json => billing.meter.error_report_triggered.json} (97%) diff --git a/pkg/fixtures/triggers.go b/pkg/fixtures/triggers.go index 5b872d24e..cdbed4b46 100644 --- a/pkg/fixtures/triggers.go +++ b/pkg/fixtures/triggers.go @@ -126,7 +126,7 @@ var Events = map[string]string{ "transfer.created": "triggers/transfer.created.json", "transfer.reversed": "triggers/transfer.reversed.json", "transfer.updated": "triggers/transfer.updated.json", - "billing.meter.thing": "triggers/billing.meter.thing.json", + "billing.meter.error_report_triggered": "triggers/billing.meter.error_report_triggered.json", } // BuildFromFixtureFile creates a new fixture struct for a file diff --git a/pkg/fixtures/triggers/billing.meter.thing.json b/pkg/fixtures/triggers/billing.meter.error_report_triggered.json similarity index 97% rename from pkg/fixtures/triggers/billing.meter.thing.json rename to pkg/fixtures/triggers/billing.meter.error_report_triggered.json index 9a25142f6..cb5e70d5c 100644 --- a/pkg/fixtures/triggers/billing.meter.thing.json +++ b/pkg/fixtures/triggers/billing.meter.error_report_triggered.json @@ -51,7 +51,7 @@ "params": {} }, { - "name": "billing_meter_event_stream", + "name": "create_billing_meter_event_stream", "path": "/v2/billing/meter_event_stream", "method": "post", "api_base": "https://events.stripe.com", From f786c3e454582e5ca75733dc8da7a25924dabddb Mon Sep 17 00:00:00 2001 From: Charlie Cruzan Date: Wed, 21 Aug 2024 11:52:33 -1000 Subject: [PATCH 3/9] add prefix --- pkg/fixtures/triggers.go | 2 +- ...ggered.json => v2.billing.meter.error_report_triggered.json} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename pkg/fixtures/triggers/{billing.meter.error_report_triggered.json => v2.billing.meter.error_report_triggered.json} (100%) diff --git a/pkg/fixtures/triggers.go b/pkg/fixtures/triggers.go index cdbed4b46..c6615c138 100644 --- a/pkg/fixtures/triggers.go +++ b/pkg/fixtures/triggers.go @@ -126,7 +126,7 @@ var Events = map[string]string{ "transfer.created": "triggers/transfer.created.json", "transfer.reversed": "triggers/transfer.reversed.json", "transfer.updated": "triggers/transfer.updated.json", - "billing.meter.error_report_triggered": "triggers/billing.meter.error_report_triggered.json", + "v2.billing.meter.error_report_triggered": "triggers/v2.billing.meter.error_report_triggered.json", } // BuildFromFixtureFile creates a new fixture struct for a file diff --git a/pkg/fixtures/triggers/billing.meter.error_report_triggered.json b/pkg/fixtures/triggers/v2.billing.meter.error_report_triggered.json similarity index 100% rename from pkg/fixtures/triggers/billing.meter.error_report_triggered.json rename to pkg/fixtures/triggers/v2.billing.meter.error_report_triggered.json From e7977648dab3c9b97d045e629a9d6a08415f902f Mon Sep 17 00:00:00 2001 From: Charlie Cruzan Date: Wed, 21 Aug 2024 11:58:19 -1000 Subject: [PATCH 4/9] lint --- pkg/requests/base.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/requests/base.go b/pkg/requests/base.go index 605ce18f8..4d17ab61f 100644 --- a/pkg/requests/base.go +++ b/pkg/requests/base.go @@ -203,6 +203,7 @@ func (rb *Base) MakeMultiPartRequest(ctx context.Context, apiKey, path string, p return rb.performRequest(ctx, client, path, params, reqBody.String(), errOnStatus, configure) } +// MakeV2Request will make a application/json request to the Stripe API with the specific payload given to it. func (rb *Base) MakeV2Request(ctx context.Context, apiKey, path string, params *RequestParameters, errOnStatus bool, additionalConfigure func(req *http.Request) error, jsonPayload string) ([]byte, error) { parsedBaseURL, err := url.Parse(rb.APIBaseURL) if err != nil { From e8f8da0060573f15c4ed8a16dd3724f4d22403f2 Mon Sep 17 00:00:00 2001 From: Charlie Cruzan Date: Thu, 22 Aug 2024 08:28:20 -1000 Subject: [PATCH 5/9] add tests to parser --- ....billing.meter.error_report_triggered.json | 4 +- pkg/parsers/parsers.go | 2 +- pkg/parsers/parsers_test.go | 137 +++++++++++++++++- 3 files changed, 138 insertions(+), 5 deletions(-) diff --git a/pkg/fixtures/triggers/v2.billing.meter.error_report_triggered.json b/pkg/fixtures/triggers/v2.billing.meter.error_report_triggered.json index cb5e70d5c..053f48d7d 100644 --- a/pkg/fixtures/triggers/v2.billing.meter.error_report_triggered.json +++ b/pkg/fixtures/triggers/v2.billing.meter.error_report_triggered.json @@ -61,10 +61,10 @@ "Authorization": "STRIPE-V2-ACCESS-TOKEN ${billing_meter_event_session:authentication_token}" }, "params": { - "as_of": "${time-now}", + "as_of": "${time-now-RFC3339}", "event_name": "${list_billing_meters_after_creation:data.0.event_name}", "external_identifier":"4242424242424242", - "timestamp":"${time-now}", + "timestamp":"${time-now-RFC3339}", "payload": { "value":"1", "stripe_customer_id":"${create_customer:id}" diff --git a/pkg/parsers/parsers.go b/pkg/parsers/parsers.go index 9005b185b..1b62e6f3b 100644 --- a/pkg/parsers/parsers.go +++ b/pkg/parsers/parsers.go @@ -287,7 +287,7 @@ func findSimilarQueryNames(queryRespMap map[string]gjson.Result, name string) ([ func ParseQuery(queryString string, queryRespMap map[string]gjson.Result) (string, error) { value := queryString - if queryString == "${time-now}" { + if queryString == "${time-now-RFC3339}" { return time.Now().Format(time.RFC3339), nil } diff --git a/pkg/parsers/parsers_test.go b/pkg/parsers/parsers_test.go index 76ddb77ee..85f2494cd 100644 --- a/pkg/parsers/parsers_test.go +++ b/pkg/parsers/parsers_test.go @@ -6,7 +6,9 @@ import ( "os" "path" "sort" + "strings" "testing" + "time" "github.com/spf13/afero" "github.com/stretchr/testify/assert" @@ -145,6 +147,22 @@ func TestParseInterfaceFromRaw(t *testing.T) { require.Equal(t, output[1], "salary=1000000000") } +func TestParseInterfaceToJSONFromRaw(t *testing.T) { + var rawFixtureData = []byte(`{ + "salary": 1000000000, + "email": "person@example.com" + }`) + + parsedFixtureData := make(map[string]interface{}) + json.Unmarshal(rawFixtureData, &parsedFixtureData) + + output, _ := ParseToApplicationJSON(parsedFixtureData, make(map[string]gjson.Result)) + jsonParams, _ := json.Marshal(output) + json := string(jsonParams) + require.Equal(t, 1000000000, int(gjson.Get(json, "salary").Num)) + require.Equal(t, "person@example.com", gjson.Get(json, "email").String()) +} + func TestParseInterfaceDeeplyNested(t *testing.T) { label := make(map[string]interface{}) label["custom"] = "First Name" @@ -167,6 +185,27 @@ func TestParseInterfaceDeeplyNested(t *testing.T) { require.Equal(t, "custom_fields[0][label][type]=custom", output[1]) } +func TestParseInterfaceToJSONDeeplyNested(t *testing.T) { + label := make(map[string]interface{}) + label["custom"] = "First Name" + label["type"] = "custom" + + customField := make(map[string]interface{}) + customField["label"] = label + + customFields := make([]interface{}, 1) + customFields[0] = customField + + data := make(map[string]interface{}) + data["custom_fields"] = customFields + + output, _ := ParseToApplicationJSON(data, make(map[string]gjson.Result)) + jsonParams, _ := json.Marshal(output) + json := string(jsonParams) + require.Equal(t, "custom", gjson.Get(json, "custom_fields.0.label.type").String()) + require.Equal(t, "First Name", gjson.Get(json, "custom_fields.0.label.custom").String()) +} + func TestParseInterface(t *testing.T) { address := make(map[string]interface{}) address["line1"] = "1 Planet Express St" @@ -185,7 +224,42 @@ func TestParseInterface(t *testing.T) { data := make(map[string]interface{}) data["name"] = "Bender Bending Rodriguez" - data["email"] = "bender@planex.com" + data["email"] = "bender@test.com" + data["address"] = address + data["tax_id_data"] = taxIDData + + output, _ := ParseToApplicationJSON(data, make(map[string]gjson.Result)) + jsonParams, _ := json.Marshal(output) + json := string(jsonParams) + require.Equal(t, "New New York", gjson.Get(json, "address.city").String()) + require.Equal(t, "1 Planet Express St", gjson.Get(json, "address.line1").String()) + require.Equal(t, "bender@test.com", gjson.Get(json, "email").String()) + require.Equal(t, "Bender Bending Rodriguez", gjson.Get(json, "name").String()) + require.Equal(t, "type_0", gjson.Get(json, "tax_id_data.0.type").String()) + require.Equal(t, "value_0", gjson.Get(json, "tax_id_data.0.value").String()) + require.Equal(t, "type_1", gjson.Get(json, "tax_id_data.1.type").String()) + require.Equal(t, "value_1", gjson.Get(json, "tax_id_data.1.value").String()) +} + +func TestParseInterfaceToJSON(t *testing.T) { + address := make(map[string]interface{}) + address["line1"] = "1 Planet Express St" + address["city"] = "New New York" + + // array of hashes + taxIDData := make([]interface{}, 2) + taxIDZero := make(map[string]interface{}) + taxIDZero["type"] = "type_0" + taxIDZero["value"] = "value_0" + taxIDOne := make(map[string]interface{}) + taxIDOne["type"] = "type_1" + taxIDOne["value"] = "value_1" + taxIDData[0] = taxIDZero + taxIDData[1] = taxIDOne + + data := make(map[string]interface{}) + data["name"] = "Bender Bending Rodriguez" + data["email"] = "bender@test.com" data["address"] = address data["tax_id_data"] = taxIDData @@ -195,7 +269,7 @@ func TestParseInterface(t *testing.T) { require.Equal(t, len(output), 8) require.Equal(t, "address[city]=New New York", output[0]) require.Equal(t, "address[line1]=1 Planet Express St", output[1]) - require.Equal(t, "email=bender@planex.com", output[2]) + require.Equal(t, "email=bender@test.com", output[2]) require.Equal(t, "name=Bender Bending Rodriguez", output[3]) require.Equal(t, "tax_id_data[0][type]=type_0", output[4]) require.Equal(t, "tax_id_data[0][value]=value_0", output[5]) @@ -224,6 +298,27 @@ func TestParseWithQueryIgnoreDefault(t *testing.T) { require.Equal(t, "source=tok_visa", output[3]) } +func TestParseJSONWithQueryIgnoreDefault(t *testing.T) { + queryRespMap := map[string]gjson.Result{ + "cust_bender": gjson.Parse(`{"id": "cust_bend123456789", "currency": "eur"}`), + } + + data := make(map[string]interface{}) + data["customer"] = "${cust_bender:id}" + data["source"] = "tok_visa" + data["amount"] = "100" + data["currency"] = "${cust_bender:currency|usd}" + + output, _ := ParseToApplicationJSON(data, queryRespMap) + jsonParams, _ := json.Marshal(output) + json := string(jsonParams) + + require.Equal(t, "100", gjson.Get(json, "amount").String()) + require.Equal(t, "eur", gjson.Get(json, "currency").String()) + require.Equal(t, "cust_bend123456789", gjson.Get(json, "customer").String()) + require.Equal(t, "tok_visa", gjson.Get(json, "source").String()) +} + func TestParseWithQueryDefaultValue(t *testing.T) { queryRespMap := map[string]gjson.Result{ "cust_bender": gjson.Parse(`{"id": "cust_bend123456789"}`), @@ -302,6 +397,21 @@ func TestParseWithEnvSubstring(t *testing.T) { fs.Remove(envPath) } +func TestParseWithTimeNow(t *testing.T) { + queryRespMap := map[string]gjson.Result{ + "cust_bender": gjson.Parse(`{"id": "cust_bend123456789"}`), + } + + data := make(map[string]interface{}) + data["time"] = "${time-now-RFC3339}" + + output, _ := ParseToFormData(data, queryRespMap) + expectedOutput := time.Now().Format(time.RFC3339) + require.Equal(t, len(output), 1) + // Check for equality except for seconds + require.True(t, strings.HasPrefix(output[0], "time="+expectedOutput[:len(expectedOutput)-3])) +} + func TestParseArray(t *testing.T) { queryRespMap := map[string]gjson.Result{ "cust_bender": gjson.Parse(`{"id": "cust_bend123456789", "timezones": ["Europe/Brussels", "Europe/Berlin"]}`), @@ -325,6 +435,29 @@ func TestParseArray(t *testing.T) { require.Equal(t, "timezones=[\"Europe/Brussels\", \"Europe/Berlin\"]", output[4]) } +func TestParseArrayToJSON(t *testing.T) { + queryRespMap := map[string]gjson.Result{ + "cust_bender": gjson.Parse(`{"id": "cust_bend123456789", "timezones": ["Europe/Brussels","Europe/Berlin"]}`), + } + + data := make(map[string]interface{}) + data["customer"] = "${cust_bender:id}" + data["timezones"] = "${cust_bender:timezones}" + data["first_timezone"] = "${cust_bender:timezones.0}" + data["second_timezone"] = "${cust_bender:timezones.1}" + data["third_timezone"] = "${cust_bender:timezones.2|notimezonefound}" + + output, _ := ParseToApplicationJSON(data, queryRespMap) + jsonParams, _ := json.Marshal(output) + json := string(jsonParams) + + require.Equal(t, "cust_bend123456789", gjson.Get(json, "customer").String()) + require.Equal(t, "Europe/Brussels", gjson.Get(json, "first_timezone").String()) + require.Equal(t, "Europe/Berlin", gjson.Get(json, "second_timezone").String()) + require.Equal(t, "notimezonefound", gjson.Get(json, "third_timezone").String()) + require.Equal(t, "[\"Europe/Brussels\",\"Europe/Berlin\"]", gjson.Get(json, "timezones").String()) +} + func TestToFixtureQuery(t *testing.T) { tests := []struct { input string From ee2cd4f6227912b3ff38be0a13d75c2b5adfe861 Mon Sep 17 00:00:00 2001 From: Charlie Cruzan Date: Fri, 23 Aug 2024 09:40:54 -1000 Subject: [PATCH 6/9] simplify trigger --- ....billing.meter.error_report_triggered.json | 42 +------------------ 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/pkg/fixtures/triggers/v2.billing.meter.error_report_triggered.json b/pkg/fixtures/triggers/v2.billing.meter.error_report_triggered.json index 053f48d7d..86dd534bf 100644 --- a/pkg/fixtures/triggers/v2.billing.meter.error_report_triggered.json +++ b/pkg/fixtures/triggers/v2.billing.meter.error_report_triggered.json @@ -3,43 +3,6 @@ "template_version": 0 }, "fixtures": [ - { - "name": "create_customer", - "path": "/v1/customers", - "method": "post", - "params": { - "description": "(created by Stripe CLI)" - } - }, - { - "name": "list_billing_meters", - "path": "/v1/billing/meters", - "method": "get", - "params": { - "status": "active" - } - }, - { - "name": "billing_meter", - "path": "/v1/billing/meters", - "method": "post", - "params": { - "display_name": "Stripe CLI Billing Meter", - "event_name": "stripe_cli_billing_meter_for_fixture", - "default_aggregation": { - "formula": "sum" - } - }, - "expected_error_type": "invalid_request_error" - }, - { - "name": "list_billing_meters_after_creation", - "path": "/v1/billing/meters", - "method": "get", - "params": { - "status": "active" - } - }, { "name": "billing_meter_event_session", "path": "/v2/billing/meter_event_session", @@ -62,12 +25,11 @@ }, "params": { "as_of": "${time-now-RFC3339}", - "event_name": "${list_billing_meters_after_creation:data.0.event_name}", + "event_name": " ", "external_identifier":"4242424242424242", "timestamp":"${time-now-RFC3339}", "payload": { - "value":"1", - "stripe_customer_id":"${create_customer:id}" + "stripe_customer_id":"cus_123foo" } } } From c0cec18a31dcf1c83567a68f772ccb326d4b596d Mon Sep 17 00:00:00 2001 From: Charlie Cruzan Date: Fri, 23 Aug 2024 14:18:39 -1000 Subject: [PATCH 7/9] 2 triggers not 1 --- pkg/fixtures/triggers.go | 3 +- ....billing.meter.error_report_triggered.json | 67 +++++++++++++++++++ ...n => v1.billing.meter.no_meter_found.json} | 6 +- 3 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 pkg/fixtures/triggers/v1.billing.meter.error_report_triggered.json rename pkg/fixtures/triggers/{v2.billing.meter.error_report_triggered.json => v1.billing.meter.no_meter_found.json} (83%) diff --git a/pkg/fixtures/triggers.go b/pkg/fixtures/triggers.go index c6615c138..f6eb843ca 100644 --- a/pkg/fixtures/triggers.go +++ b/pkg/fixtures/triggers.go @@ -126,7 +126,8 @@ var Events = map[string]string{ "transfer.created": "triggers/transfer.created.json", "transfer.reversed": "triggers/transfer.reversed.json", "transfer.updated": "triggers/transfer.updated.json", - "v2.billing.meter.error_report_triggered": "triggers/v2.billing.meter.error_report_triggered.json", + "v1.billing.meter.error_report_triggered": "triggers/v1.billing.meter.error_report_triggered.json", + "v1.billing.meter.no_meter_found": "triggers/v1.billing.meter.no_meter_found.json", } // BuildFromFixtureFile creates a new fixture struct for a file diff --git a/pkg/fixtures/triggers/v1.billing.meter.error_report_triggered.json b/pkg/fixtures/triggers/v1.billing.meter.error_report_triggered.json new file mode 100644 index 000000000..2b171b6aa --- /dev/null +++ b/pkg/fixtures/triggers/v1.billing.meter.error_report_triggered.json @@ -0,0 +1,67 @@ + + +{ + "_meta": { + "template_version": 0 + }, + "fixtures": [ + { + "name": "list_billing_meters", + "path": "/v1/billing/meters", + "method": "get", + "params": { + "status": "active" + } + }, + { + "name": "billing_meter", + "path": "/v1/billing/meters", + "method": "post", + "params": { + "display_name": "Stripe CLI Billing Meter", + "event_name": "stripe_cli_billing_meter_for_fixture", + "default_aggregation": { + "formula": "sum" + } + }, + "expected_error_type": "invalid_request_error" + }, + { + "name": "list_billing_meters_after_creation", + "path": "/v1/billing/meters", + "method": "get", + "params": { + "status": "active" + } + }, + { + "name": "billing_meter_event_session", + "path": "/v2/billing/meter_event_session", + "method": "post", + "headers": { + "Content-Type": "application/json", + "Stripe-Version": "unsafe-development" + }, + "params": {} + }, + { + "name": "create_billing_meter_event_stream", + "path": "/v2/billing/meter_event_stream", + "method": "post", + "api_base": "https://events.stripe.com", + "headers": { + "Content-Type": "application/json", + "Stripe-Version": "unsafe-development", + "Authorization": "STRIPE-V2-ACCESS-TOKEN ${billing_meter_event_session:authentication_token}" + }, + "params": { + "event_name": "${list_billing_meters_after_creation:data.0.event_name}", + "timestamp":"${time-now-RFC3339}", + "payload": { + "value":"1", + "stripe_customer_id":"cus_1nonexistentcustomer" + } + } + } + ] +} diff --git a/pkg/fixtures/triggers/v2.billing.meter.error_report_triggered.json b/pkg/fixtures/triggers/v1.billing.meter.no_meter_found.json similarity index 83% rename from pkg/fixtures/triggers/v2.billing.meter.error_report_triggered.json rename to pkg/fixtures/triggers/v1.billing.meter.no_meter_found.json index 86dd534bf..2ebb590d0 100644 --- a/pkg/fixtures/triggers/v2.billing.meter.error_report_triggered.json +++ b/pkg/fixtures/triggers/v1.billing.meter.no_meter_found.json @@ -24,12 +24,10 @@ "Authorization": "STRIPE-V2-ACCESS-TOKEN ${billing_meter_event_session:authentication_token}" }, "params": { - "as_of": "${time-now-RFC3339}", - "event_name": " ", - "external_identifier":"4242424242424242", + "event_name": "not_a_real_stripe_meter_event", "timestamp":"${time-now-RFC3339}", "payload": { - "stripe_customer_id":"cus_123foo" + "value":"10" } } } From b7c6b871e40d76602bc01c314fd27a7d103ca949 Mon Sep 17 00:00:00 2001 From: Charlie Cruzan Date: Tue, 27 Aug 2024 12:54:54 -1000 Subject: [PATCH 8/9] update payloads --- ...1.billing.meter.error_report_triggered.json | 4 ++-- .../v1.billing.meter.no_meter_found.json | 2 +- pkg/parsers/parsers.go | 3 +++ pkg/parsers/parsers_test.go | 18 ++++++++++++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/pkg/fixtures/triggers/v1.billing.meter.error_report_triggered.json b/pkg/fixtures/triggers/v1.billing.meter.error_report_triggered.json index 2b171b6aa..bc9f1fed3 100644 --- a/pkg/fixtures/triggers/v1.billing.meter.error_report_triggered.json +++ b/pkg/fixtures/triggers/v1.billing.meter.error_report_triggered.json @@ -57,9 +57,9 @@ "params": { "event_name": "${list_billing_meters_after_creation:data.0.event_name}", "timestamp":"${time-now-RFC3339}", + "identifier": "${generate-uuid}", "payload": { - "value":"1", - "stripe_customer_id":"cus_1nonexistentcustomer" + "value":"1" } } } diff --git a/pkg/fixtures/triggers/v1.billing.meter.no_meter_found.json b/pkg/fixtures/triggers/v1.billing.meter.no_meter_found.json index 2ebb590d0..de909a5a7 100644 --- a/pkg/fixtures/triggers/v1.billing.meter.no_meter_found.json +++ b/pkg/fixtures/triggers/v1.billing.meter.no_meter_found.json @@ -24,7 +24,7 @@ "Authorization": "STRIPE-V2-ACCESS-TOKEN ${billing_meter_event_session:authentication_token}" }, "params": { - "event_name": "not_a_real_stripe_meter_event", + "event_name": "${generate-uuid}", "timestamp":"${time-now-RFC3339}", "payload": { "value":"10" diff --git a/pkg/parsers/parsers.go b/pkg/parsers/parsers.go index 1b62e6f3b..328cd5d1c 100644 --- a/pkg/parsers/parsers.go +++ b/pkg/parsers/parsers.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "github.com/google/uuid" "github.com/joho/godotenv" "github.com/tidwall/gjson" @@ -289,6 +290,8 @@ func ParseQuery(queryString string, queryRespMap map[string]gjson.Result) (strin if queryString == "${time-now-RFC3339}" { return time.Now().Format(time.RFC3339), nil + } else if queryString == "${generate-uuid}" { + return uuid.NewString(), nil } if query, isQuery := ToFixtureQuery(queryString); isQuery { diff --git a/pkg/parsers/parsers_test.go b/pkg/parsers/parsers_test.go index 85f2494cd..360785e09 100644 --- a/pkg/parsers/parsers_test.go +++ b/pkg/parsers/parsers_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/google/uuid" "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -412,6 +413,23 @@ func TestParseWithTimeNow(t *testing.T) { require.True(t, strings.HasPrefix(output[0], "time="+expectedOutput[:len(expectedOutput)-3])) } +func TestParseWithUUID(t *testing.T) { + queryRespMap := map[string]gjson.Result{ + "cust_bender": gjson.Parse(`{"id": "cust_bend123456789"}`), + } + + data := make(map[string]interface{}) + data["identifier"] = "${generate-uuid}" + + output, _ := ParseToFormData(data, queryRespMap) + require.Equal(t, len(output), 1) + require.True(t, strings.HasPrefix(output[0], "identifier=")) + generatedUUID := strings.Split(output[0], "=") + res, err := uuid.Parse(generatedUUID[1]) + require.Nil(t, err) + require.NotNil(t, res) +} + func TestParseArray(t *testing.T) { queryRespMap := map[string]gjson.Result{ "cust_bender": gjson.Parse(`{"id": "cust_bend123456789", "timezones": ["Europe/Brussels", "Europe/Berlin"]}`), From 08c437126c53911c3fe959ac224cc4f8e52f79f3 Mon Sep 17 00:00:00 2001 From: Charlie Cruzan Date: Wed, 11 Sep 2024 10:24:35 -0700 Subject: [PATCH 9/9] update to bearer auth --- .../triggers/v1.billing.meter.error_report_triggered.json | 2 +- pkg/fixtures/triggers/v1.billing.meter.no_meter_found.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/fixtures/triggers/v1.billing.meter.error_report_triggered.json b/pkg/fixtures/triggers/v1.billing.meter.error_report_triggered.json index bc9f1fed3..b08cbe62a 100644 --- a/pkg/fixtures/triggers/v1.billing.meter.error_report_triggered.json +++ b/pkg/fixtures/triggers/v1.billing.meter.error_report_triggered.json @@ -52,7 +52,7 @@ "headers": { "Content-Type": "application/json", "Stripe-Version": "unsafe-development", - "Authorization": "STRIPE-V2-ACCESS-TOKEN ${billing_meter_event_session:authentication_token}" + "Authorization": "Bearer ${billing_meter_event_session:authentication_token}" }, "params": { "event_name": "${list_billing_meters_after_creation:data.0.event_name}", diff --git a/pkg/fixtures/triggers/v1.billing.meter.no_meter_found.json b/pkg/fixtures/triggers/v1.billing.meter.no_meter_found.json index de909a5a7..a2db11cf9 100644 --- a/pkg/fixtures/triggers/v1.billing.meter.no_meter_found.json +++ b/pkg/fixtures/triggers/v1.billing.meter.no_meter_found.json @@ -21,7 +21,7 @@ "headers": { "Content-Type": "application/json", "Stripe-Version": "unsafe-development", - "Authorization": "STRIPE-V2-ACCESS-TOKEN ${billing_meter_event_session:authentication_token}" + "Authorization": "Bearer ${billing_meter_event_session:authentication_token}" }, "params": { "event_name": "${generate-uuid}",