diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..a4c584c91 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,22 @@ +exclude: client +fail_fast: true +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + exclude: .cloud + - id: check-added-large-files +- repo: https://github.com/formancehq/pre-commit-hooks + rev: dd079f7c30ad72446d615f55a000d4f875e79633 + hooks: + - id: gogenerate + files: swagger.yaml + - id: gomodtidy + - id: goimports + - id: gofmt + - id: golangci-lint + - id: gotests + - id: commitlint diff --git a/Makefile b/Makefile index 0f1162921..f6057fd76 100644 --- a/Makefile +++ b/Makefile @@ -9,4 +9,3 @@ lint-fix: run-tests: go test -race -count=1 ./... - diff --git a/README.md b/README.md index 56fe9d4d8..bff14a259 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,14 @@ Payments works as a standalone binary, the latest of which can be downloaded from the [releases page](https://github.com/formancehq/payments/releases). You can move the binary to any executable path, such as to `/usr/local/bin`. Installations using brew, apt, yum or docker are also [available](https://docs.formance.com/oss/payments/get-started/installation). ```SHELL -payments +payments ``` # What is it? Basically, a framework. -A framework to ingest payin and payout coming from different payment providers (PSP). +A framework to ingest payin and payout coming from different payment providers (PSP). The framework contains connectors. Each connector is basically a translator for a PSP. Translator, because the main role of a connector is to translate specific PSP payin/payout formats to a generalized format used at Formance. diff --git a/cmd/root.go b/cmd/root.go index 6bc3dc35a..35aa100c3 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -7,6 +7,7 @@ import ( "strings" _ "github.com/bombsimon/logrusr/v3" + "github.com/formancehq/go-libs/sharedotlp/pkg/sharedotlptraces" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -37,46 +38,33 @@ func rootCommand() *cobra.Command { root.AddCommand(server) root.PersistentFlags().Bool(debugFlag, false, "Debug mode") - root.Flags().BoolP("toggle", "t", false, "Help message for toggle") - root.Flags().Bool(debugFlag, false, "Debug mode") - root.Flags().String(mongodbURIFlag, "mongodb://localhost:27017", "MongoDB address") - root.Flags().String(mongodbDatabaseFlag, "payments", "MongoDB database name") - root.Flags().Bool(otelTracesFlag, false, "Enable OpenTelemetry traces support") - root.Flags().String(otelTracesExporterFlag, "stdout", "OpenTelemetry traces exporter") - root.Flags().String(otelTracesExporterJaegerEndpointFlag, - "", "OpenTelemetry traces Jaeger exporter endpoint") - root.Flags().String(otelTracesExporterJaegerUserFlag, - "", "OpenTelemetry traces Jaeger exporter user") - root.Flags().String(otelTracesExporterJaegerPasswordFlag, - "", "OpenTelemetry traces Jaeger exporter password") - root.Flags().String(otelTracesExporterOTLPModeFlag, - "grpc", "OpenTelemetry traces OTLP exporter mode (grpc|http)") - root.Flags().String(otelTracesExporterOTLPEndpointFlag, - "", "OpenTelemetry traces grpc endpoint") - root.Flags().Bool(otelTracesExporterOTLPInsecureFlag, - false, "OpenTelemetry traces grpc insecure") - root.Flags().String(envFlag, "local", "Environment") - root.Flags().Bool(publisherKafkaEnabledFlag, false, "Publish write events to kafka") - root.Flags().StringSlice(publisherKafkaBrokerFlag, []string{}, "Kafka address is kafka enabled") - root.Flags().StringSlice(publisherTopicMappingFlag, + server.Flags().BoolP("toggle", "t", false, "Help message for toggle") + server.Flags().String(mongodbURIFlag, "mongodb://localhost:27017", "MongoDB address") + server.Flags().String(mongodbDatabaseFlag, "payments", "MongoDB database name") + server.Flags().String(envFlag, "local", "Environment") + server.Flags().Bool(publisherKafkaEnabledFlag, false, "Publish write events to kafka") + server.Flags().StringSlice(publisherKafkaBrokerFlag, []string{}, "Kafka address is kafka enabled") + server.Flags().StringSlice(publisherTopicMappingFlag, []string{}, "Define mapping between internal event types and topics") - root.Flags().Bool(publisherHTTPEnabledFlag, false, "Sent write event to http endpoint") - root.Flags().Bool(publisherKafkaSASLEnabled, false, "Enable SASL authentication on kafka publisher") - root.Flags().String(publisherKafkaSASLUsername, "", "SASL username") - root.Flags().String(publisherKafkaSASLPassword, "", "SASL password") - root.Flags().String(publisherKafkaSASLMechanism, "", "SASL authentication mechanism") - root.Flags().Int(publisherKafkaSASLScramSHASize, 512, "SASL SCRAM SHA size") - root.Flags().Bool(publisherKafkaTLSEnabled, false, "Enable TLS to connect on kafka") - root.Flags().Bool(authBasicEnabledFlag, false, "Enable basic auth") - root.Flags().StringSlice(authBasicCredentialsFlag, []string{}, + server.Flags().Bool(publisherHTTPEnabledFlag, false, "Sent write event to http endpoint") + server.Flags().Bool(publisherKafkaSASLEnabled, false, "Enable SASL authentication on kafka publisher") + server.Flags().String(publisherKafkaSASLUsername, "", "SASL username") + server.Flags().String(publisherKafkaSASLPassword, "", "SASL password") + server.Flags().String(publisherKafkaSASLMechanism, "", "SASL authentication mechanism") + server.Flags().Int(publisherKafkaSASLScramSHASize, 512, "SASL SCRAM SHA size") + server.Flags().Bool(publisherKafkaTLSEnabled, false, "Enable TLS to connect on kafka") + server.Flags().Bool(authBasicEnabledFlag, false, "Enable basic auth") + server.Flags().StringSlice(authBasicCredentialsFlag, []string{}, "HTTP basic auth credentials (:)") - root.Flags().Bool(authBearerEnabledFlag, false, "Enable bearer auth") - root.Flags().String(authBearerIntrospectURLFlag, "", "OAuth2 introspect URL") - root.Flags().StringSlice(authBearerAudienceFlag, []string{}, "Allowed audiences") - root.Flags().Bool(authBearerAudiencesWildcardFlag, false, "Don't check audience") - root.Flags().Bool(authBearerUseScopesFlag, + server.Flags().Bool(authBearerEnabledFlag, false, "Enable bearer auth") + server.Flags().String(authBearerIntrospectURLFlag, "", "OAuth2 introspect URL") + server.Flags().StringSlice(authBearerAudienceFlag, []string{}, "Allowed audiences") + server.Flags().Bool(authBearerAudiencesWildcardFlag, false, "Don't check audience") + server.Flags().Bool(authBearerUseScopesFlag, false, "Use scopes as defined by rfc https://datatracker.ietf.org/doc/html/rfc8693") + sharedotlptraces.InitOTLPTracesFlags(server.Flags()) + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) viper.AutomaticEnv() diff --git a/cmd/server.go b/cmd/server.go index 8e330238f..8ff107558 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -11,12 +11,12 @@ import ( "github.com/Shopify/sarama" "github.com/ThreeDotsLabs/watermill/message" - "github.com/numary/go-libs/sharedlogging" - "github.com/numary/go-libs/sharedlogging/sharedlogginglogrus" - "github.com/numary/go-libs/sharedotlp/pkg/sharedotlptraces" - "github.com/numary/go-libs/sharedpublish" - "github.com/numary/go-libs/sharedpublish/sharedpublishhttp" - "github.com/numary/go-libs/sharedpublish/sharedpublishkafka" + "github.com/formancehq/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging/sharedlogginglogrus" + "github.com/formancehq/go-libs/sharedotlp/pkg/sharedotlptraces" + "github.com/formancehq/go-libs/sharedpublish" + "github.com/formancehq/go-libs/sharedpublish/sharedpublishhttp" + "github.com/formancehq/go-libs/sharedpublish/sharedpublishkafka" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -83,15 +83,7 @@ func runServer(cmd *cobra.Command, args []string) error { } options = append(options, databaseOptions) - - options = append(options, sharedotlptraces.TracesModule(sharedotlptraces.ModuleConfig{ - Exporter: viper.GetString(otelTracesExporterFlag), - OTLPConfig: &sharedotlptraces.OTLPConfig{ - Mode: viper.GetString(otelTracesExporterOTLPModeFlag), - Endpoint: viper.GetString(otelTracesExporterOTLPEndpointFlag), - Insecure: viper.GetBool(otelTracesExporterOTLPInsecureFlag), - }, - })) + options = append(options, sharedotlptraces.CLITracesModule(viper.GetViper())) options = append(options, fx.Provide(fx.Annotate(func(p message.Publisher) *sharedpublish.TopicMapperPublisher { diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 000000000..3580f6021 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,5 @@ +module.exports = { + extends: [ + '@commitlint/config-conventional' + ] +} diff --git a/docs/tuto-connector.md b/docs/tuto-connector.md index 09c20e5ca..171798c76 100644 --- a/docs/tuto-connector.md +++ b/docs/tuto-connector.md @@ -3,7 +3,7 @@ _referenced in `/pkg/bridge/connectors/dummypay`_ We are going to create a fake connector which read a directory. -In this directory, a fake bank service will create files. +In this directory, a fake bank service will create files. Each files contain a payin or a payout as a json object. First, to create a connector, we need a loader. @@ -21,13 +21,13 @@ type Loader[ConnectorConfig payments.ConnectorConfigObject, TaskDescriptor payme // ApplyDefaults is used to fill default values of the provided configuration object. ApplyDefaults(t ConnectorConfig) ConnectorConfig // AllowTasks define how many task the connector can run - // If too many tasks are scheduled by the connector, + // If too many tasks are scheduled by the connector, // those will be set to pending state and restarted later when some other tasks will be terminated AllowTasks() int } ``` -A connector has a name. +A connector has a name. This name is provided by the loader by the method Name(). Also, each connector define a config object using generics which has to implement interface payments.ConnectorConfigObject. @@ -62,7 +62,7 @@ For now, the ```Config``` and ```TaskDescriptor``` are just empty structs, we wi Also, we didn't define any logic on our connector. It's time to plug our connector on the core. -Edit the file cmd/root.go and go at the end of the method HTTPModule(), you should find a code like this : +Edit the file cmd/root.go and go at the end of the method HTTPModule(), you should find a code like this : ```go ... cdi.ConnectorModule[stripe.Config, stripe.TaskDescriptor]( @@ -72,7 +72,7 @@ Edit the file cmd/root.go and go at the end of the method HTTPModule(), you shou ... ``` -You can add your connector bellow that : +You can add your connector bellow that : ```go ... cdi.ConnectorModule[example.Config, example.TaskDescriptor]( @@ -89,12 +89,12 @@ payments-payments-1 | time="2022-07-01T09:12:21Z" level=info msg="Not installed ``` This indicates your connector is properly integrated. -You can install it like this : +You can install it like this : ```bash curl http://localhost:8080/connectors/example -X POST ``` -The service will display something like this : +The service will display something like this : ```bash payments-payments-1 | time="2022-07-01T10:04:53Z" level=info msg="Install connector example" component=connector-manager config="{}" provider=example payments-payments-1 | time="2022-07-01T10:04:53Z" level=info msg="Connector installed" component=connector-manager provider=example @@ -103,12 +103,12 @@ payments-payments-1 | time="2022-07-01T10:04:53Z" level=info msg="Connector ins Your connector was installed! It makes nothing but it is installed. -Let's uninstall it before continue : +Let's uninstall it before continue : ```bash curl http://localhost:8080/connectors/example -X DELETE ``` -You should see something like this : +You should see something like this : ```bash payments-payments-1 | time="2022-07-01T10:06:16Z" level=info msg="Uninstalling connector" component=connector-manager provider=example payments-payments-1 | time="2022-07-01T10:06:16Z" level=info msg="Stopping scheduler..." component=scheduler provider=example @@ -125,7 +125,7 @@ Load(logger sharedlogging.Logger, config ConnectorConfig) Connector[TaskDescript ``` The Load function take a logger provided by the framework and a config, probably provided by the api endpoint. -It has to return a Connector object. Here the interface : +It has to return a Connector object. Here the interface : ```go // Connector provide entry point to a payment provider type Connector[TaskDescriptor payments.TaskDescriptor any] interface { @@ -139,9 +139,9 @@ type Connector[TaskDescriptor payments.TaskDescriptor any] interface { ``` When you made ```curl http://localhost:8080/connectors/example -X POST```, the framework called the ```Install()``` method. -When you made ```curl http://localhost:8080/connectors/example -X DELETE```, the framework called the ```Uninstall(ctx context.Context) error``` method. +When you made ```curl http://localhost:8080/connectors/example -X DELETE```, the framework called the ```Uninstall(ctx context.Context) error``` method. -It's time to add some logic. We have to modify our loader but before let's add some property to our config : +It's time to add some logic. We have to modify our loader but before let's add some property to our config : ```go type ( Config struct { @@ -165,7 +165,7 @@ var Loader = integration.NewLoaderBuilder[Config, TaskDescriptor]("example"). WithLoad(func(logger sharedlogging.Logger, config Config) integration.Connector[TaskDescriptor] { return integration.NewConnectorBuilder[TaskDescriptor](). WithInstall(func(ctx task.ConnectorContext[TaskDescriptor]) error { - return errors.New("not implemented") + return errors.New("not implemented") }). Build() }). @@ -184,24 +184,24 @@ type ConnectorContext[TaskDescriptor payments.TaskDescriptor] interface { } ``` -Basically this context provides two things : +Basically this context provides two things : * a ```context.Context``` : If the connector make long-running processing, it should listen on this context to abort if necessary. * a ```Scheduler[TaskDescriptor]```: A scheduler to run tasks But, what is a task ? -A task is like a process that the framework will handle for you. It is basically a simple function. +A task is like a process that the framework will handle for you. It is basically a simple function. When installed, a connector has the opportunity to schedule some tasks and let the system handle them for him. A task has a descriptor. The descriptor must be immutable and represents a specific task in the system. It can be anything. -A task also have a state. The state can change and the framework provides necessary apis to do that. We will come on that later. +A task also have a state. The state can change and the framework provides necessary apis to do that. We will come on that later. As the descriptor, the state is freely defined by the connector. In our case, the main task seems evident as we have to list the target repository. Secondary tasks will be defined to read each files present in the directory. We can define our task descriptor to a string. The value will be the file name in case of secondary tasks and a hardcoded value of "directory" for the main task. -Before add the logic, let's modify our previously introduced task descriptor : +Before add the logic, let's modify our previously introduced task descriptor : ```go type ( ... @@ -229,7 +229,7 @@ This method is in charge of providing a ```task.Task``` instance given a descrip So, when calling ```ctx.Scheduler().Schedule("directory")```, the framework will call the ```Resolve``` method with "directory" as parameter. -Let's implement the resolve method : +Let's implement the resolve method : ```go ... WithInstall(func(ctx task.ConnectorContext[TaskDescriptor]) error { @@ -238,25 +238,25 @@ Let's implement the resolve method : WithResolve(func(descriptor TaskDescriptor) task.Task { if descriptor == "directory" { return func() { - // TODO - } + // TODO + } } // Secondary tasks return func() { - // TODO - } + // TODO + } }). ... ``` Now, we have to implement the logic for each task. -Let's start with the main task which read the directory : +Let's start with the main task which read the directory : ```go ... WithResolve(func(descriptor TaskDescriptor) task.Task { if descriptor == "directory" { - return func(ctx context.Context, logget sharedlogging.Logger, scheduler task.Scheduler) + return func(ctx context.Context, logget sharedlogging.Logger, scheduler task.Scheduler) for { select { case <-ctx.Done(): @@ -290,12 +290,12 @@ Let's start with the main task which read the directory : Let's test our implementation. -Start the server as usual and issue a curl request to install the connector : +Start the server as usual and issue a curl request to install the connector : ```bash curl http://localhost:8080/connectors/example -X POST -d '{"directory": "/tmp/payments"}' ``` -Here we instruct the connector to watch the directory /tmp/payments. Check the app logs, you should see something like this : +Here we instruct the connector to watch the directory /tmp/payments. Check the app logs, you should see something like this : ```bash payments-payments-1 | time="2022-07-01T12:29:05Z" level=info msg="Install connector example" component=connector-manager config="{/tmp/payments}" provider=example payments-payments-1 | time="2022-07-01T12:29:05Z" level=info msg="Starting task..." component=scheduler provider=example task-id="ImRpcmVjdG9yeSI=" @@ -304,7 +304,7 @@ payments-payments-1 | time="2022-07-01T13:26:51Z" level=info msg="Opening direc payments-payments-1 | time="2022-07-01T13:26:51Z" level=error msg="Error opening directory '/tmp/payments': open /tmp/payments: no such file or directory" component=scheduler provider=example task-id="ImRpcmVjdG9yeSI=" ``` -As expected, the task trigger an error because of non-existent /tmp/payments directory. +As expected, the task trigger an error because of non-existent /tmp/payments directory. You can see the tasks on api too : ```bash @@ -330,18 +330,18 @@ Let's create the missing directory: docker compose exec payments mkdir /tmp/payments ``` -After a few seconds, you should see thoses logs on app : +After a few seconds, you should see thoses logs on app : ```bash payments-payments-1 | time="2022-07-01T13:29:21Z" level=info msg="Opening directory '/tmp/payments'..." component=scheduler provider=example task-id="ImRpcmVjdG9yeSI=" payments-payments-1 | time="2022-07-01T13:29:21Z" level=info msg="Found 0 files" component=scheduler provider=example task-id="ImRpcmVjdG9yeSI=" ``` -Ok, create a payin file : +Ok, create a payin file : ```bash docker compose cp docs/samples-payin.json payments:/tmp/payments/001.json ``` -You should see those lines on logs : +You should see those lines on logs : ```bash payments-payments-1 | time="2022-07-01T13:33:51Z" level=info msg="Opening directory '/tmp/payments'..." component=scheduler provider=example task-id="ImRpcmVjdG9yeSI=" payments-payments-1 | time="2022-07-01T13:33:51Z" level=info msg="Found 1 files" component=scheduler provider=example task-id="ImRpcmVjdG9yeSI=" @@ -378,26 +378,26 @@ Again, you can view the tasks on the api : As you can see, as the first task is still active, the second is flagged as failed with an error message. -It's time to implement the second task : +It's time to implement the second task : ```go ... file, err := os.Open(filepath.Join(config.Directory, string(descriptor))) if err != nil { return err } - + type JsonPayment struct { payments.Data Reference string `json:"reference"` Type string `json:"type"` } - + jsonPayment := &JsonPayment{} err = json.NewDecoder(file).Decode(jsonPayment) if err != nil { return err } - + return ingester.Ingest(ctx, ingestion.Batch{ { Referenced: payments.Referenced{ @@ -413,7 +413,7 @@ It's time to implement the second task : Now restart the service, uninstall the connector, and reinstall it. -Here the logs : +Here the logs : ```bash payments-payments-1 | time="2022-07-01T14:25:20Z" level=info msg="Install connector example" component=connector-manager config="{/tmp/payments}" provider=example payments-payments-1 | time="2022-07-01T14:25:20Z" level=info msg="Starting task..." component=scheduler provider=example task-id="ImRpcmVjdG9yeSI=" @@ -426,9 +426,9 @@ payments-payments-1 | time="2022-07-01T14:25:30Z" level=info msg="Task terminat As you can see, this time the second task has been started and was terminated with success. -It should have created a payment on database. Let's check : +It should have created a payment on database. Let's check : ```bash -curl http://localhost:8080/payments | jq +curl http://localhost:8080/payments | jq { "data": [ @@ -459,7 +459,7 @@ curl http://localhost:8080/payments | jq The last important part is the ```Ingester```. -In the code of the second task, you should have seen this part : +In the code of the second task, you should have seen this part : ```go return ingester.Ingest(ctx.Context(), ingestion.Batch{ { @@ -474,11 +474,11 @@ return ingester.Ingest(ctx.Context(), ingestion.Batch{ ``` The ingester is in charge of accepting payments from a task and an eventual state to be persisted. -In our case, we don't alter the state, but we could if we want (we passed an empty struct). +In our case, we don't alter the state, but we could if we want (we passed an empty struct). If the connector is restarted, the task will be restarted with the previously state. -The complete code : +The complete code : ```go package example @@ -490,7 +490,7 @@ import ( "path/filepath" "time" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" payments "github.com/formancehq/payments/pkg" "github.com/formancehq/payments/pkg/bridge/ingestion" "github.com/formancehq/payments/pkg/bridge/integration" diff --git a/go.mod b/go.mod index dcc8d3a93..4f66d6ed2 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,12 @@ require ( github.com/ThreeDotsLabs/watermill v1.1.1 github.com/bombsimon/logrusr/v3 v3.0.0 github.com/davecgh/go-spew v1.1.1 + github.com/formancehq/go-libs v1.2.0 + github.com/formancehq/go-libs/sharedotlp v0.0.0-20221123112229-2c8ab193c63c github.com/gibson042/canonicaljson-go v1.0.3 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/iancoleman/strcase v0.2.0 - github.com/numary/go-libs v1.0.1 - github.com/numary/go-libs/sharedotlp v0.0.0-20220829123039-3eeb76619d81 github.com/ory/dockertest v3.3.5+incompatible github.com/pborman/uuid v1.2.1 github.com/pkg/errors v0.9.1 @@ -21,13 +21,16 @@ require ( github.com/spf13/afero v1.9.2 github.com/spf13/cobra v1.5.0 github.com/spf13/viper v1.12.0 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.1 github.com/stripe/stripe-go/v72 v72.122.0 github.com/uptrace/opentelemetry-go-extra/otellogrus v0.1.15 github.com/xdg-go/scram v1.1.1 - go.mongodb.org/mongo-driver v1.10.1 + go.mongodb.org/mongo-driver v1.10.3 go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.34.0 - go.opentelemetry.io/otel v1.9.0 + go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.36.4 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.36.4 + go.opentelemetry.io/otel v1.11.1 + go.opentelemetry.io/otel/trace v1.11.1 go.uber.org/dig v1.15.0 go.uber.org/fx v1.18.1 golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde @@ -41,7 +44,7 @@ require ( github.com/ThreeDotsLabs/watermill-kafka/v2 v2.2.2 // indirect github.com/ajg/form v1.5.1 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cenkalti/backoff/v4 v4.2.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/containerd/continuity v0.2.2 // indirect github.com/dgraph-io/ristretto v0.1.0 // indirect @@ -61,9 +64,9 @@ require ( github.com/golang/glog v1.0.0 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-cmp v0.5.8 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.14.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect @@ -98,24 +101,25 @@ require ( github.com/xdg-go/stringprep v1.0.3 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect go.opentelemetry.io/contrib/instrumentation/github.com/Shopify/sarama/otelsarama v0.34.0 // indirect - go.opentelemetry.io/otel/exporters/jaeger v1.9.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.9.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.9.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.9.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.9.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.9.0 // indirect - go.opentelemetry.io/otel/sdk v1.9.0 // indirect - go.opentelemetry.io/otel/trace v1.9.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.11.1 // indirect + go.opentelemetry.io/otel/exporters/jaeger v1.11.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.1 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.11.1 // indirect + go.opentelemetry.io/otel/metric v0.33.0 // indirect + go.opentelemetry.io/otel/sdk v1.11.1 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.23.0 // indirect golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 // indirect - golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c // indirect - golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24 // indirect - golang.org/x/text v0.3.7 // indirect - google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect - google.golang.org/grpc v1.49.0 // indirect + golang.org/x/net v0.2.0 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect + google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect + google.golang.org/grpc v1.51.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 47658b676..d87a2677e 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ github.com/bombsimon/logrusr/v3 v3.0.0/go.mod h1:PksPPgSFEL2I52pla2glgCyyd2OqOHA github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= +github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -141,6 +141,10 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/formancehq/go-libs v1.2.0 h1:tiPl3usXJD1stSXBonpu1lL/ACD+m6Qh/rinptc9qag= +github.com/formancehq/go-libs v1.2.0/go.mod h1:9pIcaXQR4O1biXDfhFurYJZw1piU6sJk+0Vqvu+92ng= +github.com/formancehq/go-libs/sharedotlp v0.0.0-20221123112229-2c8ab193c63c h1:xzGPlzwbAzotpvY6rU0/OtYruK/4VDJpMX+QcagbYJM= +github.com/formancehq/go-libs/sharedotlp v0.0.0-20221123112229-2c8ab193c63c/go.mod h1:6Pj/5A7XODoIyN/k5qauu1GAHex/A5P4j+7FmkD2TGI= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= @@ -225,8 +229,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -263,8 +267,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.14.0 h1:t7uX3JBHdVwAi3G7sSSdbsk8NfgA+LnUS88V/2EKaA0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.14.0/go.mod h1:4OGVnY4qf2+gw+ssiHbW+pq4mo2yko94YxxMmXZ7jCA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -349,10 +353,6 @@ github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/numary/go-libs v1.0.1 h1:Q41dz+u+inimZMVgSneCzbtoLiPMTjr1gT3D2UeC7IA= -github.com/numary/go-libs v1.0.1/go.mod h1:u9XNKBrHJSCwu13s85GkEg4TWfRm2CF3fknav4TTLn4= -github.com/numary/go-libs/sharedotlp v0.0.0-20220829123039-3eeb76619d81 h1:4ENnzCeXQGAAXGD7CSM8CZkKvQb78q1qNJ/Gonrb5Zs= -github.com/numary/go-libs/sharedotlp v0.0.0-20220829123039-3eeb76619d81/go.mod h1:4QEZTmjeQbNMjWd/pKADguTjxTvAuWgh+ZBWzJ7xDiI= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -446,8 +446,9 @@ github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -455,8 +456,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stripe/stripe-go/v72 v72.122.0 h1:eRXWqnEwGny6dneQ5BsxGzUCED5n180u8n665JHlut8= github.com/stripe/stripe-go/v72 v72.122.0/go.mod h1:QwqJQtduHubZht9mek5sds9CtQcKFdsykV9ZepRWwo0= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= @@ -491,8 +493,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.mongodb.org/mongo-driver v1.10.1 h1:NujsPveKwHaWuKUer/ceo9DzEe7HIj1SlJ6uvXZG0S4= -go.mongodb.org/mongo-driver v1.10.1/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8= +go.mongodb.org/mongo-driver v1.10.3 h1:XDQEvmh6z1EUsXuIkXE9TaVeqHw6SwS1uf93jFs0HBA= +go.mongodb.org/mongo-driver v1.10.3/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -503,24 +505,32 @@ go.opentelemetry.io/contrib/instrumentation/github.com/Shopify/sarama/otelsarama go.opentelemetry.io/contrib/instrumentation/github.com/Shopify/sarama/otelsarama v0.34.0/go.mod h1:8cfNbNK5aJIRQnqOFGEALF17iZPDym2mmYP+ECIxi8M= go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.34.0 h1:OkXMRbgldT4yZR7RwB4SFYTjYJGTXwPQVX69pYtTnc4= go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.34.0/go.mod h1:zMu+r6aEorSQi8Ad0Y1fNrznm+VM8F10D2WlZp3HeFw= -go.opentelemetry.io/otel v1.9.0 h1:8WZNQFIB2a71LnANS9JeyidJKKGOOremcUtb/OtHISw= -go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo= -go.opentelemetry.io/otel/exporters/jaeger v1.9.0 h1:gAEgEVGDWwFjcis9jJTOJqZNxDzoZfR12WNIxr7g9Ww= -go.opentelemetry.io/otel/exporters/jaeger v1.9.0/go.mod h1:hquezOLVAybNW6vanIxkdLXTXvzlj2Vn3wevSP15RYs= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.9.0 h1:ggqApEjDKczicksfvZUCxuvoyDmR6Sbm56LwiK8DVR0= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.9.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.9.0 h1:NN90Cuna0CnBg8YNu1Q0V35i2E8LDByFOwHRCq/ZP9I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.9.0/go.mod h1:0EsCXjZAiiZGnLdEUXM9YjCKuuLZMYyglh2QDXcYKVA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.9.0 h1:M0/hqGuJBLeIEu20f89H74RGtqV2dn+SFWEz9ATAAwY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.9.0/go.mod h1:K5G92gbtCrYJ0mn6zj9Pst7YFsDFuvSYEhYKRMcufnM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.9.0 h1:FAF9l8Wjxi9Ad2k/vLTfHZyzXYX72C62wBGpV3G6AIo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.9.0/go.mod h1:smUdtylgc0YQiUr2PuifS4hBXhAS5xtR6WQhxP1wiNA= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.9.0 h1:0uV0qzHk48i1SF8qRI8odMYiwPOLh9gBhiJFpj8H6JY= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.9.0/go.mod h1:Fl1iS5ZhWgXXXTdJMuBSVsS5nkL5XluHbg97kjOuYU4= -go.opentelemetry.io/otel/sdk v1.9.0 h1:LNXp1vrr83fNXTHgU8eO89mhzxb/bbWAsHG6fNf3qWo= -go.opentelemetry.io/otel/sdk v1.9.0/go.mod h1:AEZc8nt5bd2F7BC24J5R0mrjYnpEgYHyTcM/vrSple4= -go.opentelemetry.io/otel/trace v1.9.0 h1:oZaCNJUjWcg60VXWee8lJKlqhPbXAPB51URuR47pQYc= -go.opentelemetry.io/otel/trace v1.9.0/go.mod h1:2737Q0MuG8q1uILYm2YYVkAyLtOofiTNGg6VODnOiPo= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.36.4 h1:IKvVGMy0s5MH0cKfwmwiHVtnrVOFuHU/wznLa8eN+Cs= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.36.4/go.mod h1:mHrZBcL5tUSxYX1emmDCNDDf9an1PedCEGum4p9+Ep8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.36.4 h1:aUEBEdCa6iamGzg6fuYxDA8ThxvOG240mAvWDU+XLio= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.36.4/go.mod h1:l2MdsbKTocpPS5nQZscqTR9jd8u96VYZdcpF8Sye7mA= +go.opentelemetry.io/contrib/propagators/b3 v1.11.1 h1:icQ6ttRV+r/2fnU46BIo/g/mPu6Rs5Ug8Rtohe3KqzI= +go.opentelemetry.io/contrib/propagators/b3 v1.11.1/go.mod h1:ECIveyMXgnl4gorxFcA7RYjJY/Ql9n20ubhbfDc3QfA= +go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= +go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= +go.opentelemetry.io/otel/exporters/jaeger v1.11.1 h1:F9Io8lqWdGyIbY3/SOGki34LX/l+7OL0gXNxjqwcbuQ= +go.opentelemetry.io/otel/exporters/jaeger v1.11.1/go.mod h1:lRa2w3bQ4R4QN6zYsDgy7tEezgoKEu7Ow2g35Y75+KI= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 h1:X2GndnMCsUPh6CiY2a+frAbNsXaPLbB0soHRYhAZ5Ig= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1/go.mod h1:i8vjiSzbiUC7wOQplijSXMYUpNM93DtlS5CbUT+C6oQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1 h1:MEQNafcNCB0uQIti/oHgU7CZpUMYQ7qigBwMVKycHvc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1/go.mod h1:19O5I2U5iys38SsmT2uDJja/300woyzE1KPIQxEUBUc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1 h1:LYyG/f1W/jzAix16jbksJfMQFpOH/Ma6T639pVPMgfI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1/go.mod h1:QrRRQiY3kzAoYPNLP0W/Ikg0gR6V3LMc+ODSxr7yyvg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.1 h1:tFl63cpAAcD9TOU6U8kZU7KyXuSRYAZlbx1C61aaB74= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.1/go.mod h1:X620Jww3RajCJXw/unA+8IRTgxkdS7pi+ZwK9b7KUJk= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.11.1 h1:3Yvzs7lgOw8MmbxmLRsQGwYdCubFmUHSooKaEhQunFQ= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.11.1/go.mod h1:pyHDt0YlyuENkD2VwHsiRDf+5DfI3EH7pfhUYW6sQUE= +go.opentelemetry.io/otel/metric v0.33.0 h1:xQAyl7uGEYvrLAiV/09iTJlp1pZnQ9Wl793qbVvED1E= +go.opentelemetry.io/otel/metric v0.33.0/go.mod h1:QlTYc+EnYNq/M2mNk1qDDMRLpqCOj2f/r5c7Fd5FYaI= +go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZpKxs= +go.opentelemetry.io/otel/sdk v1.11.1/go.mod h1:/l3FE4SupHJ12TduVjUkZtlfFqDCQJlOlithYrdktys= +go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= +go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -532,7 +542,7 @@ go.uber.org/dig v1.15.0 h1:vq3YWr8zRj1eFGC7Gvf907hE0eRjPTZ1d3xHadD6liE= go.uber.org/dig v1.15.0/go.mod h1:pKHs0wMynzL6brANhB2hLMro+zalv1osARTviTcqHLM= go.uber.org/fx v1.18.1 h1:I7VWkdv4iKcbpH7KVSi9Fe1LGmpJv+pbBIb9NidPb+E= go.uber.org/fx v1.18.1/go.mod h1:g0V1KMQ66zIRk8bLu3Ea5Jt2w/cHlOIp4wdRsgh0JaY= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= @@ -625,8 +635,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM= golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes= -golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -711,8 +721,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24 h1:TyKJRhyo17yWxOMCTHKWrc5rddHORMlnZ/j57umaUd8= -golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -723,8 +733,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -844,8 +855,8 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc h1:Nf+EdcTLHR8qDNN/KfkQL0u0ssxt9OhbaWCl5C0ucEI= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -867,8 +878,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/internal/app/api/connector.go b/internal/app/api/connector.go index 07abe92a5..4bf82999d 100644 --- a/internal/app/api/connector.go +++ b/internal/app/api/connector.go @@ -8,9 +8,10 @@ import ( "github.com/formancehq/payments/internal/pkg/integration" "github.com/formancehq/payments/internal/pkg/payments" + + "github.com/formancehq/go-libs/sharedapi" + "github.com/formancehq/go-libs/sharedlogging" "github.com/gorilla/mux" - "github.com/numary/go-libs/sharedapi" - "github.com/numary/go-libs/sharedlogging" ) func handleError(w http.ResponseWriter, r *http.Request, err error) { diff --git a/internal/app/api/connectormodule.go b/internal/app/api/connectormodule.go index ea7b16278..b0f9da61e 100644 --- a/internal/app/api/connectormodule.go +++ b/internal/app/api/connectormodule.go @@ -4,15 +4,13 @@ import ( "context" "net/http" + "github.com/formancehq/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedpublish" "github.com/formancehq/payments/internal/pkg/ingestion" "github.com/formancehq/payments/internal/pkg/integration" "github.com/formancehq/payments/internal/pkg/payments" "github.com/formancehq/payments/internal/pkg/task" "github.com/formancehq/payments/internal/pkg/writeonly" - "github.com/gorilla/mux" - - "github.com/numary/go-libs/sharedlogging" - "github.com/numary/go-libs/sharedpublish" "go.mongodb.org/mongo-driver/mongo" "go.uber.org/dig" "go.uber.org/fx" @@ -87,24 +85,3 @@ func addConnector[ }), ) } - -func connectorRouter[Config payments.ConnectorConfigObject, Descriptor payments.TaskDescriptor]( - name string, - manager *integration.ConnectorManager[Config, Descriptor], -) *mux.Router { - r := mux.NewRouter() - - r.Path("/" + name).Methods(http.MethodPost).Handler(install(manager)) - - r.Path("/" + name + "/reset").Methods(http.MethodPost).Handler(reset(manager)) - - r.Path("/" + name).Methods(http.MethodDelete).Handler(uninstall(manager)) - - r.Path("/" + name + "/config").Methods(http.MethodGet).Handler(readConfig(manager)) - - r.Path("/" + name + "/tasks").Methods(http.MethodGet).Handler(listTasks(manager)) - - r.Path("/" + name + "/tasks/{taskID}").Methods(http.MethodGet).Handler(readTask(manager)) - - return r -} diff --git a/internal/app/api/module.go b/internal/app/api/module.go index 0891793be..1ab1151c2 100644 --- a/internal/app/api/module.go +++ b/internal/app/api/module.go @@ -10,6 +10,11 @@ import ( "github.com/formancehq/payments/internal/pkg/connectors/bankingcircle" "github.com/formancehq/payments/internal/pkg/connectors/currencycloud" + + "github.com/formancehq/go-libs/oauth2/oauth2introspect" + "github.com/formancehq/go-libs/sharedapi" + "github.com/formancehq/go-libs/sharedauth" + sharedotlp "github.com/formancehq/go-libs/sharedotlp/pkg" "github.com/formancehq/payments/internal/pkg/connectors/dummypay" "github.com/formancehq/payments/internal/pkg/connectors/modulr" "github.com/formancehq/payments/internal/pkg/connectors/stripe" @@ -17,15 +22,10 @@ import ( "github.com/formancehq/payments/internal/pkg/integration" "github.com/formancehq/payments/internal/pkg/payments" "github.com/gorilla/mux" - "github.com/numary/go-libs/oauth2/oauth2introspect" - "github.com/numary/go-libs/sharedapi" - "github.com/numary/go-libs/sharedauth" - sharedotlp "github.com/numary/go-libs/sharedotlp/pkg" "github.com/rs/cors" "github.com/sirupsen/logrus" "github.com/spf13/viper" "go.mongodb.org/mongo-driver/mongo" - "go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux" "go.uber.org/fx" ) @@ -76,42 +76,6 @@ func HTTPModule() fx.Option { ) } -func httpRouter(db *mongo.Database, client *mongo.Client, handlers []connectorHandler) (*mux.Router, error) { - rootMux := mux.NewRouter() - - if viper.GetBool(otelTracesFlag) { - rootMux.Use(otelmux.Middleware(serviceName)) - } - - rootMux.Use(recoveryHandler(httpRecoveryFunc)) - rootMux.Use(httpCorsHandler()) - rootMux.Use(httpServeFunc) - - rootMux.Path("/_health").Handler(healthHandler(client)) - rootMux.Path("/_live").Handler(liveHandler()) - - authGroup := rootMux.Name("authenticated").Subrouter() - - if methods := sharedAuthMethods(); len(methods) > 0 { - authGroup.Use(sharedauth.Middleware(methods...)) - } - - authGroup.HandleFunc("/connectors", readConnectorsHandler(db)) - connectorGroup := authGroup.PathPrefix("/connectors").Subrouter() - - connectorGroup.Path("/configs").Handler(connectorConfigsHandler()) - - for _, h := range handlers { - connectorGroup.PathPrefix("/" + h.Name).Handler( - http.StripPrefix("/connectors", h.Handler), - ) - } - - authGroup.PathPrefix("/").Handler(paymentsRouter(db)) - - return rootMux, nil -} - func httpRecoveryFunc(ctx context.Context, e interface{}) { if viper.GetBool(otelTracesFlag) { sharedotlp.RecordAsError(ctx, e) diff --git a/internal/app/api/payments.go b/internal/app/api/payments.go index b7265ade7..1bea4fc76 100644 --- a/internal/app/api/payments.go +++ b/internal/app/api/payments.go @@ -7,9 +7,9 @@ import ( "github.com/formancehq/payments/internal/pkg/payments" + "github.com/formancehq/go-libs/sharedapi" + "github.com/formancehq/go-libs/sharedlogging" "github.com/gorilla/mux" - "github.com/numary/go-libs/sharedapi" - "github.com/numary/go-libs/sharedlogging" "github.com/pkg/errors" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" diff --git a/internal/app/api/router.go b/internal/app/api/router.go new file mode 100644 index 000000000..5775e8adf --- /dev/null +++ b/internal/app/api/router.go @@ -0,0 +1,76 @@ +package api + +import ( + "net/http" + + "github.com/formancehq/payments/internal/pkg/integration" + "github.com/formancehq/payments/internal/pkg/payments" + + "github.com/formancehq/go-libs/sharedauth" + "github.com/gorilla/mux" + "github.com/spf13/viper" + "go.mongodb.org/mongo-driver/mongo" + "go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux" +) + +func httpRouter(db *mongo.Database, client *mongo.Client, connectorHandlers []connectorHandler) (*mux.Router, error) { + rootMux := mux.NewRouter() + + if viper.GetBool(otelTracesFlag) { + rootMux.Use(otelmux.Middleware(serviceName)) + } + + rootMux.Use(recoveryHandler(httpRecoveryFunc)) + rootMux.Use(httpCorsHandler()) + rootMux.Use(httpServeFunc) + + rootMux.Path("/_health").Handler(healthHandler(client)) + rootMux.Path("/_live").Handler(liveHandler()) + + authGroup := rootMux.Name("authenticated").Subrouter() + + if methods := sharedAuthMethods(); len(methods) > 0 { + authGroup.Use(sharedauth.Middleware(methods...)) + } + + authGroup.HandleFunc("/connectors", readConnectorsHandler(db)) + connectorGroup := authGroup.PathPrefix("/connectors").Subrouter() + + connectorGroup.Path("/configs").Handler(connectorConfigsHandler()) + + for _, h := range connectorHandlers { + connectorGroup.PathPrefix("/" + h.Name).Handler( + http.StripPrefix("/connectors", h.Handler), + ) + } + + // TODO: It's not ideal to define it explicitly here + // Refactor it when refactoring the HTTP lib. + connectorGroup.Path("/stripe/transfers").Methods(http.MethodPost). + Handler(handleStripeTransfers(db)) + + authGroup.PathPrefix("/").Handler(paymentsRouter(db)) + + return rootMux, nil +} + +func connectorRouter[Config payments.ConnectorConfigObject, Descriptor payments.TaskDescriptor]( + name string, + manager *integration.ConnectorManager[Config, Descriptor], +) *mux.Router { + r := mux.NewRouter() + + r.Path("/" + name).Methods(http.MethodPost).Handler(install(manager)) + + r.Path("/" + name + "/reset").Methods(http.MethodPost).Handler(reset(manager)) + + r.Path("/" + name).Methods(http.MethodDelete).Handler(uninstall(manager)) + + r.Path("/" + name + "/config").Methods(http.MethodGet).Handler(readConfig(manager)) + + r.Path("/" + name + "/tasks").Methods(http.MethodGet).Handler(listTasks(manager)) + + r.Path("/" + name + "/tasks/{taskID}").Methods(http.MethodGet).Handler(readTask(manager)) + + return r +} diff --git a/internal/app/api/stripe.go b/internal/app/api/stripe.go new file mode 100644 index 000000000..83ff51359 --- /dev/null +++ b/internal/app/api/stripe.go @@ -0,0 +1,109 @@ +package api + +import ( + "encoding/json" + "net/http" + + "github.com/formancehq/payments/internal/pkg/integration" + + "go.mongodb.org/mongo-driver/mongo" + + "github.com/pkg/errors" + + stripeConnector "github.com/formancehq/payments/internal/pkg/connectors/stripe" + "github.com/stripe/stripe-go/v72" + "github.com/stripe/stripe-go/v72/transfer" +) + +type stripeTransferRequest struct { + Amount int64 `json:"amount"` + Asset string `json:"asset"` + Destination string `json:"destination"` + Metadata map[string]string `json:"metadata"` + + currency string +} + +func (req *stripeTransferRequest) validate() error { + if req.Amount <= 0 { + return errors.New("amount must be greater than 0") + } + + if req.Asset == "" { + return errors.New("asset is required") + } + + if req.Asset != "USD/2" && req.Asset != "EUR/2" { + return errors.New("asset must be USD/2 or EUR/2") + } + + req.currency = req.Asset[:3] + + if req.Destination == "" { + return errors.New("destination is required") + } + + return nil +} + +func handleStripeTransfers(db *mongo.Database) http.HandlerFunc { + connectorStore := integration.NewMongoDBConnectorStore(db) + + return func(w http.ResponseWriter, r *http.Request) { + var cfg stripeConnector.Config + + err := connectorStore.ReadConfig(r.Context(), stripeConnector.Name, &cfg) + if err != nil { + handleError(w, r, err) + + return + } + + stripe.Key = cfg.APIKey + + var transferRequest stripeTransferRequest + + err = json.NewDecoder(r.Body).Decode(&transferRequest) + if err != nil { + handleError(w, r, err) + + return + } + + err = transferRequest.validate() + if err != nil { + handleError(w, r, err) + + return + } + + params := &stripe.TransferParams{ + Params: stripe.Params{ + Context: r.Context(), + }, + Amount: stripe.Int64(transferRequest.Amount), + Currency: stripe.String(transferRequest.currency), + Destination: stripe.String(transferRequest.Destination), + } + + for k, v := range transferRequest.Metadata { + params.AddMetadata(k, v) + } + + transferResponse, err := transfer.New(params) + if err != nil { + handleServerError(w, r, err) + + return + } + + w.Header().Set("Content-Type", "application/json") + + err = json.NewEncoder(w).Encode(transferResponse) + if err != nil { + handleServerError(w, r, err) + + return + } + } +} diff --git a/internal/app/database/module.go b/internal/app/database/module.go index e82f211c3..0b2948076 100644 --- a/internal/app/database/module.go +++ b/internal/app/database/module.go @@ -5,13 +5,14 @@ import ( "reflect" "time" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" "github.com/pkg/errors" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/bsontype" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/readpref" + "go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo" "go.uber.org/fx" ) @@ -22,6 +23,7 @@ func MongoModule(uri string, dbName string) fx.Option { reg := bson.NewRegistryBuilder().RegisterTypeMapEntry(bsontype.EmbeddedDocument, tM).Build() return options.Client(). + SetMonitor(otelmongo.NewMonitor()). SetRegistry(reg). ApplyURI(uri) }), diff --git a/internal/pkg/connectors/bankingcircle/client.go b/internal/pkg/connectors/bankingcircle/client.go index a934d8f37..a91fb3e0c 100644 --- a/internal/pkg/connectors/bankingcircle/client.go +++ b/internal/pkg/connectors/bankingcircle/client.go @@ -1,13 +1,15 @@ package bankingcircle import ( + "context" "encoding/json" "fmt" "io" "net/http" "time" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) type client struct { @@ -25,9 +27,16 @@ type client struct { accessTokenExpiresAt time.Time } +func newHTTPClient() *http.Client { + return &http.Client{ + Timeout: 10 * time.Second, + Transport: otelhttp.NewTransport(http.DefaultTransport), + } +} + func newClient(username, password, endpoint, authorizationEndpoint string, logger sharedlogging.Logger) (*client, error) { c := &client{ - httpClient: &http.Client{Timeout: 10 * time.Second}, + httpClient: newHTTPClient(), username: username, password: password, @@ -37,15 +46,16 @@ func newClient(username, password, endpoint, authorizationEndpoint string, logge logger: logger, } - if err := c.login(); err != nil { + if err := c.login(context.TODO()); err != nil { return nil, err } return c, nil } -func (c *client) login() error { - req, err := http.NewRequest(http.MethodGet, c.authorizationEndpoint+"/api/v1/authorizations/authorize", http.NoBody) +func (c *client) login(ctx context.Context) error { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, + c.authorizationEndpoint+"/api/v1/authorizations/authorize", http.NoBody) if err != nil { return fmt.Errorf("failed to create login request: %w", err) } @@ -87,12 +97,12 @@ func (c *client) login() error { return nil } -func (c *client) ensureAccessTokenIsValid() error { +func (c *client) ensureAccessTokenIsValid(ctx context.Context) error { if c.accessTokenExpiresAt.After(time.Now()) { return nil } - return c.login() + return c.login(ctx) } //nolint:tagliatelle // allow for client-side structures @@ -164,11 +174,11 @@ type payment struct { } `json:"creditorInformation"` } -func (c *client) getAllPayments() ([]*payment, error) { +func (c *client) getAllPayments(ctx context.Context) ([]*payment, error) { var payments []*payment for page := 0; ; page++ { - pagedPayments, err := c.getPayments(page) + pagedPayments, err := c.getPayments(ctx, page) if err != nil { return nil, err } @@ -183,12 +193,12 @@ func (c *client) getAllPayments() ([]*payment, error) { return payments, nil } -func (c *client) getPayments(page int) ([]*payment, error) { - if err := c.ensureAccessTokenIsValid(); err != nil { +func (c *client) getPayments(ctx context.Context, page int) ([]*payment, error) { + if err := c.ensureAccessTokenIsValid(ctx); err != nil { return nil, err } - req, err := http.NewRequest(http.MethodGet, c.endpoint+"/api/v1/payments/singles", http.NoBody) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.endpoint+"/api/v1/payments/singles", http.NoBody) if err != nil { return nil, fmt.Errorf("failed to create login request: %w", err) } diff --git a/internal/pkg/connectors/bankingcircle/config.go b/internal/pkg/connectors/bankingcircle/config.go index ccbb551cf..c6271c61b 100644 --- a/internal/pkg/connectors/bankingcircle/config.go +++ b/internal/pkg/connectors/bankingcircle/config.go @@ -37,5 +37,5 @@ func (c Config) BuildTemplate() (string, configtemplate.Config) { cfg.AddParameter("endpoint", configtemplate.TypeString, true) cfg.AddParameter("authorizationEndpoint", configtemplate.TypeString, true) - return connectorName, cfg + return Name, cfg } diff --git a/internal/pkg/connectors/bankingcircle/loader.go b/internal/pkg/connectors/bankingcircle/loader.go index f2f02da24..19750ce87 100644 --- a/internal/pkg/connectors/bankingcircle/loader.go +++ b/internal/pkg/connectors/bankingcircle/loader.go @@ -1,16 +1,16 @@ package bankingcircle import ( + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/integration" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" ) -const connectorName = "bankingcircle" +const Name = "bankingcircle" // NewLoader creates a new loader. func NewLoader() integration.Loader[Config, TaskDescriptor] { - loader := integration.NewLoaderBuilder[Config, TaskDescriptor](connectorName). + loader := integration.NewLoaderBuilder[Config, TaskDescriptor](Name). WithLoad(func(logger sharedlogging.Logger, config Config) integration.Connector[TaskDescriptor] { return integration.NewConnectorBuilder[TaskDescriptor](). WithInstall(func(ctx task.ConnectorContext[TaskDescriptor]) error { diff --git a/internal/pkg/connectors/bankingcircle/task_fetch_payments.go b/internal/pkg/connectors/bankingcircle/task_fetch_payments.go index 45e996af6..e83d3f235 100644 --- a/internal/pkg/connectors/bankingcircle/task_fetch_payments.go +++ b/internal/pkg/connectors/bankingcircle/task_fetch_payments.go @@ -7,7 +7,7 @@ import ( "github.com/formancehq/payments/internal/pkg/payments" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" ) func taskFetchPayments(logger sharedlogging.Logger, client *client) task.Task { @@ -16,7 +16,7 @@ func taskFetchPayments(logger sharedlogging.Logger, client *client) task.Task { scheduler task.Scheduler[TaskDescriptor], ingester ingestion.Ingester, ) error { - paymentsList, err := client.getAllPayments() + paymentsList, err := client.getAllPayments(ctx) if err != nil { return err } diff --git a/internal/pkg/connectors/bankingcircle/task_resolve.go b/internal/pkg/connectors/bankingcircle/task_resolve.go index f28d0c423..634bd9262 100644 --- a/internal/pkg/connectors/bankingcircle/task_resolve.go +++ b/internal/pkg/connectors/bankingcircle/task_resolve.go @@ -5,7 +5,7 @@ import ( "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" ) const ( diff --git a/internal/pkg/connectors/currencycloud/client/client.go b/internal/pkg/connectors/currencycloud/client/client.go index 14833b57f..d402d85e7 100644 --- a/internal/pkg/connectors/currencycloud/client/client.go +++ b/internal/pkg/connectors/currencycloud/client/client.go @@ -4,16 +4,19 @@ import ( "context" "fmt" "net/http" + + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) type apiTransport struct { - authToken string + authToken string + underlying *otelhttp.Transport } func (t *apiTransport) RoundTrip(req *http.Request) (*http.Response, error) { req.Header.Add("X-Auth-Token", t.authToken) - return http.DefaultTransport.RoundTrip(req) + return t.underlying.RoundTrip(req) } type Client struct { @@ -29,6 +32,21 @@ func (c *Client) buildEndpoint(path string, args ...interface{}) string { const devAPIEndpoint = "https://devapi.currencycloud.com" +func newAuthenticatedHTTPClient(authToken string) *http.Client { + return &http.Client{ + Transport: &apiTransport{ + authToken: authToken, + underlying: otelhttp.NewTransport(http.DefaultTransport), + }, + } +} + +func newHTTPClient() *http.Client { + return &http.Client{ + Transport: otelhttp.NewTransport(http.DefaultTransport), + } +} + // NewClient creates a new client for the CurrencyCloud API. func NewClient(ctx context.Context, loginID, apiKey, endpoint string) (*Client, error) { if endpoint == "" { @@ -36,7 +54,7 @@ func NewClient(ctx context.Context, loginID, apiKey, endpoint string) (*Client, } c := &Client{ - httpClient: &http.Client{}, + httpClient: newHTTPClient(), endpoint: endpoint, loginID: loginID, apiKey: apiKey, @@ -47,7 +65,7 @@ func NewClient(ctx context.Context, loginID, apiKey, endpoint string) (*Client, return nil, err } - c.httpClient.Transport = &apiTransport{authToken: authToken} + c.httpClient = newAuthenticatedHTTPClient(authToken) return c, nil } diff --git a/internal/pkg/connectors/currencycloud/config.go b/internal/pkg/connectors/currencycloud/config.go index 15b056b74..bf0af952b 100644 --- a/internal/pkg/connectors/currencycloud/config.go +++ b/internal/pkg/connectors/currencycloud/config.go @@ -78,5 +78,5 @@ func (c Config) BuildTemplate() (string, configtemplate.Config) { cfg.AddParameter("endpoint", configtemplate.TypeString, false) cfg.AddParameter("pollingPeriod", configtemplate.TypeDurationNs, true) - return connectorName, cfg + return Name, cfg } diff --git a/internal/pkg/connectors/currencycloud/connector.go b/internal/pkg/connectors/currencycloud/connector.go index 2659f3dc8..fc750a9e4 100644 --- a/internal/pkg/connectors/currencycloud/connector.go +++ b/internal/pkg/connectors/currencycloud/connector.go @@ -3,12 +3,12 @@ package currencycloud import ( "context" + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/integration" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" ) -const connectorName = "currencycloud" +const Name = "currencycloud" type Connector struct { logger sharedlogging.Logger diff --git a/internal/pkg/connectors/currencycloud/loader.go b/internal/pkg/connectors/currencycloud/loader.go index de91a5a21..9bc9b67b4 100644 --- a/internal/pkg/connectors/currencycloud/loader.go +++ b/internal/pkg/connectors/currencycloud/loader.go @@ -1,8 +1,8 @@ package currencycloud import ( + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/integration" - "github.com/numary/go-libs/sharedlogging" ) type Loader struct{} @@ -14,7 +14,7 @@ func (l *Loader) AllowTasks() int { } func (l *Loader) Name() string { - return connectorName + return Name } func (l *Loader) Load(logger sharedlogging.Logger, config Config) integration.Connector[TaskDescriptor] { diff --git a/internal/pkg/connectors/currencycloud/task_fetch_transactions.go b/internal/pkg/connectors/currencycloud/task_fetch_transactions.go index 41f8cacc3..92ba63166 100644 --- a/internal/pkg/connectors/currencycloud/task_fetch_transactions.go +++ b/internal/pkg/connectors/currencycloud/task_fetch_transactions.go @@ -12,7 +12,7 @@ import ( "github.com/formancehq/payments/internal/pkg/payments" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" ) func taskFetchTransactions(logger sharedlogging.Logger, client *client.Client, config Config) task.Task { diff --git a/internal/pkg/connectors/currencycloud/task_resolve.go b/internal/pkg/connectors/currencycloud/task_resolve.go index cea44373b..388459feb 100644 --- a/internal/pkg/connectors/currencycloud/task_resolve.go +++ b/internal/pkg/connectors/currencycloud/task_resolve.go @@ -8,7 +8,7 @@ import ( "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" ) const ( diff --git a/internal/pkg/connectors/dummypay/config.go b/internal/pkg/connectors/dummypay/config.go index 316b59f0b..e50ab9c6b 100644 --- a/internal/pkg/connectors/dummypay/config.go +++ b/internal/pkg/connectors/dummypay/config.go @@ -55,5 +55,5 @@ func (c Config) BuildTemplate() (string, configtemplate.Config) { cfg.AddParameter("filePollingPeriod", configtemplate.TypeDurationNs, true) cfg.AddParameter("fileGenerationPeriod", configtemplate.TypeDurationNs, false) - return connectorName, cfg + return Name, cfg } diff --git a/internal/pkg/connectors/dummypay/connector.go b/internal/pkg/connectors/dummypay/connector.go index 499c1c792..70a033284 100644 --- a/internal/pkg/connectors/dummypay/connector.go +++ b/internal/pkg/connectors/dummypay/connector.go @@ -6,11 +6,11 @@ import ( "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" ) -// connectorName is the name of the connector. -const connectorName = "dummypay" +// Name is the name of the connector. +const Name = "dummypay" // Connector is the connector for the dummy payment connector. type Connector struct { diff --git a/internal/pkg/connectors/dummypay/connector_test.go b/internal/pkg/connectors/dummypay/connector_test.go index f27a3ff7f..bef4f3c14 100644 --- a/internal/pkg/connectors/dummypay/connector_test.go +++ b/internal/pkg/connectors/dummypay/connector_test.go @@ -8,7 +8,7 @@ import ( "github.com/formancehq/payments/internal/pkg/payments" task3 "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" "github.com/stretchr/testify/assert" ) diff --git a/internal/pkg/connectors/dummypay/loader.go b/internal/pkg/connectors/dummypay/loader.go index 65d9eb73d..999c6a604 100644 --- a/internal/pkg/connectors/dummypay/loader.go +++ b/internal/pkg/connectors/dummypay/loader.go @@ -3,16 +3,16 @@ package dummypay import ( "time" + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/connectors" "github.com/formancehq/payments/internal/pkg/integration" - "github.com/numary/go-libs/sharedlogging" ) type Loader struct{} // Name returns the name of the connector. func (l *Loader) Name() string { - return connectorName + return Name } // AllowTasks returns the amount of tasks that are allowed to be scheduled. diff --git a/internal/pkg/connectors/dummypay/loader_test.go b/internal/pkg/connectors/dummypay/loader_test.go index 2cb80f9d9..a1ea3a3f9 100644 --- a/internal/pkg/connectors/dummypay/loader_test.go +++ b/internal/pkg/connectors/dummypay/loader_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/connectors" - "github.com/numary/go-libs/sharedlogging" "github.com/stretchr/testify/assert" ) @@ -19,7 +19,7 @@ func TestLoader(t *testing.T) { loader := NewLoader() - assert.Equal(t, connectorName, loader.Name()) + assert.Equal(t, Name, loader.Name()) assert.Equal(t, 10, loader.AllowTasks()) assert.Equal(t, Config{ FilePollingPeriod: connectors.Duration{Duration: 10 * time.Second}, diff --git a/internal/pkg/connectors/dummypay/task_read_files.go b/internal/pkg/connectors/dummypay/task_read_files.go index c146e27f2..b9911a6ad 100644 --- a/internal/pkg/connectors/dummypay/task_read_files.go +++ b/internal/pkg/connectors/dummypay/task_read_files.go @@ -6,8 +6,8 @@ import ( "strings" "time" + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" "github.com/spf13/afero" ) diff --git a/internal/pkg/connectors/modulr/client/client.go b/internal/pkg/connectors/modulr/client/client.go index b0e9b3b7f..cf5734eed 100644 --- a/internal/pkg/connectors/modulr/client/client.go +++ b/internal/pkg/connectors/modulr/client/client.go @@ -5,17 +5,19 @@ import ( "net/http" "github.com/formancehq/payments/internal/pkg/connectors/modulr/hmac" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) type apiTransport struct { - apiKey string - headers map[string]string + apiKey string + headers map[string]string + underlying http.RoundTripper } func (t *apiTransport) RoundTrip(req *http.Request) (*http.Response, error) { req.Header.Add("Authorization", t.apiKey) - return http.DefaultTransport.RoundTrip(req) + return t.underlying.RoundTrip(req) } type responseWrapper[t any] struct { @@ -50,8 +52,9 @@ func NewClient(apiKey, apiSecret, endpoint string) (*Client, error) { return &Client{ httpClient: &http.Client{ Transport: &apiTransport{ - headers: headers, - apiKey: apiKey, + headers: headers, + apiKey: apiKey, + underlying: otelhttp.NewTransport(http.DefaultTransport), }, }, endpoint: endpoint, diff --git a/internal/pkg/connectors/modulr/config.go b/internal/pkg/connectors/modulr/config.go index 9e111366b..5bbf72a83 100644 --- a/internal/pkg/connectors/modulr/config.go +++ b/internal/pkg/connectors/modulr/config.go @@ -27,5 +27,5 @@ func (c Config) BuildTemplate() (string, configtemplate.Config) { cfg.AddParameter("apiSecret", configtemplate.TypeString, true) cfg.AddParameter("endpoint", configtemplate.TypeString, false) - return connectorName, cfg + return Name, cfg } diff --git a/internal/pkg/connectors/modulr/connector.go b/internal/pkg/connectors/modulr/connector.go index ddd8b5781..b68d429b9 100644 --- a/internal/pkg/connectors/modulr/connector.go +++ b/internal/pkg/connectors/modulr/connector.go @@ -3,12 +3,12 @@ package modulr import ( "context" + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/integration" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" ) -const connectorName = "modulr" +const Name = "modulr" type Connector struct { logger sharedlogging.Logger diff --git a/internal/pkg/connectors/modulr/loader.go b/internal/pkg/connectors/modulr/loader.go index 98d83b175..0405830d4 100644 --- a/internal/pkg/connectors/modulr/loader.go +++ b/internal/pkg/connectors/modulr/loader.go @@ -1,8 +1,8 @@ package modulr import ( + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/integration" - "github.com/numary/go-libs/sharedlogging" ) type Loader struct{} @@ -14,7 +14,7 @@ func (l *Loader) AllowTasks() int { } func (l *Loader) Name() string { - return connectorName + return Name } func (l *Loader) Load(logger sharedlogging.Logger, config Config) integration.Connector[TaskDescriptor] { diff --git a/internal/pkg/connectors/modulr/task_fetch_accounts.go b/internal/pkg/connectors/modulr/task_fetch_accounts.go index 5acab4a99..73b5aa923 100644 --- a/internal/pkg/connectors/modulr/task_fetch_accounts.go +++ b/internal/pkg/connectors/modulr/task_fetch_accounts.go @@ -6,7 +6,7 @@ import ( "github.com/formancehq/payments/internal/pkg/connectors/modulr/client" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" ) func taskFetchAccounts(logger sharedlogging.Logger, client *client.Client) task.Task { diff --git a/internal/pkg/connectors/modulr/task_fetch_transactions.go b/internal/pkg/connectors/modulr/task_fetch_transactions.go index 2b69c3e7b..813eefdd0 100644 --- a/internal/pkg/connectors/modulr/task_fetch_transactions.go +++ b/internal/pkg/connectors/modulr/task_fetch_transactions.go @@ -10,7 +10,7 @@ import ( "github.com/formancehq/payments/internal/pkg/payments" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" ) func taskFetchTransactions(logger sharedlogging.Logger, client *client.Client, accountID string) task.Task { diff --git a/internal/pkg/connectors/modulr/task_resolve.go b/internal/pkg/connectors/modulr/task_resolve.go index 0f8eef7c4..fb2a322e5 100644 --- a/internal/pkg/connectors/modulr/task_resolve.go +++ b/internal/pkg/connectors/modulr/task_resolve.go @@ -6,7 +6,7 @@ import ( "github.com/formancehq/payments/internal/pkg/connectors/modulr/client" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" ) const ( diff --git a/internal/pkg/connectors/stripe/client.go b/internal/pkg/connectors/stripe/client.go index 7e48d88ba..484da58d1 100644 --- a/internal/pkg/connectors/stripe/client.go +++ b/internal/pkg/connectors/stripe/client.go @@ -7,6 +7,7 @@ import ( "net/http" "github.com/formancehq/payments/internal/pkg/writeonly" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "github.com/pkg/errors" "github.com/stripe/stripe-go/v72" @@ -41,6 +42,14 @@ type DefaultClient struct { storage writeonly.Storage } +func NewDefaultClient(apiKey string, storage writeonly.Storage) *DefaultClient { + return &DefaultClient{ + httpClient: newHTTPClient(), + apiKey: apiKey, + storage: storage, + } +} + func (d *DefaultClient) ForAccount(account string) Client { cp := *d cp.stripeAccount = account @@ -51,13 +60,11 @@ func (d *DefaultClient) ForAccount(account string) Client { func (d *DefaultClient) BalanceTransactions(ctx context.Context, options ...ClientOption, ) ([]*stripe.BalanceTransaction, bool, error) { - req, err := http.NewRequest(http.MethodGet, balanceTransactionsEndpoint, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, balanceTransactionsEndpoint, nil) if err != nil { return nil, false, errors.Wrap(err, "creating http request") } - req = req.WithContext(ctx) - for _, opt := range options { opt.apply(req) } @@ -127,11 +134,9 @@ func (d *DefaultClient) BalanceTransactions(ctx context.Context, return asBalanceTransactions, rsp.HasMore, nil } -func NewDefaultClient(httpClient *http.Client, apiKey string, storage writeonly.Storage) *DefaultClient { - return &DefaultClient{ - httpClient: httpClient, - apiKey: apiKey, - storage: storage, +func newHTTPClient() *http.Client { + return &http.Client{ + Transport: otelhttp.NewTransport(http.DefaultTransport), } } diff --git a/internal/pkg/connectors/stripe/config.go b/internal/pkg/connectors/stripe/config.go index c707858fd..581164b16 100644 --- a/internal/pkg/connectors/stripe/config.go +++ b/internal/pkg/connectors/stripe/config.go @@ -38,5 +38,5 @@ func (c Config) BuildTemplate() (string, configtemplate.Config) { cfg.AddParameter("pollingPeriod", configtemplate.TypeDurationNs, false) cfg.AddParameter("pageSize", configtemplate.TypeDurationUnsignedInteger, false) - return connectorName, cfg + return Name, cfg } diff --git a/internal/pkg/connectors/stripe/connector.go b/internal/pkg/connectors/stripe/connector.go index 2b5e45ec2..3be25f44c 100644 --- a/internal/pkg/connectors/stripe/connector.go +++ b/internal/pkg/connectors/stripe/connector.go @@ -6,10 +6,10 @@ import ( "github.com/formancehq/payments/internal/pkg/integration" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" ) -const connectorName = "stripe" +const Name = "stripe" type Connector struct { logger sharedlogging.Logger diff --git a/internal/pkg/connectors/stripe/loader.go b/internal/pkg/connectors/stripe/loader.go index 9eabcd450..05efbc727 100644 --- a/internal/pkg/connectors/stripe/loader.go +++ b/internal/pkg/connectors/stripe/loader.go @@ -3,9 +3,9 @@ package stripe import ( "time" + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/connectors" "github.com/formancehq/payments/internal/pkg/integration" - "github.com/numary/go-libs/sharedlogging" ) type Loader struct{} @@ -17,7 +17,7 @@ func (l *Loader) AllowTasks() int { } func (l *Loader) Name() string { - return connectorName + return Name } func (l *Loader) Load(logger sharedlogging.Logger, config Config) integration.Connector[TaskDescriptor] { diff --git a/internal/pkg/connectors/stripe/runner.go b/internal/pkg/connectors/stripe/runner.go index 60c3b57e0..82578bcc9 100644 --- a/internal/pkg/connectors/stripe/runner.go +++ b/internal/pkg/connectors/stripe/runner.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" ) func NewRunner( diff --git a/internal/pkg/connectors/stripe/runner_test.go b/internal/pkg/connectors/stripe/runner_test.go index 94aad7f0d..78a65a8c0 100644 --- a/internal/pkg/connectors/stripe/runner_test.go +++ b/internal/pkg/connectors/stripe/runner_test.go @@ -7,7 +7,7 @@ import ( "github.com/stripe/stripe-go/v72" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" "github.com/stretchr/testify/require" ) diff --git a/internal/pkg/connectors/stripe/task_connected_account.go b/internal/pkg/connectors/stripe/task_connected_account.go index 6b6353527..088dc621c 100644 --- a/internal/pkg/connectors/stripe/task_connected_account.go +++ b/internal/pkg/connectors/stripe/task_connected_account.go @@ -2,13 +2,12 @@ package stripe import ( "context" - "net/http" "github.com/formancehq/payments/internal/pkg/ingestion" "github.com/formancehq/payments/internal/pkg/task" "github.com/formancehq/payments/internal/pkg/writeonly" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" "github.com/stripe/stripe-go/v72" ) @@ -57,7 +56,7 @@ func ConnectedAccountTask(config Config, account string) func(ctx context.Contex IngesterFn(func(ctx context.Context, bts []*stripe.BalanceTransaction, commitState TimelineState, tail bool) error { return ingestBatch(ctx, logger, ingester, bts, commitState, tail) }), - NewTimeline(NewDefaultClient(http.DefaultClient, config.APIKey, storage). + NewTimeline(NewDefaultClient(config.APIKey, storage). ForAccount(account), config.TimelineConfig, task.MustResolveTo(ctx, resolver, TimelineState{})), ) diff --git a/internal/pkg/connectors/stripe/task_main.go b/internal/pkg/connectors/stripe/task_main.go index 95e12ff3e..acc2473af 100644 --- a/internal/pkg/connectors/stripe/task_main.go +++ b/internal/pkg/connectors/stripe/task_main.go @@ -2,13 +2,11 @@ package stripe import ( "context" - "net/http" + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/ingestion" "github.com/formancehq/payments/internal/pkg/task" "github.com/formancehq/payments/internal/pkg/writeonly" - - "github.com/numary/go-libs/sharedlogging" "github.com/pkg/errors" "github.com/stripe/stripe-go/v72" ) @@ -62,7 +60,7 @@ func MainTask(config Config) func(ctx context.Context, logger sharedlogging.Logg ) error { return ingest(ctx, logger, scheduler, ingester, batch, commitState, tail) }), - NewTimeline(NewDefaultClient(http.DefaultClient, config.APIKey, storage), + NewTimeline(NewDefaultClient(config.APIKey, storage), config.TimelineConfig, task.MustResolveTo(ctx, resolver, TimelineState{})), ), config.PollingPeriod.Duration, diff --git a/internal/pkg/connectors/stripe/timeline_trigger.go b/internal/pkg/connectors/stripe/timeline_trigger.go index 0401d2fcf..d002b9fd6 100644 --- a/internal/pkg/connectors/stripe/timeline_trigger.go +++ b/internal/pkg/connectors/stripe/timeline_trigger.go @@ -3,7 +3,7 @@ package stripe import ( "context" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" "github.com/pkg/errors" "github.com/stripe/stripe-go/v72" "golang.org/x/sync/semaphore" diff --git a/internal/pkg/connectors/stripe/timeline_trigger_test.go b/internal/pkg/connectors/stripe/timeline_trigger_test.go index 500cd5f7e..57d57375f 100644 --- a/internal/pkg/connectors/stripe/timeline_trigger_test.go +++ b/internal/pkg/connectors/stripe/timeline_trigger_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" "github.com/stretchr/testify/require" "github.com/stripe/stripe-go/v72" ) diff --git a/internal/pkg/connectors/stripe/utils_test.go b/internal/pkg/connectors/stripe/utils_test.go index 598ae4ad5..e17eed6de 100644 --- a/internal/pkg/connectors/stripe/utils_test.go +++ b/internal/pkg/connectors/stripe/utils_test.go @@ -13,8 +13,8 @@ import ( "github.com/formancehq/payments/internal/pkg/fifo" - "github.com/numary/go-libs/sharedlogging" - "github.com/numary/go-libs/sharedlogging/sharedlogginglogrus" + "github.com/formancehq/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging/sharedlogginglogrus" "github.com/sirupsen/logrus" "github.com/stripe/stripe-go/v72" ) diff --git a/internal/pkg/connectors/wise/client.go b/internal/pkg/connectors/wise/client.go index c8e89bdf6..fb7225990 100644 --- a/internal/pkg/connectors/wise/client.go +++ b/internal/pkg/connectors/wise/client.go @@ -6,18 +6,21 @@ import ( "fmt" "io" "net/http" + + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) const apiEndpoint = "https://api.wise.com" type apiTransport struct { - APIKey string + APIKey string + underlying http.RoundTripper } func (t *apiTransport) RoundTrip(req *http.Request) (*http.Response, error) { req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", t.APIKey)) - return http.DefaultTransport.RoundTrip(req) + return t.underlying.RoundTrip(req) } type client struct { @@ -77,14 +80,14 @@ func (w *client) getProfiles() ([]profile, error) { return profiles, nil } -func (w *client) getTransfers(profile *profile) ([]transfer, error) { +func (w *client) getTransfers(ctx context.Context, profile *profile) ([]transfer, error) { var transfers []transfer limit := 10 offset := 0 for { - req, err := http.NewRequestWithContext(context.TODO(), + req, err := http.NewRequestWithContext(ctx, http.MethodGet, w.endpoint("v1/transfers"), http.NoBody) if err != nil { return transfers, err @@ -134,7 +137,8 @@ func (w *client) getTransfers(profile *profile) ([]transfer, error) { func newClient(apiKey string) *client { httpClient := &http.Client{ Transport: &apiTransport{ - APIKey: apiKey, + APIKey: apiKey, + underlying: otelhttp.NewTransport(http.DefaultTransport), }, } diff --git a/internal/pkg/connectors/wise/config.go b/internal/pkg/connectors/wise/config.go index c003666e7..b4ca63105 100644 --- a/internal/pkg/connectors/wise/config.go +++ b/internal/pkg/connectors/wise/config.go @@ -19,5 +19,5 @@ func (c Config) BuildTemplate() (string, configtemplate.Config) { cfg.AddParameter("apiKey", configtemplate.TypeString, true) - return connectorName, cfg + return Name, cfg } diff --git a/internal/pkg/connectors/wise/connector.go b/internal/pkg/connectors/wise/connector.go index 2bc156c4d..7d360a455 100644 --- a/internal/pkg/connectors/wise/connector.go +++ b/internal/pkg/connectors/wise/connector.go @@ -3,12 +3,12 @@ package wise import ( "context" + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/integration" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" ) -const connectorName = "wise" +const Name = "wise" type Connector struct { logger sharedlogging.Logger diff --git a/internal/pkg/connectors/wise/loader.go b/internal/pkg/connectors/wise/loader.go index d78a2d485..1a8d6c658 100644 --- a/internal/pkg/connectors/wise/loader.go +++ b/internal/pkg/connectors/wise/loader.go @@ -1,8 +1,8 @@ package wise import ( + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/integration" - "github.com/numary/go-libs/sharedlogging" ) type Loader struct{} @@ -14,7 +14,7 @@ func (l *Loader) AllowTasks() int { } func (l *Loader) Name() string { - return connectorName + return Name } func (l *Loader) Load(logger sharedlogging.Logger, config Config) integration.Connector[TaskDescriptor] { diff --git a/internal/pkg/connectors/wise/task_fetch_profiles.go b/internal/pkg/connectors/wise/task_fetch_profiles.go index 085bc5d9b..55d569d76 100644 --- a/internal/pkg/connectors/wise/task_fetch_profiles.go +++ b/internal/pkg/connectors/wise/task_fetch_profiles.go @@ -6,7 +6,7 @@ import ( "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" ) func taskFetchProfiles(logger sharedlogging.Logger, client *client) task.Task { diff --git a/internal/pkg/connectors/wise/task_fetch_transfers.go b/internal/pkg/connectors/wise/task_fetch_transfers.go index 062e5774c..53b3c475f 100644 --- a/internal/pkg/connectors/wise/task_fetch_transfers.go +++ b/internal/pkg/connectors/wise/task_fetch_transfers.go @@ -4,10 +4,10 @@ import ( "context" "fmt" + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/ingestion" "github.com/formancehq/payments/internal/pkg/payments" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" ) func taskFetchTransfers(logger sharedlogging.Logger, client *client, profileID uint64) task.Task { @@ -16,7 +16,7 @@ func taskFetchTransfers(logger sharedlogging.Logger, client *client, profileID u scheduler task.Scheduler[TaskDescriptor], ingester ingestion.Ingester, ) error { - transfers, err := client.getTransfers(&profile{ + transfers, err := client.getTransfers(ctx, &profile{ ID: profileID, }) if err != nil { diff --git a/internal/pkg/connectors/wise/task_resolve.go b/internal/pkg/connectors/wise/task_resolve.go index dd2476e34..d4dc47816 100644 --- a/internal/pkg/connectors/wise/task_resolve.go +++ b/internal/pkg/connectors/wise/task_resolve.go @@ -5,7 +5,7 @@ import ( "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" ) const ( diff --git a/internal/pkg/ingestion/accounts.go b/internal/pkg/ingestion/accounts.go index 2a4a7ff3b..4df95fd42 100644 --- a/internal/pkg/ingestion/accounts.go +++ b/internal/pkg/ingestion/accounts.go @@ -8,7 +8,7 @@ import ( "github.com/formancehq/payments/internal/pkg/payments" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" diff --git a/internal/pkg/ingestion/ingester.go b/internal/pkg/ingestion/ingester.go index 972ef5a09..49e95e223 100644 --- a/internal/pkg/ingestion/ingester.go +++ b/internal/pkg/ingestion/ingester.go @@ -5,8 +5,8 @@ import ( "github.com/formancehq/payments/internal/pkg/payments" - "github.com/numary/go-libs/sharedlogging" - "github.com/numary/go-libs/sharedpublish" + "github.com/formancehq/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedpublish" "go.mongodb.org/mongo-driver/mongo" ) diff --git a/internal/pkg/ingestion/ingester_test.go b/internal/pkg/ingestion/ingester_test.go index 4212a692a..41c0fb072 100644 --- a/internal/pkg/ingestion/ingester_test.go +++ b/internal/pkg/ingestion/ingester_test.go @@ -7,7 +7,7 @@ import ( "github.com/formancehq/payments/internal/pkg/payments" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" diff --git a/internal/pkg/ingestion/message.go b/internal/pkg/ingestion/message.go index 45c7f6bae..bd84d4d46 100644 --- a/internal/pkg/ingestion/message.go +++ b/internal/pkg/ingestion/message.go @@ -6,7 +6,7 @@ import ( "github.com/formancehq/payments/internal/pkg/payments" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" ) const ( diff --git a/internal/pkg/ingestion/payments.go b/internal/pkg/ingestion/payments.go index 613427172..5da37fff0 100644 --- a/internal/pkg/ingestion/payments.go +++ b/internal/pkg/ingestion/payments.go @@ -9,7 +9,7 @@ import ( "github.com/formancehq/payments/internal/pkg/payments" - "github.com/numary/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" diff --git a/internal/pkg/integration/loader.go b/internal/pkg/integration/loader.go index f9e5821f8..8e2331d65 100644 --- a/internal/pkg/integration/loader.go +++ b/internal/pkg/integration/loader.go @@ -1,8 +1,8 @@ package integration import ( + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/payments" - "github.com/numary/go-libs/sharedlogging" ) type Loader[ConnectorConfig payments.ConnectorConfigObject, TaskDescriptor payments.TaskDescriptor] interface { diff --git a/internal/pkg/integration/manager.go b/internal/pkg/integration/manager.go index 4892b04ea..cfc48ffcd 100644 --- a/internal/pkg/integration/manager.go +++ b/internal/pkg/integration/manager.go @@ -3,9 +3,9 @@ package integration import ( "context" + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/payments" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" "github.com/pkg/errors" ) diff --git a/internal/pkg/integration/manager_test.go b/internal/pkg/integration/manager_test.go index 16e094d8a..f9e8823f0 100644 --- a/internal/pkg/integration/manager_test.go +++ b/internal/pkg/integration/manager_test.go @@ -7,8 +7,8 @@ import ( "github.com/formancehq/payments/internal/pkg/payments" "github.com/formancehq/payments/internal/pkg/task" - "github.com/numary/go-libs/sharedlogging" - "github.com/numary/go-libs/sharedlogging/sharedlogginglogrus" + "github.com/formancehq/go-libs/sharedlogging" + "github.com/formancehq/go-libs/sharedlogging/sharedlogginglogrus" "github.com/pborman/uuid" "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" diff --git a/internal/pkg/task/scheduler.go b/internal/pkg/task/scheduler.go index ad369de00..521552bd0 100644 --- a/internal/pkg/task/scheduler.go +++ b/internal/pkg/task/scheduler.go @@ -7,11 +7,13 @@ import ( "sync" "time" + "github.com/formancehq/go-libs/sharedlogging" "github.com/formancehq/payments/internal/pkg/payments" - - "github.com/numary/go-libs/sharedlogging" "github.com/pkg/errors" "go.mongodb.org/mongo-driver/bson" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) var ( @@ -202,9 +204,12 @@ func (s *DefaultTaskScheduler[TaskDescriptor]) startTask(descriptor TaskDescript return ErrUnableToResolve } - // TODO: Check task using reflection - ctx, cancel := context.WithCancel(context.Background()) + ctx, span := otel.Tracer("com.formance.payments").Start(ctx, "Task", trace.WithAttributes( + attribute.String("id", taskID), + attribute.String("connector", s.provider), + )) + holder := &taskHolder[TaskDescriptor]{ cancel: cancel, logger: logger, @@ -269,6 +274,7 @@ func (s *DefaultTaskScheduler[TaskDescriptor]) startTask(descriptor TaskDescript logger.Infof("Starting task...") defer func() { + defer span.End() defer s.deleteTask(holder) if e := recover(); e != nil { diff --git a/internal/pkg/task/scheduler_test.go b/internal/pkg/task/scheduler_test.go index 6e06a69e8..25ded1cc2 100644 --- a/internal/pkg/task/scheduler_test.go +++ b/internal/pkg/task/scheduler_test.go @@ -8,7 +8,7 @@ import ( "github.com/formancehq/payments/internal/pkg/payments" - "github.com/numary/go-libs/sharedlogging/sharedlogginglogrus" + "github.com/formancehq/go-libs/sharedlogging/sharedlogginglogrus" "github.com/pborman/uuid" "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" diff --git a/swagger.yml b/swagger.yml index 6588fad44..cedf610cf 100644 --- a/swagger.yml +++ b/swagger.yml @@ -37,7 +37,7 @@ paths: application/json: schema: $ref: '#/components/schemas/ListPaymentsResponse' - /payments/{paymentId}: + '/payments/{paymentId}': get: summary: Returns a payment. tags: @@ -86,7 +86,7 @@ paths: application/json: schema: $ref: '#/components/schemas/ListConnectorsConfigsResponse' - /connectors/{connector}: + '/connectors/{connector}': post: summary: Install connector tags: @@ -133,7 +133,7 @@ paths: responses: 204: description: Connector has been uninstalled - /connectors/{connector}/config: + '/connectors/{connector}/config': get: summary: Read connector config tags: @@ -156,7 +156,7 @@ paths: application/json: schema: $ref: '#/components/schemas/ConnectorConfig' - /connectors/{connector}/reset: + '/connectors/{connector}/reset': post: summary: Reset connector tags: @@ -175,7 +175,7 @@ paths: responses: 204: description: Connector has been reset - /connectors/{connector}/tasks: + '/connectors/{connector}/tasks': get: summary: List connector tasks tags: @@ -200,7 +200,7 @@ paths: type: array items: $ref: '#/components/schemas/ConnectorTask' - /connectors/{connector}/tasks/{taskId}: + '/connectors/{connector}/tasks/{taskId}': get: summary: Read a specific task of the connector tags: @@ -230,6 +230,22 @@ paths: application/json: schema: $ref: '#/components/schemas/ConnectorTask' + /connectors/stripe/transfer: + post: + summary: Transfer funds between Stripe accounts + tags: + - Payments + operationId: connectorsStripeTransfer + description: Execute a transfer between two Stripe accounts + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/StripeTransferRequest' + responses: + 200: + description: Transfer has been executed components: schemas: ConnectorConfig: @@ -443,6 +459,20 @@ components: items: $ref: '#/components/schemas/ConnectorBaseInfo' + ListConnectorsConfigsResponse: + type: object + properties: + connector: + type: object + properties: + key: + type: object + properties: + datatype: + type: string + required: + type: boolean + ConnectorBaseInfo: type: object properties: @@ -451,4 +481,24 @@ components: example: stripe disabled: type: boolean - example: false \ No newline at end of file + example: false + + StripeTransferRequest: + type: object + properties: + amount: + type: integer + example: 100 + asset: + type: string + example: USD + destination: + type: string + example: acct_1Gqj58KZcSIg2N2q + metadata: + type: object + description: | + A set of key/value pairs that you can attach to a transfer object. + It can be useful for storing additional information about the transfer in a structured format. + example: + order_id: '6735' \ No newline at end of file