diff --git a/cmd/root.go b/cmd/root.go index cb4ff5a..3153151 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,16 +5,17 @@ import ( "fmt" "net/http" - "github.com/formancehq/go-libs/v3/auth" - "github.com/formancehq/go-libs/v3/bun/bunconnect" - "github.com/formancehq/go-libs/v3/bun/bunmigrate" - "github.com/formancehq/go-libs/v3/licence" - "github.com/formancehq/go-libs/v3/otlp" - "github.com/formancehq/go-libs/v3/otlp/otlpmetrics" - "github.com/formancehq/go-libs/v3/otlp/otlptraces" - "github.com/formancehq/go-libs/v3/publish" - "github.com/formancehq/go-libs/v3/service" - "github.com/formancehq/go-libs/v3/temporal" + "github.com/formancehq/go-libs/v5/pkg/fx/authnfx" + "github.com/formancehq/go-libs/v5/pkg/fx/messagingfx" + "github.com/formancehq/go-libs/v5/pkg/fx/observefx" + "github.com/formancehq/go-libs/v5/pkg/fx/storagefx" + "github.com/formancehq/go-libs/v5/pkg/fx/workflowfx" + otlp "github.com/formancehq/go-libs/v5/pkg/observe" + otlptraces "github.com/formancehq/go-libs/v5/pkg/observe/traces" + "github.com/formancehq/go-libs/v5/pkg/service" + bunconnect "github.com/formancehq/go-libs/v5/pkg/storage/bun/connect" + bunmigrate "github.com/formancehq/go-libs/v5/pkg/storage/bun/migrate" + "github.com/formancehq/go-libs/v5/pkg/workflow/temporal" "github.com/formancehq/orchestration/internal/storage" "github.com/formancehq/orchestration/internal/temporalworker" "github.com/formancehq/orchestration/internal/tracer" @@ -43,6 +44,7 @@ const ( topicsFlag = "topics" listenFlag = "listen" workerFlag = "worker" + stackHTTPClientName = "stack" ) func NewRootCommand() *cobra.Command { @@ -69,19 +71,44 @@ func Execute() { service.Execute(NewRootCommand()) } +func stackHTTPClientModule(cmd *cobra.Command) fx.Option { + return fx.Provide(fx.Annotate(func() *http.Client { + httpClient := &http.Client{ + Transport: otlp.NewRoundTripper(http.DefaultTransport, service.IsDebug(cmd)), + } + + stackClientID, _ := cmd.Flags().GetString(stackClientIDFlag) + stackClientSecret, _ := cmd.Flags().GetString(stackClientSecretFlag) + stackURL, _ := cmd.Flags().GetString(stackURLFlag) + + if stackClientID == "" { + return httpClient + } + oauthConfig := clientcredentials.Config{ + ClientID: stackClientID, + ClientSecret: stackClientSecret, + TokenURL: fmt.Sprintf("%s/api/auth/oauth/token", stackURL), + Scopes: []string{"openid", "ledger:read", "ledger:write", "wallets:read", "wallets:write", "payments:read", "payments:write"}, + } + return oauthConfig.Client(context.WithValue(context.Background(), + oauth2.HTTPClient, httpClient)) + }, fx.ResultTags(`name:"stack"`))) +} + func commonOptions(cmd *cobra.Command) (fx.Option, error) { - connectionOptions, err := bunconnect.ConnectionOptionsFromFlags(cmd) + connectionOptions, err := bunconnect.ConnectionOptionsFromFlags(cmd.Flags(), cmd.Context()) if err != nil { return nil, err } stack, _ := cmd.Flags().GetString(stackFlag) + stackURL, _ := cmd.Flags().GetString(stackURLFlag) temporalTaskQueue, _ := cmd.Flags().GetString(temporal.TemporalTaskQueueFlag) return fx.Options( - otlp.FXModuleFromFlags(cmd), - otlptraces.FXModuleFromFlags(cmd), - temporal.FXModuleFromFlags( + observefx.ResourceModuleFromFlags(cmd), + observefx.TracesModuleFromFlags(cmd), + workflowfx.TemporalClientModuleFromFlags( cmd, tracer.Tracer, temporal.SearchAttributes{ @@ -91,36 +118,16 @@ func commonOptions(cmd *cobra.Command) (fx.Option, error) { ), }, ), - otlpmetrics.FXModuleFromFlags(cmd), - bunconnect.Module(*connectionOptions, service.IsDebug(cmd)), - publish.FXModuleFromFlags(cmd, service.IsDebug(cmd)), - auth.FXModuleFromFlags(cmd), - licence.FXModuleFromFlags(cmd, ServiceName), + observefx.MetricsModuleFromFlags(cmd), + storagefx.BunConnectModule(*connectionOptions, service.IsDebug(cmd)), + messagingfx.PublishModuleFromFlags(cmd, service.IsDebug(cmd)), + authnfx.JWTModuleFromFlags(cmd), + authnfx.LicenceModuleFromFlags(cmd, ServiceName), workflow.NewModule(stack, temporalTaskQueue), - triggers.NewModule(stack, temporalTaskQueue), + triggers.NewModule(stack, stackURL, temporalTaskQueue, stackHTTPClientName), fx.Provide(func() *bunconnect.ConnectionOptions { return connectionOptions }), - fx.Provide(func() *http.Client { - httpClient := &http.Client{ - Transport: otlp.NewRoundTripper(http.DefaultTransport, service.IsDebug(cmd)), - } - - stackClientID, _ := cmd.Flags().GetString(stackClientIDFlag) - stackClientSecret, _ := cmd.Flags().GetString(stackClientSecretFlag) - stackURL, _ := cmd.Flags().GetString(stackURLFlag) - - if stackClientID == "" { - return httpClient - } - oauthConfig := clientcredentials.Config{ - ClientID: stackClientID, - ClientSecret: stackClientSecret, - TokenURL: fmt.Sprintf("%s/api/auth/oauth/token", stackURL), - Scopes: []string{"openid", "ledger:read", "ledger:write", "wallets:read", "wallets:write", "payments:read", "payments:write"}, - } - return oauthConfig.Client(context.WithValue(context.Background(), - oauth2.HTTPClient, httpClient)) - }), + stackHTTPClientModule(cmd), ), nil } diff --git a/cmd/root_test.go b/cmd/root_test.go new file mode 100644 index 0000000..3b127a5 --- /dev/null +++ b/cmd/root_test.go @@ -0,0 +1,26 @@ +package cmd + +import ( + "net/http" + "testing" + + "github.com/formancehq/go-libs/v5/pkg/fx/authnfx" + "github.com/stretchr/testify/require" + "go.uber.org/fx" +) + +func TestCommonOptionsBuildsWithJWTAndStackHTTPClients(t *testing.T) { + cmd := newServeCommand() + app := fx.New( + fx.NopLogger, + authnfx.JWTModuleFromFlags(cmd), + stackHTTPClientModule(cmd), + fx.Invoke(fx.Annotate( + func(stackClient *http.Client) { + require.NotNil(t, stackClient) + }, + fx.ParamTags(`name:"stack"`), + )), + ) + require.NoError(t, app.Err()) +} diff --git a/cmd/serve.go b/cmd/serve.go index 6339f47..84430f1 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -3,16 +3,18 @@ package cmd import ( "context" - "github.com/formancehq/go-libs/v3/auth" - "github.com/formancehq/go-libs/v3/aws/iam" - "github.com/formancehq/go-libs/v3/bun/bunconnect" - "github.com/formancehq/go-libs/v3/health" - "github.com/formancehq/go-libs/v3/httpserver" - "github.com/formancehq/go-libs/v3/licence" - "github.com/formancehq/go-libs/v3/otlp/otlpmetrics" - "github.com/formancehq/go-libs/v3/publish" - "github.com/formancehq/go-libs/v3/service" - "github.com/formancehq/go-libs/v3/temporal" + auth "github.com/formancehq/go-libs/v5/pkg/authn/jwt" + "github.com/formancehq/go-libs/v5/pkg/authn/licence" + "github.com/formancehq/go-libs/v5/pkg/cloud/aws/iam" + "github.com/formancehq/go-libs/v5/pkg/fx/servicefx" + "github.com/formancehq/go-libs/v5/pkg/fx/transportfx" + "github.com/formancehq/go-libs/v5/pkg/messaging/publish" + otlpmetrics "github.com/formancehq/go-libs/v5/pkg/observe/metrics" + "github.com/formancehq/go-libs/v5/pkg/service" + "github.com/formancehq/go-libs/v5/pkg/service/health" + bunconnect "github.com/formancehq/go-libs/v5/pkg/storage/bun/connect" + "github.com/formancehq/go-libs/v5/pkg/transport/httpserver" + "github.com/formancehq/go-libs/v5/pkg/workflow/temporal" "github.com/formancehq/orchestration/internal/api" v1 "github.com/formancehq/orchestration/internal/api/v1" v2 "github.com/formancehq/orchestration/internal/api/v2" @@ -25,8 +27,8 @@ import ( func healthCheckModule() fx.Option { return fx.Options( - health.Module(), - health.ProvideHealthCheck(func() health.NamedCheck { + servicefx.HealthModule(), + servicefx.ProvideHealthCheck(func() health.NamedCheck { return health.NewNamedCheck("default", health.CheckFn(func(ctx context.Context) error { return nil })) @@ -64,12 +66,16 @@ func newServeCommand() *cobra.Command { }), api.NewModule(service.IsDebug(cmd)), fx.Invoke(func(lc fx.Lifecycle, router *chi.Mux) { - lc.Append(httpserver.NewHook(router, httpserver.WithAddress(listen))) + lc.Append(transportfx.FXHook(httpserver.NewHook(router, httpserver.WithAddress(listen)))) }), } worker, _ := cmd.Flags().GetBool(workerFlag) if worker { - options = append(options, workerOptions(cmd)) + workerOptions, err := workerOptions(cmd) + if err != nil { + return err + } + options = append(options, workerOptions) } return service.New(cmd.OutOrStdout(), options...).Run(cmd) diff --git a/cmd/worker.go b/cmd/worker.go index 150d3b7..f23d826 100644 --- a/cmd/worker.go +++ b/cmd/worker.go @@ -1,16 +1,19 @@ package cmd import ( + "fmt" + "math" "net/http" + "strconv" sdk "github.com/formancehq/formance-sdk-go/v3" - "github.com/formancehq/go-libs/v3/aws/iam" - "github.com/formancehq/go-libs/v3/bun/bunconnect" - "github.com/formancehq/go-libs/v3/licence" - "github.com/formancehq/go-libs/v3/otlp/otlpmetrics" - "github.com/formancehq/go-libs/v3/publish" - "github.com/formancehq/go-libs/v3/service" - "github.com/formancehq/go-libs/v3/temporal" + "github.com/formancehq/go-libs/v5/pkg/authn/licence" + "github.com/formancehq/go-libs/v5/pkg/cloud/aws/iam" + "github.com/formancehq/go-libs/v5/pkg/messaging/publish" + otlpmetrics "github.com/formancehq/go-libs/v5/pkg/observe/metrics" + "github.com/formancehq/go-libs/v5/pkg/service" + bunconnect "github.com/formancehq/go-libs/v5/pkg/storage/bun/connect" + "github.com/formancehq/go-libs/v5/pkg/workflow/temporal" "github.com/formancehq/orchestration/internal/temporalworker" "github.com/formancehq/orchestration/internal/triggers" "github.com/spf13/cobra" @@ -22,26 +25,40 @@ func stackClientModule(cmd *cobra.Command) fx.Option { stackURL, _ := cmd.Flags().GetString(stackURLFlag) return fx.Options( - fx.Provide(func(httpClient *http.Client) *sdk.Formance { + fx.Provide(fx.Annotate(func(httpClient *http.Client) *sdk.Formance { return sdk.New( sdk.WithClient(httpClient), sdk.WithServerURL(stackURL), ) - }), + }, fx.ParamTags(`name:"stack"`))), ) } -func workerOptions(cmd *cobra.Command) fx.Option { +func workerOptions(cmd *cobra.Command) (fx.Option, error) { stack, _ := cmd.Flags().GetString(stackFlag) temporalTaskQueue, _ := cmd.Flags().GetString(temporal.TemporalTaskQueueFlag) - temporalMaxParallelActivities, _ := cmd.Flags().GetInt(temporal.TemporalMaxParallelActivitiesFlag) + // The flag is registered as a float64 in go-libs; reading it with GetInt + // silently fails and yields 0, so the configured limit was never applied. + temporalMaxParallelActivities, err := cmd.Flags().GetFloat64(temporal.TemporalMaxParallelActivitiesFlag) + if err != nil { + return nil, err + } + maxIntExclusive := math.Exp2(float64(strconv.IntSize - 1)) + if temporalMaxParallelActivities <= 0 || + math.Trunc(temporalMaxParallelActivities) != temporalMaxParallelActivities || + temporalMaxParallelActivities >= maxIntExclusive { + return nil, fmt.Errorf("%s must be a positive whole number", temporal.TemporalMaxParallelActivitiesFlag) + } topics, _ := cmd.Flags().GetStringSlice(topicsFlag) return fx.Options( stackClientModule(cmd), temporalworker.NewWorkerModule(temporalTaskQueue, worker.Options{ - TaskQueueActivitiesPerSecond: float64(temporalMaxParallelActivities), + // "max parallel activities" caps concurrency, which maps to + // MaxConcurrentActivityExecutionSize, not the queue-wide rate limit + // TaskQueueActivitiesPerSecond it was previously wired to. + MaxConcurrentActivityExecutionSize: int(temporalMaxParallelActivities), }), triggers.NewListenerModule( stack, @@ -50,7 +67,7 @@ func workerOptions(cmd *cobra.Command) fx.Option { true, topics, ), - ) + ), nil } func newWorkerCommand() *cobra.Command { @@ -61,8 +78,12 @@ func newWorkerCommand() *cobra.Command { if err != nil { return err } + workerOptions, err := workerOptions(cmd) + if err != nil { + return err + } - return service.New(cmd.OutOrStdout(), commonOptions, workerOptions(cmd)).Run(cmd) + return service.New(cmd.OutOrStdout(), commonOptions, workerOptions).Run(cmd) }, } ret.Flags().String(stackURLFlag, "", "Stack url") diff --git a/cmd/worker_test.go b/cmd/worker_test.go new file mode 100644 index 0000000..2f3d405 --- /dev/null +++ b/cmd/worker_test.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "testing" + + "github.com/formancehq/go-libs/v5/pkg/workflow/temporal" + "github.com/stretchr/testify/require" +) + +func TestWorkerOptionsValidatesMaxParallelActivities(t *testing.T) { + for _, testCase := range []struct { + name string + value string + wantErr bool + }{ + {name: "positive integer", value: "10"}, + {name: "zero", value: "0", wantErr: true}, + {name: "negative", value: "-1", wantErr: true}, + {name: "fractional", value: "0.5", wantErr: true}, + {name: "int overflow", value: "9223372036854775808", wantErr: true}, + } { + t.Run(testCase.name, func(t *testing.T) { + cmd := newWorkerCommand() + require.NoError(t, cmd.Flags().Set(temporal.TemporalMaxParallelActivitiesFlag, testCase.value)) + _, err := workerOptions(cmd) + if testCase.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) + } +} diff --git a/go.mod b/go.mod index d782612..75c7cf0 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/ThreeDotsLabs/watermill v1.5.1 github.com/expr-lang/expr v1.17.7 github.com/formancehq/formance-sdk-go/v3 v3.2.0 - github.com/formancehq/go-libs/v3 v3.3.0 github.com/formancehq/go-libs/v5 v5.7.0 github.com/go-chi/chi/v5 v5.3.0 github.com/go-playground/validator/v10 v10.24.0 @@ -42,6 +41,7 @@ require ( github.com/ajg/form v1.7.1 // indirect github.com/aws/aws-msk-iam-sasl-signer-go v1.0.4 // indirect github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect github.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect @@ -49,8 +49,12 @@ require ( github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect github.com/aws/aws-sdk-go-v2/service/sns v1.39.14 // indirect github.com/aws/aws-sdk-go-v2/service/sqs v1.42.24 // indirect @@ -67,7 +71,6 @@ require ( github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/dnwe/otelsarama v0.0.0-20240308230250-9388d9d40bc0 // indirect github.com/docker/cli v29.3.0+incompatible // indirect @@ -79,7 +82,6 @@ require ( github.com/ebitengine/purego v0.10.0 // indirect github.com/ericlagergren/decimal v0.0.0-20240411145413-00de7ca16731 // indirect github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect - github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/getkin/kin-openapi v0.144.0 // indirect @@ -96,7 +98,6 @@ require ( github.com/go-sql-driver/mysql v1.9.3 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect - github.com/goccy/go-json v0.10.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/golang/mock v1.7.0-rc.1 // indirect @@ -104,13 +105,11 @@ require ( github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/gorilla/schema v1.4.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect @@ -130,24 +129,15 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/klauspost/compress v1.18.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect - github.com/lestrrat-go/blackmagic v1.0.2 // indirect - github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/iter v1.0.2 // indirect - github.com/lestrrat-go/jwx v1.2.31 // indirect - github.com/lestrrat-go/option v1.0.1 // indirect github.com/lithammer/shortuuid/v3 v3.0.7 // indirect github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect github.com/mailru/easyjson v0.9.2 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/moby/api v1.54.0 // indirect github.com/moby/moby/client v0.3.0 // indirect github.com/moby/sys/user v0.4.0 // indirect github.com/moby/term v0.5.2 // indirect github.com/muhlemmer/gu v0.3.1 // indirect - github.com/muhlemmer/httpforwarded v0.1.0 // indirect github.com/nats-io/nats.go v1.49.0 // indirect github.com/nats-io/nkeys v0.4.15 // indirect github.com/nats-io/nuid v1.0.1 // indirect @@ -168,7 +158,6 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect github.com/riandyrn/otelchi v0.12.2 // indirect github.com/robfig/cron v1.2.0 // indirect - github.com/rs/cors v1.11.1 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/shirou/gopsutil/v4 v4.26.2 // indirect github.com/sirupsen/logrus v1.9.4 // indirect @@ -192,7 +181,6 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xo/dburl v0.24.2 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - github.com/zitadel/oidc/v2 v2.12.2 // indirect github.com/zitadel/oidc/v3 v3.45.3 // indirect github.com/zitadel/schema v1.3.2 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect @@ -229,5 +217,4 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect google.golang.org/grpc v1.82.1 // indirect google.golang.org/protobuf v1.36.11 // indirect - gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect ) diff --git a/go.sum b/go.sum index c54ad85..1ec4e23 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,8 @@ github.com/aws/aws-msk-iam-sasl-signer-go v1.0.4 h1:2jAwFwA0Xgcx94dUId+K24yFabsK github.com/aws/aws-msk-iam-sasl-signer-go v1.0.4/go.mod h1:MVYeeOhILFFemC/XlYTClvBjYZrg/EPd3ts885KrNTI= github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY= github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI= github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= @@ -45,10 +47,18 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgq github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13/go.mod h1:CEuVn5WqOMilYl+tbccq8+N2ieCy0gVn3OtRb0vBNNM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ= +github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 h1:HwxWTbTrIHm5qY+CAEur0s/figc3qwvLWsNkF4RPToo= +github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM= github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= github.com/aws/aws-sdk-go-v2/service/sns v1.39.14 h1:p8WdWDh5AwSZdp19Haa3XMyPCICi9Z375a/Nu3IIEZY= @@ -86,8 +96,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= @@ -114,15 +122,12 @@ github.com/expr-lang/expr v1.17.7 h1:Q0xY/e/2aCIp8g9s/LGvMDCC5PxYlvHgDZRQ4y16JX8 github.com/expr-lang/expr v1.17.7/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/formancehq/formance-sdk-go/v3 v3.2.0 h1:3zxYSu71jjCj5XMBvT6bTm2HlbZWvm3txYcuONJUbWw= github.com/formancehq/formance-sdk-go/v3 v3.2.0/go.mod h1:XivkqQzjOtR3W7hqIFYcUN11UIf+X8/V1yoPPnGBQVU= -github.com/formancehq/go-libs/v3 v3.3.0 h1:Qs6oPRNHJbR4xu3Lzl2zGAiQD94XLpabAFYPq3YBqK0= -github.com/formancehq/go-libs/v3 v3.3.0/go.mod h1:kr5pgap99LPdSVcYWgI+mCN51wr5D+YxEnt9A6JUiFU= github.com/formancehq/go-libs/v5 v5.7.0 h1:2Z2S3vtOJr45tKpofhPqd0qKrPr6KB/LZZ+EfL1PARw= github.com/formancehq/go-libs/v5 v5.7.0/go.mod h1:+jfCYWJ4Z10NGbhmbfon0hGoLe5pysbVQgePrg8M8W4= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -173,8 +178,6 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= -github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -202,8 +205,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E= -github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= @@ -256,8 +257,6 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6 github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/jeremija/gosubmit v0.2.8 h1:mmSITBz9JxVtu8eqbN+zmmwX7Ij2RidQxhcwRVI4wqA= -github.com/jeremija/gosubmit v0.2.8/go.mod h1:Ui+HS073lCFREXBbdfrJzMB57OI/bdxTiLtrDHHhFPI= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= @@ -272,19 +271,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A= -github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= -github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= -github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= -github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= -github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= -github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx v1.2.31 h1:/OM9oNl/fzyldpv5HKZ9m7bTywa7COUfg8gujd9nJ54= -github.com/lestrrat-go/jwx v1.2.31/go.mod h1:eQJKoRwWcLg4PfD5CFA5gIZGxhPgoPYq9pZISdxLf0c= -github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= -github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJVuvyJQQ8= @@ -295,12 +281,8 @@ github.com/mailru/easyjson v0.9.2 h1:dX8U45hQsZpxd80nLvDGihsQ/OxlvTkVUXH2r/8cb2M github.com/mailru/easyjson v0.9.2/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= @@ -319,8 +301,6 @@ github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM= github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM= -github.com/muhlemmer/httpforwarded v0.1.0 h1:x4DLrzXdliq8mprgUMR0olDvHGkou5BJsK/vWUetyzY= -github.com/muhlemmer/httpforwarded v0.1.0/go.mod h1:yo9czKedo2pdZhoXe+yDkGVbU0TJ0q9oQ90BVoDEtw0= github.com/nats-io/jwt/v2 v2.8.1 h1:V0xpGuD/N8Mi+fQNDynXohVvp7ZztevW5io8CUWlPmU= github.com/nats-io/jwt/v2 v2.8.1/go.mod h1:nWnOEEiVMiKHQpnAy4eXlizVEtSfzacZ1Q43LIRavZg= github.com/nats-io/nats-server/v2 v2.12.6 h1:Egbx9Vl7Ch8wTtpXPGqbehkZ+IncKqShUxvrt1+Enc8= @@ -370,8 +350,6 @@ github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= -github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= @@ -391,10 +369,8 @@ github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -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.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= @@ -454,8 +430,6 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zitadel/oidc/v2 v2.12.2 h1:3kpckg4rurgw7w7aLJrq7yvRxb2pkNOtD08RH42vPEs= -github.com/zitadel/oidc/v2 v2.12.2/go.mod h1:vhP26g1g4YVntcTi0amMYW3tJuid70nxqxf+kb6XKgg= github.com/zitadel/oidc/v3 v3.45.3 h1:iaicqH5M7L5a973DTaG9UVSE14Z6Nj6hN41pp7kYBIw= github.com/zitadel/oidc/v3 v3.45.3/go.mod h1:yerW4/1YA5rUgjSjHsJ4HRnMbaKsyeIJkzyQrwDQ4t8= github.com/zitadel/schema v1.3.2 h1:gfJvt7dOMfTmxzhscZ9KkapKo3Nei3B6cAxjav+lyjI= @@ -555,23 +529,17 @@ golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -612,8 +580,6 @@ google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/go-jose/go-jose.v2 v2.6.3 h1:nt80fvSDlhKWQgSWyHyy5CfmlQr+asih51R8PTWNKKs= -gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/api/backend.go b/internal/api/backend.go index 2fb14c6..dff2277 100644 --- a/internal/api/backend.go +++ b/internal/api/backend.go @@ -3,7 +3,7 @@ package api import ( "context" - "github.com/formancehq/go-libs/v3/bun/bunpaginate" + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" "github.com/formancehq/orchestration/internal/triggers" "github.com/formancehq/orchestration/internal/workflow" diff --git a/internal/api/backend_generated.go b/internal/api/backend_generated.go index 73c8685..ff37c87 100644 --- a/internal/api/backend_generated.go +++ b/internal/api/backend_generated.go @@ -13,7 +13,7 @@ import ( context "context" reflect "reflect" - bunpaginate "github.com/formancehq/go-libs/v3/bun/bunpaginate" + paginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" triggers "github.com/formancehq/orchestration/internal/triggers" workflow "github.com/formancehq/orchestration/internal/workflow" gomock "go.uber.org/mock/gomock" @@ -146,10 +146,10 @@ func (mr *MockBackendMockRecorder) GetTrigger(ctx, triggerID any) *gomock.Call { } // ListInstances mocks base method. -func (m *MockBackend) ListInstances(ctx context.Context, pagination workflow.ListInstancesQuery) (*bunpaginate.Cursor[workflow.Instance], error) { +func (m *MockBackend) ListInstances(ctx context.Context, pagination workflow.ListInstancesQuery) (*paginate.Cursor[workflow.Instance], error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListInstances", ctx, pagination) - ret0, _ := ret[0].(*bunpaginate.Cursor[workflow.Instance]) + ret0, _ := ret[0].(*paginate.Cursor[workflow.Instance]) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -161,10 +161,10 @@ func (mr *MockBackendMockRecorder) ListInstances(ctx, pagination any) *gomock.Ca } // ListTriggers mocks base method. -func (m *MockBackend) ListTriggers(ctx context.Context, query triggers.ListTriggersQuery) (*bunpaginate.Cursor[triggers.Trigger], error) { +func (m *MockBackend) ListTriggers(ctx context.Context, query triggers.ListTriggersQuery) (*paginate.Cursor[triggers.Trigger], error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTriggers", ctx, query) - ret0, _ := ret[0].(*bunpaginate.Cursor[triggers.Trigger]) + ret0, _ := ret[0].(*paginate.Cursor[triggers.Trigger]) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -176,10 +176,10 @@ func (mr *MockBackendMockRecorder) ListTriggers(ctx, query any) *gomock.Call { } // ListTriggersOccurrences mocks base method. -func (m *MockBackend) ListTriggersOccurrences(ctx context.Context, query triggers.ListTriggersOccurrencesQuery) (*bunpaginate.Cursor[triggers.Occurrence], error) { +func (m *MockBackend) ListTriggersOccurrences(ctx context.Context, query triggers.ListTriggersOccurrencesQuery) (*paginate.Cursor[triggers.Occurrence], error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTriggersOccurrences", ctx, query) - ret0, _ := ret[0].(*bunpaginate.Cursor[triggers.Occurrence]) + ret0, _ := ret[0].(*paginate.Cursor[triggers.Occurrence]) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -191,10 +191,10 @@ func (mr *MockBackendMockRecorder) ListTriggersOccurrences(ctx, query any) *gomo } // ListWorkflows mocks base method. -func (m *MockBackend) ListWorkflows(ctx context.Context, query bunpaginate.OffsetPaginatedQuery[any]) (*bunpaginate.Cursor[workflow.Workflow], error) { +func (m *MockBackend) ListWorkflows(ctx context.Context, query paginate.OffsetPaginatedQuery[any]) (*paginate.Cursor[workflow.Workflow], error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListWorkflows", ctx, query) - ret0, _ := ret[0].(*bunpaginate.Cursor[workflow.Workflow]) + ret0, _ := ret[0].(*paginate.Cursor[workflow.Workflow]) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/internal/api/errors.go b/internal/api/errors.go new file mode 100644 index 0000000..43a753a --- /dev/null +++ b/internal/api/errors.go @@ -0,0 +1,32 @@ +package api + +import ( + "database/sql" + "net/http" + + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" + "github.com/formancehq/orchestration/internal/workflow" + "github.com/pkg/errors" + "go.temporal.io/api/serviceerror" +) + +// WriteError maps a backend error to the appropriate HTTP response: +// - 404 for not-found errors: sql.ErrNoRows, the workflow not-found +// sentinels, and Temporal NotFound (raised when reading the history of an +// unknown instance/stage); +// - 400 for invalid workflow configuration; +// - 500 otherwise. +func WriteError(w http.ResponseWriter, r *http.Request, err error) { + var temporalNotFound *serviceerror.NotFound + switch { + case errors.As(err, &temporalNotFound), + errors.Is(err, sql.ErrNoRows), + errors.Is(err, workflow.ErrInstanceNotFound), + errors.Is(err, workflow.ErrWorkflowNotFound): + sharedapi.NotFound(w, err) + case errors.Is(err, workflow.ErrInvalidConfig): + sharedapi.BadRequest(w, "VALIDATION", err) + default: + sharedapi.InternalServerError(w, r, err) + } +} diff --git a/internal/api/handler_info.go b/internal/api/handler_info.go index 983ca3a..1dfed4a 100644 --- a/internal/api/handler_info.go +++ b/internal/api/handler_info.go @@ -3,7 +3,7 @@ package api import ( "net/http" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" ) type ServiceInfo struct { diff --git a/internal/api/module.go b/internal/api/module.go index a0429ec..41a5e45 100644 --- a/internal/api/module.go +++ b/internal/api/module.go @@ -2,8 +2,8 @@ package api import ( "github.com/ThreeDotsLabs/watermill/message" - "github.com/formancehq/go-libs/v3/auth" - "github.com/formancehq/go-libs/v3/health" + auth "github.com/formancehq/go-libs/v5/pkg/authn/jwt" + "github.com/formancehq/go-libs/v5/pkg/service/health" "github.com/go-chi/chi/v5" "go.uber.org/fx" ) diff --git a/internal/api/module_test.go b/internal/api/module_test.go index c71fcc1..5e467f0 100644 --- a/internal/api/module_test.go +++ b/internal/api/module_test.go @@ -8,12 +8,13 @@ import ( "github.com/ThreeDotsLabs/watermill/message" "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/bun/bunpaginate" + sharedapi "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" - "github.com/formancehq/go-libs/v3/auth" + auth "github.com/formancehq/go-libs/v5/pkg/authn/jwt" - "github.com/formancehq/go-libs/v3/health" + "github.com/formancehq/go-libs/v5/pkg/fx/authnfx" "github.com/formancehq/go-libs/v5/pkg/messaging/publish" + "github.com/formancehq/go-libs/v5/pkg/service/health" "github.com/formancehq/orchestration/internal/api" v1 "github.com/formancehq/orchestration/internal/api/v1" v2 "github.com/formancehq/orchestration/internal/api/v2" @@ -31,7 +32,7 @@ func TestModule(t *testing.T) { var mux *chi.Mux app := fxtest.New(t, - auth.Module(auth.ModuleConfig{Enabled: false}), + authnfx.JWTModule(auth.Config{Enabled: false}), fx.Supply(&health.HealthController{}), fx.Supply(api.ServiceInfo{}), fx.Provide(func() message.Publisher { return publish.InMemory() }), diff --git a/internal/api/router.go b/internal/api/router.go index 8868db5..5821f9c 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -6,9 +6,9 @@ import ( "sort" "github.com/ThreeDotsLabs/watermill/message" - "github.com/formancehq/go-libs/v3/auth" - "github.com/formancehq/go-libs/v3/health" "github.com/formancehq/go-libs/v5/pkg/audit/httpaudit" + auth "github.com/formancehq/go-libs/v5/pkg/authn/jwt" + "github.com/formancehq/go-libs/v5/pkg/service/health" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" ) diff --git a/internal/api/v1/handler_abort_workflow_instance.go b/internal/api/v1/handler_abort_workflow_instance.go index ea151fe..4aa5bbf 100644 --- a/internal/api/v1/handler_abort_workflow_instance.go +++ b/internal/api/v1/handler_abort_workflow_instance.go @@ -5,13 +5,13 @@ import ( api2 "github.com/formancehq/orchestration/internal/api" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func abortWorkflowInstance(backend api2.Backend) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if err := backend.AbortRun(r.Context(), instanceID(r)); err != nil { - api.InternalServerError(w, r, err) + api2.WriteError(w, r, err) return } api.NoContent(w) diff --git a/internal/api/v1/handler_create_trigger.go b/internal/api/v1/handler_create_trigger.go index 359f93a..5ac3279 100644 --- a/internal/api/v1/handler_create_trigger.go +++ b/internal/api/v1/handler_create_trigger.go @@ -6,7 +6,7 @@ import ( "github.com/formancehq/orchestration/internal/api" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/triggers" "github.com/pkg/errors" ) @@ -16,7 +16,7 @@ func createTrigger(backend api.Backend) func(writer http.ResponseWriter, request data := triggers.TriggerData{} if err := json.NewDecoder(r.Body).Decode(&data); err != nil { - sharedapi.InternalServerError(w, r, err) + sharedapi.BadRequest(w, "VALIDATION", err) return } diff --git a/internal/api/v1/handler_create_workflow.go b/internal/api/v1/handler_create_workflow.go index 926f637..1e67e4c 100644 --- a/internal/api/v1/handler_create_workflow.go +++ b/internal/api/v1/handler_create_workflow.go @@ -6,7 +6,7 @@ import ( "gopkg.in/yaml.v3" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" api2 "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/workflow" "github.com/pkg/errors" @@ -25,7 +25,8 @@ func createWorkflow(m api2.Backend) http.HandlerFunc { asJson, err := json.Marshal(payload) if err != nil { - panic(err) + api.InternalServerError(w, r, err) + return } if err := json.Unmarshal(asJson, &config); err != nil { @@ -41,7 +42,7 @@ func createWorkflow(m api2.Backend) http.HandlerFunc { workflow, err := m.Create(r.Context(), config) if err != nil { - api.InternalServerError(w, r, errors.Wrap(err, "creating workflow")) + api2.WriteError(w, r, errors.Wrap(err, "creating workflow")) return } diff --git a/internal/api/v1/handler_delete_trigger.go b/internal/api/v1/handler_delete_trigger.go index a10787d..be34b1d 100644 --- a/internal/api/v1/handler_delete_trigger.go +++ b/internal/api/v1/handler_delete_trigger.go @@ -6,7 +6,7 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/api" "github.com/pkg/errors" ) diff --git a/internal/api/v1/handler_delete_workflow.go b/internal/api/v1/handler_delete_workflow.go index a187a47..34a6afc 100644 --- a/internal/api/v1/handler_delete_workflow.go +++ b/internal/api/v1/handler_delete_workflow.go @@ -6,7 +6,7 @@ import ( "github.com/go-playground/validator/v10" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" api2 "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/workflow" ) diff --git a/internal/api/v1/handler_delete_workflow_test.go b/internal/api/v1/handler_delete_workflow_test.go index 6a78b98..3a6fb8b 100644 --- a/internal/api/v1/handler_delete_workflow_test.go +++ b/internal/api/v1/handler_delete_workflow_test.go @@ -9,7 +9,7 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/testing/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/testing/api" "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/workflow" diff --git a/internal/api/v1/handler_get_trigger.go b/internal/api/v1/handler_get_trigger.go index 1bdb81e..7bb2a03 100644 --- a/internal/api/v1/handler_get_trigger.go +++ b/internal/api/v1/handler_get_trigger.go @@ -6,7 +6,7 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/api" "github.com/pkg/errors" ) diff --git a/internal/api/v1/handler_list_instances.go b/internal/api/v1/handler_list_instances.go index 0aa5a68..0ce1d81 100644 --- a/internal/api/v1/handler_list_instances.go +++ b/internal/api/v1/handler_list_instances.go @@ -3,26 +3,43 @@ package v1 import ( "net/http" + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" "github.com/formancehq/orchestration/internal/workflow" api "github.com/formancehq/orchestration/internal/api" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func listInstances(backend api.Backend) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - runs, err := backend.ListInstances(r.Context(), workflow.ListInstancesQuery{ - Options: workflow.ListInstancesOptions{ - WorkflowID: r.URL.Query().Get("workflowID"), - Running: sharedapi.QueryParamBool(r, "running"), - }, + // Bound the query: without a page size, bunpaginate applies no LIMIT, + // loading the entire workflow_instances table per request. + query, err := bunpaginate.Extract[workflow.ListInstancesQuery](r, func() (*workflow.ListInstancesQuery, error) { + pageSize, err := bunpaginate.GetPageSize(r) + if err != nil { + return nil, err + } + return &workflow.ListInstancesQuery{ + PageSize: pageSize, + Options: workflow.ListInstancesOptions{ + WorkflowID: r.URL.Query().Get("workflowID"), + Running: sharedapi.QueryParamBool(r, "running"), + }, + }, nil }) + if err != nil { + sharedapi.BadRequest(w, "VALIDATION", err) + return + } + query.PageSize = normalizePageSize(query.PageSize) + + runs, err := backend.ListInstances(r.Context(), *query) if err != nil { sharedapi.InternalServerError(w, r, err) return } - sharedapi.Ok(w, runs.Data) + renderCursor(w, *runs) } } diff --git a/internal/api/v1/handler_list_instances_test.go b/internal/api/v1/handler_list_instances_test.go index 4421d12..2263ffe 100644 --- a/internal/api/v1/handler_list_instances_test.go +++ b/internal/api/v1/handler_list_instances_test.go @@ -1,17 +1,20 @@ package v1 import ( + "encoding/json" "fmt" "net/http" "net/http/httptest" + "net/url" "testing" "time" - "github.com/formancehq/go-libs/v3/logging" + "github.com/formancehq/go-libs/v5/pkg/observe/log" + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/testing/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/testing/api" "github.com/google/uuid" @@ -90,3 +93,91 @@ func TestListInstances(t *testing.T) { require.Len(t, instances, 0) }) } + +func TestListInstancesIsBounded(t *testing.T) { + ctx := logging.TestingContext() + + test(t, func(router *chi.Mux, m api.Backend, db *bun.DB) { + w := workflow.New(workflow.Config{}) + _, err := db.NewInsert().Model(&w).Exec(ctx) + require.NoError(t, err) + + for i := 0; i < 20; i++ { + instance := workflow.NewInstance(uuid.NewString(), w.ID) + _, err := db.NewInsert().Model(&instance).Exec(ctx) + require.NoError(t, err) + } + + // Without a page size the default (15) bounds the result instead of + // loading the whole table. + req := httptest.NewRequest(http.MethodGet, "/instances", nil) + rec := httptest.NewRecorder() + router.ServeHTTP(rec, req) + require.Equal(t, http.StatusOK, rec.Result().StatusCode) + var firstPage struct { + Data []workflow.Instance `json:"data"` + PageSize int `json:"pageSize"` + HasMore bool `json:"hasMore"` + Next string `json:"next"` + } + require.NoError(t, json.NewDecoder(rec.Body).Decode(&firstPage)) + require.Len(t, firstPage.Data, 15) + require.Equal(t, 15, firstPage.PageSize) + require.True(t, firstPage.HasMore) + require.NotEmpty(t, firstPage.Next) + + // The continuation cursor makes the remaining records retrievable. + req = httptest.NewRequest(http.MethodGet, "/instances?cursor="+url.QueryEscape(firstPage.Next), nil) + rec = httptest.NewRecorder() + router.ServeHTTP(rec, req) + require.Equal(t, http.StatusOK, rec.Result().StatusCode) + var secondPage struct { + Data []workflow.Instance `json:"data"` + HasMore bool `json:"hasMore"` + } + require.NoError(t, json.NewDecoder(rec.Body).Decode(&secondPage)) + require.Len(t, secondPage.Data, 5) + require.False(t, secondPage.HasMore) + + // Cursors are client-controlled base64 JSON. A forged zero page size must + // be normalized to the default instead of disabling LIMIT entirely. + forgedCursor := bunpaginate.EncodeCursor(workflow.ListInstancesQuery{}) + req = httptest.NewRequest(http.MethodGet, "/instances?cursor="+url.QueryEscape(forgedCursor), nil) + rec = httptest.NewRecorder() + router.ServeHTTP(rec, req) + require.Equal(t, http.StatusOK, rec.Result().StatusCode) + var forgedPage struct { + Data []workflow.Instance `json:"data"` + PageSize int `json:"pageSize"` + } + require.NoError(t, json.NewDecoder(rec.Body).Decode(&forgedPage)) + require.Len(t, forgedPage.Data, bunpaginate.QueryDefaultPageSize) + require.Equal(t, bunpaginate.QueryDefaultPageSize, forgedPage.PageSize) + + // Oversized page sizes embedded in cursors are capped just like explicit + // pageSize query parameters. + forgedCursor = bunpaginate.EncodeCursor(workflow.ListInstancesQuery{ + PageSize: bunpaginate.MaxPageSize + 1, + }) + req = httptest.NewRequest(http.MethodGet, "/instances?cursor="+url.QueryEscape(forgedCursor), nil) + rec = httptest.NewRecorder() + router.ServeHTTP(rec, req) + require.Equal(t, http.StatusOK, rec.Result().StatusCode) + forgedPage = struct { + Data []workflow.Instance `json:"data"` + PageSize int `json:"pageSize"` + }{} + require.NoError(t, json.NewDecoder(rec.Body).Decode(&forgedPage)) + require.Len(t, forgedPage.Data, 20) + require.Equal(t, bunpaginate.MaxPageSize, forgedPage.PageSize) + + // An explicit page size is honoured. + req = httptest.NewRequest(http.MethodGet, "/instances?pageSize=5", nil) + rec = httptest.NewRecorder() + router.ServeHTTP(rec, req) + require.Equal(t, http.StatusOK, rec.Result().StatusCode) + instances := make([]workflow.Instance, 0) + sharedapi.ReadResponse(t, rec, &instances) + require.Len(t, instances, 5) + }) +} diff --git a/internal/api/v1/handler_list_triggers.go b/internal/api/v1/handler_list_triggers.go index cba54a6..49613f2 100644 --- a/internal/api/v1/handler_list_triggers.go +++ b/internal/api/v1/handler_list_triggers.go @@ -3,12 +3,12 @@ package v1 import ( "net/http" - "github.com/formancehq/go-libs/v3/bun/bunpaginate" + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" "github.com/formancehq/orchestration/internal/triggers" "github.com/formancehq/orchestration/internal/api" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func listTriggers(backend api.Backend) func(writer http.ResponseWriter, request *http.Request) { @@ -31,6 +31,7 @@ func listTriggers(backend api.Backend) func(writer http.ResponseWriter, request sharedapi.BadRequest(w, "VALIDATION", err) return } + query.PageSize = normalizePageSize(query.PageSize) triggers, err := backend.ListTriggers(r.Context(), *query) if err != nil { @@ -38,6 +39,6 @@ func listTriggers(backend api.Backend) func(writer http.ResponseWriter, request return } - sharedapi.Ok(w, triggers.Data) + renderCursor(w, *triggers) } } diff --git a/internal/api/v1/handler_list_triggers_occurrences.go b/internal/api/v1/handler_list_triggers_occurrences.go index beda917..9ee854e 100644 --- a/internal/api/v1/handler_list_triggers_occurrences.go +++ b/internal/api/v1/handler_list_triggers_occurrences.go @@ -3,25 +3,43 @@ package v1 import ( "net/http" + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/triggers" ) func listTriggersOccurrences(backend api.Backend) func(writer http.ResponseWriter, request *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - triggersOccurrences, err := backend.ListTriggersOccurrences(r.Context(), triggers.ListTriggersOccurrencesQuery{ - Options: triggers.ListTriggersOccurrencesOptions{ - TriggerID: chi.URLParam(r, "triggerID"), - }, + query, err := bunpaginate.Extract[triggers.ListTriggersOccurrencesQuery](r, func() (*triggers.ListTriggersOccurrencesQuery, error) { + pageSize, err := bunpaginate.GetPageSize(r) + if err != nil { + return nil, err + } + return &triggers.ListTriggersOccurrencesQuery{ + PageSize: pageSize, + Options: triggers.ListTriggersOccurrencesOptions{ + TriggerID: chi.URLParam(r, "triggerID"), + }, + }, nil }) + if err != nil { + sharedapi.BadRequest(w, "VALIDATION", err) + return + } + query.PageSize = normalizePageSize(query.PageSize) + // The path parameter is authoritative even when the pagination cursor is + // supplied by the client. + query.Options.TriggerID = chi.URLParam(r, "triggerID") + + triggersOccurrences, err := backend.ListTriggersOccurrences(r.Context(), *query) if err != nil { sharedapi.InternalServerError(w, r, err) return } - sharedapi.Ok(w, triggersOccurrences.Data) + renderCursor(w, *triggersOccurrences) } } diff --git a/internal/api/v1/handler_list_triggers_occurrences_test.go b/internal/api/v1/handler_list_triggers_occurrences_test.go new file mode 100644 index 0000000..f9b5b1d --- /dev/null +++ b/internal/api/v1/handler_list_triggers_occurrences_test.go @@ -0,0 +1,86 @@ +package v1 + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" + + "github.com/formancehq/go-libs/v5/pkg/messaging/publish" + "github.com/formancehq/go-libs/v5/pkg/observe/log" + "github.com/formancehq/orchestration/internal/api" + "github.com/formancehq/orchestration/internal/triggers" + "github.com/formancehq/orchestration/internal/workflow" + "github.com/go-chi/chi/v5" + "github.com/google/uuid" + "github.com/stretchr/testify/require" + "github.com/uptrace/bun" +) + +func TestListTriggersOccurrencesIsBounded(t *testing.T) { + ctx := logging.TestingContext() + + test(t, func(router *chi.Mux, _ api.Backend, db *bun.DB) { + workflowModel := workflow.New(workflow.Config{}) + _, err := db.NewInsert().Model(&workflowModel).Exec(ctx) + require.NoError(t, err) + + trigger, err := triggers.NewTrigger(triggers.TriggerData{ + Event: "TEST_EVENT", + WorkflowID: workflowModel.ID, + }) + require.NoError(t, err) + _, err = db.NewInsert().Model(trigger).Exec(ctx) + require.NoError(t, err) + + for i := 0; i < 20; i++ { + occurrence := triggers.NewTriggerOccurrence( + uuid.NewString(), + trigger.ID, + publish.EventMessage{Type: "TEST_EVENT", Version: "v1"}, + time.Now().Add(time.Duration(i)*time.Second), + ) + _, err = db.NewInsert().Model(&occurrence).Exec(ctx) + require.NoError(t, err) + } + + type occurrencePage struct { + Data []triggers.Occurrence `json:"data"` + PageSize int `json:"pageSize"` + HasMore bool `json:"hasMore"` + Next string `json:"next"` + } + requestPage := func(query string) occurrencePage { + req := httptest.NewRequest(http.MethodGet, "/triggers/"+trigger.ID+"/occurrences"+query, nil) + rec := httptest.NewRecorder() + router.ServeHTTP(rec, req) + require.Equal(t, http.StatusOK, rec.Result().StatusCode) + var page occurrencePage + require.NoError(t, json.NewDecoder(rec.Body).Decode(&page)) + return page + } + + for _, testCase := range []struct { + name string + query string + expected int + }{ + {name: "default page size", expected: 15}, + {name: "explicit page size", query: "?pageSize=5", expected: 5}, + } { + t.Run(testCase.name, func(t *testing.T) { + page := requestPage(testCase.query) + require.Len(t, page.Data, testCase.expected) + }) + } + + firstPage := requestPage("") + require.True(t, firstPage.HasMore) + require.NotEmpty(t, firstPage.Next) + secondPage := requestPage("?cursor=" + url.QueryEscape(firstPage.Next)) + require.Len(t, secondPage.Data, 5) + require.False(t, secondPage.HasMore) + }) +} diff --git a/internal/api/v1/handler_list_workflows.go b/internal/api/v1/handler_list_workflows.go index fd72af0..4ae8ed8 100644 --- a/internal/api/v1/handler_list_workflows.go +++ b/internal/api/v1/handler_list_workflows.go @@ -3,22 +3,37 @@ package v1 import ( "net/http" - "github.com/formancehq/go-libs/v3/bun/bunpaginate" + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" api2 "github.com/formancehq/orchestration/internal/api" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func listWorkflows(backend api2.Backend) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - workflows, err := backend.ListWorkflows(r.Context(), bunpaginate.OffsetPaginatedQuery[any]{}) + // Bound the query: without a page size, bunpaginate applies no LIMIT, + // loading the entire workflows table per request. + query, err := bunpaginate.Extract[bunpaginate.OffsetPaginatedQuery[any]](r, func() (*bunpaginate.OffsetPaginatedQuery[any], error) { + pageSize, err := bunpaginate.GetPageSize(r) + if err != nil { + return nil, err + } + return &bunpaginate.OffsetPaginatedQuery[any]{PageSize: pageSize}, nil + }) + if err != nil { + api.BadRequest(w, "VALIDATION", err) + return + } + query.PageSize = normalizePageSize(query.PageSize) + + workflows, err := backend.ListWorkflows(r.Context(), *query) if err != nil { api.InternalServerError(w, r, err) return } - api.Ok(w, workflows.Data) + renderCursor(w, *workflows) } } diff --git a/internal/api/v1/handler_post_event.go b/internal/api/v1/handler_post_event.go index 5d5ce64..863fa9f 100644 --- a/internal/api/v1/handler_post_event.go +++ b/internal/api/v1/handler_post_event.go @@ -6,7 +6,7 @@ import ( api2 "github.com/formancehq/orchestration/internal/api" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/workflow" ) @@ -19,7 +19,7 @@ func postEventToWorkflowInstance(backend api2.Backend) http.HandlerFunc { } if err := backend.PostEvent(r.Context(), instanceID(r), event); err != nil { - api.InternalServerError(w, r, err) + api2.WriteError(w, r, err) return } diff --git a/internal/api/v1/handler_read_instance.go b/internal/api/v1/handler_read_instance.go index d0b94a8..2ecd733 100644 --- a/internal/api/v1/handler_read_instance.go +++ b/internal/api/v1/handler_read_instance.go @@ -5,14 +5,14 @@ import ( api2 "github.com/formancehq/orchestration/internal/api" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func readInstance(backend api2.Backend) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { workflows, err := backend.GetInstance(r.Context(), instanceID(r)) if err != nil { - api.InternalServerError(w, r, err) + api2.WriteError(w, r, err) return } diff --git a/internal/api/v1/handler_read_instance_history.go b/internal/api/v1/handler_read_instance_history.go index 4fc6de9..e33f4f0 100644 --- a/internal/api/v1/handler_read_instance_history.go +++ b/internal/api/v1/handler_read_instance_history.go @@ -5,14 +5,14 @@ import ( api2 "github.com/formancehq/orchestration/internal/api" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func readInstanceHistory(backend api2.Backend) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { workflows, err := backend.ReadInstanceHistory(r.Context(), instanceID(r)) if err != nil { - api.InternalServerError(w, r, err) + api2.WriteError(w, r, err) return } diff --git a/internal/api/v1/handler_read_instance_test.go b/internal/api/v1/handler_read_instance_test.go index 00f88ca..6e751a6 100644 --- a/internal/api/v1/handler_read_instance_test.go +++ b/internal/api/v1/handler_read_instance_test.go @@ -10,9 +10,9 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/testing/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/testing/api" - "github.com/formancehq/go-libs/v3/logging" + "github.com/formancehq/go-libs/v5/pkg/observe/log" "github.com/google/uuid" "github.com/formancehq/orchestration/internal/api" diff --git a/internal/api/v1/handler_read_stage_history.go b/internal/api/v1/handler_read_stage_history.go index 0865357..6f6906a 100644 --- a/internal/api/v1/handler_read_stage_history.go +++ b/internal/api/v1/handler_read_stage_history.go @@ -6,7 +6,7 @@ import ( "github.com/go-chi/chi/v5" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" api2 "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/workflow" "github.com/pkg/errors" diff --git a/internal/api/v1/handler_read_workflow.go b/internal/api/v1/handler_read_workflow.go index e00432c..3350b48 100644 --- a/internal/api/v1/handler_read_workflow.go +++ b/internal/api/v1/handler_read_workflow.go @@ -5,14 +5,14 @@ import ( api2 "github.com/formancehq/orchestration/internal/api" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func readWorkflow(backend api2.Backend) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { workflow, err := backend.ReadWorkflow(r.Context(), workflowID(r)) if err != nil { - api.InternalServerError(w, r, err) + api2.WriteError(w, r, err) return } diff --git a/internal/api/v1/handler_run_workflow.go b/internal/api/v1/handler_run_workflow.go index a0ac1a2..f8bad53 100644 --- a/internal/api/v1/handler_run_workflow.go +++ b/internal/api/v1/handler_run_workflow.go @@ -7,7 +7,7 @@ import ( api2 "github.com/formancehq/orchestration/internal/api" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/workflow" ) @@ -22,7 +22,7 @@ func runWorkflow(backend api2.Backend) http.HandlerFunc { } instance, err := backend.RunWorkflow(r.Context(), workflowID(r), input) if err != nil { - api.InternalServerError(w, r, err) + api2.WriteError(w, r, err) return } @@ -38,7 +38,8 @@ func runWorkflow(backend api2.Backend) http.HandlerFunc { } ret.Instance, err = backend.GetInstance(r.Context(), instance.ID) if err != nil { - panic(err) + api2.WriteError(w, r, err) + return } api.Created(w, ret) diff --git a/internal/api/v1/handler_run_workflow_test.go b/internal/api/v1/handler_run_workflow_test.go index c89bd98..d220b9b 100644 --- a/internal/api/v1/handler_run_workflow_test.go +++ b/internal/api/v1/handler_run_workflow_test.go @@ -9,7 +9,7 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/testing/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/testing/api" "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/workflow" diff --git a/internal/api/v1/main_test.go b/internal/api/v1/main_test.go index 0853ede..eb8c5d5 100644 --- a/internal/api/v1/main_test.go +++ b/internal/api/v1/main_test.go @@ -6,15 +6,15 @@ import ( "net/http" "testing" - "github.com/formancehq/go-libs/v3/auth" - "github.com/formancehq/go-libs/v3/bun/bunconnect" - "github.com/formancehq/go-libs/v3/bun/bundebug" - "github.com/formancehq/go-libs/v3/logging" - "github.com/formancehq/go-libs/v3/publish" - "github.com/formancehq/go-libs/v3/temporal" - "github.com/formancehq/go-libs/v3/testing/docker" - "github.com/formancehq/go-libs/v3/testing/platform/pgtesting" - "github.com/formancehq/go-libs/v3/testing/utils" + auth "github.com/formancehq/go-libs/v5/pkg/authn/jwt" + "github.com/formancehq/go-libs/v5/pkg/messaging/publish" + "github.com/formancehq/go-libs/v5/pkg/observe/log" + bunconnect "github.com/formancehq/go-libs/v5/pkg/storage/bun/connect" + bundebug "github.com/formancehq/go-libs/v5/pkg/storage/bun/debug" + "github.com/formancehq/go-libs/v5/pkg/testing/docker" + "github.com/formancehq/go-libs/v5/pkg/testing/platform/pgtesting" + "github.com/formancehq/go-libs/v5/pkg/testing/utils" + "github.com/formancehq/go-libs/v5/pkg/workflow/temporal" "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/storage" "github.com/formancehq/orchestration/internal/temporalworker" diff --git a/internal/api/v1/pagination.go b/internal/api/v1/pagination.go new file mode 100644 index 0000000..04692e3 --- /dev/null +++ b/internal/api/v1/pagination.go @@ -0,0 +1,39 @@ +package v1 + +import ( + "net/http" + + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" +) + +// normalizePageSize applies the same bounds to decoded cursors as GetPageSize +// applies to explicit query parameters. Cursors are client-controlled base64 +// JSON, so their embedded page size cannot be trusted. +func normalizePageSize(pageSize uint64) uint64 { + if pageSize == 0 { + return bunpaginate.QueryDefaultPageSize + } + if pageSize > bunpaginate.MaxPageSize { + return bunpaginate.MaxPageSize + } + return pageSize +} + +// renderCursor preserves the v1 {"data": [...]} response shape while adding +// enough metadata for clients to detect and retrieve subsequent pages. +func renderCursor[T any](w http.ResponseWriter, cursor bunpaginate.Cursor[T]) { + sharedapi.RawOk(w, struct { + Data []T `json:"data"` + PageSize int `json:"pageSize"` + HasMore bool `json:"hasMore"` + Previous string `json:"previous,omitempty"` + Next string `json:"next,omitempty"` + }{ + Data: cursor.Data, + PageSize: cursor.PageSize, + HasMore: cursor.HasMore, + Previous: cursor.Previous, + Next: cursor.Next, + }) +} diff --git a/internal/api/v1/router.go b/internal/api/v1/router.go index e8f2f3d..f0cf564 100644 --- a/internal/api/v1/router.go +++ b/internal/api/v1/router.go @@ -5,9 +5,9 @@ import ( "github.com/go-chi/chi/v5" - "github.com/formancehq/go-libs/v3/service" + "github.com/formancehq/go-libs/v5/pkg/transport/httpserver" - "github.com/formancehq/go-libs/v3/auth" + auth "github.com/formancehq/go-libs/v5/pkg/authn/jwt" "github.com/formancehq/orchestration/internal/api" ) @@ -16,7 +16,7 @@ func newRouter(backend api.Backend, authenticator auth.Authenticator, debug bool r.Group(func(r chi.Router) { // Plug middleware to handle traces r.Use(auth.Middleware(authenticator)) - r.Use(service.OTLPMiddleware("orchestration", debug)) + r.Use(httpserver.OTLPMiddleware("orchestration", debug)) r.Route("/triggers", func(r chi.Router) { r.Get("/", listTriggers(backend)) r.Post("/", createTrigger(backend)) diff --git a/internal/api/v2/handler_abort_workflow_instance.go b/internal/api/v2/handler_abort_workflow_instance.go index 4d16f8e..a45188a 100644 --- a/internal/api/v2/handler_abort_workflow_instance.go +++ b/internal/api/v2/handler_abort_workflow_instance.go @@ -5,13 +5,13 @@ import ( api2 "github.com/formancehq/orchestration/internal/api" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func abortWorkflowInstance(backend api2.Backend) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if err := backend.AbortRun(r.Context(), instanceID(r)); err != nil { - api.InternalServerError(w, r, err) + api2.WriteError(w, r, err) return } api.NoContent(w) diff --git a/internal/api/v2/handler_create_trigger.go b/internal/api/v2/handler_create_trigger.go index 8ca5c20..124094a 100644 --- a/internal/api/v2/handler_create_trigger.go +++ b/internal/api/v2/handler_create_trigger.go @@ -6,7 +6,7 @@ import ( "github.com/formancehq/orchestration/internal/api" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/triggers" "github.com/pkg/errors" ) @@ -16,7 +16,7 @@ func createTrigger(backend api.Backend) func(writer http.ResponseWriter, request data := triggers.TriggerData{} if err := json.NewDecoder(r.Body).Decode(&data); err != nil { - sharedapi.InternalServerError(w, r, err) + sharedapi.BadRequest(w, "VALIDATION", err) return } diff --git a/internal/api/v2/handler_create_workflow.go b/internal/api/v2/handler_create_workflow.go index 0fe7292..02db8c4 100644 --- a/internal/api/v2/handler_create_workflow.go +++ b/internal/api/v2/handler_create_workflow.go @@ -6,7 +6,7 @@ import ( "gopkg.in/yaml.v3" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/workflow" "github.com/pkg/errors" @@ -25,7 +25,8 @@ func createWorkflow(m api.Backend) http.HandlerFunc { asJson, err := json.Marshal(payload) if err != nil { - panic(err) + sharedapi.InternalServerError(w, r, err) + return } if err := json.Unmarshal(asJson, &config); err != nil { @@ -41,7 +42,7 @@ func createWorkflow(m api.Backend) http.HandlerFunc { workflow, err := m.Create(r.Context(), config) if err != nil { - sharedapi.InternalServerError(w, r, errors.Wrap(err, "creating workflow")) + api.WriteError(w, r, errors.Wrap(err, "creating workflow")) return } diff --git a/internal/api/v2/handler_create_workflow_test.go b/internal/api/v2/handler_create_workflow_test.go index 7a80f21..1ec0c3b 100644 --- a/internal/api/v2/handler_create_workflow_test.go +++ b/internal/api/v2/handler_create_workflow_test.go @@ -23,3 +23,16 @@ func TestCreateWorkflow(t *testing.T) { require.Equal(t, http.StatusCreated, rec.Result().StatusCode) }) } + +func TestCreateWorkflowValidationError(t *testing.T) { + test(t, func(router *chi.Mux, m api.Backend, db *bun.DB) { + // An empty stage specification fails config validation; the API must + // answer 400, not 500. + req := httptest.NewRequest(http.MethodPost, "/workflows", bytes.NewBufferString(`{"stages": [{}]}`)) + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + require.Equal(t, http.StatusBadRequest, rec.Result().StatusCode) + }) +} diff --git a/internal/api/v2/handler_delete_trigger.go b/internal/api/v2/handler_delete_trigger.go index 40572f2..cce2673 100644 --- a/internal/api/v2/handler_delete_trigger.go +++ b/internal/api/v2/handler_delete_trigger.go @@ -6,7 +6,7 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/api" "github.com/pkg/errors" ) diff --git a/internal/api/v2/handler_delete_workflow.go b/internal/api/v2/handler_delete_workflow.go index daf6d2f..5be4e0b 100644 --- a/internal/api/v2/handler_delete_workflow.go +++ b/internal/api/v2/handler_delete_workflow.go @@ -6,7 +6,7 @@ import ( "github.com/go-playground/validator/v10" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" api2 "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/workflow" ) diff --git a/internal/api/v2/handler_delete_workflow_test.go b/internal/api/v2/handler_delete_workflow_test.go index 47e590f..ffa73f1 100644 --- a/internal/api/v2/handler_delete_workflow_test.go +++ b/internal/api/v2/handler_delete_workflow_test.go @@ -9,7 +9,7 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/testing/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/testing/api" "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/workflow" diff --git a/internal/api/v2/handler_get_trigger.go b/internal/api/v2/handler_get_trigger.go index c62651a..4dfcfa6 100644 --- a/internal/api/v2/handler_get_trigger.go +++ b/internal/api/v2/handler_get_trigger.go @@ -6,7 +6,7 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/api" "github.com/pkg/errors" ) diff --git a/internal/api/v2/handler_list_instances.go b/internal/api/v2/handler_list_instances.go index 4cb5513..7586ab0 100644 --- a/internal/api/v2/handler_list_instances.go +++ b/internal/api/v2/handler_list_instances.go @@ -3,12 +3,12 @@ package v2 import ( "net/http" - "github.com/formancehq/go-libs/v3/bun/bunpaginate" + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" "github.com/formancehq/orchestration/internal/workflow" api "github.com/formancehq/orchestration/internal/api" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func listInstances(backend api.Backend) http.HandlerFunc { diff --git a/internal/api/v2/handler_list_instances_test.go b/internal/api/v2/handler_list_instances_test.go index 9915810..59cf6ab 100644 --- a/internal/api/v2/handler_list_instances_test.go +++ b/internal/api/v2/handler_list_instances_test.go @@ -10,9 +10,9 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/testing/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/testing/api" - "github.com/formancehq/go-libs/v3/bun/bunpaginate" + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" "github.com/google/uuid" diff --git a/internal/api/v2/handler_list_triggers.go b/internal/api/v2/handler_list_triggers.go index 78c240f..1b1607f 100644 --- a/internal/api/v2/handler_list_triggers.go +++ b/internal/api/v2/handler_list_triggers.go @@ -3,12 +3,12 @@ package v2 import ( "net/http" - "github.com/formancehq/go-libs/v3/bun/bunpaginate" + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" "github.com/formancehq/orchestration/internal/triggers" "github.com/formancehq/orchestration/internal/api" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func listTriggers(backend api.Backend) func(writer http.ResponseWriter, request *http.Request) { diff --git a/internal/api/v2/handler_list_triggers_occurrences.go b/internal/api/v2/handler_list_triggers_occurrences.go index fc4e39c..e024d03 100644 --- a/internal/api/v2/handler_list_triggers_occurrences.go +++ b/internal/api/v2/handler_list_triggers_occurrences.go @@ -5,10 +5,10 @@ import ( "github.com/go-chi/chi/v5" - "github.com/formancehq/go-libs/v3/bun/bunpaginate" + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" "github.com/formancehq/orchestration/internal/triggers" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/api" ) diff --git a/internal/api/v2/handler_list_workflows.go b/internal/api/v2/handler_list_workflows.go index e28e840..c302a7b 100644 --- a/internal/api/v2/handler_list_workflows.go +++ b/internal/api/v2/handler_list_workflows.go @@ -3,11 +3,11 @@ package v2 import ( "net/http" - "github.com/formancehq/go-libs/v3/bun/bunpaginate" + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" "github.com/formancehq/orchestration/internal/api" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func listWorkflows(backend api.Backend) http.HandlerFunc { diff --git a/internal/api/v2/handler_post_event.go b/internal/api/v2/handler_post_event.go index 2f6afea..8931978 100644 --- a/internal/api/v2/handler_post_event.go +++ b/internal/api/v2/handler_post_event.go @@ -6,7 +6,7 @@ import ( api2 "github.com/formancehq/orchestration/internal/api" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/workflow" ) @@ -19,7 +19,7 @@ func postEventToWorkflowInstance(backend api2.Backend) http.HandlerFunc { } if err := backend.PostEvent(r.Context(), instanceID(r), event); err != nil { - api.InternalServerError(w, r, err) + api2.WriteError(w, r, err) return } diff --git a/internal/api/v2/handler_read_instance.go b/internal/api/v2/handler_read_instance.go index 2ac07d4..476c82b 100644 --- a/internal/api/v2/handler_read_instance.go +++ b/internal/api/v2/handler_read_instance.go @@ -5,14 +5,14 @@ import ( "github.com/formancehq/orchestration/internal/api" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func readInstance(backend api.Backend) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { workflows, err := backend.GetInstance(r.Context(), instanceID(r)) if err != nil { - sharedapi.InternalServerError(w, r, err) + api.WriteError(w, r, err) return } diff --git a/internal/api/v2/handler_read_instance_history.go b/internal/api/v2/handler_read_instance_history.go index 11a25be..8a48dc0 100644 --- a/internal/api/v2/handler_read_instance_history.go +++ b/internal/api/v2/handler_read_instance_history.go @@ -5,14 +5,14 @@ import ( api2 "github.com/formancehq/orchestration/internal/api" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func readInstanceHistory(backend api2.Backend) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { workflows, err := backend.ReadInstanceHistory(r.Context(), instanceID(r)) if err != nil { - api.InternalServerError(w, r, err) + api2.WriteError(w, r, err) return } diff --git a/internal/api/v2/handler_read_instance_test.go b/internal/api/v2/handler_read_instance_test.go index a881b31..7bff496 100644 --- a/internal/api/v2/handler_read_instance_test.go +++ b/internal/api/v2/handler_read_instance_test.go @@ -10,7 +10,7 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/testing/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/testing/api" "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/workflow" @@ -52,3 +52,14 @@ func TestGetInstance(t *testing.T) { require.Len(t, retrievedInstance.Statuses, 10) }) } + +func TestGetInstanceNotFound(t *testing.T) { + test(t, func(router *chi.Mux, m api.Backend, db *bun.DB) { + req := httptest.NewRequest(http.MethodGet, "/instances/does-not-exist", nil) + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + require.Equal(t, http.StatusNotFound, rec.Result().StatusCode) + }) +} diff --git a/internal/api/v2/handler_read_stage_history.go b/internal/api/v2/handler_read_stage_history.go index 9244218..78ab12a 100644 --- a/internal/api/v2/handler_read_stage_history.go +++ b/internal/api/v2/handler_read_stage_history.go @@ -6,7 +6,7 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/workflow" "github.com/pkg/errors" diff --git a/internal/api/v2/handler_read_workflow.go b/internal/api/v2/handler_read_workflow.go index 024e772..6889d6d 100644 --- a/internal/api/v2/handler_read_workflow.go +++ b/internal/api/v2/handler_read_workflow.go @@ -5,14 +5,14 @@ import ( api2 "github.com/formancehq/orchestration/internal/api" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" ) func readWorkflow(backend api2.Backend) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { workflow, err := backend.ReadWorkflow(r.Context(), workflowID(r)) if err != nil { - api.InternalServerError(w, r, err) + api2.WriteError(w, r, err) return } diff --git a/internal/api/v2/handler_run_workflow.go b/internal/api/v2/handler_run_workflow.go index 8bc56ab..e89cce0 100644 --- a/internal/api/v2/handler_run_workflow.go +++ b/internal/api/v2/handler_run_workflow.go @@ -7,7 +7,7 @@ import ( api2 "github.com/formancehq/orchestration/internal/api" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/workflow" ) @@ -22,7 +22,7 @@ func runWorkflow(backend api2.Backend) http.HandlerFunc { } instance, err := backend.RunWorkflow(r.Context(), workflowID(r), input) if err != nil { - api.InternalServerError(w, r, err) + api2.WriteError(w, r, err) return } @@ -38,7 +38,8 @@ func runWorkflow(backend api2.Backend) http.HandlerFunc { } ret.Instance, err = backend.GetInstance(r.Context(), instance.ID) if err != nil { - panic(err) + api2.WriteError(w, r, err) + return } api.Created(w, ret) diff --git a/internal/api/v2/handler_run_workflow_test.go b/internal/api/v2/handler_run_workflow_test.go index 9cbbc61..53eaa51 100644 --- a/internal/api/v2/handler_run_workflow_test.go +++ b/internal/api/v2/handler_run_workflow_test.go @@ -9,7 +9,7 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/testing/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/testing/api" "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/workflow" diff --git a/internal/api/v2/handler_test_trigger.go b/internal/api/v2/handler_test_trigger.go index 152537e..5f253d7 100644 --- a/internal/api/v2/handler_test_trigger.go +++ b/internal/api/v2/handler_test_trigger.go @@ -6,7 +6,7 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/formancehq/orchestration/internal/api" ) @@ -15,13 +15,13 @@ func testTrigger(backend api.Backend) http.HandlerFunc { data := make(map[string]any) if err := json.NewDecoder(r.Body).Decode(&data); err != nil { - sharedapi.InternalServerError(w, r, err) + sharedapi.BadRequest(w, "VALIDATION", err) return } o, err := backend.TestTrigger(r.Context(), chi.URLParam(r, "triggerID"), data) if err != nil { - sharedapi.InternalServerError(w, r, err) + api.WriteError(w, r, err) return } diff --git a/internal/api/v2/handler_test_trigger_test.go b/internal/api/v2/handler_test_trigger_test.go index ed55f89..add8743 100644 --- a/internal/api/v2/handler_test_trigger_test.go +++ b/internal/api/v2/handler_test_trigger_test.go @@ -10,9 +10,9 @@ import ( "github.com/go-chi/chi/v5" - sharedapi "github.com/formancehq/go-libs/v3/testing/api" + sharedapi "github.com/formancehq/go-libs/v5/pkg/testing/api" - "github.com/formancehq/go-libs/v3/pointer" + "github.com/formancehq/go-libs/v5/pkg/types/pointer" "github.com/formancehq/orchestration/internal/triggers" "github.com/formancehq/orchestration/internal/api" diff --git a/internal/api/v2/main_test.go b/internal/api/v2/main_test.go index 7f2d2df..bd28b32 100644 --- a/internal/api/v2/main_test.go +++ b/internal/api/v2/main_test.go @@ -6,15 +6,15 @@ import ( "net/http" "testing" - "github.com/formancehq/go-libs/v3/auth" - "github.com/formancehq/go-libs/v3/bun/bunconnect" - "github.com/formancehq/go-libs/v3/bun/bundebug" - "github.com/formancehq/go-libs/v3/logging" - "github.com/formancehq/go-libs/v3/publish" - "github.com/formancehq/go-libs/v3/temporal" - "github.com/formancehq/go-libs/v3/testing/docker" - "github.com/formancehq/go-libs/v3/testing/platform/pgtesting" - "github.com/formancehq/go-libs/v3/testing/utils" + auth "github.com/formancehq/go-libs/v5/pkg/authn/jwt" + "github.com/formancehq/go-libs/v5/pkg/messaging/publish" + "github.com/formancehq/go-libs/v5/pkg/observe/log" + bunconnect "github.com/formancehq/go-libs/v5/pkg/storage/bun/connect" + bundebug "github.com/formancehq/go-libs/v5/pkg/storage/bun/debug" + "github.com/formancehq/go-libs/v5/pkg/testing/docker" + "github.com/formancehq/go-libs/v5/pkg/testing/platform/pgtesting" + "github.com/formancehq/go-libs/v5/pkg/testing/utils" + "github.com/formancehq/go-libs/v5/pkg/workflow/temporal" "github.com/formancehq/orchestration/internal/api" "github.com/formancehq/orchestration/internal/storage" "github.com/formancehq/orchestration/internal/temporalworker" diff --git a/internal/api/v2/router.go b/internal/api/v2/router.go index 1058a02..1126022 100644 --- a/internal/api/v2/router.go +++ b/internal/api/v2/router.go @@ -5,9 +5,9 @@ import ( "github.com/go-chi/chi/v5" - "github.com/formancehq/go-libs/v3/service" + "github.com/formancehq/go-libs/v5/pkg/transport/httpserver" - "github.com/formancehq/go-libs/v3/auth" + auth "github.com/formancehq/go-libs/v5/pkg/authn/jwt" "github.com/formancehq/orchestration/internal/api" ) @@ -16,7 +16,7 @@ func newRouter(backend api.Backend, authenticator auth.Authenticator, debug bool r.Group(func(r chi.Router) { // Plug middleware to handle traces r.Use(auth.Middleware(authenticator)) - r.Use(service.OTLPMiddleware("orchestration", debug)) + r.Use(httpserver.OTLPMiddleware("orchestration", debug)) r.Route("/triggers", func(r chi.Router) { r.Get("/", listTriggers(backend)) r.Post("/", createTrigger(backend)) diff --git a/internal/schema/map.go b/internal/schema/map.go index 8586a2f..ae2dc8d 100644 --- a/internal/schema/map.go +++ b/internal/schema/map.go @@ -8,7 +8,7 @@ import ( "strconv" "strings" - "github.com/formancehq/go-libs/v3/time" + "github.com/formancehq/go-libs/v5/pkg/types/time" "github.com/formancehq/orchestration/internal/workflow/stages" "github.com/pkg/errors" diff --git a/internal/storage/main_test.go b/internal/storage/main_test.go index 230d333..b612c31 100644 --- a/internal/storage/main_test.go +++ b/internal/storage/main_test.go @@ -3,11 +3,11 @@ package storage import ( "testing" - "github.com/formancehq/go-libs/v3/logging" - "github.com/formancehq/go-libs/v3/testing/docker" - "github.com/formancehq/go-libs/v3/testing/utils" + "github.com/formancehq/go-libs/v5/pkg/observe/log" + "github.com/formancehq/go-libs/v5/pkg/testing/docker" + "github.com/formancehq/go-libs/v5/pkg/testing/utils" - "github.com/formancehq/go-libs/v3/testing/platform/pgtesting" + "github.com/formancehq/go-libs/v5/pkg/testing/platform/pgtesting" ) var srv *pgtesting.PostgresServer diff --git a/internal/storage/migrations.go b/internal/storage/migrations.go index 33ff453..4b7e460 100644 --- a/internal/storage/migrations.go +++ b/internal/storage/migrations.go @@ -3,7 +3,7 @@ package storage import ( "context" - "github.com/formancehq/go-libs/v3/migrations" + "github.com/formancehq/go-libs/v5/pkg/storage/migrations" "github.com/uptrace/bun" ) @@ -172,6 +172,22 @@ var _migrations = []migrations.Migration{ return nil }, }, + { + // Migration 8 dropped the (trigger_id, event_id) primary key in favour + // of (id), leaving trigger_id unindexed. ListTriggersOccurrences filters + // WHERE trigger_id = ? and orders by date, so without this index every + // page is a sequential scan over an ever-growing table. Built + // CONCURRENTLY to avoid blocking writes during deploy. + Up: func(ctx context.Context, tx bun.IDB) error { + if _, err := tx.ExecContext(ctx, ` + create index concurrently if not exists triggers_occurrences_trigger_id_date_idx + on triggers_occurrences (trigger_id, date); + `); err != nil { + return err + } + return nil + }, + }, } func Migrate(ctx context.Context, db *bun.DB) error { diff --git a/internal/storage/migrations_test.go b/internal/storage/migrations_test.go index 5918cec..098b0c1 100644 --- a/internal/storage/migrations_test.go +++ b/internal/storage/migrations_test.go @@ -3,11 +3,11 @@ package storage import ( "testing" - "github.com/formancehq/go-libs/v3/bun/bundebug" + bundebug "github.com/formancehq/go-libs/v5/pkg/storage/bun/debug" "github.com/uptrace/bun" - "github.com/formancehq/go-libs/v3/bun/bunconnect" - "github.com/formancehq/go-libs/v3/logging" + "github.com/formancehq/go-libs/v5/pkg/observe/log" + bunconnect "github.com/formancehq/go-libs/v5/pkg/storage/bun/connect" "github.com/stretchr/testify/require" ) diff --git a/internal/temporalworker/module.go b/internal/temporalworker/module.go index d7e6bad..bacb42f 100644 --- a/internal/temporalworker/module.go +++ b/internal/temporalworker/module.go @@ -3,7 +3,7 @@ package temporalworker import ( "context" - "github.com/formancehq/go-libs/v3/logging" + "github.com/formancehq/go-libs/v5/pkg/observe/log" "go.temporal.io/api/enums/v1" "go.temporal.io/sdk/activity" "go.temporal.io/sdk/client" diff --git a/internal/triggers/activities.go b/internal/triggers/activities.go index d4eedd1..32b2e23 100644 --- a/internal/triggers/activities.go +++ b/internal/triggers/activities.go @@ -5,8 +5,8 @@ import ( "strings" "github.com/ThreeDotsLabs/watermill/message" - "github.com/formancehq/go-libs/v3/collectionutils" - "github.com/formancehq/go-libs/v3/pointer" + collectionutils "github.com/formancehq/go-libs/v5/pkg/types/collections" + "github.com/formancehq/go-libs/v5/pkg/types/pointer" "github.com/formancehq/orchestration/internal/temporalworker" "github.com/formancehq/orchestration/internal/tracer" "github.com/formancehq/orchestration/internal/workflow" @@ -58,6 +58,7 @@ func (a Activities) ListTriggers(ctx context.Context, request ProcessEventReques Model(&triggers). Relation("Workflow"). Where("trigger.deleted_at is null"). + Where("workflow_id IN (SELECT id FROM workflows WHERE deleted_at IS NULL)"). Where("event = ?", request.Event.Type). Where("CASE WHEN trigger.version IS NULL THEN true ELSE trigger.version = ? END", request.Event.Version). Scan(ctx); err != nil { @@ -81,8 +82,13 @@ func (a Activities) EvalTriggerVariables(ctx context.Context, trigger Trigger, r } func (a Activities) InsertTriggerOccurrence(ctx context.Context, occurrence Occurrence) error { + // Idempotent: a Temporal retry after a lost ack (row committed but the + // activity result never reached the server) must not fail on the duplicate + // primary key. The occurrence id is deterministic (see + // NewTriggerOccurrence), so DO NOTHING is safe. _, err := a.db.NewInsert(). Model(pointer.For(occurrence)). + On("CONFLICT DO NOTHING"). Exec(ctx) return err } diff --git a/internal/triggers/expression.go b/internal/triggers/expression.go index b9ccd85..0033bce 100644 --- a/internal/triggers/expression.go +++ b/internal/triggers/expression.go @@ -4,18 +4,55 @@ import ( "encoding/json" "fmt" "net/http" + "net/url" + "strings" "go.temporal.io/sdk/temporal" - "github.com/formancehq/go-libs/v3/collectionutils" + collectionutils "github.com/formancehq/go-libs/v5/pkg/types/collections" "github.com/expr-lang/expr" - "github.com/formancehq/go-libs/v3/api" + "github.com/formancehq/go-libs/v5/pkg/transport/api" "github.com/pkg/errors" ) type expressionEvaluator struct { httpClient *http.Client + // allowedHosts permits bare-host configuration for callers that do not have + // a canonical stack URL. allowedOrigins is preferred and pins both scheme + // and host, preventing redirects from downgrading an HTTPS stack to HTTP. + allowedOrigins map[string]struct{} + // The allowlist exists + // to prevent the (credential-bearing) HTTP client from being pointed at an + // arbitrary, attacker-controlled host via a user-defined trigger + // expression (SSRF + bearer-token exfiltration). An empty set denies every + // network call. + allowedHosts map[string]struct{} +} + +// checkLinkURL enforces that a link() target uses an http(s) scheme and points +// at an allow-listed host (typically the stack gateway the HTTP client is +// scoped to). +func (h *expressionEvaluator) checkLinkURL(raw string) error { + u, err := url.Parse(raw) + if err != nil { + return temporal.NewNonRetryableApplicationError( + fmt.Sprintf("invalid link url: %s", raw), "APPLICATION", err) + } + if u.Scheme != "http" && u.Scheme != "https" { + return temporal.NewNonRetryableApplicationError( + fmt.Sprintf("link url scheme not allowed: %q", u.Scheme), "APPLICATION", + fmt.Errorf("scheme %q not allowed", u.Scheme)) + } + origin := strings.ToLower(u.Scheme + "://" + u.Host) + _, originAllowed := h.allowedOrigins[origin] + _, hostAllowed := h.allowedHosts[strings.ToLower(u.Host)] + if !originAllowed && !hostAllowed { + return temporal.NewNonRetryableApplicationError( + fmt.Sprintf("link url host not allowed: %q", u.Host), "APPLICATION", + fmt.Errorf("host %q is not in the allowlist", u.Host)) + } + return nil } func (h *expressionEvaluator) link(params ...any) (any, error) { @@ -54,10 +91,16 @@ func (h *expressionEvaluator) link(params ...any) (any, error) { fmt.Errorf("link '%s' not defined for object", rel), ) case 1: + if err := h.checkLinkURL(filteredLinks[0].URI); err != nil { + return nil, err + } rsp, err := h.httpClient.Get(filteredLinks[0].URI) if err != nil { return nil, errors.Wrapf(err, "reading resource: %s", filteredLinks[0].URI) } + defer func() { + _ = rsp.Body.Close() + }() if rsp.StatusCode >= 400 { return nil, fmt.Errorf("unexpected status code when reading resource: %d", rsp.StatusCode) } @@ -141,10 +184,45 @@ func (h *expressionEvaluator) evalVariables(rawObject any, vars map[string]strin return results, nil } -func NewExpressionEvaluator(httpClient *http.Client) *expressionEvaluator { - return &expressionEvaluator{ - httpClient: httpClient, +// NewExpressionEvaluator builds an evaluator whose link() function may only +// reach the provided targets. A full URL pins both scheme and host; a bare host +// permits either HTTP scheme for callers without a canonical stack URL. With +// no allowed target, link() network calls are denied. +func NewExpressionEvaluator(httpClient *http.Client, allowedHosts ...string) *expressionEvaluator { + if httpClient == nil { + httpClient = http.DefaultClient } + + hosts := make(map[string]struct{}, len(allowedHosts)) + origins := make(map[string]struct{}, len(allowedHosts)) + for _, h := range allowedHosts { + if h == "" { + continue + } + if u, err := url.Parse(h); err == nil && u.Scheme != "" && u.Host != "" { + origins[strings.ToLower(u.Scheme+"://"+u.Host)] = struct{}{} + continue + } + hosts[strings.ToLower(h)] = struct{}{} + } + client := *httpClient + evaluator := &expressionEvaluator{ + httpClient: &client, + allowedOrigins: origins, + allowedHosts: hosts, + } + previousCheckRedirect := client.CheckRedirect + client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + if err := evaluator.checkLinkURL(req.URL.String()); err != nil { + return err + } + if previousCheckRedirect != nil { + return previousCheckRedirect(req, via) + } + return nil + } + + return evaluator } func NewDefaultExpressionEvaluator() *expressionEvaluator { diff --git a/internal/triggers/listener.go b/internal/triggers/listener.go index de0cc17..a13e46b 100644 --- a/internal/triggers/listener.go +++ b/internal/triggers/listener.go @@ -9,31 +9,31 @@ import ( "go.temporal.io/api/enums/v1" - "github.com/formancehq/go-libs/v3/collectionutils" + collectionutils "github.com/formancehq/go-libs/v5/pkg/types/collections" "github.com/formancehq/orchestration/internal/tracer" "github.com/formancehq/orchestration/internal/workflow" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - "github.com/formancehq/go-libs/v3/pointer" + "github.com/formancehq/go-libs/v5/pkg/types/pointer" "go.temporal.io/api/serviceerror" - "github.com/formancehq/go-libs/v3/logging" + "github.com/formancehq/go-libs/v5/pkg/observe/log" "github.com/ThreeDotsLabs/watermill/message" - "github.com/formancehq/go-libs/v3/publish" + "github.com/formancehq/go-libs/v5/pkg/messaging/publish" "github.com/pkg/errors" "github.com/uptrace/bun" "go.temporal.io/sdk/client" ) // Quick hack to filter already processed events -func getWorkflowIDFromEvent(event publish.EventMessage) *string { +func getWorkflowIDFromEvent(event publish.EventMessage) (*string, error) { switch event.Type { case "SAVED_PAYMENT", "SAVED_ACCOUNT": data, err := json.Marshal(event.Payload) if err != nil { - panic(err) + return nil, errors.Wrap(err, "marshalling event payload") } type object struct { @@ -41,12 +41,15 @@ func getWorkflowIDFromEvent(event publish.EventMessage) *string { } o := &object{} if err := json.Unmarshal(data, o); err != nil { - panic(err) + return nil, errors.Wrap(err, "unmarshalling event payload") + } + if o.ID == "" { + return nil, errors.New("event payload id is required") } - return pointer.For(o.ID) + return pointer.For(o.ID), nil default: - return nil + return nil, nil } } @@ -56,6 +59,7 @@ func listMatchingTriggers(ctx context.Context, db *bun.DB, evaluator *expression Model(&triggers). Relation("Workflow"). Where("trigger.deleted_at is null"). + Where("workflow_id IN (SELECT id FROM workflows WHERE deleted_at IS NULL)"). Where("event = ?", event.Type). Where("CASE WHEN trigger.version IS NULL THEN true ELSE trigger.version = ? END", event.Version). Scan(ctx); err != nil { @@ -96,11 +100,14 @@ func handleMessage( stack, taskIDPrefix, taskQueue string, includeSearchAttributes bool, msg *message.Message, -) error { +) (err error) { defer func() { if e := recover(); e != nil { - fmt.Println(e) - debug.PrintStack() + // Convert a panic into a returned error so the message is NACKed + // (and redelivered / dead-lettered) instead of being silently + // acked and lost. + err = fmt.Errorf("panic while handling event: %v", e) + logging.FromContext(msg.Context()).Errorf("%s\n%s", err, debug.Stack()) } }() @@ -138,7 +145,10 @@ func handleMessage( return nil } - objectID := getWorkflowIDFromEvent(*event) + objectID, err := getWorkflowIDFromEvent(*event) + if err != nil { + return errors.Wrap(err, "extracting workflow id from event") + } for _, trigger := range matched { searchAttributes := map[string]interface{}{ @@ -148,14 +158,24 @@ func handleMessage( searchAttributes[workflow.SearchAttributeTriggerID] = trigger.ID } - options := client.StartWorkflowOptions{ - TaskQueue: taskQueue, - SearchAttributes: searchAttributes, - } + // Derive a deterministic workflow ID so an at-least-once redelivery of + // the same event does not start a second trigger execution (which would + // replay side-effecting stages such as money movements). For + // SAVED_PAYMENT/SAVED_ACCOUNT we key on the object id (dedup across + // distinct deliveries of the same object); for every other event type + // we fall back to the message UUID, which is preserved across + // redeliveries. + dedupKey := msg.UUID if objectID != nil { - options.ID = taskIDPrefix + "-" + trigger.ID + "-" + *objectID - options.WorkflowIDReusePolicy = enums.WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE - options.WorkflowExecutionErrorWhenAlreadyStarted = true + dedupKey = *objectID + } + + options := client.StartWorkflowOptions{ + TaskQueue: taskQueue, + SearchAttributes: searchAttributes, + ID: taskIDPrefix + "-" + trigger.ID + "-" + dedupKey, + WorkflowIDReusePolicy: enums.WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE, + WorkflowExecutionErrorWhenAlreadyStarted: true, } _, execErr := temporalClient.ExecuteWorkflow(ctx, options, ExecuteTrigger, ProcessEventRequest{ diff --git a/internal/triggers/listener_test.go b/internal/triggers/listener_test.go index fb25dfd..da65ee4 100644 --- a/internal/triggers/listener_test.go +++ b/internal/triggers/listener_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - "github.com/formancehq/go-libs/v3/bun/bunconnect" - "github.com/formancehq/go-libs/v3/bun/bundebug" - "github.com/formancehq/go-libs/v3/logging" - "github.com/formancehq/go-libs/v3/pointer" - "github.com/formancehq/go-libs/v3/publish" + "github.com/formancehq/go-libs/v5/pkg/messaging/publish" + "github.com/formancehq/go-libs/v5/pkg/observe/log" + bunconnect "github.com/formancehq/go-libs/v5/pkg/storage/bun/connect" + bundebug "github.com/formancehq/go-libs/v5/pkg/storage/bun/debug" + "github.com/formancehq/go-libs/v5/pkg/types/pointer" "github.com/formancehq/orchestration/internal/storage" "github.com/formancehq/orchestration/internal/temporalworker" "github.com/formancehq/orchestration/internal/workflow" @@ -40,6 +40,32 @@ func setupTestDB(t *testing.T) *bun.DB { return db } +func TestGetWorkflowIDFromEvent(t *testing.T) { + for _, testCase := range []struct { + name string + payload any + }{ + {name: "missing id", payload: map[string]any{}}, + {name: "null id", payload: map[string]any{"id": nil}}, + {name: "empty id", payload: map[string]any{"id": ""}}, + } { + t.Run(testCase.name, func(t *testing.T) { + _, err := getWorkflowIDFromEvent(publish.EventMessage{ + Type: "SAVED_PAYMENT", + Payload: testCase.payload, + }) + require.Error(t, err) + }) + } + + id, err := getWorkflowIDFromEvent(publish.EventMessage{ + Type: "SAVED_PAYMENT", + Payload: map[string]any{"id": "payment-id"}, + }) + require.NoError(t, err) + require.Equal(t, "payment-id", *id) +} + func insertNoOpWorkflow(t *testing.T, db *bun.DB) workflow.Workflow { t.Helper() @@ -218,6 +244,23 @@ func TestListMatchingTriggers(t *testing.T) { }, expectedMatched: 0, }, + { + name: "trigger of soft deleted workflow excluded", + triggers: func(t *testing.T, db *bun.DB, workflowID string) { + insertTrigger(t, db, workflowID, "NEW_TRANSACTION", nil, nil) + _, err := db.NewUpdate(). + Model(&workflow.Workflow{}). + Where("id = ?", workflowID). + Set("deleted_at = ?", time.Now()). + Exec(logging.TestingContext()) + require.NoError(t, err) + }, + event: publish.EventMessage{ + Type: "NEW_TRANSACTION", + Version: "v1", + }, + expectedMatched: 0, + }, } for _, tc := range testCases { @@ -379,4 +422,62 @@ func TestHandleMessage(t *testing.T) { require.NoError(t, err) require.Equal(t, 1, count) }) + + t.Run("redelivery of a non-payment event is skipped", func(t *testing.T) { + t.Parallel() + + db := setupTestDB(t) + taskQueue := setupWorker(t, db) + + w := insertNoOpWorkflow(t, db) + insertTrigger(t, db, w.ID, "NEW_TRANSACTION", nil, nil) + + event := makeMessage("NEW_TRANSACTION", "v1", map[string]any{}) + evaluator := NewDefaultExpressionEvaluator() + + // Simulate an at-least-once redelivery: the broker re-delivers the same + // message, preserving its UUID. + msg1 := publish.NewMessage(logging.TestingContext(), *event) + msg2 := publish.NewMessage(logging.TestingContext(), *event) + msg2.UUID = msg1.UUID + + require.NoError(t, handleMessage(devServer.Client(), db, evaluator, "test", "test", taskQueue, false, msg1)) + require.Eventually(t, func() bool { + count, err := db.NewSelect(). + Model((*Occurrence)(nil)). + Count(logging.TestingContext()) + return err == nil && count == 1 + }, 10*time.Second, 200*time.Millisecond) + + require.NoError(t, handleMessage(devServer.Client(), db, evaluator, "test", "test", taskQueue, false, msg2)) + + // The deterministic workflow id (keyed on the message UUID) must reject + // the duplicate, so no second trigger execution / occurrence is created. + time.Sleep(2 * time.Second) + count, err := db.NewSelect(). + Model((*Occurrence)(nil)). + Count(logging.TestingContext()) + require.NoError(t, err) + require.Equal(t, 1, count) + }) + + t.Run("malformed payment payload returns an error instead of being dropped", func(t *testing.T) { + t.Parallel() + + db := setupTestDB(t) + taskQueue := setupWorker(t, db) + + w := insertNoOpWorkflow(t, db) + insertTrigger(t, db, w.ID, "SAVED_PAYMENT", nil, nil) + + // "id" is a number, so extracting the dedup id fails. This used to + // panic and be silently acked; it must now surface as an error so the + // message is NACKed. + event := makeMessage("SAVED_PAYMENT", "v1", map[string]any{"id": 123}) + msg := publish.NewMessage(logging.TestingContext(), *event) + + evaluator := NewDefaultExpressionEvaluator() + err := handleMessage(devServer.Client(), db, evaluator, "test", "test", taskQueue, false, msg) + require.Error(t, err) + }) } diff --git a/internal/triggers/main_test.go b/internal/triggers/main_test.go index 42cad95..ff0a2a5 100644 --- a/internal/triggers/main_test.go +++ b/internal/triggers/main_test.go @@ -3,14 +3,14 @@ package triggers import ( "testing" - "github.com/formancehq/go-libs/v3/testing/docker" - "github.com/formancehq/go-libs/v3/testing/utils" + "github.com/formancehq/go-libs/v5/pkg/testing/docker" + "github.com/formancehq/go-libs/v5/pkg/testing/utils" "github.com/stretchr/testify/require" - "github.com/formancehq/go-libs/v3/logging" + "github.com/formancehq/go-libs/v5/pkg/observe/log" "go.temporal.io/sdk/testsuite" - "github.com/formancehq/go-libs/v3/testing/platform/pgtesting" + "github.com/formancehq/go-libs/v5/pkg/testing/platform/pgtesting" ) var ( diff --git a/internal/triggers/manager.go b/internal/triggers/manager.go index 5d35766..dc2e711 100644 --- a/internal/triggers/manager.go +++ b/internal/triggers/manager.go @@ -5,7 +5,7 @@ import ( "database/sql" "time" - "github.com/formancehq/go-libs/v3/bun/bunpaginate" + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" "github.com/formancehq/orchestration/internal/workflow" "github.com/pkg/errors" @@ -41,7 +41,7 @@ func (m *TriggerManager) ListTriggers(ctx context.Context, paramsQuery ListTrigg func(query *bun.SelectQuery) *bun.SelectQuery { if paramsQuery.Options.Name != "" { - query = query.Where("Name ILIKE '%?%';", paramsQuery.Options.Name) + query = query.Where("name ILIKE ?", "%"+paramsQuery.Options.Name+"%") } return query.Where("deleted_at is null") }) diff --git a/internal/triggers/manager_test.go b/internal/triggers/manager_test.go new file mode 100644 index 0000000..f577b2e --- /dev/null +++ b/internal/triggers/manager_test.go @@ -0,0 +1,74 @@ +package triggers + +import ( + "testing" + "time" + + "github.com/formancehq/go-libs/v5/pkg/observe/log" + "github.com/google/uuid" + "github.com/stretchr/testify/require" + "github.com/uptrace/bun" +) + +func insertNamedTrigger(t *testing.T, db *bun.DB, workflowID, name string) Trigger { + t.Helper() + + trigger := Trigger{ + TriggerData: TriggerData{ + Name: name, + Event: "NEW_TRANSACTION", + WorkflowID: workflowID, + }, + ID: uuid.NewString(), + CreatedAt: time.Now().Round(time.Microsecond).UTC(), + } + _, err := db.NewInsert().Model(&trigger).Exec(logging.TestingContext()) + require.NoError(t, err) + + return trigger +} + +func TestListTriggersNameFilter(t *testing.T) { + t.Parallel() + + db := setupTestDB(t) + w := insertNoOpWorkflow(t, db) + insertNamedTrigger(t, db, w.ID, "payment-processor") + insertNamedTrigger(t, db, w.ID, "ledger-sync") + + manager := &TriggerManager{db: db} + + listWithName := func(name string) []Trigger { + t.Helper() + cursor, err := manager.ListTriggers(logging.TestingContext(), ListTriggersQuery{ + PageSize: 15, + Options: ListTriggerParams{Name: name}, + }) + require.NoError(t, err) + return cursor.Data + } + + t.Run("substring match", func(t *testing.T) { + got := listWithName("payment") + require.Len(t, got, 1) + require.Equal(t, "payment-processor", got[0].Name) + }) + + t.Run("case insensitive", func(t *testing.T) { + got := listWithName("LEDGER") + require.Len(t, got, 1) + require.Equal(t, "ledger-sync", got[0].Name) + }) + + t.Run("no match", func(t *testing.T) { + require.Empty(t, listWithName("does-not-exist")) + }) + + t.Run("no filter returns all", func(t *testing.T) { + cursor, err := manager.ListTriggers(logging.TestingContext(), ListTriggersQuery{ + PageSize: 15, + }) + require.NoError(t, err) + require.Len(t, cursor.Data, 2) + }) +} diff --git a/internal/triggers/module.go b/internal/triggers/module.go index e512770..5d64f63 100644 --- a/internal/triggers/module.go +++ b/internal/triggers/module.go @@ -7,19 +7,20 @@ import ( "github.com/formancehq/orchestration/internal/temporalworker" "github.com/ThreeDotsLabs/watermill/message" - "github.com/formancehq/go-libs/v3/logging" + "github.com/formancehq/go-libs/v5/pkg/observe/log" "github.com/formancehq/orchestration/internal/workflow" "github.com/uptrace/bun" "go.temporal.io/sdk/client" "go.uber.org/fx" ) -func NewModule(stack, taskQueue string) fx.Option { +func NewModule(stack, stackURL, taskQueue, httpClientName string) fx.Option { + httpClientTag := `name:"` + httpClientName + `"` return fx.Options( fx.Provide(NewManager), - fx.Provide(func(httpClient *http.Client) *expressionEvaluator { - return NewExpressionEvaluator(httpClient) - }), + fx.Provide(fx.Annotate(func(httpClient *http.Client) *expressionEvaluator { + return NewExpressionEvaluator(httpClient, stackURL) + }, fx.ParamTags(httpClientTag))), fx.Provide(func() *triggerWorkflow { return NewWorkflow(stack, taskQueue, true) }), diff --git a/internal/triggers/trigger.go b/internal/triggers/trigger.go index ad2860c..4ab1b47 100644 --- a/internal/triggers/trigger.go +++ b/internal/triggers/trigger.go @@ -6,7 +6,7 @@ import ( "github.com/formancehq/orchestration/internal/workflow" - "github.com/formancehq/go-libs/v3/publish" + "github.com/formancehq/go-libs/v5/pkg/messaging/publish" "github.com/expr-lang/expr" "github.com/google/uuid" @@ -112,9 +112,13 @@ type Occurrence struct { Error *string `json:"error,omitempty" bun:"error"` } -func NewTriggerOccurrence(triggerID string, event publish.EventMessage, at time.Time) Occurrence { +// NewTriggerOccurrence builds an occurrence with a caller-provided id. The id +// must be deterministic when called from workflow code (e.g. the workflow +// execution id) so that it stays stable across Temporal replays — generating a +// random uuid here would yield a different occurrence id on every replay. +func NewTriggerOccurrence(id, triggerID string, event publish.EventMessage, at time.Time) Occurrence { return Occurrence{ - ID: uuid.NewString(), + ID: id, TriggerID: triggerID, Date: at, Event: event, diff --git a/internal/triggers/trigger_test.go b/internal/triggers/trigger_test.go index 635c2e1..7860c63 100644 --- a/internal/triggers/trigger_test.go +++ b/internal/triggers/trigger_test.go @@ -168,10 +168,80 @@ func TestEvalVariables(t *testing.T) { } { testCase := testCase t.Run(testCase.name, func(t *testing.T) { - e := NewExpressionEvaluator(http.DefaultClient) + e := NewExpressionEvaluator(http.DefaultClient, srv.URL) evaluated, err := e.evalVariables(testCase.rawObject, testCase.variables) require.NoError(t, err) require.Equal(t, testCase.expectedResult, evaluated) }) } } + +func TestLinkHostAllowlist(t *testing.T) { + var hit bool + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + hit = true + _, _ = w.Write([]byte(`{"data": {"role": "admin"}}`)) + })) + t.Cleanup(srv.Close) + + rawObject := map[string]any{ + "links": []map[string]any{ + {"name": "source_account", "uri": srv.URL}, + }, + } + variables := map[string]string{"role": `link(event, "source_account").role`} + + t.Run("denied when host not allowlisted", func(t *testing.T) { + hit = false + e := NewExpressionEvaluator(http.DefaultClient, "allowed.example.com") + _, err := e.evalVariables(rawObject, variables) + require.Error(t, err) + require.False(t, hit, "a non-allowlisted host must never be contacted") + }) + + t.Run("denied with empty allowlist", func(t *testing.T) { + hit = false + e := NewDefaultExpressionEvaluator() + _, err := e.evalVariables(rawObject, variables) + require.Error(t, err) + require.False(t, hit) + }) + + t.Run("configured origin rejects a scheme downgrade", func(t *testing.T) { + e := NewExpressionEvaluator(http.DefaultClient, "https://allowed.example.com") + require.Error(t, e.checkLinkURL("http://allowed.example.com/resource")) + }) + + t.Run("allowed when host matches", func(t *testing.T) { + hit = false + e := NewExpressionEvaluator(http.DefaultClient, srv.URL) + result, err := e.evalVariables(rawObject, variables) + require.NoError(t, err) + require.Equal(t, map[string]string{"role": "admin"}, result) + require.True(t, hit) + }) + + t.Run("denied when an allowed host redirects to a non-allowlisted host", func(t *testing.T) { + redirectTargetHit := false + redirectTarget := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + redirectTargetHit = true + _, _ = w.Write([]byte(`{"data": {"role": "admin"}}`)) + })) + t.Cleanup(redirectTarget.Close) + + redirector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, redirectTarget.URL, http.StatusFound) + })) + t.Cleanup(redirector.Close) + + redirectedObject := map[string]any{ + "links": []map[string]any{ + {"name": "source_account", "uri": redirector.URL}, + }, + } + e := NewExpressionEvaluator(http.DefaultClient, redirector.URL) + _, err := e.evalVariables(redirectedObject, variables) + require.Error(t, err) + require.False(t, redirectTargetHit, "a redirect target must be checked before it is contacted") + }) +} diff --git a/internal/triggers/workflow_trigger.go b/internal/triggers/workflow_trigger.go index 16bbf2a..10a4384 100644 --- a/internal/triggers/workflow_trigger.go +++ b/internal/triggers/workflow_trigger.go @@ -3,8 +3,8 @@ package triggers import ( "time" - "github.com/formancehq/go-libs/v3/pointer" - "github.com/formancehq/go-libs/v3/publish" + "github.com/formancehq/go-libs/v5/pkg/messaging/publish" + "github.com/formancehq/go-libs/v5/pkg/types/pointer" "github.com/formancehq/orchestration/internal/temporalworker" "github.com/formancehq/orchestration/internal/workflow" "go.temporal.io/api/enums/v1" @@ -68,7 +68,11 @@ func (w triggerWorkflow) RunTrigger(ctx temporalworkflow.Context, req ProcessEve func (w triggerWorkflow) ExecuteTrigger(ctx temporalworkflow.Context, req ProcessEventRequest, trigger Trigger) error { vars := make(map[string]string) - occurrence := NewTriggerOccurrence(trigger.ID, req.Event, temporalworkflow.Now(ctx)) + // Use the (deterministic, replay-stable) workflow execution id as the + // occurrence id rather than a random uuid generated in workflow code. + occurrence := NewTriggerOccurrence( + temporalworkflow.GetInfo(ctx).WorkflowExecution.ID, + trigger.ID, req.Event, temporalworkflow.Now(ctx)) err := temporalworkflow.ExecuteActivity( temporalworkflow.WithActivityOptions(ctx, temporalworkflow.ActivityOptions{ StartToCloseTimeout: 10 * time.Second, diff --git a/internal/triggers/workflow_trigger_test.go b/internal/triggers/workflow_trigger_test.go index eb2be47..e928b02 100644 --- a/internal/triggers/workflow_trigger_test.go +++ b/internal/triggers/workflow_trigger_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - "github.com/formancehq/go-libs/v3/bun/bunconnect" - "github.com/formancehq/go-libs/v3/bun/bundebug" - "github.com/formancehq/go-libs/v3/logging" - "github.com/formancehq/go-libs/v3/pointer" - "github.com/formancehq/go-libs/v3/publish" + "github.com/formancehq/go-libs/v5/pkg/messaging/publish" + "github.com/formancehq/go-libs/v5/pkg/observe/log" + bunconnect "github.com/formancehq/go-libs/v5/pkg/storage/bun/connect" + bundebug "github.com/formancehq/go-libs/v5/pkg/storage/bun/debug" + "github.com/formancehq/go-libs/v5/pkg/types/pointer" "github.com/formancehq/orchestration/internal/storage" "github.com/formancehq/orchestration/internal/temporalworker" "github.com/formancehq/orchestration/internal/workflow" diff --git a/internal/workflow/activities.go b/internal/workflow/activities.go index b06d487..f0c49a6 100644 --- a/internal/workflow/activities.go +++ b/internal/workflow/activities.go @@ -70,12 +70,27 @@ func (a Activities) SendWorkflowStageTerminationEvent(ctx context.Context, insta func (a Activities) InsertNewInstance(ctx context.Context, workflowID string) (*Instance, error) { instance := NewInstance(activity.GetInfo(ctx).WorkflowExecution.ID, workflowID) - if _, err := a.db. + // Idempotent: the primary key is the (deterministic) workflow execution id, + // so a Temporal retry after a lost ack must not fail on the duplicate key. + // On a duplicate, reload the persisted row so timestamps from the first + // attempt are preserved. + result, err := a.db. NewInsert(). Model(&instance). - Exec(ctx); err != nil { + On("CONFLICT DO NOTHING"). + Exec(ctx) + if err != nil { + return nil, err + } + rowsAffected, err := result.RowsAffected() + if err != nil { return nil, err } + if rowsAffected == 0 { + if err := a.db.NewSelect().Model(&instance).WherePK().Scan(ctx); err != nil { + return nil, err + } + } return &instance, nil } @@ -90,11 +105,25 @@ func (a Activities) UpdateInstance(ctx context.Context, instance *Instance) erro func (a Activities) InsertNewStage(ctx context.Context, instance Instance, ind int) (*Stage, error) { stage := NewStage(instance.ID, activity.GetInfo(ctx).WorkflowExecution.RunID, ind) - if _, err := a.db.NewInsert(). + // Idempotent: the primary key is deterministic (instance id + run id + + // index), so a Temporal retry after a lost ack must not fail on the + // duplicate key. Reload duplicates to preserve the original StartedAt. + result, err := a.db.NewInsert(). Model(&stage). - Exec(ctx); err != nil { + On("CONFLICT DO NOTHING"). + Exec(ctx) + if err != nil { + return nil, err + } + rowsAffected, err := result.RowsAffected() + if err != nil { return nil, err } + if rowsAffected == 0 { + if err := a.db.NewSelect().Model(&stage).WherePK().Scan(ctx); err != nil { + return nil, err + } + } return &stage, nil } diff --git a/internal/workflow/activities/activity.go b/internal/workflow/activities/activity.go index a762050..bdd8fcd 100644 --- a/internal/workflow/activities/activity.go +++ b/internal/workflow/activities/activity.go @@ -5,7 +5,7 @@ import ( "fmt" sdk "github.com/formancehq/formance-sdk-go/v3" - "github.com/formancehq/go-libs/v3/pointer" + "github.com/formancehq/go-libs/v5/pkg/types/pointer" "github.com/formancehq/orchestration/internal/temporalworker" "github.com/pkg/errors" "go.temporal.io/sdk/activity" diff --git a/internal/workflow/activities/activity_ledger_create_transaction.go b/internal/workflow/activities/activity_ledger_create_transaction.go index bbf9436..32a6ac1 100644 --- a/internal/workflow/activities/activity_ledger_create_transaction.go +++ b/internal/workflow/activities/activity_ledger_create_transaction.go @@ -5,7 +5,7 @@ import ( stdtime "time" "github.com/formancehq/formance-sdk-go/v3/pkg/models/sdkerrors" - "github.com/formancehq/go-libs/v3/time" + "github.com/formancehq/go-libs/v5/pkg/types/time" "github.com/formancehq/formance-sdk-go/v3/pkg/models/operations" "github.com/formancehq/formance-sdk-go/v3/pkg/models/shared" diff --git a/internal/workflow/activities/activity_wallet_credit.go b/internal/workflow/activities/activity_wallet_credit.go index 0256d88..229497d 100644 --- a/internal/workflow/activities/activity_wallet_credit.go +++ b/internal/workflow/activities/activity_wallet_credit.go @@ -4,7 +4,7 @@ import ( "context" stdtime "time" - "github.com/formancehq/go-libs/v3/time" + "github.com/formancehq/go-libs/v5/pkg/types/time" "github.com/formancehq/formance-sdk-go/v3/pkg/models/operations" "github.com/formancehq/formance-sdk-go/v3/pkg/models/shared" diff --git a/internal/workflow/activities/activity_wallet_debit.go b/internal/workflow/activities/activity_wallet_debit.go index 3f84ba6..a57ba95 100644 --- a/internal/workflow/activities/activity_wallet_debit.go +++ b/internal/workflow/activities/activity_wallet_debit.go @@ -7,7 +7,7 @@ import ( "github.com/formancehq/formance-sdk-go/v3/pkg/models/sdkerrors" "github.com/pkg/errors" - "github.com/formancehq/go-libs/v3/time" + "github.com/formancehq/go-libs/v5/pkg/types/time" "github.com/formancehq/formance-sdk-go/v3/pkg/models/operations" "github.com/formancehq/formance-sdk-go/v3/pkg/models/shared" diff --git a/internal/workflow/activities/activity_wallet_list.go b/internal/workflow/activities/activity_wallet_list.go index 182d34a..5b61284 100644 --- a/internal/workflow/activities/activity_wallet_list.go +++ b/internal/workflow/activities/activity_wallet_list.go @@ -3,7 +3,7 @@ package activities import ( "context" - "github.com/formancehq/go-libs/v3/pointer" + "github.com/formancehq/go-libs/v5/pkg/types/pointer" "github.com/formancehq/formance-sdk-go/v3/pkg/models/operations" "github.com/formancehq/formance-sdk-go/v3/pkg/models/shared" diff --git a/internal/workflow/activities_test.go b/internal/workflow/activities_test.go index 413adc6..36cd0a3 100644 --- a/internal/workflow/activities_test.go +++ b/internal/workflow/activities_test.go @@ -2,13 +2,15 @@ package workflow import ( "testing" + "time" - "github.com/formancehq/go-libs/v3/bun/bundebug" + bundebug "github.com/formancehq/go-libs/v5/pkg/storage/bun/debug" "github.com/uptrace/bun" - "github.com/formancehq/go-libs/v3/bun/bunconnect" - "github.com/formancehq/go-libs/v3/logging" - "github.com/formancehq/go-libs/v3/publish" + "github.com/formancehq/go-libs/v5/pkg/messaging/publish" + "github.com/formancehq/go-libs/v5/pkg/observe/log" + bunconnect "github.com/formancehq/go-libs/v5/pkg/storage/bun/connect" + "github.com/formancehq/orchestration/internal/storage" "github.com/stretchr/testify/require" "go.temporal.io/sdk/testsuite" ) @@ -31,6 +33,7 @@ func TestActivities(t *testing.T) { publisher := publish.InMemory() activities := NewActivities(publisher, db) + require.NoError(t, storage.Migrate(logging.TestingContext(), db)) testSuite := &testsuite.WorkflowTestSuite{} env := testSuite.NewTestActivityEnvironment() @@ -38,4 +41,43 @@ func TestActivities(t *testing.T) { _, err = env.ExecuteActivity(SendWorkflowTerminationEventActivity, NewInstance("vvv", "xxx")) require.NoError(t, err) require.NotEmpty(t, publisher.AllMessages()) + + env.RegisterActivity(activities.InsertNewInstance) + workflowModel := New(Config{}) + _, err = db.NewInsert().Model(&workflowModel).Exec(logging.TestingContext()) + require.NoError(t, err) + + firstValue, err := env.ExecuteActivity(InsertNewInstanceActivity, workflowModel.ID) + require.NoError(t, err) + var firstInstance Instance + require.NoError(t, firstValue.Get(&firstInstance)) + persistedInstance := Instance{ID: firstInstance.ID} + require.NoError(t, db.NewSelect().Model(&persistedInstance).WherePK().Scan(logging.TestingContext())) + + time.Sleep(time.Millisecond) + secondValue, err := env.ExecuteActivity(InsertNewInstanceActivity, workflowModel.ID) + require.NoError(t, err) + var secondInstance Instance + require.NoError(t, secondValue.Get(&secondInstance)) + require.True(t, persistedInstance.CreatedAt.Equal(secondInstance.CreatedAt)) + require.True(t, persistedInstance.UpdatedAt.Equal(secondInstance.UpdatedAt)) + + env.RegisterActivity(activities.InsertNewStage) + firstStageValue, err := env.ExecuteActivity(InsertNewStageActivity, firstInstance, 0) + require.NoError(t, err) + var firstStage Stage + require.NoError(t, firstStageValue.Get(&firstStage)) + persistedStage := Stage{ + InstanceID: firstStage.InstanceID, + TemporalRunID: firstStage.TemporalRunID, + Number: firstStage.Number, + } + require.NoError(t, db.NewSelect().Model(&persistedStage).WherePK().Scan(logging.TestingContext())) + + time.Sleep(time.Millisecond) + secondStageValue, err := env.ExecuteActivity(InsertNewStageActivity, firstInstance, 0) + require.NoError(t, err) + var secondStage Stage + require.NoError(t, secondStageValue.Get(&secondStage)) + require.True(t, persistedStage.StartedAt.Equal(secondStage.StartedAt)) } diff --git a/internal/workflow/config.go b/internal/workflow/config.go index 90b0b9b..e485bb1 100644 --- a/internal/workflow/config.go +++ b/internal/workflow/config.go @@ -10,6 +10,20 @@ import ( "go.temporal.io/sdk/workflow" ) +// terminationContext returns a context safe for running terminal bookkeeping +// activities (status updates, termination events). When the workflow has been +// cancelled, the supplied context is already cancelled and any activity started +// on it fails immediately -- which would leave the instance/stage rows stuck +// "running" and skip the termination event. In that case a disconnected context +// is returned so the bookkeeping still runs. +func terminationContext(ctx workflow.Context) workflow.Context { + if ctx.Err() == nil { + return ctx + } + disconnected, _ := workflow.NewDisconnectedContext(ctx) + return disconnected +} + type RawStage map[string]map[string]any type Config struct { @@ -85,16 +99,20 @@ func (c *Config) run(ctx workflow.Context, instance Instance, variables map[stri } stage.SetTerminated(runError, workflow.Now(ctx).Round(time.Nanosecond)) - err = workflow.ExecuteActivity(workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ + // Record the stage termination on a context that survives cancellation, + // otherwise a cancelled stage would never be marked terminated. + cleanupCtx := terminationContext(ctx) + + err = workflow.ExecuteActivity(workflow.WithActivityOptions(cleanupCtx, workflow.ActivityOptions{ StartToCloseTimeout: 10 * time.Second, - }), UpdateStageActivity, stage).Get(ctx, nil) + }), UpdateStageActivity, stage).Get(cleanupCtx, nil) if err != nil { return err } - err = workflow.ExecuteActivity(workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ + err = workflow.ExecuteActivity(workflow.WithActivityOptions(cleanupCtx, workflow.ActivityOptions{ StartToCloseTimeout: 10 * time.Second, - }), SendWorkflowStageTerminationEventActivity, instance, stage).Get(ctx, nil) + }), SendWorkflowStageTerminationEventActivity, instance, stage).Get(cleanupCtx, nil) if err != nil { return err } diff --git a/internal/workflow/main_test.go b/internal/workflow/main_test.go index 3404e9c..f2b7fdd 100644 --- a/internal/workflow/main_test.go +++ b/internal/workflow/main_test.go @@ -4,11 +4,11 @@ import ( "context" "testing" - "github.com/formancehq/go-libs/v3/logging" - "github.com/formancehq/go-libs/v3/temporal" - "github.com/formancehq/go-libs/v3/testing/docker" - "github.com/formancehq/go-libs/v3/testing/platform/pgtesting" - "github.com/formancehq/go-libs/v3/testing/utils" + "github.com/formancehq/go-libs/v5/pkg/observe/log" + "github.com/formancehq/go-libs/v5/pkg/testing/docker" + "github.com/formancehq/go-libs/v5/pkg/testing/platform/pgtesting" + "github.com/formancehq/go-libs/v5/pkg/testing/utils" + "github.com/formancehq/go-libs/v5/pkg/workflow/temporal" "github.com/stretchr/testify/require" "go.temporal.io/sdk/testsuite" ) diff --git a/internal/workflow/manager.go b/internal/workflow/manager.go index 8460c61..3b8c588 100644 --- a/internal/workflow/manager.go +++ b/internal/workflow/manager.go @@ -7,12 +7,13 @@ import ( "fmt" "time" - "github.com/formancehq/go-libs/v3/pointer" + "github.com/formancehq/go-libs/v5/pkg/types/pointer" + common "go.temporal.io/api/common/v1" enums "go.temporal.io/api/enums/v1" history "go.temporal.io/api/history/v1" - "github.com/formancehq/go-libs/v3/bun/bunpaginate" + bunpaginate "github.com/formancehq/go-libs/v5/pkg/storage/bun/paginate" "github.com/pkg/errors" "github.com/uptrace/bun" @@ -23,6 +24,9 @@ import ( var ( ErrInstanceNotFound = errors.New("Instance not found") ErrWorkflowNotFound = errors.New("Workflow not found") + // ErrInvalidConfig wraps workflow configuration validation failures so the + // API can map them to 400 instead of 500. + ErrInvalidConfig = errors.New("invalid workflow configuration") ) const ( @@ -44,7 +48,7 @@ type WorkflowManager struct { func (m *WorkflowManager) Create(ctx context.Context, config Config) (*Workflow, error) { if err := config.Validate(); err != nil { - return nil, err + return nil, fmt.Errorf("%w: %s", ErrInvalidConfig, err) } workflow := New(config) @@ -63,7 +67,7 @@ func (m *WorkflowManager) DeleteWorkflow(ctx context.Context, id string) error { var workflow Workflow - res, err := m.db.NewUpdate().Model(&workflow).Where("id = ?", id).Set("deleted_at = ?", time.Now()).Exec(ctx) + res, err := m.db.NewUpdate().Model(&workflow).Where("id = ?", id).Where("deleted_at IS NULL").Set("deleted_at = ?", time.Now()).Exec(ctx) if err != nil { return err @@ -85,6 +89,7 @@ func (m *WorkflowManager) RunWorkflow(ctx context.Context, id string, variables workflow := Workflow{} if err := m.db.NewSelect(). Where("id = ?", id). + Where("deleted_at IS NULL"). Model(&workflow). Scan(ctx); err != nil { return nil, err @@ -117,13 +122,18 @@ func (m *WorkflowManager) RunWorkflow(ctx context.Context, id string, variables } func (m *WorkflowManager) Wait(ctx context.Context, instanceID string) error { + // The actual work runs in the detached child workflow "-main"; + // the Initiate workflow (id == instanceID) completes as soon as that child + // has started. Waiting on instanceID would therefore return immediately, + // before the run is terminated, so we wait on the running child. if err := m.temporalClient. - GetWorkflow(ctx, instanceID, ""). + GetWorkflow(ctx, instanceID+"-main", ""). Get(ctx, nil); err != nil { - if errors.Is(err, &serviceerror.NotFound{}) { + var notFound *serviceerror.NotFound + if errors.As(err, ¬Found) { return ErrInstanceNotFound } - return errors.Unwrap(err) + return err } return nil } @@ -142,6 +152,7 @@ func (m *WorkflowManager) ReadWorkflow(ctx context.Context, id string) (Workflow if err := m.db.NewSelect(). Model(&workflow). Where("id = ?", id). + Where("deleted_at IS NULL"). Scan(ctx); err != nil { return Workflow{}, err } @@ -176,7 +187,10 @@ func (m *WorkflowManager) AbortRun(ctx context.Context, instanceID string) error return errors.Wrap(err, "retrieving workflow execution") } - return m.temporalClient.CancelWorkflow(ctx, instanceID, "") + // Cancel the detached child workflow that carries the actual run; the + // Initiate workflow (id == instanceID) has already completed, so cancelling + // it would be a no-op and never reach the running stages. + return m.temporalClient.CancelWorkflow(ctx, instanceID+"-main", "") } func (m *WorkflowManager) ListInstances(ctx context.Context, pagination ListInstancesQuery) (*bunpaginate.Cursor[Instance], error) { @@ -209,6 +223,16 @@ type StageHistory struct { TerminatedAt *time.Time `json:"terminatedAt,omitempty"` } +// unmarshalFirstPayload decodes the first Temporal payload into v. It tolerates +// a nil/empty payload set (leaving v untouched) instead of panicking on an +// out-of-range index, and returns the decode error rather than panicking. +func unmarshalFirstPayload(payloads *common.Payloads, v any) error { + if payloads == nil || len(payloads.Payloads) == 0 { + return nil + } + return json.Unmarshal(payloads.Payloads[0].Data, v) +} + func (m *WorkflowManager) ReadInstanceHistory(ctx context.Context, instanceID string) ([]StageHistory, error) { historyIterator := m.temporalClient.GetWorkflowHistory(ctx, instanceID+"-main", "", @@ -223,8 +247,8 @@ func (m *WorkflowManager) ReadInstanceHistory(ctx context.Context, instanceID st case enums.EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED: attributes := event.Attributes.(*history.HistoryEvent_StartChildWorkflowExecutionInitiatedEventAttributes) input := make(map[string]any) - if err := json.Unmarshal(attributes.StartChildWorkflowExecutionInitiatedEventAttributes.Input.Payloads[0].Data, &input); err != nil { - panic(err) + if err := unmarshalFirstPayload(attributes.StartChildWorkflowExecutionInitiatedEventAttributes.Input, &input); err != nil { + return nil, errors.Wrap(err, "unmarshalling stage input") } stageHistory := StageHistory{ Name: attributes.StartChildWorkflowExecutionInitiatedEventAttributes.WorkflowType.Name, @@ -281,7 +305,7 @@ func (m *WorkflowManager) ReadStageHistory(ctx context.Context, instanceID strin if _, ok := err.(*serviceerror.NotFound); ok { return nil, ErrInstanceNotFound } - panic(err) + return nil, errors.Wrap(err, "describing workflow execution") } historyIterator := m.temporalClient.GetWorkflowHistory(ctx, stageID, "", @@ -296,8 +320,8 @@ func (m *WorkflowManager) ReadStageHistory(ctx context.Context, instanceID strin case enums.EVENT_TYPE_ACTIVITY_TASK_SCHEDULED: activityTaskScheduledEventAttributes := event.Attributes.(*history.HistoryEvent_ActivityTaskScheduledEventAttributes).ActivityTaskScheduledEventAttributes input := make(map[string]any) - if err := json.Unmarshal(activityTaskScheduledEventAttributes.Input.Payloads[0].Data, &input); err != nil { - panic(err) + if err := unmarshalFirstPayload(activityTaskScheduledEventAttributes.Input, &input); err != nil { + return nil, errors.Wrap(err, "unmarshalling activity input") } activityHistory := &ActivityHistory{ @@ -334,8 +358,8 @@ func (m *WorkflowManager) ReadStageHistory(ctx context.Context, instanceID strin result := event.Attributes.(*history.HistoryEvent_ActivityTaskCompletedEventAttributes).ActivityTaskCompletedEventAttributes.Result if result != nil && len(result.Payloads) > 0 { output := make(map[string]any) - if err := json.Unmarshal(result.Payloads[0].Data, &output); err != nil { - panic(err) + if err := unmarshalFirstPayload(result, &output); err != nil { + return nil, errors.Wrap(err, "unmarshalling activity output") } // notes(gfyrag): keep compat with format from ledger v1 (since we have moved to ledger v2 api) diff --git a/internal/workflow/manager_test.go b/internal/workflow/manager_test.go index 1ce2646..0d74d67 100644 --- a/internal/workflow/manager_test.go +++ b/internal/workflow/manager_test.go @@ -4,22 +4,44 @@ import ( "testing" "time" - "github.com/formancehq/go-libs/v3/bun/bundebug" + bundebug "github.com/formancehq/go-libs/v5/pkg/storage/bun/debug" "github.com/uptrace/bun" "go.temporal.io/sdk/worker" - "github.com/formancehq/go-libs/v3/logging" - "github.com/formancehq/go-libs/v3/publish" + "github.com/formancehq/go-libs/v5/pkg/messaging/publish" + "github.com/formancehq/go-libs/v5/pkg/observe/log" "github.com/formancehq/orchestration/internal/temporalworker" "github.com/formancehq/orchestration/internal/workflow/stages" "github.com/google/uuid" - "github.com/formancehq/go-libs/v3/bun/bunconnect" + bunconnect "github.com/formancehq/go-libs/v5/pkg/storage/bun/connect" "github.com/formancehq/orchestration/internal/storage" "github.com/stretchr/testify/require" + common "go.temporal.io/api/common/v1" ) +func TestUnmarshalFirstPayload(t *testing.T) { + t.Parallel() + + var v map[string]any + // nil and empty payload sets are tolerated (no panic, no error). + require.NoError(t, unmarshalFirstPayload(nil, &v)) + require.NoError(t, unmarshalFirstPayload(&common.Payloads{}, &v)) + + // A malformed payload returns an error instead of panicking. + err := unmarshalFirstPayload(&common.Payloads{ + Payloads: []*common.Payload{{Data: []byte("{not-json")}}, + }, &v) + require.Error(t, err) + + // A well-formed payload decodes. + require.NoError(t, unmarshalFirstPayload(&common.Payloads{ + Payloads: []*common.Payload{{Data: []byte(`{"a":"b"}`)}}, + }, &v)) + require.Equal(t, map[string]any{"a": "b"}, v) +} + func TestConfig(t *testing.T) { t.Parallel() @@ -76,3 +98,96 @@ func TestConfig(t *testing.T) { return len(updatedInstance.Statuses) == 1 }, 2*time.Second, 100*time.Millisecond) } + +func TestWait(t *testing.T) { + t.Parallel() + + database := srv.NewDatabase(t) + db, err := bunconnect.OpenSQLDB(logging.TestingContext(), bunconnect.ConnectionOptions{ + DatabaseSourceName: database.ConnString(), + }) + require.NoError(t, err) + t.Cleanup(func() { + _ = db.Close() + }) + require.NoError(t, storage.Migrate(logging.TestingContext(), db)) + + taskQueue := uuid.NewString() + w := temporalworker.New(logging.Testing(), devServer.Client(), taskQueue, + []temporalworker.DefinitionSet{ + NewWorkflows("test", false).DefinitionSet(), + temporalworker.NewDefinitionSet().Append(temporalworker.Definition{ + Name: "NoOp", + Func: (&stages.NoOp{}).GetWorkflow(), + }), + }, + []temporalworker.DefinitionSet{ + NewActivities(publish.NoOpPublisher, db).DefinitionSet(), + }, + worker.Options{}, + ) + require.NoError(t, w.Start()) + t.Cleanup(w.Stop) + + manager := NewManager(db, devServer.Client(), "test", taskQueue, false) + + t.Run("waits for the -main run to terminate", func(t *testing.T) { + config := Config{Stages: []RawStage{{"noop": map[string]any{}}}} + wf, err := manager.Create(logging.TestingContext(), config) + require.NoError(t, err) + i, err := manager.RunWorkflow(logging.TestingContext(), wf.ID, map[string]string{}) + require.NoError(t, err) + + // Wait must block on the detached "-main" child, not the Initiate + // workflow (which returns immediately). Once it returns, the instance + // must already be terminated. + require.NoError(t, manager.Wait(logging.TestingContext(), i.ID)) + + updated, err := manager.GetInstance(logging.TestingContext(), i.ID) + require.NoError(t, err) + require.True(t, updated.Terminated) + }) + + t.Run("unknown instance returns ErrInstanceNotFound", func(t *testing.T) { + err := manager.Wait(logging.TestingContext(), "does-not-exist") + require.ErrorIs(t, err, ErrInstanceNotFound) + }) +} + +func TestSoftDeletedWorkflowIsNotUsable(t *testing.T) { + t.Parallel() + + database := srv.NewDatabase(t) + db, err := bunconnect.OpenSQLDB(logging.TestingContext(), bunconnect.ConnectionOptions{ + DatabaseSourceName: database.ConnString(), + }) + require.NoError(t, err) + t.Cleanup(func() { + _ = db.Close() + }) + require.NoError(t, storage.Migrate(logging.TestingContext(), db)) + + manager := NewManager(db, devServer.Client(), "test", uuid.NewString(), false) + + w, err := manager.Create(logging.TestingContext(), Config{ + Stages: []RawStage{{"noop": map[string]any{}}}, + }) + require.NoError(t, err) + require.NoError(t, manager.DeleteWorkflow(logging.TestingContext(), w.ID)) + + t.Run("ReadWorkflow excludes it", func(t *testing.T) { + _, err := manager.ReadWorkflow(logging.TestingContext(), w.ID) + require.Error(t, err) + }) + + t.Run("RunWorkflow refuses it", func(t *testing.T) { + // The select filters deleted_at, so this fails before reaching Temporal. + _, err := manager.RunWorkflow(logging.TestingContext(), w.ID, map[string]string{}) + require.Error(t, err) + }) + + t.Run("re-delete returns not found", func(t *testing.T) { + err := manager.DeleteWorkflow(logging.TestingContext(), w.ID) + require.ErrorIs(t, err, ErrWorkflowNotFound) + }) +} diff --git a/internal/workflow/run.go b/internal/workflow/run.go index 267da96..1bc0262 100644 --- a/internal/workflow/run.go +++ b/internal/workflow/run.go @@ -74,16 +74,21 @@ func (w Workflows) Run(ctx workflow.Context, i Input, instance Instance) error { instance.SetTerminated(workflow.Now(ctx)) } - err = workflow.ExecuteActivity(workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ + // Record the instance termination on a context that survives cancellation, + // otherwise a cancelled run would never be marked terminated and no + // termination event would be published. + cleanupCtx := terminationContext(ctx) + + err = workflow.ExecuteActivity(workflow.WithActivityOptions(cleanupCtx, workflow.ActivityOptions{ StartToCloseTimeout: 10 * time.Second, - }), UpdateInstanceActivity, instance).Get(ctx, nil) + }), UpdateInstanceActivity, instance).Get(cleanupCtx, nil) if err != nil { return err } - err = workflow.ExecuteActivity(workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ + err = workflow.ExecuteActivity(workflow.WithActivityOptions(cleanupCtx, workflow.ActivityOptions{ StartToCloseTimeout: 10 * time.Second, - }), SendWorkflowTerminationEventActivity, instance).Get(ctx, nil) + }), SendWorkflowTerminationEventActivity, instance).Get(cleanupCtx, nil) if err != nil { return err } diff --git a/internal/workflow/stage.go b/internal/workflow/stage.go index 5131b63..406f1db 100644 --- a/internal/workflow/stage.go +++ b/internal/workflow/stage.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/formancehq/go-libs/v3/pointer" + "github.com/formancehq/go-libs/v5/pkg/types/pointer" "github.com/uptrace/bun" ) diff --git a/internal/workflow/stages/delay/delay.go b/internal/workflow/stages/delay/delay.go index 9ed7f82..5015f80 100644 --- a/internal/workflow/stages/delay/delay.go +++ b/internal/workflow/stages/delay/delay.go @@ -1,7 +1,7 @@ package delay import ( - "github.com/formancehq/go-libs/v3/time" + "github.com/formancehq/go-libs/v5/pkg/types/time" "github.com/formancehq/orchestration/internal/schema" "github.com/formancehq/orchestration/internal/workflow/stages" ) diff --git a/internal/workflow/stages/delay/run.go b/internal/workflow/stages/delay/run.go index a8c087c..c851f7c 100644 --- a/internal/workflow/stages/delay/run.go +++ b/internal/workflow/stages/delay/run.go @@ -1,7 +1,7 @@ package delay import ( - "github.com/formancehq/go-libs/v3/time" + "github.com/formancehq/go-libs/v5/pkg/types/time" "go.temporal.io/sdk/workflow" ) diff --git a/internal/workflow/stages/delay/run_test.go b/internal/workflow/stages/delay/run_test.go index a6e98a7..a146cca 100644 --- a/internal/workflow/stages/delay/run_test.go +++ b/internal/workflow/stages/delay/run_test.go @@ -3,7 +3,7 @@ package delay import ( "testing" - "github.com/formancehq/go-libs/v3/time" + "github.com/formancehq/go-libs/v5/pkg/types/time" "github.com/formancehq/orchestration/internal/schema" "github.com/formancehq/orchestration/internal/workflow/stages/internal/stagestesting" diff --git a/internal/workflow/stages/send/run.go b/internal/workflow/stages/send/run.go index 4270353..e024413 100644 --- a/internal/workflow/stages/send/run.go +++ b/internal/workflow/stages/send/run.go @@ -5,10 +5,10 @@ import ( "reflect" "strings" - "github.com/formancehq/go-libs/v3/time" + "github.com/formancehq/go-libs/v5/pkg/types/time" - "github.com/formancehq/go-libs/v3/collectionutils" - "github.com/formancehq/go-libs/v3/metadata" + collectionutils "github.com/formancehq/go-libs/v5/pkg/types/collections" + "github.com/formancehq/go-libs/v5/pkg/types/metadata" "github.com/formancehq/formance-sdk-go/v3/pkg/models/shared" "github.com/formancehq/orchestration/internal/workflow/activities" diff --git a/internal/workflow/stages/send/run_test.go b/internal/workflow/stages/send/run_test.go index 24a0a15..9270cd0 100644 --- a/internal/workflow/stages/send/run_test.go +++ b/internal/workflow/stages/send/run_test.go @@ -4,10 +4,10 @@ import ( "math/big" "testing" - "github.com/formancehq/go-libs/v3/time" + "github.com/formancehq/go-libs/v5/pkg/types/time" "github.com/formancehq/formance-sdk-go/v3/pkg/models/shared" - "github.com/formancehq/go-libs/v3/pointer" + "github.com/formancehq/go-libs/v5/pkg/types/pointer" "github.com/formancehq/orchestration/internal/workflow/activities" "github.com/formancehq/orchestration/internal/workflow/stages/internal/stagestesting" "github.com/stretchr/testify/mock" diff --git a/internal/workflow/stages/send/send.go b/internal/workflow/stages/send/send.go index 329db11..fa50c0e 100644 --- a/internal/workflow/stages/send/send.go +++ b/internal/workflow/stages/send/send.go @@ -2,8 +2,8 @@ package send import ( "github.com/formancehq/formance-sdk-go/v3/pkg/models/shared" - "github.com/formancehq/go-libs/v3/metadata" - "github.com/formancehq/go-libs/v3/time" + "github.com/formancehq/go-libs/v5/pkg/types/metadata" + "github.com/formancehq/go-libs/v5/pkg/types/time" "github.com/formancehq/orchestration/internal/schema" "github.com/formancehq/orchestration/internal/workflow/stages" ) diff --git a/internal/workflow/stages/wait_event/run.go b/internal/workflow/stages/wait_event/run.go index fd42208..0945489 100644 --- a/internal/workflow/stages/wait_event/run.go +++ b/internal/workflow/stages/wait_event/run.go @@ -7,16 +7,29 @@ import ( func RunWaitEvent(ctx workflow.Context, waitEvent WaitEvent) error { channel := workflow.GetSignalChannel(ctx, internalWorkflow.EventSignalName) - return workflow.Await(ctx, func() bool { + // Drain the signal channel one signal at a time until the expected event + // arrives. Using a blocking Receive loop (rather than ReceiveAsync inside + // an Await predicate) guarantees no buffered signal is consumed and + // dropped: an Await predicate is only evaluated once per workflow-task + // wakeup, so two signals delivered in the same task would leave the second + // one buffered with nothing left to re-wake the coroutine, blocking forever. + for { var signal internalWorkflow.Event - ok := channel.ReceiveAsync(&signal) - if !ok { - return false + canceled := false + selector := workflow.NewSelector(ctx) + selector.AddReceive(channel, func(channel workflow.ReceiveChannel, _ bool) { + channel.Receive(ctx, &signal) + }) + selector.AddReceive(ctx.Done(), func(workflow.ReceiveChannel, bool) { + canceled = true + }) + selector.Select(ctx) + if canceled { + return ctx.Err() } - if signal.Name != waitEvent.Event { - workflow.GetLogger(ctx).Debug("receive unexpected event", "event", signal.Name) - return false + if signal.Name == waitEvent.Event { + return nil } - return true - }) + workflow.GetLogger(ctx).Debug("received unexpected event, still waiting", "event", signal.Name) + } } diff --git a/internal/workflow/stages/wait_event/wait_event_test.go b/internal/workflow/stages/wait_event/wait_event_test.go index 7eed168..a094c02 100644 --- a/internal/workflow/stages/wait_event/wait_event_test.go +++ b/internal/workflow/stages/wait_event/wait_event_test.go @@ -6,6 +6,8 @@ import ( "github.com/formancehq/orchestration/internal/workflow" "github.com/formancehq/orchestration/internal/workflow/stages/internal/stagestesting" + "github.com/stretchr/testify/require" + "go.temporal.io/sdk/temporal" "go.temporal.io/sdk/testsuite" ) @@ -49,5 +51,41 @@ func TestWaitEvent(t *testing.T) { }}, Name: "nominal", }, + { + Stage: WaitEvent{ + Event: "test", + }, + DelayedCallbacks: []stagestesting.DelayedCallback{{ + Fn: func(environment *testsuite.TestWorkflowEnvironment) func() { + return func() { + // Two signals delivered in the same workflow task: a + // non-matching one followed by the matching one. The + // stage must consume the first, keep the second, and + // complete (the previous ReceiveAsync-in-Await + // implementation would drop the buffered match and hang). + environment.SignalWorkflow(workflow.EventSignalName, workflow.Event{ + Name: "other", + }) + environment.SignalWorkflow(workflow.EventSignalName, workflow.Event{ + Name: "test", + }) + } + }, + Duration: 100 * time.Millisecond, + }}, + Name: "ignores non-matching event delivered in the same task", + }, }...) } + +func TestWaitEventCancellation(t *testing.T) { + testSuite := &testsuite.WorkflowTestSuite{} + env := testSuite.NewTestWorkflowEnvironment() + env.RegisterDelayedCallback(env.CancelWorkflow, 100*time.Millisecond) + + env.ExecuteWorkflow(RunWaitEvent, WaitEvent{Event: "test"}) + + require.True(t, env.IsWorkflowCompleted()) + require.Error(t, env.GetWorkflowError()) + require.True(t, temporal.IsCanceledError(env.GetWorkflowError())) +} diff --git a/openapi.yaml b/openapi.yaml index a3afab3..1a33f0b 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -35,6 +35,8 @@ paths: required: false schema: type: string + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Cursor' responses: '200': description: List of triggers @@ -118,6 +120,8 @@ paths: schema: type: string required: true + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Cursor' get: summary: List triggers occurrences operationId: listTriggersOccurrences @@ -143,6 +147,9 @@ paths: description: List registered workflows tags: - orchestration.v1 + parameters: + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Cursor' responses: '200': description: List of workflows @@ -278,6 +285,8 @@ paths: type: boolean example: true required: false + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Cursor' tags: - orchestration.v1 responses: @@ -1105,8 +1114,18 @@ components: items: $ref: '#/components/schemas/Workflow' type: array + pageSize: + type: integer + hasMore: + type: boolean + previous: + type: string + next: + type: string required: - data + - pageSize + - hasMore TriggerData: type: object required: @@ -1167,8 +1186,18 @@ components: items: $ref: '#/components/schemas/TriggerOccurrence' type: array + pageSize: + type: integer + hasMore: + type: boolean + previous: + type: string + next: + type: string required: - data + - pageSize + - hasMore ListTriggersResponse: type: object properties: @@ -1176,8 +1205,18 @@ components: items: $ref: '#/components/schemas/Trigger' type: array + pageSize: + type: integer + hasMore: + type: boolean + previous: + type: string + next: + type: string required: - data + - pageSize + - hasMore CreateWorkflowRequest: $ref: '#/components/schemas/WorkflowConfig' CreateWorkflowResponse: @@ -1206,13 +1245,24 @@ components: data: $ref: '#/components/schemas/WorkflowInstance' ListRunsResponse: - required: - - data + type: object properties: data: items: $ref: '#/components/schemas/WorkflowInstance' type: array + pageSize: + type: integer + hasMore: + type: boolean + previous: + type: string + next: + type: string + required: + - data + - pageSize + - hasMore GetWorkflowResponse: type: object required: @@ -3554,6 +3604,32 @@ components: type: string error: type: string + parameters: + PageSize: + name: pageSize + in: query + description: | + The maximum number of results to return per page. + required: false + schema: + type: integer + minimum: 1 + maximum: 100 + default: 15 + format: int64 + example: 100 + Cursor: + name: cursor + in: query + description: | + Parameter used in pagination requests. + Set to the value of next for the next page of results. + Set to the value of previous for the previous page of results. + No other parameters can be set when this parameter is set. + required: false + schema: + type: string + example: aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ== responses: ErrorResponse: description: General error @@ -3567,29 +3643,6 @@ components: application/json: schema: $ref: '#/components/schemas/V2Error' - parameters: - Cursor: - name: cursor - in: query - description: | - Parameter used in pagination requests. - Set to the value of next for the next page of results. - Set to the value of previous for the previous page of results. - No other parameters can be set when this parameter is set. - schema: - type: string - example: aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ== - PageSize: - name: pageSize - in: query - description: | - The maximum number of results to return per page. - example: 100 - schema: - type: integer - format: int64 - minimum: 1 - maximum: 1000 securitySchemes: Authorization: type: oauth2 diff --git a/openapi/v1.yaml b/openapi/v1.yaml index 0b71127..b4b52a5 100644 --- a/openapi/v1.yaml +++ b/openapi/v1.yaml @@ -35,6 +35,8 @@ paths: required: false schema: type: string + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Cursor' responses: '200': description: List of triggers @@ -118,6 +120,8 @@ paths: schema: type: string required: true + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Cursor' get: summary: List triggers occurrences operationId: listTriggersOccurrences @@ -143,6 +147,9 @@ paths: description: List registered workflows tags: - orchestration.v1 + parameters: + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Cursor' responses: '200': description: List of workflows @@ -278,6 +285,8 @@ paths: type: boolean example: true required: false + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Cursor' tags: - orchestration.v1 responses: @@ -609,8 +618,18 @@ components: items: $ref: '#/components/schemas/Workflow' type: array + pageSize: + type: integer + hasMore: + type: boolean + previous: + type: string + next: + type: string required: - data + - pageSize + - hasMore TriggerData: type: object required: @@ -671,8 +690,18 @@ components: items: $ref: '#/components/schemas/TriggerOccurrence' type: array + pageSize: + type: integer + hasMore: + type: boolean + previous: + type: string + next: + type: string required: - data + - pageSize + - hasMore ListTriggersResponse: type: object properties: @@ -680,8 +709,18 @@ components: items: $ref: '#/components/schemas/Trigger' type: array + pageSize: + type: integer + hasMore: + type: boolean + previous: + type: string + next: + type: string required: - data + - pageSize + - hasMore CreateWorkflowRequest: $ref: '#/components/schemas/WorkflowConfig' CreateWorkflowResponse: @@ -710,13 +749,24 @@ components: data: $ref: '#/components/schemas/WorkflowInstance' ListRunsResponse: - required: - - data + type: object properties: data: items: $ref: '#/components/schemas/WorkflowInstance' type: array + pageSize: + type: integer + hasMore: + type: boolean + previous: + type: string + next: + type: string + required: + - data + - pageSize + - hasMore GetWorkflowResponse: type: object required: @@ -1701,6 +1751,24 @@ components: input: 100 output: 10 balance: 90 + parameters: + PageSize: + name: pageSize + in: query + description: The maximum number of results to return per page. + required: false + schema: + type: integer + minimum: 1 + maximum: 100 + default: 15 + Cursor: + name: cursor + in: query + description: The continuation cursor returned by the previous page. + required: false + schema: + type: string responses: ErrorResponse: description: General error diff --git a/openapi/v2.yaml b/openapi/v2.yaml index 0f87545..97f2569 100644 --- a/openapi/v2.yaml +++ b/openapi/v2.yaml @@ -519,7 +519,7 @@ components: type: integer format: int64 minimum: 1 - maximum: 1000 + maximum: 100 schemas: V2ServerInfo: type: object diff --git a/pkg/client/.speakeasy/gen.lock b/pkg/client/.speakeasy/gen.lock index cbc786c..dd84bf3 100644 --- a/pkg/client/.speakeasy/gen.lock +++ b/pkg/client/.speakeasy/gen.lock @@ -1,12 +1,12 @@ lockVersion: 2.0.0 id: e26e5b37-4391-4462-9a20-c517a83a166a management: - docChecksum: dfc3ed9eb806fc9e8c6629a3d2a0a637 + docChecksum: aec6b9bcce4eb2538e129ed17e2a5589 docVersion: 0.1.0 speakeasyVersion: 1.351.0 generationVersion: 2.384.1 - releaseVersion: 0.1.4 - configChecksum: c78571211c83f4afeeaed43ac964a7c8 + releaseVersion: 0.1.7 + configChecksum: ef44e4268063f8dbf9a2b5a9ec677b65 features: go: additionalDependencies: 0.1.0 @@ -157,6 +157,7 @@ generatedFiles: - /models/components/creditwalletrequest.go - /models/components/activityconfirmhold.go - /models/components/activitygetpayment.go + - /models/components/activitycreatetransferinitiation.go - /models/components/activitystripetransfer.go - /models/components/activityreverttransaction.go - /models/components/activitycreatetransaction.go @@ -236,6 +237,7 @@ generatedFiles: - /models/components/v2creditwalletrequest.go - /models/components/v2activityconfirmhold.go - /models/components/v2activitygetpayment.go + - /models/components/v2activitycreatetransferinitiation.go - /models/components/v2activitystripetransfer.go - /models/components/v2activitycreatetransaction.go - /models/components/v2posttransaction.go @@ -254,6 +256,7 @@ generatedFiles: - docs/models/operations/deletetriggerresponse.md - docs/models/operations/listtriggersoccurrencesrequest.md - docs/models/operations/listtriggersoccurrencesresponse.md + - docs/models/operations/listworkflowsrequest.md - docs/models/operations/listworkflowsresponse.md - docs/models/operations/createworkflowresponse.md - docs/models/operations/getworkflowrequest.md @@ -338,6 +341,7 @@ generatedFiles: - docs/models/components/stagesendsourceaccount.md - docs/models/components/stagesendsourcewallet.md - docs/models/components/stagesenddestination.md + - docs/models/components/type.md - docs/models/components/stagesenddestinationpayment.md - docs/models/components/stagesenddestinationaccount.md - docs/models/components/stagesenddestinationwallet.md @@ -362,7 +366,7 @@ generatedFiles: - docs/models/components/ledgeraccountsubject.md - docs/models/components/walletsubject.md - docs/models/components/activitygetpaymentoutput.md - - docs/models/components/type.md + - docs/models/components/paymenttype.md - docs/models/components/scheme.md - docs/models/components/raw.md - docs/models/components/payment.md @@ -388,6 +392,9 @@ generatedFiles: - docs/models/components/creditwalletrequest.md - docs/models/components/activityconfirmhold.md - docs/models/components/activitygetpayment.md + - docs/models/components/activitycreatetransferinitiationtype.md + - docs/models/components/activitycreatetransferinitiationmetadata.md + - docs/models/components/activitycreatetransferinitiation.md - docs/models/components/metadata.md - docs/models/components/activitystripetransfer.md - docs/models/components/activityreverttransaction.md @@ -432,6 +439,7 @@ generatedFiles: - docs/models/components/v2stagesendsourceaccount.md - docs/models/components/v2stagesendsourcewallet.md - docs/models/components/v2stagesenddestination.md + - docs/models/components/v2stagesenddestinationpaymenttype.md - docs/models/components/v2stagesenddestinationpayment.md - docs/models/components/v2stagesenddestinationaccount.md - docs/models/components/v2stagesenddestinationwallet.md @@ -481,6 +489,9 @@ generatedFiles: - docs/models/components/v2creditwalletrequest.md - docs/models/components/v2activityconfirmhold.md - docs/models/components/v2activitygetpayment.md + - docs/models/components/v2activitycreatetransferinitiationtype.md + - docs/models/components/v2activitycreatetransferinitiationmetadata.md + - docs/models/components/v2activitycreatetransferinitiation.md - docs/models/components/v2activitystripetransfermetadata.md - docs/models/components/v2activitystripetransfer.md - docs/models/components/v2activitycreatetransaction.md diff --git a/pkg/client/.speakeasy/gen.yaml b/pkg/client/.speakeasy/gen.yaml index cf3767f..091373d 100644 --- a/pkg/client/.speakeasy/gen.yaml +++ b/pkg/client/.speakeasy/gen.yaml @@ -12,7 +12,7 @@ generation: auth: oAuth2ClientCredentialsEnabled: true go: - version: 0.1.4 + version: 0.1.7 additionalDependencies: {} allowUnknownFieldsInWeakUnions: false clientServerStatusCodesAsErrors: true @@ -29,5 +29,5 @@ go: maxMethodParams: 0 methodArguments: require-security-and-request outputModelSuffix: output - packageName: openapi + packageName: github.com/formancehq/flows/pkg/client responseFormat: envelope-http diff --git a/pkg/client/README.md b/pkg/client/README.md index 1860f83..beb8f5b 100644 --- a/pkg/client/README.md +++ b/pkg/client/README.md @@ -20,7 +20,7 @@ It has been generated successfully based on your OpenAPI spec. However, it is no ## SDK Installation ```bash -go get openapi +go get github.com/formancehq/flows/pkg/client ``` @@ -34,14 +34,14 @@ package main import ( "context" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/components" "log" - "openapi" - "openapi/models/components" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -116,16 +116,16 @@ package main import ( "context" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client/retry" "log" "models/operations" - "openapi" - "openapi/models/components" - "openapi/retry" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -159,15 +159,15 @@ package main import ( "context" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client/retry" "log" - "openapi" - "openapi/models/components" - "openapi/retry" ) func main() { - s := openapi.New( - openapi.WithRetryConfig( + s := client.New( + client.WithRetryConfig( retry.Config{ Strategy: "backoff", Backoff: &retry.BackoffStrategy{ @@ -178,7 +178,7 @@ func main() { }, RetryConnectionErrors: false, }), - openapi.WithSecurity(components.Security{ + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -215,15 +215,15 @@ package main import ( "context" "errors" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client/models/sdkerrors" "log" - "openapi" - "openapi/models/components" - "openapi/models/sdkerrors" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -268,15 +268,15 @@ package main import ( "context" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/components" "log" - "openapi" - "openapi/models/components" ) func main() { - s := openapi.New( - openapi.WithServerIndex(0), - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithServerIndex(0), + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -303,15 +303,15 @@ package main import ( "context" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/components" "log" - "openapi" - "openapi/models/components" ) func main() { - s := openapi.New( - openapi.WithServerURL("http://localhost:8080/"), - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithServerURL("http://localhost:8080/"), + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -377,14 +377,14 @@ package main import ( "context" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/components" "log" - "openapi" - "openapi/models/components" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), diff --git a/pkg/client/USAGE.md b/pkg/client/USAGE.md index 8ad1029..5fedf1a 100644 --- a/pkg/client/USAGE.md +++ b/pkg/client/USAGE.md @@ -4,14 +4,14 @@ package main import ( "context" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/components" "log" - "openapi" - "openapi/models/components" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), diff --git a/pkg/client/docs/models/components/activitycreatetransferinitiation.md b/pkg/client/docs/models/components/activitycreatetransferinitiation.md new file mode 100644 index 0000000..f6cf9ed --- /dev/null +++ b/pkg/client/docs/models/components/activitycreatetransferinitiation.md @@ -0,0 +1,17 @@ +# ActivityCreateTransferInitiation + + +## Fields + +| Field | Type | Required | Description | Example | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `ConnectorID` | **string* | :heavy_minus_sign: | N/A | | +| `Provider` | **string* | :heavy_minus_sign: | Payment service provider name (e.g., stripe, wise, mangopay).
Validated by the Payments service based on installed connectors.
| stripe | +| `Amount` | [*big.Int](https://pkg.go.dev/math/big#Int) | :heavy_minus_sign: | N/A | 100 | +| `Asset` | **string* | :heavy_minus_sign: | N/A | USD | +| `Destination` | **string* | :heavy_minus_sign: | Destination account ID | acct_1Gqj58KZcSIg2N2q | +| `Source` | **string* | :heavy_minus_sign: | Source account ID (required for TRANSFER type) | | +| `Type` | [*components.ActivityCreateTransferInitiationType](../../models/components/activitycreatetransferinitiationtype.md) | :heavy_minus_sign: | Type of transfer initiation:
- TRANSFER: Internal to internal account transfer
- PAYOUT: Internal to external account payout
| | +| `Description` | **string* | :heavy_minus_sign: | Description for the transfer initiation | | +| `WaitingValidation` | **bool* | :heavy_minus_sign: | N/A | false | +| `Metadata` | [*components.ActivityCreateTransferInitiationMetadata](../../models/components/activitycreatetransferinitiationmetadata.md) | :heavy_minus_sign: | 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.
| {
"order_id": "6735"
} | \ No newline at end of file diff --git a/pkg/client/docs/models/components/activitycreatetransferinitiationmetadata.md b/pkg/client/docs/models/components/activitycreatetransferinitiationmetadata.md new file mode 100644 index 0000000..e6c69e5 --- /dev/null +++ b/pkg/client/docs/models/components/activitycreatetransferinitiationmetadata.md @@ -0,0 +1,10 @@ +# ActivityCreateTransferInitiationMetadata + +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. + + + +## Fields + +| Field | Type | Required | Description | +| ----------- | ----------- | ----------- | ----------- | \ No newline at end of file diff --git a/pkg/client/docs/models/components/activitycreatetransferinitiationtype.md b/pkg/client/docs/models/components/activitycreatetransferinitiationtype.md new file mode 100644 index 0000000..011d457 --- /dev/null +++ b/pkg/client/docs/models/components/activitycreatetransferinitiationtype.md @@ -0,0 +1,14 @@ +# ActivityCreateTransferInitiationType + +Type of transfer initiation: +- TRANSFER: Internal to internal account transfer +- PAYOUT: Internal to external account payout + + + +## Values + +| Name | Value | +| ---------------------------------------------- | ---------------------------------------------- | +| `ActivityCreateTransferInitiationTypeTransfer` | TRANSFER | +| `ActivityCreateTransferInitiationTypePayout` | PAYOUT | \ No newline at end of file diff --git a/pkg/client/docs/models/components/listrunsresponse.md b/pkg/client/docs/models/components/listrunsresponse.md index 466213c..0460049 100644 --- a/pkg/client/docs/models/components/listrunsresponse.md +++ b/pkg/client/docs/models/components/listrunsresponse.md @@ -5,4 +5,8 @@ | Field | Type | Required | Description | | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -| `Data` | [][components.WorkflowInstance](../../models/components/workflowinstance.md) | :heavy_check_mark: | N/A | \ No newline at end of file +| `Data` | [][components.WorkflowInstance](../../models/components/workflowinstance.md) | :heavy_check_mark: | N/A | +| `PageSize` | *int64* | :heavy_check_mark: | N/A | +| `HasMore` | *bool* | :heavy_check_mark: | N/A | +| `Previous` | **string* | :heavy_minus_sign: | N/A | +| `Next` | **string* | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/pkg/client/docs/models/components/listtriggersoccurrencesresponse.md b/pkg/client/docs/models/components/listtriggersoccurrencesresponse.md index 367c2f8..8342e37 100644 --- a/pkg/client/docs/models/components/listtriggersoccurrencesresponse.md +++ b/pkg/client/docs/models/components/listtriggersoccurrencesresponse.md @@ -5,4 +5,8 @@ | Field | Type | Required | Description | | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | -| `Data` | [][components.TriggerOccurrence](../../models/components/triggeroccurrence.md) | :heavy_check_mark: | N/A | \ No newline at end of file +| `Data` | [][components.TriggerOccurrence](../../models/components/triggeroccurrence.md) | :heavy_check_mark: | N/A | +| `PageSize` | *int64* | :heavy_check_mark: | N/A | +| `HasMore` | *bool* | :heavy_check_mark: | N/A | +| `Previous` | **string* | :heavy_minus_sign: | N/A | +| `Next` | **string* | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/pkg/client/docs/models/components/listtriggersresponse.md b/pkg/client/docs/models/components/listtriggersresponse.md index bf4eae6..94ea974 100644 --- a/pkg/client/docs/models/components/listtriggersresponse.md +++ b/pkg/client/docs/models/components/listtriggersresponse.md @@ -5,4 +5,8 @@ | Field | Type | Required | Description | | ---------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------- | -| `Data` | [][components.Trigger](../../models/components/trigger.md) | :heavy_check_mark: | N/A | \ No newline at end of file +| `Data` | [][components.Trigger](../../models/components/trigger.md) | :heavy_check_mark: | N/A | +| `PageSize` | *int64* | :heavy_check_mark: | N/A | +| `HasMore` | *bool* | :heavy_check_mark: | N/A | +| `Previous` | **string* | :heavy_minus_sign: | N/A | +| `Next` | **string* | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/pkg/client/docs/models/components/listworkflowsresponse.md b/pkg/client/docs/models/components/listworkflowsresponse.md index d521bec..c80cdce 100644 --- a/pkg/client/docs/models/components/listworkflowsresponse.md +++ b/pkg/client/docs/models/components/listworkflowsresponse.md @@ -5,4 +5,8 @@ | Field | Type | Required | Description | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | -| `Data` | [][components.Workflow](../../models/components/workflow.md) | :heavy_check_mark: | N/A | \ No newline at end of file +| `Data` | [][components.Workflow](../../models/components/workflow.md) | :heavy_check_mark: | N/A | +| `PageSize` | *int64* | :heavy_check_mark: | N/A | +| `HasMore` | *bool* | :heavy_check_mark: | N/A | +| `Previous` | **string* | :heavy_minus_sign: | N/A | +| `Next` | **string* | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/pkg/client/docs/models/components/payment.md b/pkg/client/docs/models/components/payment.md index 99f603e..dc3f49a 100644 --- a/pkg/client/docs/models/components/payment.md +++ b/pkg/client/docs/models/components/payment.md @@ -11,7 +11,7 @@ | `DestinationAccountID` | *string* | :heavy_check_mark: | N/A | | | `ConnectorID` | *string* | :heavy_check_mark: | N/A | | | `Provider` | [*components.Connector](../../models/components/connector.md) | :heavy_minus_sign: | N/A | | -| `Type` | [components.Type](../../models/components/type.md) | :heavy_check_mark: | N/A | | +| `Type` | [components.PaymentType](../../models/components/paymenttype.md) | :heavy_check_mark: | N/A | | | `Status` | [components.PaymentStatus](../../models/components/paymentstatus.md) | :heavy_check_mark: | N/A | | | `InitialAmount` | [*big.Int](https://pkg.go.dev/math/big#Int) | :heavy_check_mark: | N/A | 100 | | `Scheme` | [components.Scheme](../../models/components/scheme.md) | :heavy_check_mark: | N/A | | diff --git a/pkg/client/docs/models/components/paymenttype.md b/pkg/client/docs/models/components/paymenttype.md new file mode 100644 index 0000000..dfc9842 --- /dev/null +++ b/pkg/client/docs/models/components/paymenttype.md @@ -0,0 +1,11 @@ +# PaymentType + + +## Values + +| Name | Value | +| --------------------- | --------------------- | +| `PaymentTypePayIn` | PAY-IN | +| `PaymentTypePayout` | PAYOUT | +| `PaymentTypeTransfer` | TRANSFER | +| `PaymentTypeOther` | OTHER | \ No newline at end of file diff --git a/pkg/client/docs/models/components/stagesenddestinationaccount.md b/pkg/client/docs/models/components/stagesenddestinationaccount.md index de0e831..7b32335 100644 --- a/pkg/client/docs/models/components/stagesenddestinationaccount.md +++ b/pkg/client/docs/models/components/stagesenddestinationaccount.md @@ -3,7 +3,9 @@ ## Fields -| Field | Type | Required | Description | -| ------------------ | ------------------ | ------------------ | ------------------ | -| `ID` | *string* | :heavy_check_mark: | N/A | -| `Ledger` | **string* | :heavy_minus_sign: | N/A | \ No newline at end of file +| Field | Type | Required | Description | Example | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ID` | *string* | :heavy_check_mark: | N/A | | +| `Ledger` | **string* | :heavy_minus_sign: | N/A | | +| `ThroughAccount` | **string* | :heavy_minus_sign: | Account used when this ledger account interacts with external systems (payments, cross-ledger).
- As SOURCE going to payment: funds are sent to this account (e.g., "liabilities:payouts-pending")
- As DESTINATION from payment: funds come from this account (e.g., "assets:stripe:incoming")
- For cross-ledger transfers: replaces "world" on both sides
| liabilities:payouts-pending | +| `AllowOverdraft` | **bool* | :heavy_minus_sign: | Enables unbounded overdraft on the throughAccount when set to true.
This is useful when the throughAccount represents a liability or bridge account
that needs to go negative (e.g., "liabilities:payouts-pending").
Only applies when throughAccount is not "world" (which already has unbounded overdraft).
| true | \ No newline at end of file diff --git a/pkg/client/docs/models/components/stagesenddestinationpayment.md b/pkg/client/docs/models/components/stagesenddestinationpayment.md index 44bfc23..9505c0b 100644 --- a/pkg/client/docs/models/components/stagesenddestinationpayment.md +++ b/pkg/client/docs/models/components/stagesenddestinationpayment.md @@ -3,6 +3,8 @@ ## Fields -| Field | Type | Required | Description | -| ------------------ | ------------------ | ------------------ | ------------------ | -| `Psp` | *string* | :heavy_check_mark: | N/A | \ No newline at end of file +| Field | Type | Required | Description | Example | +| -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Psp` | *string* | :heavy_check_mark: | Payment service provider name (e.g., stripe, wise, mangopay).
Validated by the Payments service based on installed connectors.
| stripe | +| `Type` | [*components.Type](../../models/components/type.md) | :heavy_minus_sign: | Type of transfer initiation:
- TRANSFER: Internal to internal account transfer
- PAYOUT: Internal to external account payout
| PAYOUT | +| `SourceAccount` | **string* | :heavy_minus_sign: | Formance Payments account ID for the source (internal PSP account).
If not specified, the Payments service may use a default account for the connector.
| | \ No newline at end of file diff --git a/pkg/client/docs/models/components/stagesendsourceaccount.md b/pkg/client/docs/models/components/stagesendsourceaccount.md index 2330e55..1b851ff 100644 --- a/pkg/client/docs/models/components/stagesendsourceaccount.md +++ b/pkg/client/docs/models/components/stagesendsourceaccount.md @@ -3,7 +3,9 @@ ## Fields -| Field | Type | Required | Description | -| ------------------ | ------------------ | ------------------ | ------------------ | -| `ID` | *string* | :heavy_check_mark: | N/A | -| `Ledger` | **string* | :heavy_minus_sign: | N/A | \ No newline at end of file +| Field | Type | Required | Description | Example | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ID` | *string* | :heavy_check_mark: | N/A | | +| `Ledger` | **string* | :heavy_minus_sign: | N/A | | +| `ThroughAccount` | **string* | :heavy_minus_sign: | Account used when this ledger account interacts with external systems (payments, cross-ledger).
- As SOURCE going to payment: funds are sent to this account (e.g., "liabilities:payouts-pending")
- As DESTINATION from payment: funds come from this account (e.g., "assets:stripe:incoming")
- For cross-ledger transfers: replaces "world" on both sides
| liabilities:payouts-pending | +| `AllowOverdraft` | **bool* | :heavy_minus_sign: | Enables unbounded overdraft on the throughAccount when set to true.
This is useful when the throughAccount represents a liability or bridge account
that needs to go negative (e.g., "liabilities:payouts-pending").
Only applies when throughAccount is not "world" (which already has unbounded overdraft).
| true | \ No newline at end of file diff --git a/pkg/client/docs/models/components/stagesendsourcepayment.md b/pkg/client/docs/models/components/stagesendsourcepayment.md index e884994..37cda39 100644 --- a/pkg/client/docs/models/components/stagesendsourcepayment.md +++ b/pkg/client/docs/models/components/stagesendsourcepayment.md @@ -3,6 +3,10 @@ ## Fields -| Field | Type | Required | Description | -| ------------------ | ------------------ | ------------------ | ------------------ | -| `ID` | *string* | :heavy_check_mark: | N/A | \ No newline at end of file +| Field | Type | Required | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ID` | *string* | :heavy_check_mark: | N/A | +| `Ledger` | **string* | :heavy_minus_sign: | Ledger to use for payment ingestion.
Defaults to the internal orchestration ledger.
| +| `HoldingAccount` | **string* | :heavy_minus_sign: | Intermediate account where payment funds are held.
Defaults to "payment:{paymentID}" format.
| +| `ThroughAccount` | **string* | :heavy_minus_sign: | Source account for the payment ingestion transaction.
Defaults to "world".
| +| `AllowOverdraft` | **bool* | :heavy_minus_sign: | Enables unbounded overdraft on the throughAccount when set to true.
Only applies when throughAccount is not "world" (which already has unbounded overdraft).
| \ No newline at end of file diff --git a/pkg/client/docs/models/components/type.md b/pkg/client/docs/models/components/type.md index 5c514be..0b56eb1 100644 --- a/pkg/client/docs/models/components/type.md +++ b/pkg/client/docs/models/components/type.md @@ -1,11 +1,14 @@ # Type +Type of transfer initiation: +- TRANSFER: Internal to internal account transfer +- PAYOUT: Internal to external account payout + + ## Values | Name | Value | | -------------- | -------------- | -| `TypePayIn` | PAY-IN | -| `TypePayout` | PAYOUT | | `TypeTransfer` | TRANSFER | -| `TypeOther` | OTHER | \ No newline at end of file +| `TypePayout` | PAYOUT | \ No newline at end of file diff --git a/pkg/client/docs/models/components/v2activitycreatetransferinitiation.md b/pkg/client/docs/models/components/v2activitycreatetransferinitiation.md new file mode 100644 index 0000000..3d2badf --- /dev/null +++ b/pkg/client/docs/models/components/v2activitycreatetransferinitiation.md @@ -0,0 +1,17 @@ +# V2ActivityCreateTransferInitiation + + +## Fields + +| Field | Type | Required | Description | Example | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `ConnectorID` | **string* | :heavy_minus_sign: | N/A | | +| `Provider` | **string* | :heavy_minus_sign: | Payment service provider name (e.g., stripe, wise, mangopay).
Validated by the Payments service based on installed connectors.
| stripe | +| `Amount` | [*big.Int](https://pkg.go.dev/math/big#Int) | :heavy_minus_sign: | N/A | 100 | +| `Asset` | **string* | :heavy_minus_sign: | N/A | USD | +| `Destination` | **string* | :heavy_minus_sign: | Destination account ID | acct_1Gqj58KZcSIg2N2q | +| `Source` | **string* | :heavy_minus_sign: | Source account ID (required for TRANSFER type) | | +| `Type` | [*components.V2ActivityCreateTransferInitiationType](../../models/components/v2activitycreatetransferinitiationtype.md) | :heavy_minus_sign: | Type of transfer initiation:
- TRANSFER: Internal to internal account transfer
- PAYOUT: Internal to external account payout
| | +| `Description` | **string* | :heavy_minus_sign: | Description for the transfer initiation | | +| `WaitingValidation` | **bool* | :heavy_minus_sign: | N/A | false | +| `Metadata` | [*components.V2ActivityCreateTransferInitiationMetadata](../../models/components/v2activitycreatetransferinitiationmetadata.md) | :heavy_minus_sign: | 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.
| {
"order_id": "6735"
} | \ No newline at end of file diff --git a/pkg/client/docs/models/components/v2activitycreatetransferinitiationmetadata.md b/pkg/client/docs/models/components/v2activitycreatetransferinitiationmetadata.md new file mode 100644 index 0000000..58cc736 --- /dev/null +++ b/pkg/client/docs/models/components/v2activitycreatetransferinitiationmetadata.md @@ -0,0 +1,10 @@ +# V2ActivityCreateTransferInitiationMetadata + +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. + + + +## Fields + +| Field | Type | Required | Description | +| ----------- | ----------- | ----------- | ----------- | \ No newline at end of file diff --git a/pkg/client/docs/models/components/v2activitycreatetransferinitiationtype.md b/pkg/client/docs/models/components/v2activitycreatetransferinitiationtype.md new file mode 100644 index 0000000..edf2150 --- /dev/null +++ b/pkg/client/docs/models/components/v2activitycreatetransferinitiationtype.md @@ -0,0 +1,14 @@ +# V2ActivityCreateTransferInitiationType + +Type of transfer initiation: +- TRANSFER: Internal to internal account transfer +- PAYOUT: Internal to external account payout + + + +## Values + +| Name | Value | +| ------------------------------------------------ | ------------------------------------------------ | +| `V2ActivityCreateTransferInitiationTypeTransfer` | TRANSFER | +| `V2ActivityCreateTransferInitiationTypePayout` | PAYOUT | \ No newline at end of file diff --git a/pkg/client/docs/models/components/v2stagesenddestinationaccount.md b/pkg/client/docs/models/components/v2stagesenddestinationaccount.md index 09f432f..fc0e7bd 100644 --- a/pkg/client/docs/models/components/v2stagesenddestinationaccount.md +++ b/pkg/client/docs/models/components/v2stagesenddestinationaccount.md @@ -3,7 +3,9 @@ ## Fields -| Field | Type | Required | Description | -| ------------------ | ------------------ | ------------------ | ------------------ | -| `ID` | *string* | :heavy_check_mark: | N/A | -| `Ledger` | **string* | :heavy_minus_sign: | N/A | \ No newline at end of file +| Field | Type | Required | Description | Example | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ID` | *string* | :heavy_check_mark: | N/A | | +| `Ledger` | **string* | :heavy_minus_sign: | N/A | | +| `ThroughAccount` | **string* | :heavy_minus_sign: | Account used when this ledger account interacts with external systems (payments, cross-ledger).
- As SOURCE going to payment: funds are sent to this account (e.g., "liabilities:payouts-pending")
- As DESTINATION from payment: funds come from this account (e.g., "assets:stripe:incoming")
- For cross-ledger transfers: replaces "world" on both sides
| liabilities:payouts-pending | +| `AllowOverdraft` | **bool* | :heavy_minus_sign: | Enables unbounded overdraft on the throughAccount when set to true.
This is useful when the throughAccount represents a liability or bridge account
that needs to go negative (e.g., "liabilities:payouts-pending").
Only applies when throughAccount is not "world" (which already has unbounded overdraft).
| true | \ No newline at end of file diff --git a/pkg/client/docs/models/components/v2stagesenddestinationpayment.md b/pkg/client/docs/models/components/v2stagesenddestinationpayment.md index e37e485..680d9a2 100644 --- a/pkg/client/docs/models/components/v2stagesenddestinationpayment.md +++ b/pkg/client/docs/models/components/v2stagesenddestinationpayment.md @@ -3,6 +3,8 @@ ## Fields -| Field | Type | Required | Description | -| ------------------ | ------------------ | ------------------ | ------------------ | -| `Psp` | *string* | :heavy_check_mark: | N/A | \ No newline at end of file +| Field | Type | Required | Description | Example | +| -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Psp` | *string* | :heavy_check_mark: | Payment service provider name (e.g., stripe, wise, mangopay).
Validated by the Payments service based on installed connectors.
| stripe | +| `Type` | [*components.V2StageSendDestinationPaymentType](../../models/components/v2stagesenddestinationpaymenttype.md) | :heavy_minus_sign: | Type of transfer initiation:
- TRANSFER: Internal to internal account transfer
- PAYOUT: Internal to external account payout
| PAYOUT | +| `SourceAccount` | **string* | :heavy_minus_sign: | Formance Payments account ID for the source (internal PSP account).
If not specified, the Payments service may use a default account for the connector.
| | \ No newline at end of file diff --git a/pkg/client/docs/models/components/v2stagesenddestinationpaymenttype.md b/pkg/client/docs/models/components/v2stagesenddestinationpaymenttype.md new file mode 100644 index 0000000..a1e9bdd --- /dev/null +++ b/pkg/client/docs/models/components/v2stagesenddestinationpaymenttype.md @@ -0,0 +1,14 @@ +# V2StageSendDestinationPaymentType + +Type of transfer initiation: +- TRANSFER: Internal to internal account transfer +- PAYOUT: Internal to external account payout + + + +## Values + +| Name | Value | +| ------------------------------------------- | ------------------------------------------- | +| `V2StageSendDestinationPaymentTypeTransfer` | TRANSFER | +| `V2StageSendDestinationPaymentTypePayout` | PAYOUT | \ No newline at end of file diff --git a/pkg/client/docs/models/components/v2stagesendsourceaccount.md b/pkg/client/docs/models/components/v2stagesendsourceaccount.md index e590db4..4b304ba 100644 --- a/pkg/client/docs/models/components/v2stagesendsourceaccount.md +++ b/pkg/client/docs/models/components/v2stagesendsourceaccount.md @@ -3,7 +3,9 @@ ## Fields -| Field | Type | Required | Description | -| ------------------ | ------------------ | ------------------ | ------------------ | -| `ID` | *string* | :heavy_check_mark: | N/A | -| `Ledger` | **string* | :heavy_minus_sign: | N/A | \ No newline at end of file +| Field | Type | Required | Description | Example | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ID` | *string* | :heavy_check_mark: | N/A | | +| `Ledger` | **string* | :heavy_minus_sign: | N/A | | +| `ThroughAccount` | **string* | :heavy_minus_sign: | Account used when this ledger account interacts with external systems (payments, cross-ledger).
- As SOURCE going to payment: funds are sent to this account (e.g., "liabilities:payouts-pending")
- As DESTINATION from payment: funds come from this account (e.g., "assets:stripe:incoming")
- For cross-ledger transfers: replaces "world" on both sides
| liabilities:payouts-pending | +| `AllowOverdraft` | **bool* | :heavy_minus_sign: | Enables unbounded overdraft on the throughAccount when set to true.
This is useful when the throughAccount represents a liability or bridge account
that needs to go negative (e.g., "liabilities:payouts-pending").
Only applies when throughAccount is not "world" (which already has unbounded overdraft).
| true | \ No newline at end of file diff --git a/pkg/client/docs/models/components/v2stagesendsourcepayment.md b/pkg/client/docs/models/components/v2stagesendsourcepayment.md index 2548411..3e7f9c8 100644 --- a/pkg/client/docs/models/components/v2stagesendsourcepayment.md +++ b/pkg/client/docs/models/components/v2stagesendsourcepayment.md @@ -3,6 +3,10 @@ ## Fields -| Field | Type | Required | Description | -| ------------------ | ------------------ | ------------------ | ------------------ | -| `ID` | *string* | :heavy_check_mark: | N/A | \ No newline at end of file +| Field | Type | Required | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ID` | *string* | :heavy_check_mark: | N/A | +| `Ledger` | **string* | :heavy_minus_sign: | Ledger to use for payment ingestion.
Defaults to the internal orchestration ledger.
| +| `HoldingAccount` | **string* | :heavy_minus_sign: | Intermediate account where payment funds are held.
Defaults to "payment:{paymentID}" format.
| +| `ThroughAccount` | **string* | :heavy_minus_sign: | Source account for the payment ingestion transaction.
Defaults to "world".
| +| `AllowOverdraft` | **bool* | :heavy_minus_sign: | Enables unbounded overdraft on the throughAccount when set to true.
Only applies when throughAccount is not "world" (which already has unbounded overdraft).
| \ No newline at end of file diff --git a/pkg/client/docs/models/components/v2workflowinstancehistorystageinput.md b/pkg/client/docs/models/components/v2workflowinstancehistorystageinput.md index e4737f7..8173184 100644 --- a/pkg/client/docs/models/components/v2workflowinstancehistorystageinput.md +++ b/pkg/client/docs/models/components/v2workflowinstancehistorystageinput.md @@ -3,16 +3,17 @@ ## Fields -| Field | Type | Required | Description | -| --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | -| `GetAccount` | [*components.V2ActivityGetAccount](../../models/components/v2activitygetaccount.md) | :heavy_minus_sign: | N/A | -| `AddAccountMetadata` | [*components.V2ActivityAddAccountMetadata](../../models/components/v2activityaddaccountmetadata.md) | :heavy_minus_sign: | N/A | -| `CreateTransaction` | [*components.V2ActivityCreateTransaction](../../models/components/v2activitycreatetransaction.md) | :heavy_minus_sign: | N/A | -| `StripeTransfer` | [*components.V2ActivityStripeTransfer](../../models/components/v2activitystripetransfer.md) | :heavy_minus_sign: | N/A | -| `GetPayment` | [*components.V2ActivityGetPayment](../../models/components/v2activitygetpayment.md) | :heavy_minus_sign: | N/A | -| `ConfirmHold` | [*components.V2ActivityConfirmHold](../../models/components/v2activityconfirmhold.md) | :heavy_minus_sign: | N/A | -| `CreditWallet` | [*components.V2ActivityCreditWallet](../../models/components/v2activitycreditwallet.md) | :heavy_minus_sign: | N/A | -| `DebitWallet` | [*components.V2ActivityDebitWallet](../../models/components/v2activitydebitwallet.md) | :heavy_minus_sign: | N/A | -| `GetWallet` | [*components.V2ActivityGetWallet](../../models/components/v2activitygetwallet.md) | :heavy_minus_sign: | N/A | -| `VoidHold` | [*components.V2ActivityVoidHold](../../models/components/v2activityvoidhold.md) | :heavy_minus_sign: | N/A | -| `ListWallets` | [*components.V2ActivityListWallets](../../models/components/v2activitylistwallets.md) | :heavy_minus_sign: | N/A | \ No newline at end of file +| Field | Type | Required | Description | +| --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | +| `GetAccount` | [*components.V2ActivityGetAccount](../../models/components/v2activitygetaccount.md) | :heavy_minus_sign: | N/A | +| `AddAccountMetadata` | [*components.V2ActivityAddAccountMetadata](../../models/components/v2activityaddaccountmetadata.md) | :heavy_minus_sign: | N/A | +| `CreateTransaction` | [*components.V2ActivityCreateTransaction](../../models/components/v2activitycreatetransaction.md) | :heavy_minus_sign: | N/A | +| `StripeTransfer` | [*components.V2ActivityStripeTransfer](../../models/components/v2activitystripetransfer.md) | :heavy_minus_sign: | N/A | +| `CreateTransferInitiation` | [*components.V2ActivityCreateTransferInitiation](../../models/components/v2activitycreatetransferinitiation.md) | :heavy_minus_sign: | N/A | +| `GetPayment` | [*components.V2ActivityGetPayment](../../models/components/v2activitygetpayment.md) | :heavy_minus_sign: | N/A | +| `ConfirmHold` | [*components.V2ActivityConfirmHold](../../models/components/v2activityconfirmhold.md) | :heavy_minus_sign: | N/A | +| `CreditWallet` | [*components.V2ActivityCreditWallet](../../models/components/v2activitycreditwallet.md) | :heavy_minus_sign: | N/A | +| `DebitWallet` | [*components.V2ActivityDebitWallet](../../models/components/v2activitydebitwallet.md) | :heavy_minus_sign: | N/A | +| `GetWallet` | [*components.V2ActivityGetWallet](../../models/components/v2activitygetwallet.md) | :heavy_minus_sign: | N/A | +| `VoidHold` | [*components.V2ActivityVoidHold](../../models/components/v2activityvoidhold.md) | :heavy_minus_sign: | N/A | +| `ListWallets` | [*components.V2ActivityListWallets](../../models/components/v2activitylistwallets.md) | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/pkg/client/docs/models/components/workflowinstancehistorystageinput.md b/pkg/client/docs/models/components/workflowinstancehistorystageinput.md index a05289e..521c99e 100644 --- a/pkg/client/docs/models/components/workflowinstancehistorystageinput.md +++ b/pkg/client/docs/models/components/workflowinstancehistorystageinput.md @@ -3,17 +3,18 @@ ## Fields -| Field | Type | Required | Description | -| ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| `GetAccount` | [*components.ActivityGetAccount](../../models/components/activitygetaccount.md) | :heavy_minus_sign: | N/A | -| `AddAccountMetadata` | [*components.ActivityAddAccountMetadata](../../models/components/activityaddaccountmetadata.md) | :heavy_minus_sign: | N/A | -| `CreateTransaction` | [*components.ActivityCreateTransaction](../../models/components/activitycreatetransaction.md) | :heavy_minus_sign: | N/A | -| `RevertTransaction` | [*components.ActivityRevertTransaction](../../models/components/activityreverttransaction.md) | :heavy_minus_sign: | N/A | -| `StripeTransfer` | [*components.ActivityStripeTransfer](../../models/components/activitystripetransfer.md) | :heavy_minus_sign: | N/A | -| `GetPayment` | [*components.ActivityGetPayment](../../models/components/activitygetpayment.md) | :heavy_minus_sign: | N/A | -| `ConfirmHold` | [*components.ActivityConfirmHold](../../models/components/activityconfirmhold.md) | :heavy_minus_sign: | N/A | -| `CreditWallet` | [*components.ActivityCreditWallet](../../models/components/activitycreditwallet.md) | :heavy_minus_sign: | N/A | -| `DebitWallet` | [*components.ActivityDebitWallet](../../models/components/activitydebitwallet.md) | :heavy_minus_sign: | N/A | -| `GetWallet` | [*components.ActivityGetWallet](../../models/components/activitygetwallet.md) | :heavy_minus_sign: | N/A | -| `VoidHold` | [*components.ActivityVoidHold](../../models/components/activityvoidhold.md) | :heavy_minus_sign: | N/A | -| `ListWallets` | [*components.ActivityListWallets](../../models/components/activitylistwallets.md) | :heavy_minus_sign: | N/A | \ No newline at end of file +| Field | Type | Required | Description | +| ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| `GetAccount` | [*components.ActivityGetAccount](../../models/components/activitygetaccount.md) | :heavy_minus_sign: | N/A | +| `AddAccountMetadata` | [*components.ActivityAddAccountMetadata](../../models/components/activityaddaccountmetadata.md) | :heavy_minus_sign: | N/A | +| `CreateTransaction` | [*components.ActivityCreateTransaction](../../models/components/activitycreatetransaction.md) | :heavy_minus_sign: | N/A | +| `RevertTransaction` | [*components.ActivityRevertTransaction](../../models/components/activityreverttransaction.md) | :heavy_minus_sign: | N/A | +| `StripeTransfer` | [*components.ActivityStripeTransfer](../../models/components/activitystripetransfer.md) | :heavy_minus_sign: | N/A | +| `CreateTransferInitiation` | [*components.ActivityCreateTransferInitiation](../../models/components/activitycreatetransferinitiation.md) | :heavy_minus_sign: | N/A | +| `GetPayment` | [*components.ActivityGetPayment](../../models/components/activitygetpayment.md) | :heavy_minus_sign: | N/A | +| `ConfirmHold` | [*components.ActivityConfirmHold](../../models/components/activityconfirmhold.md) | :heavy_minus_sign: | N/A | +| `CreditWallet` | [*components.ActivityCreditWallet](../../models/components/activitycreditwallet.md) | :heavy_minus_sign: | N/A | +| `DebitWallet` | [*components.ActivityDebitWallet](../../models/components/activitydebitwallet.md) | :heavy_minus_sign: | N/A | +| `GetWallet` | [*components.ActivityGetWallet](../../models/components/activitygetwallet.md) | :heavy_minus_sign: | N/A | +| `VoidHold` | [*components.ActivityVoidHold](../../models/components/activityvoidhold.md) | :heavy_minus_sign: | N/A | +| `ListWallets` | [*components.ActivityListWallets](../../models/components/activitylistwallets.md) | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/pkg/client/docs/models/operations/listinstancesrequest.md b/pkg/client/docs/models/operations/listinstancesrequest.md index 4ae85d5..cc11efd 100644 --- a/pkg/client/docs/models/operations/listinstancesrequest.md +++ b/pkg/client/docs/models/operations/listinstancesrequest.md @@ -3,7 +3,9 @@ ## Fields -| Field | Type | Required | Description | Example | -| ------------------------ | ------------------------ | ------------------------ | ------------------------ | ------------------------ | -| `WorkflowID` | **string* | :heavy_minus_sign: | A workflow id | xxx | -| `Running` | **bool* | :heavy_minus_sign: | Filter running instances | true | \ No newline at end of file +| Field | Type | Required | Description | Example | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `WorkflowID` | **string* | :heavy_minus_sign: | A workflow id | xxx | +| `Running` | **bool* | :heavy_minus_sign: | Filter running instances | true | +| `PageSize` | **int64* | :heavy_minus_sign: | The maximum number of results to return per page.
| 100 | +| `Cursor` | **string* | :heavy_minus_sign: | Parameter used in pagination requests.
Set to the value of next for the next page of results.
Set to the value of previous for the previous page of results.
No other parameters can be set when this parameter is set.
| aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ== | \ No newline at end of file diff --git a/pkg/client/docs/models/operations/listtriggersoccurrencesrequest.md b/pkg/client/docs/models/operations/listtriggersoccurrencesrequest.md index ee2406b..e026e59 100644 --- a/pkg/client/docs/models/operations/listtriggersoccurrencesrequest.md +++ b/pkg/client/docs/models/operations/listtriggersoccurrencesrequest.md @@ -3,6 +3,8 @@ ## Fields -| Field | Type | Required | Description | -| ------------------ | ------------------ | ------------------ | ------------------ | -| `TriggerID` | *string* | :heavy_check_mark: | The trigger id | \ No newline at end of file +| Field | Type | Required | Description | Example | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `TriggerID` | *string* | :heavy_check_mark: | The trigger id | | +| `PageSize` | **int64* | :heavy_minus_sign: | The maximum number of results to return per page.
| 100 | +| `Cursor` | **string* | :heavy_minus_sign: | Parameter used in pagination requests.
Set to the value of next for the next page of results.
Set to the value of previous for the previous page of results.
No other parameters can be set when this parameter is set.
| aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ== | \ No newline at end of file diff --git a/pkg/client/docs/models/operations/listtriggersrequest.md b/pkg/client/docs/models/operations/listtriggersrequest.md index cfc708a..9e1047d 100644 --- a/pkg/client/docs/models/operations/listtriggersrequest.md +++ b/pkg/client/docs/models/operations/listtriggersrequest.md @@ -3,6 +3,8 @@ ## Fields -| Field | Type | Required | Description | -| ------------------ | ------------------ | ------------------ | ------------------ | -| `Name` | **string* | :heavy_minus_sign: | search by name | \ No newline at end of file +| Field | Type | Required | Description | Example | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `Name` | **string* | :heavy_minus_sign: | search by name | | +| `PageSize` | **int64* | :heavy_minus_sign: | The maximum number of results to return per page.
| 100 | +| `Cursor` | **string* | :heavy_minus_sign: | Parameter used in pagination requests.
Set to the value of next for the next page of results.
Set to the value of previous for the previous page of results.
No other parameters can be set when this parameter is set.
| aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ== | \ No newline at end of file diff --git a/pkg/client/docs/models/operations/listworkflowsrequest.md b/pkg/client/docs/models/operations/listworkflowsrequest.md new file mode 100644 index 0000000..e5bc8c3 --- /dev/null +++ b/pkg/client/docs/models/operations/listworkflowsrequest.md @@ -0,0 +1,9 @@ +# ListWorkflowsRequest + + +## Fields + +| Field | Type | Required | Description | Example | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `PageSize` | **int64* | :heavy_minus_sign: | The maximum number of results to return per page.
| 100 | +| `Cursor` | **string* | :heavy_minus_sign: | Parameter used in pagination requests.
Set to the value of next for the next page of results.
Set to the value of previous for the previous page of results.
No other parameters can be set when this parameter is set.
| aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ== | \ No newline at end of file diff --git a/pkg/client/docs/sdks/v1/README.md b/pkg/client/docs/sdks/v1/README.md index 6fd4a1a..2e97d33 100644 --- a/pkg/client/docs/sdks/v1/README.md +++ b/pkg/client/docs/sdks/v1/README.md @@ -31,15 +31,15 @@ Get server info package main import( - "openapi/models/components" - "openapi" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -82,21 +82,24 @@ List triggers package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), ) - request := operations.ListTriggersRequest{} + request := operations.ListTriggersRequest{ + PageSize: client.Int64(100), + Cursor: client.String("aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ=="), + } ctx := context.Background() res, err := s.Orchestration.V1.ListTriggers(ctx, request) if err != nil { @@ -135,15 +138,15 @@ Create trigger package main import( - "openapi/models/components" - "openapi" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -187,16 +190,16 @@ Read trigger package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -242,16 +245,16 @@ Read trigger package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -297,22 +300,24 @@ List triggers occurrences package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), ) request := operations.ListTriggersOccurrencesRequest{ TriggerID: "", + PageSize: client.Int64(100), + Cursor: client.String("aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ=="), } ctx := context.Background() res, err := s.Orchestration.V1.ListTriggersOccurrences(ctx, request) @@ -352,22 +357,26 @@ List registered workflows package main import( - "openapi/models/components" - "openapi" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), ) - + request := operations.ListWorkflowsRequest{ + PageSize: client.Int64(100), + Cursor: client.String("aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ=="), + } ctx := context.Background() - res, err := s.Orchestration.V1.ListWorkflows(ctx) + res, err := s.Orchestration.V1.ListWorkflows(ctx, request) if err != nil { log.Fatal(err) } @@ -379,10 +388,11 @@ func main() { ### Parameters -| Parameter | Type | Required | Description | -| -------------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------- | -| `ctx` | [context.Context](https://pkg.go.dev/context#Context) | :heavy_check_mark: | The context to use for the request. | -| `opts` | [][operations.Option](../../models/operations/option.md) | :heavy_minus_sign: | The options for this request. | +| Parameter | Type | Required | Description | +| ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | +| `ctx` | [context.Context](https://pkg.go.dev/context#Context) | :heavy_check_mark: | The context to use for the request. | +| `request` | [operations.ListWorkflowsRequest](../../models/operations/listworkflowsrequest.md) | :heavy_check_mark: | The request object to use for the request. | +| `opts` | [][operations.Option](../../models/operations/option.md) | :heavy_minus_sign: | The options for this request. | ### Response @@ -403,15 +413,15 @@ Create a workflow package main import( - "openapi/models/components" - "openapi" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -455,16 +465,16 @@ Get a flow by id package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -510,16 +520,16 @@ Delete a flow by id package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -565,16 +575,16 @@ Run workflow package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -620,23 +630,25 @@ List instances of a workflow package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), ) request := operations.ListInstancesRequest{ - WorkflowID: openapi.String("xxx"), - Running: openapi.Bool(true), + WorkflowID: client.String("xxx"), + Running: client.Bool(true), + PageSize: client.Int64(100), + Cursor: client.String("aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ=="), } ctx := context.Background() res, err := s.Orchestration.V1.ListInstances(ctx, request) @@ -676,16 +688,16 @@ Get a workflow instance by id package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -731,16 +743,16 @@ Send an event to a running workflow package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -786,16 +798,16 @@ Cancel a running workflow package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -841,16 +853,16 @@ Get a workflow instance history by id package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -896,16 +908,16 @@ Get a workflow instance stage history package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), diff --git a/pkg/client/docs/sdks/v2/README.md b/pkg/client/docs/sdks/v2/README.md index e230fcb..f725b56 100644 --- a/pkg/client/docs/sdks/v2/README.md +++ b/pkg/client/docs/sdks/v2/README.md @@ -32,15 +32,15 @@ Get server info package main import( - "openapi/models/components" - "openapi" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -83,23 +83,23 @@ List triggers package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), ) request := operations.V2ListTriggersRequest{ - Cursor: openapi.String("aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ=="), - PageSize: openapi.Int64(100), + Cursor: client.String("aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ=="), + PageSize: client.Int64(100), } ctx := context.Background() res, err := s.Orchestration.V2.ListTriggers(ctx, request) @@ -139,15 +139,15 @@ Create trigger package main import( - "openapi/models/components" - "openapi" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -191,16 +191,16 @@ Read trigger package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -246,16 +246,16 @@ Read trigger package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -301,16 +301,16 @@ Test trigger package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -356,24 +356,24 @@ List triggers occurrences package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), ) request := operations.V2ListTriggersOccurrencesRequest{ TriggerID: "", - Cursor: openapi.String("aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ=="), - PageSize: openapi.Int64(100), + Cursor: client.String("aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ=="), + PageSize: client.Int64(100), } ctx := context.Background() res, err := s.Orchestration.V2.ListTriggersOccurrences(ctx, request) @@ -413,23 +413,23 @@ List registered workflows package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), ) request := operations.V2ListWorkflowsRequest{ - Cursor: openapi.String("aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ=="), - PageSize: openapi.Int64(100), + Cursor: client.String("aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ=="), + PageSize: client.Int64(100), } ctx := context.Background() res, err := s.Orchestration.V2.ListWorkflows(ctx, request) @@ -469,15 +469,15 @@ Create a workflow package main import( - "openapi/models/components" - "openapi" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -521,16 +521,16 @@ Get a flow by id package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -576,16 +576,16 @@ Delete a flow by id package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -631,16 +631,16 @@ Run workflow package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -686,25 +686,25 @@ List instances of a workflow package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), ) request := operations.V2ListInstancesRequest{ - Cursor: openapi.String("aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ=="), - PageSize: openapi.Int64(100), - WorkflowID: openapi.String("xxx"), - Running: openapi.Bool(true), + Cursor: client.String("aHR0cHM6Ly9nLnBhZ2UvTmVrby1SYW1lbj9zaGFyZQ=="), + PageSize: client.Int64(100), + WorkflowID: client.String("xxx"), + Running: client.Bool(true), } ctx := context.Background() res, err := s.Orchestration.V2.ListInstances(ctx, request) @@ -744,16 +744,16 @@ Get a workflow instance by id package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -799,16 +799,16 @@ Send an event to a running workflow package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -854,16 +854,16 @@ Cancel a running workflow package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -909,16 +909,16 @@ Get a workflow instance history by id package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), @@ -964,16 +964,16 @@ Get a workflow instance stage history package main import( - "openapi/models/components" - "openapi" - "openapi/models/operations" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client" + "github.com/formancehq/flows/pkg/client/models/operations" "context" "log" ) func main() { - s := openapi.New( - openapi.WithSecurity(components.Security{ + s := client.New( + client.WithSecurity(components.Security{ ClientID: "", ClientSecret: "", }), diff --git a/pkg/client/formance.go b/pkg/client/formance.go deleted file mode 100644 index 5785518..0000000 --- a/pkg/client/formance.go +++ /dev/null @@ -1,178 +0,0 @@ -// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. - -package client - -import ( - "context" - "fmt" - "github.com/formancehq/flows/pkg/client/internal/hooks" - "github.com/formancehq/flows/pkg/client/internal/utils" - "github.com/formancehq/flows/pkg/client/models/components" - "github.com/formancehq/flows/pkg/client/retry" - "net/http" - "time" -) - -// ServerList contains the list of servers available to the SDK -var ServerList = []string{ - "http://localhost:8080/", -} - -// HTTPClient provides an interface for suplying the SDK with a custom HTTP client -type HTTPClient interface { - Do(req *http.Request) (*http.Response, error) -} - -// String provides a helper function to return a pointer to a string -func String(s string) *string { return &s } - -// Bool provides a helper function to return a pointer to a bool -func Bool(b bool) *bool { return &b } - -// Int provides a helper function to return a pointer to an int -func Int(i int) *int { return &i } - -// Int64 provides a helper function to return a pointer to an int64 -func Int64(i int64) *int64 { return &i } - -// Float32 provides a helper function to return a pointer to a float32 -func Float32(f float32) *float32 { return &f } - -// Float64 provides a helper function to return a pointer to a float64 -func Float64(f float64) *float64 { return &f } - -type sdkConfiguration struct { - Client HTTPClient - Security func(context.Context) (interface{}, error) - ServerURL string - ServerIndex int - Language string - OpenAPIDocVersion string - SDKVersion string - GenVersion string - UserAgent string - RetryConfig *retry.Config - Hooks *hooks.Hooks - Timeout *time.Duration -} - -func (c *sdkConfiguration) GetServerDetails() (string, map[string]string) { - if c.ServerURL != "" { - return c.ServerURL, nil - } - - return ServerList[c.ServerIndex], nil -} - -type Formance struct { - Orchestration *Orchestration - - sdkConfiguration sdkConfiguration -} - -type SDKOption func(*Formance) - -// WithServerURL allows the overriding of the default server URL -func WithServerURL(serverURL string) SDKOption { - return func(sdk *Formance) { - sdk.sdkConfiguration.ServerURL = serverURL - } -} - -// WithTemplatedServerURL allows the overriding of the default server URL with a templated URL populated with the provided parameters -func WithTemplatedServerURL(serverURL string, params map[string]string) SDKOption { - return func(sdk *Formance) { - if params != nil { - serverURL = utils.ReplaceParameters(serverURL, params) - } - - sdk.sdkConfiguration.ServerURL = serverURL - } -} - -// WithServerIndex allows the overriding of the default server by index -func WithServerIndex(serverIndex int) SDKOption { - return func(sdk *Formance) { - if serverIndex < 0 || serverIndex >= len(ServerList) { - panic(fmt.Errorf("server index %d out of range", serverIndex)) - } - - sdk.sdkConfiguration.ServerIndex = serverIndex - } -} - -// WithClient allows the overriding of the default HTTP client used by the SDK -func WithClient(client HTTPClient) SDKOption { - return func(sdk *Formance) { - sdk.sdkConfiguration.Client = client - } -} - -// WithSecurity configures the SDK to use the provided security details -func WithSecurity(security components.Security) SDKOption { - return func(sdk *Formance) { - sdk.sdkConfiguration.Security = utils.AsSecuritySource(security) - } -} - -// WithSecuritySource configures the SDK to invoke the Security Source function on each method call to determine authentication -func WithSecuritySource(security func(context.Context) (components.Security, error)) SDKOption { - return func(sdk *Formance) { - sdk.sdkConfiguration.Security = func(ctx context.Context) (interface{}, error) { - return security(ctx) - } - } -} - -func WithRetryConfig(retryConfig retry.Config) SDKOption { - return func(sdk *Formance) { - sdk.sdkConfiguration.RetryConfig = &retryConfig - } -} - -// WithTimeout Optional request timeout applied to each operation -func WithTimeout(timeout time.Duration) SDKOption { - return func(sdk *Formance) { - sdk.sdkConfiguration.Timeout = &timeout - } -} - -// New creates a new instance of the SDK with the provided options -func New(opts ...SDKOption) *Formance { - sdk := &Formance{ - sdkConfiguration: sdkConfiguration{ - Language: "go", - OpenAPIDocVersion: "0.1.0", - SDKVersion: "0.8.0", - GenVersion: "2.409.8", - UserAgent: "speakeasy-sdk/go 0.8.0 2.409.8 0.1.0 github.com/formancehq/flows/pkg/client", - Hooks: hooks.New(), - }, - } - for _, opt := range opts { - opt(sdk) - } - - if sdk.sdkConfiguration.Security == nil { - var envVarSecurity components.Security - if utils.PopulateSecurityFromEnv(&envVarSecurity) { - sdk.sdkConfiguration.Security = utils.AsSecuritySource(envVarSecurity) - } - } - - // Use WithClient to override the default client if you would like to customize the timeout - if sdk.sdkConfiguration.Client == nil { - sdk.sdkConfiguration.Client = &http.Client{Timeout: 60 * time.Second} - } - - currentServerURL, _ := sdk.sdkConfiguration.GetServerDetails() - serverURL := currentServerURL - serverURL, sdk.sdkConfiguration.Client = sdk.sdkConfiguration.Hooks.SDKInit(currentServerURL, sdk.sdkConfiguration.Client) - if serverURL != currentServerURL { - sdk.sdkConfiguration.ServerURL = serverURL - } - - sdk.Orchestration = newOrchestration(sdk.sdkConfiguration) - - return sdk -} diff --git a/pkg/client/go.mod b/pkg/client/go.mod index 180f4dd..33a5dbc 100644 --- a/pkg/client/go.mod +++ b/pkg/client/go.mod @@ -1,10 +1,8 @@ - -module openapi +module github.com/formancehq/flows/pkg/client go 1.20 require ( github.com/cenkalti/backoff/v4 v4.2.0 - github.com/ericlagergren/decimal v0.0.0-20221120152707-495c53812d05 - github.com/spyzhov/ajson v0.8.0 + github.com/ericlagergren/decimal v0.0.0-20221120152707-495c53812d05 ) diff --git a/pkg/client/go.sum b/pkg/client/go.sum index 0fee03f..f955779 100644 --- a/pkg/client/go.sum +++ b/pkg/client/go.sum @@ -1,3 +1,4 @@ +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/ericlagergren/decimal v0.0.0-20221120152707-495c53812d05 h1:S92OBrGuLLZsyM5ybUzgc/mPjIYk2AZqufieooe98uw= github.com/ericlagergren/decimal v0.0.0-20221120152707-495c53812d05/go.mod h1:M9R1FoZ3y//hwwnJtO51ypFGwm8ZfpxPT/ZLtO1mcgQ= -github.com/spyzhov/ajson v0.8.0/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA= diff --git a/pkg/client/internal/hooks/clientcredentials.go b/pkg/client/internal/hooks/clientcredentials.go index cde7c67..8ffa2e6 100644 --- a/pkg/client/internal/hooks/clientcredentials.go +++ b/pkg/client/internal/hooks/clientcredentials.go @@ -9,10 +9,10 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/formancehq/flows/pkg/client/models/components" "io" "net/http" "net/url" - "openapi/models/components" "strings" "time" ) diff --git a/pkg/client/internal/utils/form.go b/pkg/client/internal/utils/form.go index a01d3a4..3c52b01 100644 --- a/pkg/client/internal/utils/form.go +++ b/pkg/client/internal/utils/form.go @@ -12,7 +12,7 @@ import ( "github.com/ericlagergren/decimal" - "openapi/types" + "github.com/formancehq/flows/pkg/client/types" ) func populateForm(paramName string, explode bool, objType reflect.Type, objValue reflect.Value, delimiter string, getFieldName func(reflect.StructField) string) url.Values { diff --git a/pkg/client/internal/utils/json.go b/pkg/client/internal/utils/json.go index f740dcd..3fdfd68 100644 --- a/pkg/client/internal/utils/json.go +++ b/pkg/client/internal/utils/json.go @@ -13,7 +13,7 @@ import ( "time" "unsafe" - "openapi/types" + "github.com/formancehq/flows/pkg/client/types" "github.com/ericlagergren/decimal" ) diff --git a/pkg/client/internal/utils/pathparams.go b/pkg/client/internal/utils/pathparams.go index a01f74d..36b464d 100644 --- a/pkg/client/internal/utils/pathparams.go +++ b/pkg/client/internal/utils/pathparams.go @@ -13,7 +13,7 @@ import ( "github.com/ericlagergren/decimal" - "openapi/types" + "github.com/formancehq/flows/pkg/client/types" ) func GenerateURL(_ context.Context, serverURL, path string, pathParams interface{}, globals interface{}) (string, error) { diff --git a/pkg/client/internal/utils/queryparams.go b/pkg/client/internal/utils/queryparams.go index 6bd2b1a..eb52f38 100644 --- a/pkg/client/internal/utils/queryparams.go +++ b/pkg/client/internal/utils/queryparams.go @@ -14,7 +14,7 @@ import ( "github.com/ericlagergren/decimal" - "openapi/types" + "github.com/formancehq/flows/pkg/client/types" ) func PopulateQueryParams(_ context.Context, req *http.Request, queryParams interface{}, globals interface{}) error { diff --git a/pkg/client/internal/utils/retries.go b/pkg/client/internal/utils/retries.go index 03549bf..ebcd076 100644 --- a/pkg/client/internal/utils/retries.go +++ b/pkg/client/internal/utils/retries.go @@ -7,9 +7,9 @@ import ( "errors" "fmt" "github.com/cenkalti/backoff/v4" + "github.com/formancehq/flows/pkg/client/retry" "net/http" "net/url" - "openapi/retry" "strconv" "strings" "time" diff --git a/pkg/client/models/components/activitycreatetransferinitiation.go b/pkg/client/models/components/activitycreatetransferinitiation.go new file mode 100644 index 0000000..c625c4c --- /dev/null +++ b/pkg/client/models/components/activitycreatetransferinitiation.go @@ -0,0 +1,149 @@ +// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + +package components + +import ( + "encoding/json" + "fmt" + "github.com/formancehq/flows/pkg/client/internal/utils" + "math/big" +) + +// ActivityCreateTransferInitiationType - Type of transfer initiation: +// - TRANSFER: Internal to internal account transfer +// - PAYOUT: Internal to external account payout +type ActivityCreateTransferInitiationType string + +const ( + ActivityCreateTransferInitiationTypeTransfer ActivityCreateTransferInitiationType = "TRANSFER" + ActivityCreateTransferInitiationTypePayout ActivityCreateTransferInitiationType = "PAYOUT" +) + +func (e ActivityCreateTransferInitiationType) ToPointer() *ActivityCreateTransferInitiationType { + return &e +} +func (e *ActivityCreateTransferInitiationType) UnmarshalJSON(data []byte) error { + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v { + case "TRANSFER": + fallthrough + case "PAYOUT": + *e = ActivityCreateTransferInitiationType(v) + return nil + default: + return fmt.Errorf("invalid value for ActivityCreateTransferInitiationType: %v", v) + } +} + +// ActivityCreateTransferInitiationMetadata - 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. +type ActivityCreateTransferInitiationMetadata struct { +} + +type ActivityCreateTransferInitiation struct { + ConnectorID *string `json:"connectorID,omitempty"` + // Payment service provider name (e.g., stripe, wise, mangopay). + // Validated by the Payments service based on installed connectors. + // + Provider *string `json:"provider,omitempty"` + Amount *big.Int `json:"amount,omitempty"` + Asset *string `json:"asset,omitempty"` + // Destination account ID + Destination *string `json:"destination,omitempty"` + // Source account ID (required for TRANSFER type) + Source *string `json:"source,omitempty"` + // Type of transfer initiation: + // - TRANSFER: Internal to internal account transfer + // - PAYOUT: Internal to external account payout + // + Type *ActivityCreateTransferInitiationType `default:"TRANSFER" json:"type"` + // Description for the transfer initiation + Description *string `json:"description,omitempty"` + WaitingValidation *bool `default:"false" json:"waitingValidation"` + // 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. + // + Metadata *ActivityCreateTransferInitiationMetadata `json:"metadata,omitempty"` +} + +func (a ActivityCreateTransferInitiation) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(a, "", false) +} + +func (a *ActivityCreateTransferInitiation) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &a, "", false, false); err != nil { + return err + } + return nil +} + +func (o *ActivityCreateTransferInitiation) GetConnectorID() *string { + if o == nil { + return nil + } + return o.ConnectorID +} + +func (o *ActivityCreateTransferInitiation) GetProvider() *string { + if o == nil { + return nil + } + return o.Provider +} + +func (o *ActivityCreateTransferInitiation) GetAmount() *big.Int { + if o == nil { + return nil + } + return o.Amount +} + +func (o *ActivityCreateTransferInitiation) GetAsset() *string { + if o == nil { + return nil + } + return o.Asset +} + +func (o *ActivityCreateTransferInitiation) GetDestination() *string { + if o == nil { + return nil + } + return o.Destination +} + +func (o *ActivityCreateTransferInitiation) GetSource() *string { + if o == nil { + return nil + } + return o.Source +} + +func (o *ActivityCreateTransferInitiation) GetType() *ActivityCreateTransferInitiationType { + if o == nil { + return nil + } + return o.Type +} + +func (o *ActivityCreateTransferInitiation) GetDescription() *string { + if o == nil { + return nil + } + return o.Description +} + +func (o *ActivityCreateTransferInitiation) GetWaitingValidation() *bool { + if o == nil { + return nil + } + return o.WaitingValidation +} + +func (o *ActivityCreateTransferInitiation) GetMetadata() *ActivityCreateTransferInitiationMetadata { + if o == nil { + return nil + } + return o.Metadata +} diff --git a/pkg/client/models/components/activitystripetransfer.go b/pkg/client/models/components/activitystripetransfer.go index 9e09771..f1f4ceb 100644 --- a/pkg/client/models/components/activitystripetransfer.go +++ b/pkg/client/models/components/activitystripetransfer.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" ) // Metadata - A set of key/value pairs that you can attach to a transfer object. diff --git a/pkg/client/models/components/assetholder.go b/pkg/client/models/components/assetholder.go index 7a1a2b3..455e265 100644 --- a/pkg/client/models/components/assetholder.go +++ b/pkg/client/models/components/assetholder.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" ) type AssetHolder struct { diff --git a/pkg/client/models/components/creditwalletrequest.go b/pkg/client/models/components/creditwalletrequest.go index 91857d8..dc772b4 100644 --- a/pkg/client/models/components/creditwalletrequest.go +++ b/pkg/client/models/components/creditwalletrequest.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/debitwalletrequest.go b/pkg/client/models/components/debitwalletrequest.go index ccd9535..dfe8252 100644 --- a/pkg/client/models/components/debitwalletrequest.go +++ b/pkg/client/models/components/debitwalletrequest.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/listrunsresponse.go b/pkg/client/models/components/listrunsresponse.go index 5af27ef..e3c8776 100644 --- a/pkg/client/models/components/listrunsresponse.go +++ b/pkg/client/models/components/listrunsresponse.go @@ -3,7 +3,11 @@ package components type ListRunsResponse struct { - Data []WorkflowInstance `json:"data"` + Data []WorkflowInstance `json:"data"` + PageSize int64 `json:"pageSize"` + HasMore bool `json:"hasMore"` + Previous *string `json:"previous,omitempty"` + Next *string `json:"next,omitempty"` } func (o *ListRunsResponse) GetData() []WorkflowInstance { @@ -12,3 +16,31 @@ func (o *ListRunsResponse) GetData() []WorkflowInstance { } return o.Data } + +func (o *ListRunsResponse) GetPageSize() int64 { + if o == nil { + return 0 + } + return o.PageSize +} + +func (o *ListRunsResponse) GetHasMore() bool { + if o == nil { + return false + } + return o.HasMore +} + +func (o *ListRunsResponse) GetPrevious() *string { + if o == nil { + return nil + } + return o.Previous +} + +func (o *ListRunsResponse) GetNext() *string { + if o == nil { + return nil + } + return o.Next +} diff --git a/pkg/client/models/components/listtriggersoccurrencesresponse.go b/pkg/client/models/components/listtriggersoccurrencesresponse.go index 06368b3..7993513 100644 --- a/pkg/client/models/components/listtriggersoccurrencesresponse.go +++ b/pkg/client/models/components/listtriggersoccurrencesresponse.go @@ -3,7 +3,11 @@ package components type ListTriggersOccurrencesResponse struct { - Data []TriggerOccurrence `json:"data"` + Data []TriggerOccurrence `json:"data"` + PageSize int64 `json:"pageSize"` + HasMore bool `json:"hasMore"` + Previous *string `json:"previous,omitempty"` + Next *string `json:"next,omitempty"` } func (o *ListTriggersOccurrencesResponse) GetData() []TriggerOccurrence { @@ -12,3 +16,31 @@ func (o *ListTriggersOccurrencesResponse) GetData() []TriggerOccurrence { } return o.Data } + +func (o *ListTriggersOccurrencesResponse) GetPageSize() int64 { + if o == nil { + return 0 + } + return o.PageSize +} + +func (o *ListTriggersOccurrencesResponse) GetHasMore() bool { + if o == nil { + return false + } + return o.HasMore +} + +func (o *ListTriggersOccurrencesResponse) GetPrevious() *string { + if o == nil { + return nil + } + return o.Previous +} + +func (o *ListTriggersOccurrencesResponse) GetNext() *string { + if o == nil { + return nil + } + return o.Next +} diff --git a/pkg/client/models/components/listtriggersresponse.go b/pkg/client/models/components/listtriggersresponse.go index 7e06bbb..7b05a6a 100644 --- a/pkg/client/models/components/listtriggersresponse.go +++ b/pkg/client/models/components/listtriggersresponse.go @@ -3,7 +3,11 @@ package components type ListTriggersResponse struct { - Data []Trigger `json:"data"` + Data []Trigger `json:"data"` + PageSize int64 `json:"pageSize"` + HasMore bool `json:"hasMore"` + Previous *string `json:"previous,omitempty"` + Next *string `json:"next,omitempty"` } func (o *ListTriggersResponse) GetData() []Trigger { @@ -12,3 +16,31 @@ func (o *ListTriggersResponse) GetData() []Trigger { } return o.Data } + +func (o *ListTriggersResponse) GetPageSize() int64 { + if o == nil { + return 0 + } + return o.PageSize +} + +func (o *ListTriggersResponse) GetHasMore() bool { + if o == nil { + return false + } + return o.HasMore +} + +func (o *ListTriggersResponse) GetPrevious() *string { + if o == nil { + return nil + } + return o.Previous +} + +func (o *ListTriggersResponse) GetNext() *string { + if o == nil { + return nil + } + return o.Next +} diff --git a/pkg/client/models/components/listworkflowsresponse.go b/pkg/client/models/components/listworkflowsresponse.go index 8b734b1..07c8bee 100644 --- a/pkg/client/models/components/listworkflowsresponse.go +++ b/pkg/client/models/components/listworkflowsresponse.go @@ -3,7 +3,11 @@ package components type ListWorkflowsResponse struct { - Data []Workflow `json:"data"` + Data []Workflow `json:"data"` + PageSize int64 `json:"pageSize"` + HasMore bool `json:"hasMore"` + Previous *string `json:"previous,omitempty"` + Next *string `json:"next,omitempty"` } func (o *ListWorkflowsResponse) GetData() []Workflow { @@ -12,3 +16,31 @@ func (o *ListWorkflowsResponse) GetData() []Workflow { } return o.Data } + +func (o *ListWorkflowsResponse) GetPageSize() int64 { + if o == nil { + return 0 + } + return o.PageSize +} + +func (o *ListWorkflowsResponse) GetHasMore() bool { + if o == nil { + return false + } + return o.HasMore +} + +func (o *ListWorkflowsResponse) GetPrevious() *string { + if o == nil { + return nil + } + return o.Previous +} + +func (o *ListWorkflowsResponse) GetNext() *string { + if o == nil { + return nil + } + return o.Next +} diff --git a/pkg/client/models/components/monetary.go b/pkg/client/models/components/monetary.go index c2e4ac6..daa2629 100644 --- a/pkg/client/models/components/monetary.go +++ b/pkg/client/models/components/monetary.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" ) type Monetary struct { diff --git a/pkg/client/models/components/payment.go b/pkg/client/models/components/payment.go index a0665f4..d2654ca 100644 --- a/pkg/client/models/components/payment.go +++ b/pkg/client/models/components/payment.go @@ -5,24 +5,24 @@ package components import ( "encoding/json" "fmt" + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" "time" ) -type Type string +type PaymentType string const ( - TypePayIn Type = "PAY-IN" - TypePayout Type = "PAYOUT" - TypeTransfer Type = "TRANSFER" - TypeOther Type = "OTHER" + PaymentTypePayIn PaymentType = "PAY-IN" + PaymentTypePayout PaymentType = "PAYOUT" + PaymentTypeTransfer PaymentType = "TRANSFER" + PaymentTypeOther PaymentType = "OTHER" ) -func (e Type) ToPointer() *Type { +func (e PaymentType) ToPointer() *PaymentType { return &e } -func (e *Type) UnmarshalJSON(data []byte) error { +func (e *PaymentType) UnmarshalJSON(data []byte) error { var v string if err := json.Unmarshal(data, &v); err != nil { return err @@ -35,10 +35,10 @@ func (e *Type) UnmarshalJSON(data []byte) error { case "TRANSFER": fallthrough case "OTHER": - *e = Type(v) + *e = PaymentType(v) return nil default: - return fmt.Errorf("invalid value for Type: %v", v) + return fmt.Errorf("invalid value for PaymentType: %v", v) } } @@ -126,7 +126,7 @@ type Payment struct { DestinationAccountID string `json:"destinationAccountID"` ConnectorID string `json:"connectorID"` Provider *Connector `json:"provider,omitempty"` - Type Type `json:"type"` + Type PaymentType `json:"type"` Status PaymentStatus `json:"status"` InitialAmount *big.Int `json:"initialAmount"` Scheme Scheme `json:"scheme"` @@ -190,9 +190,9 @@ func (o *Payment) GetProvider() *Connector { return o.Provider } -func (o *Payment) GetType() Type { +func (o *Payment) GetType() PaymentType { if o == nil { - return Type("") + return PaymentType("") } return o.Type } diff --git a/pkg/client/models/components/paymentadjustment.go b/pkg/client/models/components/paymentadjustment.go index d605ef1..873b365 100644 --- a/pkg/client/models/components/paymentadjustment.go +++ b/pkg/client/models/components/paymentadjustment.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" "time" ) diff --git a/pkg/client/models/components/posting.go b/pkg/client/models/components/posting.go index 2a37eed..7eeb3cd 100644 --- a/pkg/client/models/components/posting.go +++ b/pkg/client/models/components/posting.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" ) type Posting struct { diff --git a/pkg/client/models/components/posttransaction.go b/pkg/client/models/components/posttransaction.go index 1bb8ea3..b233d18 100644 --- a/pkg/client/models/components/posttransaction.go +++ b/pkg/client/models/components/posttransaction.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/security.go b/pkg/client/models/components/security.go index 575119c..a3b9c69 100644 --- a/pkg/client/models/components/security.go +++ b/pkg/client/models/components/security.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" ) type Security struct { diff --git a/pkg/client/models/components/stage.go b/pkg/client/models/components/stage.go index 957d906..47d89e1 100644 --- a/pkg/client/models/components/stage.go +++ b/pkg/client/models/components/stage.go @@ -5,7 +5,7 @@ package components import ( "errors" "fmt" - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" ) type StageType string diff --git a/pkg/client/models/components/stagedelay.go b/pkg/client/models/components/stagedelay.go index 6202e66..c11d528 100644 --- a/pkg/client/models/components/stagedelay.go +++ b/pkg/client/models/components/stagedelay.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/stagesend.go b/pkg/client/models/components/stagesend.go index 0a71505..30e8bda 100644 --- a/pkg/client/models/components/stagesend.go +++ b/pkg/client/models/components/stagesend.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/stagesenddestinationaccount.go b/pkg/client/models/components/stagesenddestinationaccount.go index a782592..b721092 100644 --- a/pkg/client/models/components/stagesenddestinationaccount.go +++ b/pkg/client/models/components/stagesenddestinationaccount.go @@ -2,9 +2,36 @@ package components +import ( + "github.com/formancehq/flows/pkg/client/internal/utils" +) + type StageSendDestinationAccount struct { ID string `json:"id"` Ledger *string `json:"ledger,omitempty"` + // Account used when this ledger account interacts with external systems (payments, cross-ledger). + // - As SOURCE going to payment: funds are sent to this account (e.g., "liabilities:payouts-pending") + // - As DESTINATION from payment: funds come from this account (e.g., "assets:stripe:incoming") + // - For cross-ledger transfers: replaces "world" on both sides + // + ThroughAccount *string `default:"world" json:"throughAccount"` + // Enables unbounded overdraft on the throughAccount when set to true. + // This is useful when the throughAccount represents a liability or bridge account + // that needs to go negative (e.g., "liabilities:payouts-pending"). + // Only applies when throughAccount is not "world" (which already has unbounded overdraft). + // + AllowOverdraft *bool `default:"false" json:"allowOverdraft"` +} + +func (s StageSendDestinationAccount) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(s, "", false) +} + +func (s *StageSendDestinationAccount) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &s, "", false, false); err != nil { + return err + } + return nil } func (o *StageSendDestinationAccount) GetID() string { @@ -20,3 +47,17 @@ func (o *StageSendDestinationAccount) GetLedger() *string { } return o.Ledger } + +func (o *StageSendDestinationAccount) GetThroughAccount() *string { + if o == nil { + return nil + } + return o.ThroughAccount +} + +func (o *StageSendDestinationAccount) GetAllowOverdraft() *bool { + if o == nil { + return nil + } + return o.AllowOverdraft +} diff --git a/pkg/client/models/components/stagesenddestinationpayment.go b/pkg/client/models/components/stagesenddestinationpayment.go index 74394b6..4e7d9df 100644 --- a/pkg/client/models/components/stagesenddestinationpayment.go +++ b/pkg/client/models/components/stagesenddestinationpayment.go @@ -2,8 +2,66 @@ package components +import ( + "encoding/json" + "fmt" + "github.com/formancehq/flows/pkg/client/internal/utils" +) + +// Type of transfer initiation: +// - TRANSFER: Internal to internal account transfer +// - PAYOUT: Internal to external account payout +type Type string + +const ( + TypeTransfer Type = "TRANSFER" + TypePayout Type = "PAYOUT" +) + +func (e Type) ToPointer() *Type { + return &e +} +func (e *Type) UnmarshalJSON(data []byte) error { + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v { + case "TRANSFER": + fallthrough + case "PAYOUT": + *e = Type(v) + return nil + default: + return fmt.Errorf("invalid value for Type: %v", v) + } +} + type StageSendDestinationPayment struct { + // Payment service provider name (e.g., stripe, wise, mangopay). + // Validated by the Payments service based on installed connectors. + // Psp string `json:"psp"` + // Type of transfer initiation: + // - TRANSFER: Internal to internal account transfer + // - PAYOUT: Internal to external account payout + // + Type *Type `default:"TRANSFER" json:"type"` + // Formance Payments account ID for the source (internal PSP account). + // If not specified, the Payments service may use a default account for the connector. + // + SourceAccount *string `json:"sourceAccount,omitempty"` +} + +func (s StageSendDestinationPayment) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(s, "", false) +} + +func (s *StageSendDestinationPayment) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &s, "", false, false); err != nil { + return err + } + return nil } func (o *StageSendDestinationPayment) GetPsp() string { @@ -12,3 +70,17 @@ func (o *StageSendDestinationPayment) GetPsp() string { } return o.Psp } + +func (o *StageSendDestinationPayment) GetType() *Type { + if o == nil { + return nil + } + return o.Type +} + +func (o *StageSendDestinationPayment) GetSourceAccount() *string { + if o == nil { + return nil + } + return o.SourceAccount +} diff --git a/pkg/client/models/components/stagesendsourceaccount.go b/pkg/client/models/components/stagesendsourceaccount.go index 0e459b6..ae0df1c 100644 --- a/pkg/client/models/components/stagesendsourceaccount.go +++ b/pkg/client/models/components/stagesendsourceaccount.go @@ -2,9 +2,36 @@ package components +import ( + "github.com/formancehq/flows/pkg/client/internal/utils" +) + type StageSendSourceAccount struct { ID string `json:"id"` Ledger *string `json:"ledger,omitempty"` + // Account used when this ledger account interacts with external systems (payments, cross-ledger). + // - As SOURCE going to payment: funds are sent to this account (e.g., "liabilities:payouts-pending") + // - As DESTINATION from payment: funds come from this account (e.g., "assets:stripe:incoming") + // - For cross-ledger transfers: replaces "world" on both sides + // + ThroughAccount *string `default:"world" json:"throughAccount"` + // Enables unbounded overdraft on the throughAccount when set to true. + // This is useful when the throughAccount represents a liability or bridge account + // that needs to go negative (e.g., "liabilities:payouts-pending"). + // Only applies when throughAccount is not "world" (which already has unbounded overdraft). + // + AllowOverdraft *bool `default:"false" json:"allowOverdraft"` +} + +func (s StageSendSourceAccount) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(s, "", false) +} + +func (s *StageSendSourceAccount) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &s, "", false, false); err != nil { + return err + } + return nil } func (o *StageSendSourceAccount) GetID() string { @@ -20,3 +47,17 @@ func (o *StageSendSourceAccount) GetLedger() *string { } return o.Ledger } + +func (o *StageSendSourceAccount) GetThroughAccount() *string { + if o == nil { + return nil + } + return o.ThroughAccount +} + +func (o *StageSendSourceAccount) GetAllowOverdraft() *bool { + if o == nil { + return nil + } + return o.AllowOverdraft +} diff --git a/pkg/client/models/components/stagesendsourcepayment.go b/pkg/client/models/components/stagesendsourcepayment.go index 49b85c3..d985c44 100644 --- a/pkg/client/models/components/stagesendsourcepayment.go +++ b/pkg/client/models/components/stagesendsourcepayment.go @@ -2,8 +2,39 @@ package components +import ( + "github.com/formancehq/flows/pkg/client/internal/utils" +) + type StageSendSourcePayment struct { ID string `json:"id"` + // Ledger to use for payment ingestion. + // Defaults to the internal orchestration ledger. + // + Ledger *string `json:"ledger,omitempty"` + // Intermediate account where payment funds are held. + // Defaults to "payment:{paymentID}" format. + // + HoldingAccount *string `json:"holdingAccount,omitempty"` + // Source account for the payment ingestion transaction. + // Defaults to "world". + // + ThroughAccount *string `default:"world" json:"throughAccount"` + // Enables unbounded overdraft on the throughAccount when set to true. + // Only applies when throughAccount is not "world" (which already has unbounded overdraft). + // + AllowOverdraft *bool `default:"false" json:"allowOverdraft"` +} + +func (s StageSendSourcePayment) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(s, "", false) +} + +func (s *StageSendSourcePayment) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &s, "", false, false); err != nil { + return err + } + return nil } func (o *StageSendSourcePayment) GetID() string { @@ -12,3 +43,31 @@ func (o *StageSendSourcePayment) GetID() string { } return o.ID } + +func (o *StageSendSourcePayment) GetLedger() *string { + if o == nil { + return nil + } + return o.Ledger +} + +func (o *StageSendSourcePayment) GetHoldingAccount() *string { + if o == nil { + return nil + } + return o.HoldingAccount +} + +func (o *StageSendSourcePayment) GetThroughAccount() *string { + if o == nil { + return nil + } + return o.ThroughAccount +} + +func (o *StageSendSourcePayment) GetAllowOverdraft() *bool { + if o == nil { + return nil + } + return o.AllowOverdraft +} diff --git a/pkg/client/models/components/stagestatus.go b/pkg/client/models/components/stagestatus.go index 8654083..3ae595a 100644 --- a/pkg/client/models/components/stagestatus.go +++ b/pkg/client/models/components/stagestatus.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/subject.go b/pkg/client/models/components/subject.go index 04a51be..c181615 100644 --- a/pkg/client/models/components/subject.go +++ b/pkg/client/models/components/subject.go @@ -6,7 +6,7 @@ import ( "encoding/json" "errors" "fmt" - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" ) type SubjectType string diff --git a/pkg/client/models/components/transaction.go b/pkg/client/models/components/transaction.go index 77e2396..d7aed46 100644 --- a/pkg/client/models/components/transaction.go +++ b/pkg/client/models/components/transaction.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" "time" ) diff --git a/pkg/client/models/components/trigger.go b/pkg/client/models/components/trigger.go index 9f0492b..cf95dd4 100644 --- a/pkg/client/models/components/trigger.go +++ b/pkg/client/models/components/trigger.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/triggeroccurrence.go b/pkg/client/models/components/triggeroccurrence.go index e1f4352..fde22b8 100644 --- a/pkg/client/models/components/triggeroccurrence.go +++ b/pkg/client/models/components/triggeroccurrence.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2activitycreatetransferinitiation.go b/pkg/client/models/components/v2activitycreatetransferinitiation.go new file mode 100644 index 0000000..83e8cb8 --- /dev/null +++ b/pkg/client/models/components/v2activitycreatetransferinitiation.go @@ -0,0 +1,149 @@ +// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + +package components + +import ( + "encoding/json" + "fmt" + "github.com/formancehq/flows/pkg/client/internal/utils" + "math/big" +) + +// V2ActivityCreateTransferInitiationType - Type of transfer initiation: +// - TRANSFER: Internal to internal account transfer +// - PAYOUT: Internal to external account payout +type V2ActivityCreateTransferInitiationType string + +const ( + V2ActivityCreateTransferInitiationTypeTransfer V2ActivityCreateTransferInitiationType = "TRANSFER" + V2ActivityCreateTransferInitiationTypePayout V2ActivityCreateTransferInitiationType = "PAYOUT" +) + +func (e V2ActivityCreateTransferInitiationType) ToPointer() *V2ActivityCreateTransferInitiationType { + return &e +} +func (e *V2ActivityCreateTransferInitiationType) UnmarshalJSON(data []byte) error { + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v { + case "TRANSFER": + fallthrough + case "PAYOUT": + *e = V2ActivityCreateTransferInitiationType(v) + return nil + default: + return fmt.Errorf("invalid value for V2ActivityCreateTransferInitiationType: %v", v) + } +} + +// V2ActivityCreateTransferInitiationMetadata - 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. +type V2ActivityCreateTransferInitiationMetadata struct { +} + +type V2ActivityCreateTransferInitiation struct { + ConnectorID *string `json:"connectorID,omitempty"` + // Payment service provider name (e.g., stripe, wise, mangopay). + // Validated by the Payments service based on installed connectors. + // + Provider *string `json:"provider,omitempty"` + Amount *big.Int `json:"amount,omitempty"` + Asset *string `json:"asset,omitempty"` + // Destination account ID + Destination *string `json:"destination,omitempty"` + // Source account ID (required for TRANSFER type) + Source *string `json:"source,omitempty"` + // Type of transfer initiation: + // - TRANSFER: Internal to internal account transfer + // - PAYOUT: Internal to external account payout + // + Type *V2ActivityCreateTransferInitiationType `default:"TRANSFER" json:"type"` + // Description for the transfer initiation + Description *string `json:"description,omitempty"` + WaitingValidation *bool `default:"false" json:"waitingValidation"` + // 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. + // + Metadata *V2ActivityCreateTransferInitiationMetadata `json:"metadata,omitempty"` +} + +func (v V2ActivityCreateTransferInitiation) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(v, "", false) +} + +func (v *V2ActivityCreateTransferInitiation) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &v, "", false, false); err != nil { + return err + } + return nil +} + +func (o *V2ActivityCreateTransferInitiation) GetConnectorID() *string { + if o == nil { + return nil + } + return o.ConnectorID +} + +func (o *V2ActivityCreateTransferInitiation) GetProvider() *string { + if o == nil { + return nil + } + return o.Provider +} + +func (o *V2ActivityCreateTransferInitiation) GetAmount() *big.Int { + if o == nil { + return nil + } + return o.Amount +} + +func (o *V2ActivityCreateTransferInitiation) GetAsset() *string { + if o == nil { + return nil + } + return o.Asset +} + +func (o *V2ActivityCreateTransferInitiation) GetDestination() *string { + if o == nil { + return nil + } + return o.Destination +} + +func (o *V2ActivityCreateTransferInitiation) GetSource() *string { + if o == nil { + return nil + } + return o.Source +} + +func (o *V2ActivityCreateTransferInitiation) GetType() *V2ActivityCreateTransferInitiationType { + if o == nil { + return nil + } + return o.Type +} + +func (o *V2ActivityCreateTransferInitiation) GetDescription() *string { + if o == nil { + return nil + } + return o.Description +} + +func (o *V2ActivityCreateTransferInitiation) GetWaitingValidation() *bool { + if o == nil { + return nil + } + return o.WaitingValidation +} + +func (o *V2ActivityCreateTransferInitiation) GetMetadata() *V2ActivityCreateTransferInitiationMetadata { + if o == nil { + return nil + } + return o.Metadata +} diff --git a/pkg/client/models/components/v2activitystripetransfer.go b/pkg/client/models/components/v2activitystripetransfer.go index 7def3bd..90ad08f 100644 --- a/pkg/client/models/components/v2activitystripetransfer.go +++ b/pkg/client/models/components/v2activitystripetransfer.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" ) // V2ActivityStripeTransferMetadata - A set of key/value pairs that you can attach to a transfer object. diff --git a/pkg/client/models/components/v2assetholder.go b/pkg/client/models/components/v2assetholder.go index aef51b8..2c5184b 100644 --- a/pkg/client/models/components/v2assetholder.go +++ b/pkg/client/models/components/v2assetholder.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" ) type V2AssetHolder struct { diff --git a/pkg/client/models/components/v2creditwalletrequest.go b/pkg/client/models/components/v2creditwalletrequest.go index c766487..6bf531e 100644 --- a/pkg/client/models/components/v2creditwalletrequest.go +++ b/pkg/client/models/components/v2creditwalletrequest.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2debitwalletrequest.go b/pkg/client/models/components/v2debitwalletrequest.go index 195bf87..666018d 100644 --- a/pkg/client/models/components/v2debitwalletrequest.go +++ b/pkg/client/models/components/v2debitwalletrequest.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2monetary.go b/pkg/client/models/components/v2monetary.go index d7445ed..39a3a74 100644 --- a/pkg/client/models/components/v2monetary.go +++ b/pkg/client/models/components/v2monetary.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" ) type V2Monetary struct { diff --git a/pkg/client/models/components/v2payment.go b/pkg/client/models/components/v2payment.go index a69d42a..723f191 100644 --- a/pkg/client/models/components/v2payment.go +++ b/pkg/client/models/components/v2payment.go @@ -5,8 +5,8 @@ package components import ( "encoding/json" "fmt" + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2paymentadjustment.go b/pkg/client/models/components/v2paymentadjustment.go index 80240d6..627780f 100644 --- a/pkg/client/models/components/v2paymentadjustment.go +++ b/pkg/client/models/components/v2paymentadjustment.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2posting.go b/pkg/client/models/components/v2posting.go index c0204f8..75a8520 100644 --- a/pkg/client/models/components/v2posting.go +++ b/pkg/client/models/components/v2posting.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" ) type V2Posting struct { diff --git a/pkg/client/models/components/v2posttransaction.go b/pkg/client/models/components/v2posttransaction.go index 7c2a928..1b7bfdd 100644 --- a/pkg/client/models/components/v2posttransaction.go +++ b/pkg/client/models/components/v2posttransaction.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2stage.go b/pkg/client/models/components/v2stage.go index 76941f6..c8b6e61 100644 --- a/pkg/client/models/components/v2stage.go +++ b/pkg/client/models/components/v2stage.go @@ -5,7 +5,7 @@ package components import ( "errors" "fmt" - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" ) type V2StageType string diff --git a/pkg/client/models/components/v2stagedelay.go b/pkg/client/models/components/v2stagedelay.go index 3fb7f73..0c3f75d 100644 --- a/pkg/client/models/components/v2stagedelay.go +++ b/pkg/client/models/components/v2stagedelay.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2stagesend.go b/pkg/client/models/components/v2stagesend.go index 7c9e834..c7b24e0 100644 --- a/pkg/client/models/components/v2stagesend.go +++ b/pkg/client/models/components/v2stagesend.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2stagesenddestinationaccount.go b/pkg/client/models/components/v2stagesenddestinationaccount.go index 75a7b5c..c487be8 100644 --- a/pkg/client/models/components/v2stagesenddestinationaccount.go +++ b/pkg/client/models/components/v2stagesenddestinationaccount.go @@ -2,9 +2,36 @@ package components +import ( + "github.com/formancehq/flows/pkg/client/internal/utils" +) + type V2StageSendDestinationAccount struct { ID string `json:"id"` Ledger *string `json:"ledger,omitempty"` + // Account used when this ledger account interacts with external systems (payments, cross-ledger). + // - As SOURCE going to payment: funds are sent to this account (e.g., "liabilities:payouts-pending") + // - As DESTINATION from payment: funds come from this account (e.g., "assets:stripe:incoming") + // - For cross-ledger transfers: replaces "world" on both sides + // + ThroughAccount *string `default:"world" json:"throughAccount"` + // Enables unbounded overdraft on the throughAccount when set to true. + // This is useful when the throughAccount represents a liability or bridge account + // that needs to go negative (e.g., "liabilities:payouts-pending"). + // Only applies when throughAccount is not "world" (which already has unbounded overdraft). + // + AllowOverdraft *bool `default:"false" json:"allowOverdraft"` +} + +func (v V2StageSendDestinationAccount) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(v, "", false) +} + +func (v *V2StageSendDestinationAccount) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &v, "", false, false); err != nil { + return err + } + return nil } func (o *V2StageSendDestinationAccount) GetID() string { @@ -20,3 +47,17 @@ func (o *V2StageSendDestinationAccount) GetLedger() *string { } return o.Ledger } + +func (o *V2StageSendDestinationAccount) GetThroughAccount() *string { + if o == nil { + return nil + } + return o.ThroughAccount +} + +func (o *V2StageSendDestinationAccount) GetAllowOverdraft() *bool { + if o == nil { + return nil + } + return o.AllowOverdraft +} diff --git a/pkg/client/models/components/v2stagesenddestinationpayment.go b/pkg/client/models/components/v2stagesenddestinationpayment.go index 9ce969e..6893507 100644 --- a/pkg/client/models/components/v2stagesenddestinationpayment.go +++ b/pkg/client/models/components/v2stagesenddestinationpayment.go @@ -2,8 +2,66 @@ package components +import ( + "encoding/json" + "fmt" + "github.com/formancehq/flows/pkg/client/internal/utils" +) + +// V2StageSendDestinationPaymentType - Type of transfer initiation: +// - TRANSFER: Internal to internal account transfer +// - PAYOUT: Internal to external account payout +type V2StageSendDestinationPaymentType string + +const ( + V2StageSendDestinationPaymentTypeTransfer V2StageSendDestinationPaymentType = "TRANSFER" + V2StageSendDestinationPaymentTypePayout V2StageSendDestinationPaymentType = "PAYOUT" +) + +func (e V2StageSendDestinationPaymentType) ToPointer() *V2StageSendDestinationPaymentType { + return &e +} +func (e *V2StageSendDestinationPaymentType) UnmarshalJSON(data []byte) error { + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v { + case "TRANSFER": + fallthrough + case "PAYOUT": + *e = V2StageSendDestinationPaymentType(v) + return nil + default: + return fmt.Errorf("invalid value for V2StageSendDestinationPaymentType: %v", v) + } +} + type V2StageSendDestinationPayment struct { + // Payment service provider name (e.g., stripe, wise, mangopay). + // Validated by the Payments service based on installed connectors. + // Psp string `json:"psp"` + // Type of transfer initiation: + // - TRANSFER: Internal to internal account transfer + // - PAYOUT: Internal to external account payout + // + Type *V2StageSendDestinationPaymentType `default:"TRANSFER" json:"type"` + // Formance Payments account ID for the source (internal PSP account). + // If not specified, the Payments service may use a default account for the connector. + // + SourceAccount *string `json:"sourceAccount,omitempty"` +} + +func (v V2StageSendDestinationPayment) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(v, "", false) +} + +func (v *V2StageSendDestinationPayment) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &v, "", false, false); err != nil { + return err + } + return nil } func (o *V2StageSendDestinationPayment) GetPsp() string { @@ -12,3 +70,17 @@ func (o *V2StageSendDestinationPayment) GetPsp() string { } return o.Psp } + +func (o *V2StageSendDestinationPayment) GetType() *V2StageSendDestinationPaymentType { + if o == nil { + return nil + } + return o.Type +} + +func (o *V2StageSendDestinationPayment) GetSourceAccount() *string { + if o == nil { + return nil + } + return o.SourceAccount +} diff --git a/pkg/client/models/components/v2stagesendsourceaccount.go b/pkg/client/models/components/v2stagesendsourceaccount.go index 9320921..a09e47f 100644 --- a/pkg/client/models/components/v2stagesendsourceaccount.go +++ b/pkg/client/models/components/v2stagesendsourceaccount.go @@ -2,9 +2,36 @@ package components +import ( + "github.com/formancehq/flows/pkg/client/internal/utils" +) + type V2StageSendSourceAccount struct { ID string `json:"id"` Ledger *string `json:"ledger,omitempty"` + // Account used when this ledger account interacts with external systems (payments, cross-ledger). + // - As SOURCE going to payment: funds are sent to this account (e.g., "liabilities:payouts-pending") + // - As DESTINATION from payment: funds come from this account (e.g., "assets:stripe:incoming") + // - For cross-ledger transfers: replaces "world" on both sides + // + ThroughAccount *string `default:"world" json:"throughAccount"` + // Enables unbounded overdraft on the throughAccount when set to true. + // This is useful when the throughAccount represents a liability or bridge account + // that needs to go negative (e.g., "liabilities:payouts-pending"). + // Only applies when throughAccount is not "world" (which already has unbounded overdraft). + // + AllowOverdraft *bool `default:"false" json:"allowOverdraft"` +} + +func (v V2StageSendSourceAccount) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(v, "", false) +} + +func (v *V2StageSendSourceAccount) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &v, "", false, false); err != nil { + return err + } + return nil } func (o *V2StageSendSourceAccount) GetID() string { @@ -20,3 +47,17 @@ func (o *V2StageSendSourceAccount) GetLedger() *string { } return o.Ledger } + +func (o *V2StageSendSourceAccount) GetThroughAccount() *string { + if o == nil { + return nil + } + return o.ThroughAccount +} + +func (o *V2StageSendSourceAccount) GetAllowOverdraft() *bool { + if o == nil { + return nil + } + return o.AllowOverdraft +} diff --git a/pkg/client/models/components/v2stagesendsourcepayment.go b/pkg/client/models/components/v2stagesendsourcepayment.go index 3590fca..3cd6bc9 100644 --- a/pkg/client/models/components/v2stagesendsourcepayment.go +++ b/pkg/client/models/components/v2stagesendsourcepayment.go @@ -2,8 +2,39 @@ package components +import ( + "github.com/formancehq/flows/pkg/client/internal/utils" +) + type V2StageSendSourcePayment struct { ID string `json:"id"` + // Ledger to use for payment ingestion. + // Defaults to the internal orchestration ledger. + // + Ledger *string `json:"ledger,omitempty"` + // Intermediate account where payment funds are held. + // Defaults to "payment:{paymentID}" format. + // + HoldingAccount *string `json:"holdingAccount,omitempty"` + // Source account for the payment ingestion transaction. + // Defaults to "world". + // + ThroughAccount *string `default:"world" json:"throughAccount"` + // Enables unbounded overdraft on the throughAccount when set to true. + // Only applies when throughAccount is not "world" (which already has unbounded overdraft). + // + AllowOverdraft *bool `default:"false" json:"allowOverdraft"` +} + +func (v V2StageSendSourcePayment) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(v, "", false) +} + +func (v *V2StageSendSourcePayment) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &v, "", false, false); err != nil { + return err + } + return nil } func (o *V2StageSendSourcePayment) GetID() string { @@ -12,3 +43,31 @@ func (o *V2StageSendSourcePayment) GetID() string { } return o.ID } + +func (o *V2StageSendSourcePayment) GetLedger() *string { + if o == nil { + return nil + } + return o.Ledger +} + +func (o *V2StageSendSourcePayment) GetHoldingAccount() *string { + if o == nil { + return nil + } + return o.HoldingAccount +} + +func (o *V2StageSendSourcePayment) GetThroughAccount() *string { + if o == nil { + return nil + } + return o.ThroughAccount +} + +func (o *V2StageSendSourcePayment) GetAllowOverdraft() *bool { + if o == nil { + return nil + } + return o.AllowOverdraft +} diff --git a/pkg/client/models/components/v2stagestatus.go b/pkg/client/models/components/v2stagestatus.go index 02cbd13..d4bcec4 100644 --- a/pkg/client/models/components/v2stagestatus.go +++ b/pkg/client/models/components/v2stagestatus.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2subject.go b/pkg/client/models/components/v2subject.go index b0f9e50..84fa255 100644 --- a/pkg/client/models/components/v2subject.go +++ b/pkg/client/models/components/v2subject.go @@ -6,7 +6,7 @@ import ( "encoding/json" "errors" "fmt" - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" ) type V2SubjectType string diff --git a/pkg/client/models/components/v2transaction.go b/pkg/client/models/components/v2transaction.go index 7a8e803..9f74673 100644 --- a/pkg/client/models/components/v2transaction.go +++ b/pkg/client/models/components/v2transaction.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2trigger.go b/pkg/client/models/components/v2trigger.go index 5d3a503..5a67cd1 100644 --- a/pkg/client/models/components/v2trigger.go +++ b/pkg/client/models/components/v2trigger.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2triggeroccurrence.go b/pkg/client/models/components/v2triggeroccurrence.go index 9b99452..077c43c 100644 --- a/pkg/client/models/components/v2triggeroccurrence.go +++ b/pkg/client/models/components/v2triggeroccurrence.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2volume.go b/pkg/client/models/components/v2volume.go index 9c89aed..89a24cc 100644 --- a/pkg/client/models/components/v2volume.go +++ b/pkg/client/models/components/v2volume.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" ) type V2Volume struct { diff --git a/pkg/client/models/components/v2wallet.go b/pkg/client/models/components/v2wallet.go index 2743b27..9d0e9a6 100644 --- a/pkg/client/models/components/v2wallet.go +++ b/pkg/client/models/components/v2wallet.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2walletwithbalances.go b/pkg/client/models/components/v2walletwithbalances.go index d3e0664..946af98 100644 --- a/pkg/client/models/components/v2walletwithbalances.go +++ b/pkg/client/models/components/v2walletwithbalances.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2workflow.go b/pkg/client/models/components/v2workflow.go index 07527a8..c823d98 100644 --- a/pkg/client/models/components/v2workflow.go +++ b/pkg/client/models/components/v2workflow.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2workflowinstance.go b/pkg/client/models/components/v2workflowinstance.go index b83bbc5..9cf631c 100644 --- a/pkg/client/models/components/v2workflowinstance.go +++ b/pkg/client/models/components/v2workflowinstance.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2workflowinstancehistory.go b/pkg/client/models/components/v2workflowinstancehistory.go index affcc79..0034a94 100644 --- a/pkg/client/models/components/v2workflowinstancehistory.go +++ b/pkg/client/models/components/v2workflowinstancehistory.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2workflowinstancehistorystage.go b/pkg/client/models/components/v2workflowinstancehistorystage.go index 3a05930..9dc5676 100644 --- a/pkg/client/models/components/v2workflowinstancehistorystage.go +++ b/pkg/client/models/components/v2workflowinstancehistorystage.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/v2workflowinstancehistorystageinput.go b/pkg/client/models/components/v2workflowinstancehistorystageinput.go index d179e20..1a20e26 100644 --- a/pkg/client/models/components/v2workflowinstancehistorystageinput.go +++ b/pkg/client/models/components/v2workflowinstancehistorystageinput.go @@ -3,17 +3,18 @@ package components type V2WorkflowInstanceHistoryStageInput struct { - GetAccount *V2ActivityGetAccount `json:"GetAccount,omitempty"` - AddAccountMetadata *V2ActivityAddAccountMetadata `json:"AddAccountMetadata,omitempty"` - CreateTransaction *V2ActivityCreateTransaction `json:"CreateTransaction,omitempty"` - StripeTransfer *V2ActivityStripeTransfer `json:"StripeTransfer,omitempty"` - GetPayment *V2ActivityGetPayment `json:"GetPayment,omitempty"` - ConfirmHold *V2ActivityConfirmHold `json:"ConfirmHold,omitempty"` - CreditWallet *V2ActivityCreditWallet `json:"CreditWallet,omitempty"` - DebitWallet *V2ActivityDebitWallet `json:"DebitWallet,omitempty"` - GetWallet *V2ActivityGetWallet `json:"GetWallet,omitempty"` - VoidHold *V2ActivityVoidHold `json:"VoidHold,omitempty"` - ListWallets *V2ActivityListWallets `json:"ListWallets,omitempty"` + GetAccount *V2ActivityGetAccount `json:"GetAccount,omitempty"` + AddAccountMetadata *V2ActivityAddAccountMetadata `json:"AddAccountMetadata,omitempty"` + CreateTransaction *V2ActivityCreateTransaction `json:"CreateTransaction,omitempty"` + StripeTransfer *V2ActivityStripeTransfer `json:"StripeTransfer,omitempty"` + CreateTransferInitiation *V2ActivityCreateTransferInitiation `json:"CreateTransferInitiation,omitempty"` + GetPayment *V2ActivityGetPayment `json:"GetPayment,omitempty"` + ConfirmHold *V2ActivityConfirmHold `json:"ConfirmHold,omitempty"` + CreditWallet *V2ActivityCreditWallet `json:"CreditWallet,omitempty"` + DebitWallet *V2ActivityDebitWallet `json:"DebitWallet,omitempty"` + GetWallet *V2ActivityGetWallet `json:"GetWallet,omitempty"` + VoidHold *V2ActivityVoidHold `json:"VoidHold,omitempty"` + ListWallets *V2ActivityListWallets `json:"ListWallets,omitempty"` } func (o *V2WorkflowInstanceHistoryStageInput) GetGetAccount() *V2ActivityGetAccount { @@ -44,6 +45,13 @@ func (o *V2WorkflowInstanceHistoryStageInput) GetStripeTransfer() *V2ActivityStr return o.StripeTransfer } +func (o *V2WorkflowInstanceHistoryStageInput) GetCreateTransferInitiation() *V2ActivityCreateTransferInitiation { + if o == nil { + return nil + } + return o.CreateTransferInitiation +} + func (o *V2WorkflowInstanceHistoryStageInput) GetGetPayment() *V2ActivityGetPayment { if o == nil { return nil diff --git a/pkg/client/models/components/volume.go b/pkg/client/models/components/volume.go index cfbe4f5..cfa8427 100644 --- a/pkg/client/models/components/volume.go +++ b/pkg/client/models/components/volume.go @@ -3,8 +3,8 @@ package components import ( + "github.com/formancehq/flows/pkg/client/internal/utils" "math/big" - "openapi/internal/utils" ) type Volume struct { diff --git a/pkg/client/models/components/wallet.go b/pkg/client/models/components/wallet.go index 24eb6dd..01123a2 100644 --- a/pkg/client/models/components/wallet.go +++ b/pkg/client/models/components/wallet.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/walletwithbalances.go b/pkg/client/models/components/walletwithbalances.go index 94e13dc..0252604 100644 --- a/pkg/client/models/components/walletwithbalances.go +++ b/pkg/client/models/components/walletwithbalances.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/workflow.go b/pkg/client/models/components/workflow.go index 2c4eb09..bac2b2a 100644 --- a/pkg/client/models/components/workflow.go +++ b/pkg/client/models/components/workflow.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/workflowinstance.go b/pkg/client/models/components/workflowinstance.go index 12a7047..e0ddc77 100644 --- a/pkg/client/models/components/workflowinstance.go +++ b/pkg/client/models/components/workflowinstance.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/workflowinstancehistory.go b/pkg/client/models/components/workflowinstancehistory.go index 4c5deea..2cb9713 100644 --- a/pkg/client/models/components/workflowinstancehistory.go +++ b/pkg/client/models/components/workflowinstancehistory.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/workflowinstancehistorystage.go b/pkg/client/models/components/workflowinstancehistorystage.go index 229315c..bb6c6a5 100644 --- a/pkg/client/models/components/workflowinstancehistorystage.go +++ b/pkg/client/models/components/workflowinstancehistorystage.go @@ -3,7 +3,7 @@ package components import ( - "openapi/internal/utils" + "github.com/formancehq/flows/pkg/client/internal/utils" "time" ) diff --git a/pkg/client/models/components/workflowinstancehistorystageinput.go b/pkg/client/models/components/workflowinstancehistorystageinput.go index a247f6d..cacc1f5 100644 --- a/pkg/client/models/components/workflowinstancehistorystageinput.go +++ b/pkg/client/models/components/workflowinstancehistorystageinput.go @@ -3,18 +3,19 @@ package components type WorkflowInstanceHistoryStageInput struct { - GetAccount *ActivityGetAccount `json:"GetAccount,omitempty"` - AddAccountMetadata *ActivityAddAccountMetadata `json:"AddAccountMetadata,omitempty"` - CreateTransaction *ActivityCreateTransaction `json:"CreateTransaction,omitempty"` - RevertTransaction *ActivityRevertTransaction `json:"RevertTransaction,omitempty"` - StripeTransfer *ActivityStripeTransfer `json:"StripeTransfer,omitempty"` - GetPayment *ActivityGetPayment `json:"GetPayment,omitempty"` - ConfirmHold *ActivityConfirmHold `json:"ConfirmHold,omitempty"` - CreditWallet *ActivityCreditWallet `json:"CreditWallet,omitempty"` - DebitWallet *ActivityDebitWallet `json:"DebitWallet,omitempty"` - GetWallet *ActivityGetWallet `json:"GetWallet,omitempty"` - VoidHold *ActivityVoidHold `json:"VoidHold,omitempty"` - ListWallets *ActivityListWallets `json:"ListWallets,omitempty"` + GetAccount *ActivityGetAccount `json:"GetAccount,omitempty"` + AddAccountMetadata *ActivityAddAccountMetadata `json:"AddAccountMetadata,omitempty"` + CreateTransaction *ActivityCreateTransaction `json:"CreateTransaction,omitempty"` + RevertTransaction *ActivityRevertTransaction `json:"RevertTransaction,omitempty"` + StripeTransfer *ActivityStripeTransfer `json:"StripeTransfer,omitempty"` + CreateTransferInitiation *ActivityCreateTransferInitiation `json:"CreateTransferInitiation,omitempty"` + GetPayment *ActivityGetPayment `json:"GetPayment,omitempty"` + ConfirmHold *ActivityConfirmHold `json:"ConfirmHold,omitempty"` + CreditWallet *ActivityCreditWallet `json:"CreditWallet,omitempty"` + DebitWallet *ActivityDebitWallet `json:"DebitWallet,omitempty"` + GetWallet *ActivityGetWallet `json:"GetWallet,omitempty"` + VoidHold *ActivityVoidHold `json:"VoidHold,omitempty"` + ListWallets *ActivityListWallets `json:"ListWallets,omitempty"` } func (o *WorkflowInstanceHistoryStageInput) GetGetAccount() *ActivityGetAccount { @@ -52,6 +53,13 @@ func (o *WorkflowInstanceHistoryStageInput) GetStripeTransfer() *ActivityStripeT return o.StripeTransfer } +func (o *WorkflowInstanceHistoryStageInput) GetCreateTransferInitiation() *ActivityCreateTransferInitiation { + if o == nil { + return nil + } + return o.CreateTransferInitiation +} + func (o *WorkflowInstanceHistoryStageInput) GetGetPayment() *ActivityGetPayment { if o == nil { return nil diff --git a/pkg/client/models/operations/cancelevent.go b/pkg/client/models/operations/cancelevent.go index 91bcf9a..69968c2 100644 --- a/pkg/client/models/operations/cancelevent.go +++ b/pkg/client/models/operations/cancelevent.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type CancelEventRequest struct { diff --git a/pkg/client/models/operations/createtrigger.go b/pkg/client/models/operations/createtrigger.go index b924137..b1c5119 100644 --- a/pkg/client/models/operations/createtrigger.go +++ b/pkg/client/models/operations/createtrigger.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type CreateTriggerResponse struct { diff --git a/pkg/client/models/operations/createworkflow.go b/pkg/client/models/operations/createworkflow.go index fb47f79..1256e10 100644 --- a/pkg/client/models/operations/createworkflow.go +++ b/pkg/client/models/operations/createworkflow.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type CreateWorkflowResponse struct { diff --git a/pkg/client/models/operations/deletetrigger.go b/pkg/client/models/operations/deletetrigger.go index 70b186c..d12c6e2 100644 --- a/pkg/client/models/operations/deletetrigger.go +++ b/pkg/client/models/operations/deletetrigger.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type DeleteTriggerRequest struct { diff --git a/pkg/client/models/operations/deleteworkflow.go b/pkg/client/models/operations/deleteworkflow.go index 02494b8..087cf2f 100644 --- a/pkg/client/models/operations/deleteworkflow.go +++ b/pkg/client/models/operations/deleteworkflow.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type DeleteWorkflowRequest struct { diff --git a/pkg/client/models/operations/getinstance.go b/pkg/client/models/operations/getinstance.go index d85f2d1..9cf9dba 100644 --- a/pkg/client/models/operations/getinstance.go +++ b/pkg/client/models/operations/getinstance.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type GetInstanceRequest struct { diff --git a/pkg/client/models/operations/getinstancehistory.go b/pkg/client/models/operations/getinstancehistory.go index 62248da..0ca4c17 100644 --- a/pkg/client/models/operations/getinstancehistory.go +++ b/pkg/client/models/operations/getinstancehistory.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type GetInstanceHistoryRequest struct { diff --git a/pkg/client/models/operations/getinstancestagehistory.go b/pkg/client/models/operations/getinstancestagehistory.go index 9a75d26..6e25681 100644 --- a/pkg/client/models/operations/getinstancestagehistory.go +++ b/pkg/client/models/operations/getinstancestagehistory.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type GetInstanceStageHistoryRequest struct { diff --git a/pkg/client/models/operations/getserverinfo.go b/pkg/client/models/operations/getserverinfo.go index be93577..e9f1549 100644 --- a/pkg/client/models/operations/getserverinfo.go +++ b/pkg/client/models/operations/getserverinfo.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type GetServerInfoResponse struct { diff --git a/pkg/client/models/operations/getworkflow.go b/pkg/client/models/operations/getworkflow.go index 86159c7..552703b 100644 --- a/pkg/client/models/operations/getworkflow.go +++ b/pkg/client/models/operations/getworkflow.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type GetWorkflowRequest struct { diff --git a/pkg/client/models/operations/listinstances.go b/pkg/client/models/operations/listinstances.go index 2a7fb59..c53a7da 100644 --- a/pkg/client/models/operations/listinstances.go +++ b/pkg/client/models/operations/listinstances.go @@ -3,7 +3,8 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/internal/utils" + "github.com/formancehq/flows/pkg/client/models/components" ) type ListInstancesRequest struct { @@ -11,6 +12,26 @@ type ListInstancesRequest struct { WorkflowID *string `queryParam:"style=form,explode=true,name=workflowID"` // Filter running instances Running *bool `queryParam:"style=form,explode=true,name=running"` + // The maximum number of results to return per page. + // + PageSize *int64 `default:"15" queryParam:"style=form,explode=true,name=pageSize"` + // Parameter used in pagination requests. + // Set to the value of next for the next page of results. + // Set to the value of previous for the previous page of results. + // No other parameters can be set when this parameter is set. + // + Cursor *string `queryParam:"style=form,explode=true,name=cursor"` +} + +func (l ListInstancesRequest) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(l, "", false) +} + +func (l *ListInstancesRequest) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &l, "", false, false); err != nil { + return err + } + return nil } func (o *ListInstancesRequest) GetWorkflowID() *string { @@ -27,6 +48,20 @@ func (o *ListInstancesRequest) GetRunning() *bool { return o.Running } +func (o *ListInstancesRequest) GetPageSize() *int64 { + if o == nil { + return nil + } + return o.PageSize +} + +func (o *ListInstancesRequest) GetCursor() *string { + if o == nil { + return nil + } + return o.Cursor +} + type ListInstancesResponse struct { HTTPMeta components.HTTPMetadata `json:"-"` // List of workflow instances diff --git a/pkg/client/models/operations/listtriggers.go b/pkg/client/models/operations/listtriggers.go index 64fbd4e..63897da 100644 --- a/pkg/client/models/operations/listtriggers.go +++ b/pkg/client/models/operations/listtriggers.go @@ -3,12 +3,33 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/internal/utils" + "github.com/formancehq/flows/pkg/client/models/components" ) type ListTriggersRequest struct { // search by name Name *string `queryParam:"style=form,explode=true,name=name"` + // The maximum number of results to return per page. + // + PageSize *int64 `default:"15" queryParam:"style=form,explode=true,name=pageSize"` + // Parameter used in pagination requests. + // Set to the value of next for the next page of results. + // Set to the value of previous for the previous page of results. + // No other parameters can be set when this parameter is set. + // + Cursor *string `queryParam:"style=form,explode=true,name=cursor"` +} + +func (l ListTriggersRequest) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(l, "", false) +} + +func (l *ListTriggersRequest) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &l, "", false, false); err != nil { + return err + } + return nil } func (o *ListTriggersRequest) GetName() *string { @@ -18,6 +39,20 @@ func (o *ListTriggersRequest) GetName() *string { return o.Name } +func (o *ListTriggersRequest) GetPageSize() *int64 { + if o == nil { + return nil + } + return o.PageSize +} + +func (o *ListTriggersRequest) GetCursor() *string { + if o == nil { + return nil + } + return o.Cursor +} + type ListTriggersResponse struct { HTTPMeta components.HTTPMetadata `json:"-"` // List of triggers diff --git a/pkg/client/models/operations/listtriggersoccurrences.go b/pkg/client/models/operations/listtriggersoccurrences.go index 58ffc01..9b9445e 100644 --- a/pkg/client/models/operations/listtriggersoccurrences.go +++ b/pkg/client/models/operations/listtriggersoccurrences.go @@ -3,12 +3,33 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/internal/utils" + "github.com/formancehq/flows/pkg/client/models/components" ) type ListTriggersOccurrencesRequest struct { // The trigger id TriggerID string `pathParam:"style=simple,explode=false,name=triggerID"` + // The maximum number of results to return per page. + // + PageSize *int64 `default:"15" queryParam:"style=form,explode=true,name=pageSize"` + // Parameter used in pagination requests. + // Set to the value of next for the next page of results. + // Set to the value of previous for the previous page of results. + // No other parameters can be set when this parameter is set. + // + Cursor *string `queryParam:"style=form,explode=true,name=cursor"` +} + +func (l ListTriggersOccurrencesRequest) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(l, "", false) +} + +func (l *ListTriggersOccurrencesRequest) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &l, "", false, false); err != nil { + return err + } + return nil } func (o *ListTriggersOccurrencesRequest) GetTriggerID() string { @@ -18,6 +39,20 @@ func (o *ListTriggersOccurrencesRequest) GetTriggerID() string { return o.TriggerID } +func (o *ListTriggersOccurrencesRequest) GetPageSize() *int64 { + if o == nil { + return nil + } + return o.PageSize +} + +func (o *ListTriggersOccurrencesRequest) GetCursor() *string { + if o == nil { + return nil + } + return o.Cursor +} + type ListTriggersOccurrencesResponse struct { HTTPMeta components.HTTPMetadata `json:"-"` // List of triggers occurrences diff --git a/pkg/client/models/operations/listworkflows.go b/pkg/client/models/operations/listworkflows.go index 77e863b..7b99f1d 100644 --- a/pkg/client/models/operations/listworkflows.go +++ b/pkg/client/models/operations/listworkflows.go @@ -3,9 +3,47 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/internal/utils" + "github.com/formancehq/flows/pkg/client/models/components" ) +type ListWorkflowsRequest struct { + // The maximum number of results to return per page. + // + PageSize *int64 `default:"15" queryParam:"style=form,explode=true,name=pageSize"` + // Parameter used in pagination requests. + // Set to the value of next for the next page of results. + // Set to the value of previous for the previous page of results. + // No other parameters can be set when this parameter is set. + // + Cursor *string `queryParam:"style=form,explode=true,name=cursor"` +} + +func (l ListWorkflowsRequest) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(l, "", false) +} + +func (l *ListWorkflowsRequest) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &l, "", false, false); err != nil { + return err + } + return nil +} + +func (o *ListWorkflowsRequest) GetPageSize() *int64 { + if o == nil { + return nil + } + return o.PageSize +} + +func (o *ListWorkflowsRequest) GetCursor() *string { + if o == nil { + return nil + } + return o.Cursor +} + type ListWorkflowsResponse struct { HTTPMeta components.HTTPMetadata `json:"-"` // List of workflows diff --git a/pkg/client/models/operations/options.go b/pkg/client/models/operations/options.go index db37fac..9f2db90 100644 --- a/pkg/client/models/operations/options.go +++ b/pkg/client/models/operations/options.go @@ -4,8 +4,8 @@ package operations import ( "errors" - "openapi/internal/utils" - "openapi/retry" + "github.com/formancehq/flows/pkg/client/internal/utils" + "github.com/formancehq/flows/pkg/client/retry" "time" ) diff --git a/pkg/client/models/operations/readtrigger.go b/pkg/client/models/operations/readtrigger.go index cd30d8d..9b7daf0 100644 --- a/pkg/client/models/operations/readtrigger.go +++ b/pkg/client/models/operations/readtrigger.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type ReadTriggerRequest struct { diff --git a/pkg/client/models/operations/runworkflow.go b/pkg/client/models/operations/runworkflow.go index ba194f7..32808d6 100644 --- a/pkg/client/models/operations/runworkflow.go +++ b/pkg/client/models/operations/runworkflow.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type RunWorkflowRequest struct { diff --git a/pkg/client/models/operations/sendevent.go b/pkg/client/models/operations/sendevent.go index 53aacd5..34df589 100644 --- a/pkg/client/models/operations/sendevent.go +++ b/pkg/client/models/operations/sendevent.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type SendEventRequestBody struct { diff --git a/pkg/client/models/operations/testtrigger.go b/pkg/client/models/operations/testtrigger.go index db41c2a..94f1f6e 100644 --- a/pkg/client/models/operations/testtrigger.go +++ b/pkg/client/models/operations/testtrigger.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type TestTriggerRequest struct { diff --git a/pkg/client/models/operations/v2cancelevent.go b/pkg/client/models/operations/v2cancelevent.go index 33b81a6..1146ce5 100644 --- a/pkg/client/models/operations/v2cancelevent.go +++ b/pkg/client/models/operations/v2cancelevent.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2CancelEventRequest struct { diff --git a/pkg/client/models/operations/v2createtrigger.go b/pkg/client/models/operations/v2createtrigger.go index a22f497..86120a6 100644 --- a/pkg/client/models/operations/v2createtrigger.go +++ b/pkg/client/models/operations/v2createtrigger.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2CreateTriggerResponse struct { diff --git a/pkg/client/models/operations/v2createworkflow.go b/pkg/client/models/operations/v2createworkflow.go index c5ae3c0..c3beece 100644 --- a/pkg/client/models/operations/v2createworkflow.go +++ b/pkg/client/models/operations/v2createworkflow.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2CreateWorkflowResponse struct { diff --git a/pkg/client/models/operations/v2deletetrigger.go b/pkg/client/models/operations/v2deletetrigger.go index a91bac2..034d167 100644 --- a/pkg/client/models/operations/v2deletetrigger.go +++ b/pkg/client/models/operations/v2deletetrigger.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2DeleteTriggerRequest struct { diff --git a/pkg/client/models/operations/v2deleteworkflow.go b/pkg/client/models/operations/v2deleteworkflow.go index aacbb14..b17b7a7 100644 --- a/pkg/client/models/operations/v2deleteworkflow.go +++ b/pkg/client/models/operations/v2deleteworkflow.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2DeleteWorkflowRequest struct { diff --git a/pkg/client/models/operations/v2getinstance.go b/pkg/client/models/operations/v2getinstance.go index c2c9628..5095356 100644 --- a/pkg/client/models/operations/v2getinstance.go +++ b/pkg/client/models/operations/v2getinstance.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2GetInstanceRequest struct { diff --git a/pkg/client/models/operations/v2getinstancehistory.go b/pkg/client/models/operations/v2getinstancehistory.go index 097e29d..f944651 100644 --- a/pkg/client/models/operations/v2getinstancehistory.go +++ b/pkg/client/models/operations/v2getinstancehistory.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2GetInstanceHistoryRequest struct { diff --git a/pkg/client/models/operations/v2getinstancestagehistory.go b/pkg/client/models/operations/v2getinstancestagehistory.go index 7b817c0..1a4cc81 100644 --- a/pkg/client/models/operations/v2getinstancestagehistory.go +++ b/pkg/client/models/operations/v2getinstancestagehistory.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2GetInstanceStageHistoryRequest struct { diff --git a/pkg/client/models/operations/v2getserverinfo.go b/pkg/client/models/operations/v2getserverinfo.go index 109b13c..0dc868b 100644 --- a/pkg/client/models/operations/v2getserverinfo.go +++ b/pkg/client/models/operations/v2getserverinfo.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2GetServerInfoResponse struct { diff --git a/pkg/client/models/operations/v2getworkflow.go b/pkg/client/models/operations/v2getworkflow.go index 88269d1..1b264c4 100644 --- a/pkg/client/models/operations/v2getworkflow.go +++ b/pkg/client/models/operations/v2getworkflow.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2GetWorkflowRequest struct { diff --git a/pkg/client/models/operations/v2listinstances.go b/pkg/client/models/operations/v2listinstances.go index a481c22..9ce001f 100644 --- a/pkg/client/models/operations/v2listinstances.go +++ b/pkg/client/models/operations/v2listinstances.go @@ -3,7 +3,8 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/internal/utils" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2ListInstancesRequest struct { @@ -15,13 +16,24 @@ type V2ListInstancesRequest struct { Cursor *string `queryParam:"style=form,explode=true,name=cursor"` // The maximum number of results to return per page. // - PageSize *int64 `queryParam:"style=form,explode=true,name=pageSize"` + PageSize *int64 `default:"15" queryParam:"style=form,explode=true,name=pageSize"` // A workflow id WorkflowID *string `queryParam:"style=form,explode=true,name=workflowID"` // Filter running instances Running *bool `queryParam:"style=form,explode=true,name=running"` } +func (v V2ListInstancesRequest) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(v, "", false) +} + +func (v *V2ListInstancesRequest) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &v, "", false, false); err != nil { + return err + } + return nil +} + func (o *V2ListInstancesRequest) GetCursor() *string { if o == nil { return nil diff --git a/pkg/client/models/operations/v2listtriggers.go b/pkg/client/models/operations/v2listtriggers.go index 78451e8..b37050a 100644 --- a/pkg/client/models/operations/v2listtriggers.go +++ b/pkg/client/models/operations/v2listtriggers.go @@ -3,7 +3,8 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/internal/utils" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2ListTriggersRequest struct { @@ -15,11 +16,22 @@ type V2ListTriggersRequest struct { Cursor *string `queryParam:"style=form,explode=true,name=cursor"` // The maximum number of results to return per page. // - PageSize *int64 `queryParam:"style=form,explode=true,name=pageSize"` + PageSize *int64 `default:"15" queryParam:"style=form,explode=true,name=pageSize"` // search by name Name *string `queryParam:"style=form,explode=true,name=name"` } +func (v V2ListTriggersRequest) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(v, "", false) +} + +func (v *V2ListTriggersRequest) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &v, "", false, false); err != nil { + return err + } + return nil +} + func (o *V2ListTriggersRequest) GetCursor() *string { if o == nil { return nil diff --git a/pkg/client/models/operations/v2listtriggersoccurrences.go b/pkg/client/models/operations/v2listtriggersoccurrences.go index 4da007a..6234d92 100644 --- a/pkg/client/models/operations/v2listtriggersoccurrences.go +++ b/pkg/client/models/operations/v2listtriggersoccurrences.go @@ -3,7 +3,8 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/internal/utils" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2ListTriggersOccurrencesRequest struct { @@ -17,7 +18,18 @@ type V2ListTriggersOccurrencesRequest struct { Cursor *string `queryParam:"style=form,explode=true,name=cursor"` // The maximum number of results to return per page. // - PageSize *int64 `queryParam:"style=form,explode=true,name=pageSize"` + PageSize *int64 `default:"15" queryParam:"style=form,explode=true,name=pageSize"` +} + +func (v V2ListTriggersOccurrencesRequest) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(v, "", false) +} + +func (v *V2ListTriggersOccurrencesRequest) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &v, "", false, false); err != nil { + return err + } + return nil } func (o *V2ListTriggersOccurrencesRequest) GetTriggerID() string { diff --git a/pkg/client/models/operations/v2listworkflows.go b/pkg/client/models/operations/v2listworkflows.go index a849397..ac9a3f7 100644 --- a/pkg/client/models/operations/v2listworkflows.go +++ b/pkg/client/models/operations/v2listworkflows.go @@ -3,7 +3,8 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/internal/utils" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2ListWorkflowsRequest struct { @@ -15,7 +16,18 @@ type V2ListWorkflowsRequest struct { Cursor *string `queryParam:"style=form,explode=true,name=cursor"` // The maximum number of results to return per page. // - PageSize *int64 `queryParam:"style=form,explode=true,name=pageSize"` + PageSize *int64 `default:"15" queryParam:"style=form,explode=true,name=pageSize"` +} + +func (v V2ListWorkflowsRequest) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(v, "", false) +} + +func (v *V2ListWorkflowsRequest) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &v, "", false, false); err != nil { + return err + } + return nil } func (o *V2ListWorkflowsRequest) GetCursor() *string { diff --git a/pkg/client/models/operations/v2readtrigger.go b/pkg/client/models/operations/v2readtrigger.go index 3d279cc..8ebaf50 100644 --- a/pkg/client/models/operations/v2readtrigger.go +++ b/pkg/client/models/operations/v2readtrigger.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2ReadTriggerRequest struct { diff --git a/pkg/client/models/operations/v2runworkflow.go b/pkg/client/models/operations/v2runworkflow.go index 9a9ca09..f258376 100644 --- a/pkg/client/models/operations/v2runworkflow.go +++ b/pkg/client/models/operations/v2runworkflow.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2RunWorkflowRequest struct { diff --git a/pkg/client/models/operations/v2sendevent.go b/pkg/client/models/operations/v2sendevent.go index 9599ef3..fe18716 100644 --- a/pkg/client/models/operations/v2sendevent.go +++ b/pkg/client/models/operations/v2sendevent.go @@ -3,7 +3,7 @@ package operations import ( - "openapi/models/components" + "github.com/formancehq/flows/pkg/client/models/components" ) type V2SendEventRequestBody struct { diff --git a/pkg/client/orchestration.go b/pkg/client/orchestration.go index 75c97e3..847e9c9 100644 --- a/pkg/client/orchestration.go +++ b/pkg/client/orchestration.go @@ -1,6 +1,6 @@ // Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. -package openapi +package client type Orchestration struct { V1 *V1 diff --git a/pkg/client/sdk.go b/pkg/client/sdk.go index 7226a21..e0f43b3 100644 --- a/pkg/client/sdk.go +++ b/pkg/client/sdk.go @@ -1,15 +1,15 @@ // Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. -package openapi +package client import ( "context" "fmt" + "github.com/formancehq/flows/pkg/client/internal/hooks" + "github.com/formancehq/flows/pkg/client/internal/utils" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client/retry" "net/http" - "openapi/internal/hooks" - "openapi/internal/utils" - "openapi/models/components" - "openapi/retry" "time" ) @@ -143,9 +143,9 @@ func New(opts ...SDKOption) *SDK { sdkConfiguration: sdkConfiguration{ Language: "go", OpenAPIDocVersion: "0.1.0", - SDKVersion: "0.1.4", + SDKVersion: "0.1.7", GenVersion: "2.384.1", - UserAgent: "speakeasy-sdk/go 0.1.4 2.384.1 0.1.0 openapi", + UserAgent: "speakeasy-sdk/go 0.1.7 2.384.1 0.1.0 github.com/formancehq/flows/pkg/client", Hooks: hooks.New(), }, } diff --git a/pkg/client/v1.go b/pkg/client/v1.go index 1ad16bc..1c29f53 100644 --- a/pkg/client/v1.go +++ b/pkg/client/v1.go @@ -1,20 +1,20 @@ // Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. -package openapi +package client import ( "bytes" "context" "fmt" "github.com/cenkalti/backoff/v4" + "github.com/formancehq/flows/pkg/client/internal/hooks" + "github.com/formancehq/flows/pkg/client/internal/utils" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client/models/operations" + "github.com/formancehq/flows/pkg/client/models/sdkerrors" "io" "net/http" "net/url" - "openapi/internal/hooks" - "openapi/internal/utils" - "openapi/models/components" - "openapi/models/operations" - "openapi/models/sdkerrors" ) type V1 struct { @@ -971,6 +971,10 @@ func (s *V1) ListTriggersOccurrences(ctx context.Context, request operations.Lis req.Header.Set("Accept", "application/json") req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) + if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { + return nil, fmt.Errorf("error populating query params: %w", err) + } + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { return nil, err } @@ -1107,7 +1111,7 @@ func (s *V1) ListTriggersOccurrences(ctx context.Context, request operations.Lis // ListWorkflows - List registered workflows // List registered workflows -func (s *V1) ListWorkflows(ctx context.Context, opts ...operations.Option) (*operations.ListWorkflowsResponse, error) { +func (s *V1) ListWorkflows(ctx context.Context, request operations.ListWorkflowsRequest, opts ...operations.Option) (*operations.ListWorkflowsResponse, error) { hookCtx := hooks.HookContext{ Context: ctx, OperationID: "listWorkflows", @@ -1151,6 +1155,10 @@ func (s *V1) ListWorkflows(ctx context.Context, opts ...operations.Option) (*ope req.Header.Set("Accept", "application/json") req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) + if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { + return nil, fmt.Errorf("error populating query params: %w", err) + } + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { return nil, err } diff --git a/pkg/client/v2.go b/pkg/client/v2.go index f9bc834..03b75e1 100644 --- a/pkg/client/v2.go +++ b/pkg/client/v2.go @@ -1,20 +1,20 @@ // Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. -package openapi +package client import ( "bytes" "context" "fmt" "github.com/cenkalti/backoff/v4" + "github.com/formancehq/flows/pkg/client/internal/hooks" + "github.com/formancehq/flows/pkg/client/internal/utils" + "github.com/formancehq/flows/pkg/client/models/components" + "github.com/formancehq/flows/pkg/client/models/operations" + "github.com/formancehq/flows/pkg/client/models/sdkerrors" "io" "net/http" "net/url" - "openapi/internal/hooks" - "openapi/internal/utils" - "openapi/models/components" - "openapi/models/operations" - "openapi/models/sdkerrors" ) type V2 struct { diff --git a/pkg/events/events.go b/pkg/events/events.go index 420d765..ad20bcb 100644 --- a/pkg/events/events.go +++ b/pkg/events/events.go @@ -5,7 +5,7 @@ import ( "time" "github.com/ThreeDotsLabs/watermill/message" - "github.com/formancehq/go-libs/v3/publish" + "github.com/formancehq/go-libs/v5/pkg/messaging/publish" ) const (