diff --git a/ee/webhooks/cmd/webhook-all-in-one.go b/ee/webhooks/cmd/webhook-all-in-one.go index 5d4726e0ce..50e5a0e4bb 100644 --- a/ee/webhooks/cmd/webhook-all-in-one.go +++ b/ee/webhooks/cmd/webhook-all-in-one.go @@ -133,19 +133,18 @@ func allInOneRun(cmd *cobra.Command, _ []string) error { r.AddNoPublisherHandler(fmt.Sprintf("messages-%s", topic), topic, subscriber, Worker.HandleMessage) } - lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { logging.FromContext(ctx).Infof("Start Webhook_Worker") - + return nil }, OnStop: func(ctx context.Context) error { logging.FromContext(ctx).Infof("Stop Webhook_Worker") - + subscriber.Close() - + Worker.Stop() return nil }, diff --git a/ee/webhooks/cmd/webhook-worker.go b/ee/webhooks/cmd/webhook-worker.go index c136f96d7e..f2264e6982 100644 --- a/ee/webhooks/cmd/webhook-worker.go +++ b/ee/webhooks/cmd/webhook-worker.go @@ -90,16 +90,16 @@ func webhookWorkerRun(cmd *cobra.Command, _ []string) error { Worker.Init() for _, topic := range topics { r.AddNoPublisherHandler(fmt.Sprintf("messages-%s", topic), topic, subscriber, Worker.HandleMessage) - + } - + lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { - + return nil }, OnStop: func(ctx context.Context) error { - + subscriber.Close() r.Close() Worker.Stop() diff --git a/ee/webhooks/internal/commons/attempt.go b/ee/webhooks/internal/commons/attempt.go index b2d5871520..0129f56f3f 100644 --- a/ee/webhooks/internal/commons/attempt.go +++ b/ee/webhooks/internal/commons/attempt.go @@ -31,7 +31,7 @@ const ( type Attempt struct { ID string `json:"id" bun:",pk"` - HookID string `json:"hookID" bun:"webhook_id"` + HookID string `json:"hookId" bun:"webhook_id"` HookName string `json:"hookName" bun:"hook_name"` HookEndpoint string `json:"hookEndpoint" bun:"hook_endpoint"` Event string `json:"event" bun:"event"` diff --git a/ee/webhooks/internal/components/commons/webhook_runner.go b/ee/webhooks/internal/components/commons/webhook_runner.go index 828f60c491..ad3a0798d1 100644 --- a/ee/webhooks/internal/components/commons/webhook_runner.go +++ b/ee/webhooks/internal/components/commons/webhook_runner.go @@ -55,8 +55,9 @@ func (wr *WebhookRunner) StartHandleFreshLogs() { } func (wr *WebhookRunner) HandleFreshLogs(stopChan chan struct{}) { - - ticker := time.NewTicker(time.Duration(wr.RunnerParams.DelayPull) * time.Second) + + delay := time.Duration(wr.RunnerParams.DelayPull) * time.Second + ticker := time.NewTicker(time.Duration(delay)) var last_time time.Time = time.Now() for { @@ -64,6 +65,7 @@ func (wr *WebhookRunner) HandleFreshLogs(stopChan chan struct{}) { case <-stopChan: return case <-ticker.C: + logging.Infof("Webhook_Runner :: HandleFreshLogs() - Case Ticker C : ") freezeTime := time.Now() logs, err := wr.Database.GetFreshLogs(wr.LogChannels, last_time) last_time = freezeTime @@ -74,6 +76,7 @@ func (wr *WebhookRunner) HandleFreshLogs(stopChan chan struct{}) { } for _, log := range *logs { + logging.Infof("Webhook_Runner :: HandleFreshLogs() - NewLog ") wr.HandleFreshLog(log) } } @@ -82,7 +85,7 @@ func (wr *WebhookRunner) HandleFreshLogs(stopChan chan struct{}) { } func (wr *WebhookRunner) HandleFreshLog(log *commons.Log) { - fmt.Println("HandleFreshLog") + logging.Infof("Webhook_Runner :: HandleFreshLog() - Log.Payload : %s", log.Payload) e, err := commons.Event{}.FromPayload(log.Payload) if err != nil { message := fmt.Sprintf("WebhookRunner:HandleFreshLogs() - LogChannels : %s : Error while Event.FromPayload(log.payload): %x", wr.LogChannels, err) @@ -104,11 +107,9 @@ func (wr *WebhookRunner) HandleFreshLog(log *commons.Log) { } case commons.ChangeHookStatusType: - fmt.Println(e.Value) strValue := e.Value.(string) switch commons.HookStatus(strValue) { case commons.EnableStatus: - fmt.Println("ENABLE CASE") wr.State.ActivateHook(e.ID) case commons.DisableStatus: wr.State.DisableHook(e.ID) diff --git a/ee/webhooks/internal/components/webhook_collector/collector.go b/ee/webhooks/internal/components/webhook_collector/collector.go index 28ee15ea66..db6cb493a5 100644 --- a/ee/webhooks/internal/components/webhook_collector/collector.go +++ b/ee/webhooks/internal/components/webhook_collector/collector.go @@ -94,6 +94,18 @@ func (c *Collector) AsyncHandleSharedAttempt(sAttempt *commons.SharedAttempt, wg return } + if(sAttempt.Val.HookEndpoint != sHook.Val.Endpoint){ + sAttempt.Val.HookEndpoint = sHook.Val.Endpoint + go func(){ + _, err := c.Database.UpdateAttemptEndpoint(sAttempt.Val.ID, sAttempt.Val.HookEndpoint) + if err != nil { + message := fmt.Sprintf("Collector:AsyncHandleSharedAttempt:Database.UpdateAttemptEndpoint() : %x", err) + logging.Error(message) + panic(message) + } + }() + } + sAttempt.Val.LastHttpStatusCode = statusCode if commons.IsHTTPRequestSuccess(statusCode) { @@ -117,7 +129,7 @@ func (c *Collector) handleSuccess(sAttempt *commons.SharedAttempt) { func (c *Collector) handleNextRetry(sAttempt *commons.SharedAttempt) { sAttempt.Val.NbTry += 1 - if(c.RunnerParams.MaxRetry <= sAttempt.Val.NbTry ) { + if c.RunnerParams.MaxRetry <= sAttempt.Val.NbTry { commons.SetAbortMaxRetryStatus(sAttempt.Val) _, err := c.Database.AbortAttempt(sAttempt.Val.ID, string(sAttempt.Val.Comment), false) if err != nil { @@ -125,7 +137,7 @@ func (c *Collector) handleNextRetry(sAttempt *commons.SharedAttempt) { logging.Error(message) panic(message) } - }else { + } else { commons.SetNextRetry(sAttempt.Val) c.State.WaitingAttempts.Add(sAttempt) diff --git a/ee/webhooks/internal/components/webhook_controller/controllers/utils/utils.go b/ee/webhooks/internal/components/webhook_controller/controllers/utils/utils.go index f5377f3a6c..2b0221c535 100644 --- a/ee/webhooks/internal/components/webhook_controller/controllers/utils/utils.go +++ b/ee/webhooks/internal/components/webhook_controller/controllers/utils/utils.go @@ -17,9 +17,9 @@ type ErrorType string const ( NoneType ErrorType = "NONE" - ValidationType ErrorType = "VALIDATION" + ValidationType ErrorType = "VALIDATION_TYPE" NotFoundType ErrorType = "NOT_FOUND" - InternalType ErrorType = "INTERNAL" + InternalType ErrorType = "INTERNAL_TYPE" ) type Response[T interface{}] struct { @@ -61,7 +61,7 @@ func NotFoundErrorResp[T interface{}](err error) Response[T] { } const ( - ErrValidation = "VALIDATION_REQUEST" + ErrValidation = "VALIDATION_TYPE" ErrHealthcheck = "HEALTHCHECK_STATUS" ) diff --git a/ee/webhooks/internal/components/webhook_controller/controllers/utils/v1-compat.go b/ee/webhooks/internal/components/webhook_controller/controllers/utils/v1-compat.go index 48cb128a75..63959c8e77 100644 --- a/ee/webhooks/internal/components/webhook_controller/controllers/utils/v1-compat.go +++ b/ee/webhooks/internal/components/webhook_controller/controllers/utils/v1-compat.go @@ -63,12 +63,23 @@ func ToV1Hooks(hooks *[]*commons.Hook) []V1Hook { } func ToV1Attempt(hook commons.Hook, attempt commons.Attempt) V1Attempt { + + var status string + if attempt.Status == commons.SuccessStatus { + status = "success" + } else { + status = "failed" + } return V1Attempt{ - ID: attempt.ID, - WebhookID: hook.ID, - Config: ToV1Hook(hook), - Payload: attempt.Payload, - StatusCode: attempt.LastHttpStatusCode, - RetryAttempt: 0, + ID: attempt.ID, + WebhookID: hook.ID, + Config: ToV1Hook(hook), + Payload: attempt.Payload, + StatusCode: attempt.LastHttpStatusCode, + Status: status, + RetryAttempt: attempt.RetryAttempt, + UpdatedAt: attempt.DateStatus, + NextRetryAfter: attempt.NextTry, + CreatedAt: attempt.CreatedAt, } } diff --git a/ee/webhooks/internal/components/webhook_controller/controllers/v1/controllers-hook.go b/ee/webhooks/internal/components/webhook_controller/controllers/v1/controllers-hook.go index 2a5757749b..a2548a3dbc 100644 --- a/ee/webhooks/internal/components/webhook_controller/controllers/v1/controllers-hook.go +++ b/ee/webhooks/internal/components/webhook_controller/controllers/v1/controllers-hook.go @@ -205,10 +205,7 @@ func RegisterV1HookControllers(server serverInterfaces.IHTTPServer, database sto id := chi.URLParam(r, "id") payload := PayloadBody{} - if err := utils.DecodeJSONBody(r, &payload); err != nil { - sharedapi.BadRequest(w, utils.ErrValidation, err) - return - } + payload.Payload = "{\"data\":\"test\"}" resp := V1TestHookController(database, client, id, payload.Payload) diff --git a/ee/webhooks/internal/components/webhook_controller/controllers/v2/controllers-hooks.go b/ee/webhooks/internal/components/webhook_controller/controllers/v2/controllers-hooks.go index 37e3c9776b..175c8471a9 100644 --- a/ee/webhooks/internal/components/webhook_controller/controllers/v2/controllers-hooks.go +++ b/ee/webhooks/internal/components/webhook_controller/controllers/v2/controllers-hooks.go @@ -60,6 +60,31 @@ func RegisterV2HookControllers(server serverInterfaces.IHTTPServer, database sto }) + server.Register(string(r.V2GetHook.Method), r.V2GetHook.Url, func(w http.ResponseWriter, r *http.Request) { + id := chi.URLParam(r, "id") + resp := V2GetHookController(database, id) + + if resp.Err != nil { + if resp.T == utils.ValidationType { + sharedapi.BadRequest(w, string(resp.T), resp.Err) + return + } + if resp.T == utils.InternalType { + sharedapi.InternalServerError(w, r, resp.Err) + return + } + if resp.T == utils.NotFoundType { + sharedapi.NotFound(w, resp.Err) + return + } + + sharedapi.InternalServerError(w, r, resp.Err) + return + } + + sharedapi.Ok(w, *resp.Data) + }) + server.Register(string(r.V2GetHooks.Method), r.V2GetHooks.Url, func(w http.ResponseWriter, r *http.Request) { filterEndpoint := r.URL.Query().Get("endpoint") @@ -251,19 +276,28 @@ func RegisterV2HookControllers(server serverInterfaces.IHTTPServer, database sto sharedapi.BadRequest(w, utils.ErrValidation, err) } - hook, err := controllersCommons.UpdateEndpoint(database, id, ep.Endpoint) + resp := V2ChangeEndpointController(database, id, ep.Endpoint) - if err != nil { - sharedapi.InternalServerError(w, r, err) - return - } + if resp.Err != nil { + if resp.T == utils.ValidationType { + sharedapi.BadRequest(w, string(resp.T), resp.Err) + return + } + if resp.T == utils.InternalType { + sharedapi.InternalServerError(w, r, resp.Err) + return + } + if resp.T == utils.NotFoundType { + sharedapi.NotFound(w, resp.Err) + return + } - if hook.ID == "" { - sharedapi.NotFound(w, errors.New(fmt.Sprintf("Hook (id : %s) doesn't exist", id))) + sharedapi.InternalServerError(w, r, resp.Err) return } - sharedapi.Ok(w, hook) + sharedapi.Ok(w, *resp.Data) + return }) server.Register(string(r.V2ChangeHookRetry.Method), r.V2ChangeHookRetry.Url, func(w http.ResponseWriter, r *http.Request) { @@ -319,6 +353,18 @@ func V2CreateHookController(database storeInterface.IStoreProvider, hookParams c return utils.SuccessResp(hook) } +func V2GetHookController(database storeInterface.IStoreProvider, id string) utils.Response[commons.Hook] { + + hook, err := controllersCommons.GetHook(database, id) + if err != nil { + return utils.InternalErrorResp[commons.Hook](err) + } + if hook.ID == "" { + return utils.NotFoundErrorResp[commons.Hook](errors.New(fmt.Sprintf("Hook (id : %s) doesn't exist", id))) + } + return utils.SuccessResp(hook) +} + func V2GetHooksController(database storeInterface.IStoreProvider, filterEndpoint, filterCursor string) utils.Response[bunpaginate.Cursor[commons.Hook]] { hasMore := false strPrevious := " " diff --git a/ee/webhooks/internal/components/webhook_controller/routes/v1.go b/ee/webhooks/internal/components/webhook_controller/routes/v1.go index c6f16e0496..48b7d38de9 100644 --- a/ee/webhooks/internal/components/webhook_controller/routes/v1.go +++ b/ee/webhooks/internal/components/webhook_controller/routes/v1.go @@ -10,4 +10,4 @@ var V1DeleteHook = s.NewRoute(s.DELETE, "/configs/{id}") var V1TestHook = s.NewRoute(s.GET, "/configs/{id}/test") var V1ActiveHook = s.NewRoute(s.PUT, "/configs/{id}/activate") var V1DeactiveHook = s.NewRoute(s.PUT, "/configs/{id}/deactivate") -var V1ChangeSecret = s.NewRoute(s.PUT, "/configs/{id}/secret") +var V1ChangeSecret = s.NewRoute(s.PUT, "/configs/{id}/secret/change") diff --git a/ee/webhooks/internal/components/webhook_controller/routes/v2.go b/ee/webhooks/internal/components/webhook_controller/routes/v2.go index 1c6439c436..7500c5b3c7 100644 --- a/ee/webhooks/internal/components/webhook_controller/routes/v2.go +++ b/ee/webhooks/internal/components/webhook_controller/routes/v2.go @@ -6,6 +6,7 @@ import ( var V2GetHooks = s.NewRoute(s.GET, "/v2/hooks") var V2CreateHook = s.NewRoute(s.POST, "/v2/hooks") +var V2GetHook = s.NewRoute(s.GET, "/v2/hooks/{id}") var V2DeleteHook = s.NewRoute(s.DELETE, "/v2/hooks/{id}") var V2TestHook = s.NewRoute(s.POST, "/v2/hooks/{id}/test") var V2ActiveHook = s.NewRoute(s.PUT, "/v2/hooks/{id}/activate") diff --git a/ee/webhooks/internal/components/webhook_worker/worker.go b/ee/webhooks/internal/components/webhook_worker/worker.go index 69305515d1..7d61b5fc5c 100644 --- a/ee/webhooks/internal/components/webhook_worker/worker.go +++ b/ee/webhooks/internal/components/webhook_worker/worker.go @@ -75,8 +75,9 @@ func (w *Worker) HandleMessage(msg *message.Message) error { if eventApp != "" { event = strings.Join([]string{eventApp, event}, ".") } - + logging.Infof("Webhook_Worker :: Handling The Event : %s", event) triggedSHooks := w.State.ActiveHooksByEvent.Get(event) + if triggedSHooks == nil || triggedSHooks.Size() == 0 { return nil } diff --git a/ee/webhooks/internal/services/httpclient/default_client.go b/ee/webhooks/internal/services/httpclient/default_client.go index c5b8df6c7e..20a3e77380 100644 --- a/ee/webhooks/internal/services/httpclient/default_client.go +++ b/ee/webhooks/internal/services/httpclient/default_client.go @@ -54,7 +54,7 @@ func (dc *DefaultHttpClient) Call(context context.Context, hook *commons.Hook, a ts := time.Now().UTC() timestamp := ts.Unix() - signature, err := security.Sign(hook.ID, timestamp, hook.Secret, []byte(attempt.Payload)) + signature, err := security.Sign(attempt.ID, timestamp, hook.Secret, []byte(attempt.Payload)) req.Header.Set("content-type", "application/json") req.Header.Set("user-agent", "formance-webhooks/v2") @@ -62,7 +62,6 @@ func (dc *DefaultHttpClient) Call(context context.Context, hook *commons.Hook, a req.Header.Set("formance-webhook-timestamp", fmt.Sprintf("%d", timestamp)) req.Header.Set("formance-webhook-signature", signature) req.Header.Set("formance-webhook-test", fmt.Sprintf("%v", isTest)) - resp, err := dc.httpClient.Do(req) if err != nil { span.RecordError(err) diff --git a/ee/webhooks/internal/services/httpserver/default_server.go b/ee/webhooks/internal/services/httpserver/default_server.go index 3996d14c47..5e30070350 100644 --- a/ee/webhooks/internal/services/httpserver/default_server.go +++ b/ee/webhooks/internal/services/httpserver/default_server.go @@ -4,6 +4,7 @@ import ( "context" "net" "net/http" + "time" sharedapi "github.com/formancehq/stack/libs/go-libs/api" "github.com/formancehq/stack/libs/go-libs/auth" @@ -72,7 +73,7 @@ func NewDefaultHTTPServer(addr string, info commons.ServiceInfo, a auth.Auth, lo server: &http.Server{ Addr: addr, Handler: router, - + ReadHeaderTimeout: time.Duration(1 * time.Second), }, } diff --git a/ee/webhooks/internal/services/storage/interfaces/iprovider.go b/ee/webhooks/internal/services/storage/interfaces/iprovider.go index 9604c5e783..da2b66a8b8 100644 --- a/ee/webhooks/internal/services/storage/interfaces/iprovider.go +++ b/ee/webhooks/internal/services/storage/interfaces/iprovider.go @@ -27,6 +27,7 @@ type IStoreProvider interface { CompleteAttempt(index string) (commons.Attempt, error) AbortAttempt(index string, comment string, wrapInLog bool) (commons.Attempt, error) ChangeAttemptStatus(index string, status commons.AttemptStatus, comment string, wrapInLog bool) (commons.Attempt, error) + UpdateAttemptEndpoint(index string, endpoint string) (commons.Attempt, error) UpdateAttemptNextTry(index string, nextTry time.Time, statusCode int) (commons.Attempt, error) GetWaitingAttempts(page int, size int) (*[]*commons.Attempt, bool, error) diff --git a/ee/webhooks/internal/services/storage/postgres/attempt_queries.go b/ee/webhooks/internal/services/storage/postgres/attempt_queries.go index 95e2f6ffc6..9bb4c31747 100644 --- a/ee/webhooks/internal/services/storage/postgres/attempt_queries.go +++ b/ee/webhooks/internal/services/storage/postgres/attempt_queries.go @@ -15,6 +15,7 @@ const ( insertAttemptQuery = "INSERT INTO attempts (id, webhook_id, hook_name, hook_endpoint, event, payload, status_code, date_occured, status, date_status, comment, next_retry_after) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *" updateAttemptStatus = "UPDATE attempts SET status = ?, date_status = NOW(), comment = ? WHERE id = ? RETURNING *" updateAttemptNextTry = "UPDATE attempts SET next_retry_after = ?, status_code = ? WHERE id = ? RETURNING *" + updateAttemptEndpoint = "UPDATE attempts SET hook_endpoint = ? WHERE id = ? RETURNING *" ) func (store PostgresStore) GetAttempt(index string) (commons.Attempt, error) { @@ -130,6 +131,18 @@ func (store PostgresStore) ChangeAttemptStatus(index string, status commons.Atte return attempt, err } +func (store PostgresStore) UpdateAttemptEndpoint(index string, endpoint string) (commons.Attempt, error){ + var attempt commons.Attempt + + _, err := store.db.NewRaw(updateAttemptEndpoint, endpoint, index).Exec(context.Background(), &attempt) + + if err == sql.ErrNoRows { + return attempt, nil + } + + return attempt, nil +} + func (store PostgresStore) UpdateAttemptNextTry(index string, nextTry time.Time, statusCode int) (commons.Attempt, error) { var attempt commons.Attempt diff --git a/ee/webhooks/internal/services/storage/postgres/hook_queries.go b/ee/webhooks/internal/services/storage/postgres/hook_queries.go index ce9e7ae91c..ee94e18349 100644 --- a/ee/webhooks/internal/services/storage/postgres/hook_queries.go +++ b/ee/webhooks/internal/services/storage/postgres/hook_queries.go @@ -90,7 +90,6 @@ func (store PostgresStore) changeHookStatus(index string, status commons.HookSta wrapQuery := wrapWithLogQuery(updateHookStatusQuery) - _, err = store.db.NewRaw(wrapQuery, string(status), index, log.ID, log.Channel, log.Payload, log.CreatedAt).Exec(context.Background(), &hook) _, err = store.db.NewRaw(wrapQuery, string(status), index, log.ID, log.Channel, log.Payload, log.CreatedAt).Exec(context.Background(), &hook) if err == sql.ErrNoRows { diff --git a/ee/webhooks/internal/tests/components/webhook_controller/v2_controllers_hook_test.go b/ee/webhooks/internal/tests/components/webhook_controller/v2_controllers_hook_test.go index 5f4a4a5578..3257528c4a 100644 --- a/ee/webhooks/internal/tests/components/webhook_controller/v2_controllers_hook_test.go +++ b/ee/webhooks/internal/tests/components/webhook_controller/v2_controllers_hook_test.go @@ -21,6 +21,8 @@ func TestRunHookV2(t *testing.T) { t.Run("GetHooks", v2GetHooks) + t.Run("GetHook", v2GetHook) + t.Run("DeleteHook", v2DeleteHook) t.Run("DeactiveHook", v2DeactiveHook) @@ -126,6 +128,16 @@ func v2GetHooks(t *testing.T) { require.Len(t, resp.Data.Data, 3) } +func v2GetHook(t *testing.T) { + resp := v2Ctrl.V2GetHooksController(Database, "", "") + require.NoError(t, resp.Err) + hook := resp.Data.Data[0] + + resp2 := v2Ctrl.V2GetHookController(Database, hook.ID) + require.NoError(t, resp2.Err) + require.Equal(t, hook.ID, resp2.Data.ID) +} + func v2DeleteHook(t *testing.T) { wrongId := "23" diff --git a/ee/webhooks/openapi.yaml b/ee/webhooks/openapi.yaml index 81a9b213ef..ad02c7dd4e 100644 --- a/ee/webhooks/openapi.yaml +++ b/ee/webhooks/openapi.yaml @@ -277,10 +277,39 @@ paths: schema: $ref: '#/components/schemas/ErrorResponse' /v2/hooks/{hookId}: + get: + summary: Get one Hook by its ID + description: Get one Hook by its ID + operationId: getHook + tags: + - Webhooks + parameters: + - name: hookId + in: path + description: Hook ID + required: true + schema: + type: string + example: 4997257d-dfb6-445b-929c-cbe2ab182818 + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/V2HookResponse' + description: The hook + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' delete: summary: Delete one Hook - operationId: deleteHook description: Set the status of one Hook as "DELETED" + operationId: deleteHook + tags: + - Webhooks parameters: - name: hookId in: path @@ -799,10 +828,10 @@ components: ErrorsEnum: type: string enum: - - INTERNAL - - VALIDATION + - INTERNAL_TYPE + - VALIDATION_TYPE - NOT_FOUND - example: VALIDATION + example: VALIDATION_TYPE V2Cursor: type: object required: @@ -820,25 +849,28 @@ components: type: string next: type: string - data: - type: array - items: - type: object - V2BaseResponse: - type: object - required: - - data - properties: - data: - type: object V2HookCursorResponse: type: object required: - cursor properties: cursor: - $ref: '#/components/schemas/V2Cursor' + type: object + required: + - pageSize + - hasMore + - previous + - next + - data properties: + pageSize: + type: integer + hasMore: + type: boolean + previous: + type: string + next: + type: string data: type: array items: @@ -849,26 +881,39 @@ components: - cursor properties: cursor: - $ref: '#/components/schemas/V2Cursor' + required: + - pageSize + - hasMore + - previous + - next + - data properties: + pageSize: + type: integer + hasMore: + type: boolean + previous: + type: string + next: + type: string data: type: array items: $ref: '#/components/schemas/V2Attempt' V2HookResponse: - allOf: - - $ref: '#/components/schemas/V2BaseResponse' - - type: object - properties: - data: - $ref: '#/components/schemas/V2Hook' + type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/V2Hook' V2AttemptResponse: - allOf: - - $ref: '#/components/schemas/V2BaseResponse' - - type: object - properties: - data: - $ref: '#/components/schemas/V2Attempt' + type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/V2Attempt' V2Hook: type: object required: @@ -889,6 +934,10 @@ components: type: string status: type: string + enum: + - ENABLED + - DISABLED + - DELETED events: type: array items: @@ -942,6 +991,10 @@ components: format: date-time status: type: string + enum: + - WAITING + - SUCCESS + - ABORT dateStatus: type: string format: date-time diff --git a/ee/webhooks/openapi/v1.yaml b/ee/webhooks/openapi/v1.yaml index 389546cce4..e6e561d0cf 100644 --- a/ee/webhooks/openapi/v1.yaml +++ b/ee/webhooks/openapi/v1.yaml @@ -410,4 +410,4 @@ components: - INTERNAL_TYPE - VALIDATION_TYPE - NOT_FOUND - example: VALIDATION + example: VALIDATION_TYPE diff --git a/ee/webhooks/openapi/v2.yaml b/ee/webhooks/openapi/v2.yaml index 63c8908217..7cae80c30a 100644 --- a/ee/webhooks/openapi/v2.yaml +++ b/ee/webhooks/openapi/v2.yaml @@ -3,7 +3,7 @@ info: title: Webhooks version: "WEBHOOKS_VERSION" -paths: +paths: /v2/hooks: get: summary: Get Many hooks @@ -64,18 +64,47 @@ paths: schema: $ref: '#/components/schemas/ErrorResponse' /v2/hooks/{hookId}: + get: + summary: Get one Hook by its ID + description: Get one Hook by its ID + operationId: getHook + tags: + - Webhooks + parameters: + - name: hookId + in: path + description: Hook ID + required: true + schema: + type: string + example: 4997257d-dfb6-445b-929c-cbe2ab182818 + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/V2HookResponse' + description: The hook + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' delete: summary: Delete one Hook - operationId: deleteHook description: Set the status of one Hook as "DELETED" + operationId: deleteHook + tags: + - Webhooks parameters: - - name: hookId - in: path - description: Hook ID - required: true - schema: - type: string - example: 4997257d-dfb6-445b-929c-cbe2ab182818 + - name: hookId + in: path + description: Hook ID + required: true + schema: + type: string + example: 4997257d-dfb6-445b-929c-cbe2ab182818 responses: "200": content: @@ -435,55 +464,71 @@ components: type: string next : type: string - data: - type: array - items: - type: object - V2BaseResponse: - type: object - required: - - data - properties: - data: - type: object V2HookCursorResponse: type: object required: - - cursor + - cursor properties: cursor: - $ref: '#/components/schemas/V2Cursor' + type: object + required: + - pageSize + - hasMore + - previous + - next + - data properties: - data: - type: array - items: + pageSize: + type: integer + hasMore: + type: boolean + previous: + type: string + next : + type: string + data: + type: array + items: $ref: '#/components/schemas/V2Hook' V2AttemptCursorResponse: type: object required: - - cursor - properties: - cursor: - $ref: '#/components/schemas/V2Cursor' - properties: - data: - type: array - items: - $ref: '#/components/schemas/V2Attempt' - V2HookResponse: - allOf: - - $ref: '#/components/schemas/V2BaseResponse' - - type: object + - cursor + properties: + cursor: + required: + - pageSize + - hasMore + - previous + - next + - data properties: - data: - $ref: '#/components/schemas/V2Hook' + pageSize: + type: integer + hasMore: + type: boolean + previous: + type: string + next : + type: string + data: + type: array + items: + $ref: '#/components/schemas/V2Attempt' + V2HookResponse: + type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/V2Hook' V2AttemptResponse: - allOf: - - $ref: '#/components/schemas/V2BaseResponse' - - type: object - properties: - data: - $ref: '#/components/schemas/V2Attempt' + type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/V2Attempt' V2Hook: type: object required: @@ -504,6 +549,7 @@ components: type: string status: type: string + enum: [ENABLED, DISABLED, DELETED] events: type: array items: @@ -556,7 +602,8 @@ components: type: string format: date-time status: - type: string + type: string + enum: [WAITING, SUCCESS, ABORT] dateStatus: type: string format: date-time diff --git a/releases/sdks/go/.speakeasy/gen.lock b/releases/sdks/go/.speakeasy/gen.lock index 154045aa2d..a773720b59 100755 --- a/releases/sdks/go/.speakeasy/gen.lock +++ b/releases/sdks/go/.speakeasy/gen.lock @@ -1,7 +1,7 @@ lockVersion: 2.0.0 id: 7eac0a45-60a2-40bb-9e85-26bd77ec2a6d management: - docChecksum: 43d981bdee1125bc84b3fe2e83be327f + docChecksum: b03e791e9f3e760510270d7444f4bce3 docVersion: v0.0.0 speakeasyVersion: 1.292.0 generationVersion: 2.332.4 @@ -52,7 +52,6 @@ generatedFiles: - pkg/utils/retries.go - pkg/utils/security.go - pkg/utils/utils.go - - /pkg/models/operations/deletehook.go - /pkg/models/operations/getoidcwellknowns.go - /pkg/models/operations/getversions.go - /pkg/models/operations/createclient.go @@ -218,7 +217,9 @@ generatedFiles: - /pkg/models/operations/deactivateconfig.go - /pkg/models/operations/deactivatehook.go - /pkg/models/operations/deleteconfig.go + - /pkg/models/operations/deletehook.go - /pkg/models/operations/getabortedattempts.go + - /pkg/models/operations/gethook.go - /pkg/models/operations/getmanyconfigs.go - /pkg/models/operations/getmanyhooks.go - /pkg/models/operations/getwaitingattempts.go @@ -232,9 +233,6 @@ generatedFiles: - /pkg/models/operations/updateendpointhook.go - /pkg/models/operations/updateretryhook.go - /pkg/models/operations/updatesecrethook.go - - /pkg/models/shared/v2hookresponse.go - - /pkg/models/shared/v2hook.go - - /pkg/models/shared/webhookserrorsenum.go - /pkg/models/shared/getversionsresponse.go - /pkg/models/shared/version.go - /pkg/models/shared/createclientresponse.go @@ -572,19 +570,20 @@ generatedFiles: - /pkg/models/shared/listwalletsresponse.go - /pkg/models/shared/configresponse.go - /pkg/models/shared/webhooksconfig.go + - /pkg/models/shared/webhookserrorsenum.go + - /pkg/models/shared/v2hookresponse.go + - /pkg/models/shared/v2hook.go - /pkg/models/shared/configchangesecret.go - /pkg/models/shared/v2attemptcursorresponse.go - - /pkg/models/shared/v2cursor.go + - /pkg/models/shared/v2attempt.go - /pkg/models/shared/configsresponse.go - /pkg/models/shared/v2hookcursorresponse.go - /pkg/models/shared/configuser.go - /pkg/models/shared/v2hookbodyparams.go - /pkg/models/shared/v2attemptresponse.go - - /pkg/models/shared/v2attempt.go - /pkg/models/shared/attemptresponse.go - /pkg/models/shared/attempt.go - /pkg/models/shared/security.go - - /pkg/models/sdkerrors/webhookserrorresponse.go - /pkg/models/sdkerrors/errorresponse.go - /pkg/models/sdkerrors/v2errorresponse.go - /pkg/models/sdkerrors/error.go @@ -592,8 +591,7 @@ generatedFiles: - /pkg/models/sdkerrors/paymentserrorresponse.go - /pkg/models/sdkerrors/reconciliationerrorresponse.go - /pkg/models/sdkerrors/walletserrorresponse.go - - docs/pkg/models/operations/deletehookrequest.md - - docs/pkg/models/operations/deletehookresponse.md + - /pkg/models/sdkerrors/webhookserrorresponse.go - docs/pkg/models/operations/getoidcwellknownsresponse.md - docs/pkg/models/operations/getversionsresponse.md - docs/pkg/models/operations/createclientresponse.md @@ -900,8 +898,12 @@ generatedFiles: - docs/pkg/models/operations/deactivatehookresponse.md - docs/pkg/models/operations/deleteconfigrequest.md - docs/pkg/models/operations/deleteconfigresponse.md + - docs/pkg/models/operations/deletehookrequest.md + - docs/pkg/models/operations/deletehookresponse.md - docs/pkg/models/operations/getabortedattemptsrequest.md - docs/pkg/models/operations/getabortedattemptsresponse.md + - docs/pkg/models/operations/gethookrequest.md + - docs/pkg/models/operations/gethookresponse.md - docs/pkg/models/operations/getmanyconfigsrequest.md - docs/pkg/models/operations/getmanyconfigsresponse.md - docs/pkg/models/operations/getmanyhooksrequest.md @@ -929,9 +931,6 @@ generatedFiles: - docs/pkg/models/operations/updatesecrethookrequestbody.md - docs/pkg/models/operations/updatesecrethookrequest.md - docs/pkg/models/operations/updatesecrethookresponse.md - - docs/pkg/models/shared/v2hookresponse.md - - docs/pkg/models/shared/v2hook.md - - docs/pkg/models/shared/webhookserrorsenum.md - docs/pkg/models/shared/getversionsresponse.md - docs/pkg/models/shared/version.md - docs/pkg/models/shared/createclientresponse.md @@ -1362,21 +1361,25 @@ generatedFiles: - docs/pkg/models/shared/listwalletsresponse.md - docs/pkg/models/shared/configresponse.md - docs/pkg/models/shared/webhooksconfig.md + - docs/pkg/models/shared/webhookserrorsenum.md + - docs/pkg/models/shared/v2hookresponse.md + - docs/pkg/models/shared/v2hookstatus.md + - docs/pkg/models/shared/v2hook.md - docs/pkg/models/shared/configchangesecret.md + - docs/pkg/models/shared/v2attemptcursorresponsecursor.md - docs/pkg/models/shared/v2attemptcursorresponse.md - - docs/pkg/models/shared/v2cursordata.md - - docs/pkg/models/shared/v2cursor.md + - docs/pkg/models/shared/v2attemptstatus.md + - docs/pkg/models/shared/v2attempt.md - docs/pkg/models/shared/configsresponsecursor.md - docs/pkg/models/shared/configsresponse.md + - docs/pkg/models/shared/v2hookcursorresponsecursor.md - docs/pkg/models/shared/v2hookcursorresponse.md - docs/pkg/models/shared/configuser.md - docs/pkg/models/shared/v2hookbodyparams.md - docs/pkg/models/shared/v2attemptresponse.md - - docs/pkg/models/shared/v2attempt.md - docs/pkg/models/shared/attemptresponse.md - docs/pkg/models/shared/attempt.md - docs/pkg/models/shared/security.md - - docs/pkg/models/sdkerrors/webhookserrorresponse.md - docs/pkg/models/sdkerrors/errorresponse.md - docs/pkg/models/sdkerrors/v2errorresponse.md - docs/pkg/models/sdkerrors/errorcode.md @@ -1387,6 +1390,7 @@ generatedFiles: - docs/pkg/models/sdkerrors/reconciliationerrorresponse.md - docs/pkg/models/sdkerrors/schemaswalletserrorresponseerrorcode.md - docs/pkg/models/sdkerrors/walletserrorresponse.md + - docs/pkg/models/sdkerrors/webhookserrorresponse.md - docs/sdks/formance/README.md - docs/sdks/auth/README.md - docs/sdks/ledger/README.md diff --git a/releases/sdks/go/README.md b/releases/sdks/go/README.md index df094b430b..bc67609ac9 100644 --- a/releases/sdks/go/README.md +++ b/releases/sdks/go/README.md @@ -35,7 +35,6 @@ package main import ( "context" "github.com/formancehq/formance-sdk-go/v2" - "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" "log" ) @@ -47,16 +46,12 @@ func main() { }), ) - request := operations.DeleteHookRequest{ - HookID: "4997257d-dfb6-445b-929c-cbe2ab182818", - } - ctx := context.Background() - res, err := s.DeleteHook(ctx, request) + res, err := s.GetOIDCWellKnowns(ctx) if err != nil { log.Fatal(err) } - if res.V2HookResponse != nil { + if res != nil { // handle response } } @@ -69,7 +64,6 @@ func main() { ### [Formance SDK](docs/sdks/formance/README.md) -* [DeleteHook](docs/sdks/formance/README.md#deletehook) - Delete one Hook * [GetOIDCWellKnowns](docs/sdks/formance/README.md#getoidcwellknowns) - Retrieve OpenID connect well-knowns. * [GetVersions](docs/sdks/formance/README.md#getversions) - Show stack version information @@ -259,7 +253,9 @@ func main() { * [DeactivateConfig](docs/sdks/webhooks/README.md#deactivateconfig) - Deactivate one config * [DeactivateHook](docs/sdks/webhooks/README.md#deactivatehook) - Deactivate one Hook * [DeleteConfig](docs/sdks/webhooks/README.md#deleteconfig) - Delete one config +* [DeleteHook](docs/sdks/webhooks/README.md#deletehook) - Delete one Hook * [GetAbortedAttempts](docs/sdks/webhooks/README.md#getabortedattempts) - Get aborted Attempts +* [GetHook](docs/sdks/webhooks/README.md#gethook) - Get one Hook by its ID * [GetManyConfigs](docs/sdks/webhooks/README.md#getmanyconfigs) - Get many configs * [GetManyHooks](docs/sdks/webhooks/README.md#getmanyhooks) - Get Many hooks * [GetWaitingAttempts](docs/sdks/webhooks/README.md#getwaitingattempts) - Get Waiting Attempts @@ -280,10 +276,10 @@ func main() { Handling errors in this SDK should largely match your expectations. All operations return a response object or an error, they will never return both. When specified by the OpenAPI spec document, the SDK will return the appropriate subclass. -| Error Object | Status Code | Content Type | -| ------------------------------- | ------------------------------- | ------------------------------- | -| sdkerrors.WebhooksErrorResponse | default | application/json | -| sdkerrors.SDKError | 4xx-5xx | */* | +| Error Object | Status Code | Content Type | +| ----------------------- | ----------------------- | ----------------------- | +| sdkerrors.ErrorResponse | default | application/json | +| sdkerrors.SDKError | 4xx-5xx | */* | ### Example @@ -298,6 +294,7 @@ import ( "github.com/formancehq/formance-sdk-go/v2/pkg/models/sdkerrors" "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" "log" + "math/big" ) func main() { @@ -307,15 +304,30 @@ func main() { }), ) - request := operations.DeleteHookRequest{ - HookID: "4997257d-dfb6-445b-929c-cbe2ab182818", + request := operations.CreateTransactionsRequest{ + Transactions: shared.Transactions{ + Transactions: []shared.TransactionData{ + shared.TransactionData{ + Postings: []shared.Posting{ + shared.Posting{ + Amount: big.NewInt(100), + Asset: "COIN", + Destination: "users:002", + Source: "users:001", + }, + }, + Reference: v2.String("ref:001"), + }, + }, + }, + Ledger: "ledger001", } ctx := context.Background() - res, err := s.DeleteHook(ctx, request) + res, err := s.Ledger.CreateTransactions(ctx, request) if err != nil { - var e *sdkerrors.WebhooksErrorResponse + var e *sdkerrors.ErrorResponse if errors.As(err, &e) { // handle error log.Fatal(e.Error()) @@ -351,7 +363,6 @@ package main import ( "context" "github.com/formancehq/formance-sdk-go/v2" - "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" "log" ) @@ -364,16 +375,12 @@ func main() { }), ) - request := operations.DeleteHookRequest{ - HookID: "4997257d-dfb6-445b-929c-cbe2ab182818", - } - ctx := context.Background() - res, err := s.DeleteHook(ctx, request) + res, err := s.GetOIDCWellKnowns(ctx) if err != nil { log.Fatal(err) } - if res.V2HookResponse != nil { + if res != nil { // handle response } } @@ -390,7 +397,6 @@ package main import ( "context" "github.com/formancehq/formance-sdk-go/v2" - "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" "log" ) @@ -403,16 +409,12 @@ func main() { }), ) - request := operations.DeleteHookRequest{ - HookID: "4997257d-dfb6-445b-929c-cbe2ab182818", - } - ctx := context.Background() - res, err := s.DeleteHook(ctx, request) + res, err := s.GetOIDCWellKnowns(ctx) if err != nil { log.Fatal(err) } - if res.V2HookResponse != nil { + if res != nil { // handle response } } @@ -467,7 +469,6 @@ package main import ( "context" "github.com/formancehq/formance-sdk-go/v2" - "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" "log" ) @@ -479,16 +480,12 @@ func main() { }), ) - request := operations.DeleteHookRequest{ - HookID: "4997257d-dfb6-445b-929c-cbe2ab182818", - } - ctx := context.Background() - res, err := s.DeleteHook(ctx, request) + res, err := s.GetOIDCWellKnowns(ctx) if err != nil { log.Fatal(err) } - if res.V2HookResponse != nil { + if res != nil { // handle response } } diff --git a/releases/sdks/go/USAGE.md b/releases/sdks/go/USAGE.md index d99895cdf8..55d8396a2f 100644 --- a/releases/sdks/go/USAGE.md +++ b/releases/sdks/go/USAGE.md @@ -5,7 +5,6 @@ package main import ( "context" "github.com/formancehq/formance-sdk-go/v2" - "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" "log" ) @@ -17,16 +16,12 @@ func main() { }), ) - request := operations.DeleteHookRequest{ - HookID: "4997257d-dfb6-445b-929c-cbe2ab182818", - } - ctx := context.Background() - res, err := s.DeleteHook(ctx, request) + res, err := s.GetOIDCWellKnowns(ctx) if err != nil { log.Fatal(err) } - if res.V2HookResponse != nil { + if res != nil { // handle response } } diff --git a/releases/sdks/go/docs/pkg/models/operations/gethookrequest.md b/releases/sdks/go/docs/pkg/models/operations/gethookrequest.md new file mode 100644 index 0000000000..ef36986123 --- /dev/null +++ b/releases/sdks/go/docs/pkg/models/operations/gethookrequest.md @@ -0,0 +1,8 @@ +# GetHookRequest + + +## Fields + +| Field | Type | Required | Description | Example | +| ------------------------------------ | ------------------------------------ | ------------------------------------ | ------------------------------------ | ------------------------------------ | +| `HookID` | *string* | :heavy_check_mark: | Hook ID | 4997257d-dfb6-445b-929c-cbe2ab182818 | \ No newline at end of file diff --git a/releases/sdks/go/docs/pkg/models/operations/gethookresponse.md b/releases/sdks/go/docs/pkg/models/operations/gethookresponse.md new file mode 100644 index 0000000000..75a904faa8 --- /dev/null +++ b/releases/sdks/go/docs/pkg/models/operations/gethookresponse.md @@ -0,0 +1,11 @@ +# GetHookResponse + + +## Fields + +| Field | Type | Required | Description | +| ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| `ContentType` | *string* | :heavy_check_mark: | HTTP response content type for this operation | +| `StatusCode` | *int* | :heavy_check_mark: | HTTP response status code for this operation | +| `RawResponse` | [*http.Response](https://pkg.go.dev/net/http#Response) | :heavy_check_mark: | Raw HTTP response; suitable for custom response parsing | +| `V2HookResponse` | [*shared.V2HookResponse](../../../pkg/models/shared/v2hookresponse.md) | :heavy_minus_sign: | The hook | \ No newline at end of file diff --git a/releases/sdks/go/docs/pkg/models/sdkerrors/webhookserrorresponse.md b/releases/sdks/go/docs/pkg/models/sdkerrors/webhookserrorresponse.md index 04214f4f3b..a9f431df67 100644 --- a/releases/sdks/go/docs/pkg/models/sdkerrors/webhookserrorresponse.md +++ b/releases/sdks/go/docs/pkg/models/sdkerrors/webhookserrorresponse.md @@ -8,5 +8,5 @@ Error | Field | Type | Required | Description | Example | | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | `Details` | **string* | :heavy_minus_sign: | N/A | https://play.numscript.org/?payload=eyJlcnJvciI6ImFjY291bnQgaGFkIGluc3VmZmljaWVudCBmdW5kcyJ9 | -| `ErrorCode` | [shared.WebhooksErrorsEnum](../../../pkg/models/shared/webhookserrorsenum.md) | :heavy_check_mark: | N/A | VALIDATION | +| `ErrorCode` | [shared.WebhooksErrorsEnum](../../../pkg/models/shared/webhookserrorsenum.md) | :heavy_check_mark: | N/A | VALIDATION_TYPE | | `ErrorMessage` | *string* | :heavy_check_mark: | N/A | [VALIDATION] invalid 'cursor' query param | \ No newline at end of file diff --git a/releases/sdks/go/docs/pkg/models/shared/v2attempt.md b/releases/sdks/go/docs/pkg/models/shared/v2attempt.md index 815b1eaebb..e36c292ad8 100644 --- a/releases/sdks/go/docs/pkg/models/shared/v2attempt.md +++ b/releases/sdks/go/docs/pkg/models/shared/v2attempt.md @@ -3,17 +3,17 @@ ## Fields -| Field | Type | Required | Description | -| ----------------------------------------- | ----------------------------------------- | ----------------------------------------- | ----------------------------------------- | -| `Comment` | *string* | :heavy_check_mark: | N/A | -| `DateOccured` | [time.Time](https://pkg.go.dev/time#Time) | :heavy_check_mark: | N/A | -| `DateStatus` | [time.Time](https://pkg.go.dev/time#Time) | :heavy_check_mark: | N/A | -| `Event` | *string* | :heavy_check_mark: | N/A | -| `HookEndpoint` | *string* | :heavy_check_mark: | N/A | -| `HookID` | *string* | :heavy_check_mark: | N/A | -| `HookName` | *string* | :heavy_check_mark: | N/A | -| `ID` | *string* | :heavy_check_mark: | N/A | -| `NextRetryAfter` | [time.Time](https://pkg.go.dev/time#Time) | :heavy_check_mark: | N/A | -| `Payload` | *string* | :heavy_check_mark: | N/A | -| `Status` | *string* | :heavy_check_mark: | N/A | -| `StatusCode` | *int64* | :heavy_check_mark: | N/A | \ No newline at end of file +| Field | Type | Required | Description | +| ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| `Comment` | *string* | :heavy_check_mark: | N/A | +| `DateOccured` | [time.Time](https://pkg.go.dev/time#Time) | :heavy_check_mark: | N/A | +| `DateStatus` | [time.Time](https://pkg.go.dev/time#Time) | :heavy_check_mark: | N/A | +| `Event` | *string* | :heavy_check_mark: | N/A | +| `HookEndpoint` | *string* | :heavy_check_mark: | N/A | +| `HookID` | *string* | :heavy_check_mark: | N/A | +| `HookName` | *string* | :heavy_check_mark: | N/A | +| `ID` | *string* | :heavy_check_mark: | N/A | +| `NextRetryAfter` | [time.Time](https://pkg.go.dev/time#Time) | :heavy_check_mark: | N/A | +| `Payload` | *string* | :heavy_check_mark: | N/A | +| `Status` | [shared.V2AttemptStatus](../../../pkg/models/shared/v2attemptstatus.md) | :heavy_check_mark: | N/A | +| `StatusCode` | *int64* | :heavy_check_mark: | N/A | \ No newline at end of file diff --git a/releases/sdks/go/docs/pkg/models/shared/v2attemptcursorresponse.md b/releases/sdks/go/docs/pkg/models/shared/v2attemptcursorresponse.md index 9deee7681a..60bfcc01a5 100644 --- a/releases/sdks/go/docs/pkg/models/shared/v2attemptcursorresponse.md +++ b/releases/sdks/go/docs/pkg/models/shared/v2attemptcursorresponse.md @@ -3,6 +3,6 @@ ## Fields -| Field | Type | Required | Description | -| --------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- | -| `Cursor` | [shared.V2Cursor](../../../pkg/models/shared/v2cursor.md) | :heavy_check_mark: | N/A | \ No newline at end of file +| Field | Type | Required | Description | +| --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | +| `Cursor` | [shared.V2AttemptCursorResponseCursor](../../../pkg/models/shared/v2attemptcursorresponsecursor.md) | :heavy_check_mark: | N/A | \ No newline at end of file diff --git a/releases/sdks/go/docs/pkg/models/shared/v2attemptcursorresponsecursor.md b/releases/sdks/go/docs/pkg/models/shared/v2attemptcursorresponsecursor.md new file mode 100644 index 0000000000..1a3479b9ca --- /dev/null +++ b/releases/sdks/go/docs/pkg/models/shared/v2attemptcursorresponsecursor.md @@ -0,0 +1,12 @@ +# V2AttemptCursorResponseCursor + + +## Fields + +| Field | Type | Required | Description | +| ------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------- | +| `Data` | [][shared.V2Attempt](../../../pkg/models/shared/v2attempt.md) | :heavy_check_mark: | N/A | +| `HasMore` | *bool* | :heavy_check_mark: | N/A | +| `Next` | *string* | :heavy_check_mark: | N/A | +| `PageSize` | *int64* | :heavy_check_mark: | N/A | +| `Previous` | *string* | :heavy_check_mark: | N/A | \ No newline at end of file diff --git a/releases/sdks/go/docs/pkg/models/shared/v2attemptstatus.md b/releases/sdks/go/docs/pkg/models/shared/v2attemptstatus.md new file mode 100644 index 0000000000..59657bf096 --- /dev/null +++ b/releases/sdks/go/docs/pkg/models/shared/v2attemptstatus.md @@ -0,0 +1,10 @@ +# V2AttemptStatus + + +## Values + +| Name | Value | +| ------------------------ | ------------------------ | +| `V2AttemptStatusWaiting` | WAITING | +| `V2AttemptStatusSuccess` | SUCCESS | +| `V2AttemptStatusAbort` | ABORT | \ No newline at end of file diff --git a/releases/sdks/go/docs/pkg/models/shared/v2cursor.md b/releases/sdks/go/docs/pkg/models/shared/v2cursor.md deleted file mode 100644 index d9b0fb21d8..0000000000 --- a/releases/sdks/go/docs/pkg/models/shared/v2cursor.md +++ /dev/null @@ -1,12 +0,0 @@ -# V2Cursor - - -## Fields - -| Field | Type | Required | Description | -| ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | -| `Data` | [][shared.V2CursorData](../../../pkg/models/shared/v2cursordata.md) | :heavy_check_mark: | N/A | -| `HasMore` | *bool* | :heavy_check_mark: | N/A | -| `Next` | *string* | :heavy_check_mark: | N/A | -| `PageSize` | *int64* | :heavy_check_mark: | N/A | -| `Previous` | *string* | :heavy_check_mark: | N/A | \ No newline at end of file diff --git a/releases/sdks/go/docs/pkg/models/shared/v2cursordata.md b/releases/sdks/go/docs/pkg/models/shared/v2cursordata.md deleted file mode 100644 index 2f2d9c9918..0000000000 --- a/releases/sdks/go/docs/pkg/models/shared/v2cursordata.md +++ /dev/null @@ -1,7 +0,0 @@ -# V2CursorData - - -## Fields - -| Field | Type | Required | Description | -| ----------- | ----------- | ----------- | ----------- | \ No newline at end of file diff --git a/releases/sdks/go/docs/pkg/models/shared/v2hook.md b/releases/sdks/go/docs/pkg/models/shared/v2hook.md index cf8a751744..3965315410 100644 --- a/releases/sdks/go/docs/pkg/models/shared/v2hook.md +++ b/releases/sdks/go/docs/pkg/models/shared/v2hook.md @@ -3,14 +3,14 @@ ## Fields -| Field | Type | Required | Description | -| ----------------------------------------- | ----------------------------------------- | ----------------------------------------- | ----------------------------------------- | -| `DateCreation` | [time.Time](https://pkg.go.dev/time#Time) | :heavy_check_mark: | N/A | -| `DateStatus` | [time.Time](https://pkg.go.dev/time#Time) | :heavy_check_mark: | N/A | -| `Endpoint` | *string* | :heavy_check_mark: | N/A | -| `Events` | []*string* | :heavy_check_mark: | N/A | -| `ID` | *string* | :heavy_check_mark: | N/A | -| `Name` | *string* | :heavy_check_mark: | N/A | -| `Retry` | *bool* | :heavy_check_mark: | N/A | -| `Secret` | *string* | :heavy_check_mark: | N/A | -| `Status` | *string* | :heavy_check_mark: | N/A | \ No newline at end of file +| Field | Type | Required | Description | +| ----------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | +| `DateCreation` | [time.Time](https://pkg.go.dev/time#Time) | :heavy_check_mark: | N/A | +| `DateStatus` | [time.Time](https://pkg.go.dev/time#Time) | :heavy_check_mark: | N/A | +| `Endpoint` | *string* | :heavy_check_mark: | N/A | +| `Events` | []*string* | :heavy_check_mark: | N/A | +| `ID` | *string* | :heavy_check_mark: | N/A | +| `Name` | *string* | :heavy_check_mark: | N/A | +| `Retry` | *bool* | :heavy_check_mark: | N/A | +| `Secret` | *string* | :heavy_check_mark: | N/A | +| `Status` | [shared.V2HookStatus](../../../pkg/models/shared/v2hookstatus.md) | :heavy_check_mark: | N/A | \ No newline at end of file diff --git a/releases/sdks/go/docs/pkg/models/shared/v2hookcursorresponse.md b/releases/sdks/go/docs/pkg/models/shared/v2hookcursorresponse.md index f6b324d884..4967e3e3bb 100644 --- a/releases/sdks/go/docs/pkg/models/shared/v2hookcursorresponse.md +++ b/releases/sdks/go/docs/pkg/models/shared/v2hookcursorresponse.md @@ -3,6 +3,6 @@ ## Fields -| Field | Type | Required | Description | -| --------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- | -| `Cursor` | [shared.V2Cursor](../../../pkg/models/shared/v2cursor.md) | :heavy_check_mark: | N/A | \ No newline at end of file +| Field | Type | Required | Description | +| --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | +| `Cursor` | [shared.V2HookCursorResponseCursor](../../../pkg/models/shared/v2hookcursorresponsecursor.md) | :heavy_check_mark: | N/A | \ No newline at end of file diff --git a/releases/sdks/go/docs/pkg/models/shared/v2hookcursorresponsecursor.md b/releases/sdks/go/docs/pkg/models/shared/v2hookcursorresponsecursor.md new file mode 100644 index 0000000000..61d2e9b5be --- /dev/null +++ b/releases/sdks/go/docs/pkg/models/shared/v2hookcursorresponsecursor.md @@ -0,0 +1,12 @@ +# V2HookCursorResponseCursor + + +## Fields + +| Field | Type | Required | Description | +| ------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------- | +| `Data` | [][shared.V2Hook](../../../pkg/models/shared/v2hook.md) | :heavy_check_mark: | N/A | +| `HasMore` | *bool* | :heavy_check_mark: | N/A | +| `Next` | *string* | :heavy_check_mark: | N/A | +| `PageSize` | *int64* | :heavy_check_mark: | N/A | +| `Previous` | *string* | :heavy_check_mark: | N/A | \ No newline at end of file diff --git a/releases/sdks/go/docs/pkg/models/shared/v2hookstatus.md b/releases/sdks/go/docs/pkg/models/shared/v2hookstatus.md new file mode 100644 index 0000000000..97aa20ed00 --- /dev/null +++ b/releases/sdks/go/docs/pkg/models/shared/v2hookstatus.md @@ -0,0 +1,10 @@ +# V2HookStatus + + +## Values + +| Name | Value | +| ---------------------- | ---------------------- | +| `V2HookStatusEnabled` | ENABLED | +| `V2HookStatusDisabled` | DISABLED | +| `V2HookStatusDeleted` | DELETED | \ No newline at end of file diff --git a/releases/sdks/go/docs/pkg/models/shared/webhookserrorsenum.md b/releases/sdks/go/docs/pkg/models/shared/webhookserrorsenum.md index 1ea7b27d68..12bd5b0165 100644 --- a/releases/sdks/go/docs/pkg/models/shared/webhookserrorsenum.md +++ b/releases/sdks/go/docs/pkg/models/shared/webhookserrorsenum.md @@ -3,8 +3,8 @@ ## Values -| Name | Value | -| ------------------------------ | ------------------------------ | -| `WebhooksErrorsEnumInternal` | INTERNAL | -| `WebhooksErrorsEnumValidation` | VALIDATION | -| `WebhooksErrorsEnumNotFound` | NOT_FOUND | \ No newline at end of file +| Name | Value | +| ---------------------------------- | ---------------------------------- | +| `WebhooksErrorsEnumInternalType` | INTERNAL_TYPE | +| `WebhooksErrorsEnumValidationType` | VALIDATION_TYPE | +| `WebhooksErrorsEnumNotFound` | NOT_FOUND | \ No newline at end of file diff --git a/releases/sdks/go/docs/sdks/formance/README.md b/releases/sdks/go/docs/sdks/formance/README.md index 711f735bab..6a6817409a 100644 --- a/releases/sdks/go/docs/sdks/formance/README.md +++ b/releases/sdks/go/docs/sdks/formance/README.md @@ -18,65 +18,9 @@ and standard method from web, mobile and desktop applications. ### Available Operations -* [DeleteHook](#deletehook) - Delete one Hook * [GetOIDCWellKnowns](#getoidcwellknowns) - Retrieve OpenID connect well-knowns. * [GetVersions](#getversions) - Show stack version information -## DeleteHook - -Set the status of one Hook as "DELETED" - -### Example Usage - -```go -package main - -import( - "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" - "github.com/formancehq/formance-sdk-go/v2" - "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" - "context" - "log" -) - -func main() { - s := v2.New( - v2.WithSecurity(shared.Security{ - Authorization: "", - }), - ) - - request := operations.DeleteHookRequest{ - HookID: "4997257d-dfb6-445b-929c-cbe2ab182818", - } - - ctx := context.Background() - res, err := s.DeleteHook(ctx, request) - if err != nil { - log.Fatal(err) - } - if res.V2HookResponse != nil { - // handle response - } -} -``` - -### Parameters - -| 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.DeleteHookRequest](../../pkg/models/operations/deletehookrequest.md) | :heavy_check_mark: | The request object to use for the request. | - - -### Response - -**[*operations.DeleteHookResponse](../../pkg/models/operations/deletehookresponse.md), error** -| Error Object | Status Code | Content Type | -| ------------------------------- | ------------------------------- | ------------------------------- | -| sdkerrors.WebhooksErrorResponse | default | application/json | -| sdkerrors.SDKError | 4xx-5xx | */* | - ## GetOIDCWellKnowns Retrieve OpenID connect well-knowns. diff --git a/releases/sdks/go/docs/sdks/webhooks/README.md b/releases/sdks/go/docs/sdks/webhooks/README.md index b671775788..ecd92b68cb 100644 --- a/releases/sdks/go/docs/sdks/webhooks/README.md +++ b/releases/sdks/go/docs/sdks/webhooks/README.md @@ -9,7 +9,9 @@ * [DeactivateConfig](#deactivateconfig) - Deactivate one config * [DeactivateHook](#deactivatehook) - Deactivate one Hook * [DeleteConfig](#deleteconfig) - Delete one config +* [DeleteHook](#deletehook) - Delete one Hook * [GetAbortedAttempts](#getabortedattempts) - Get aborted Attempts +* [GetHook](#gethook) - Get one Hook by its ID * [GetManyConfigs](#getmanyconfigs) - Get many configs * [GetManyHooks](#getmanyhooks) - Get Many hooks * [GetWaitingAttempts](#getwaitingattempts) - Get Waiting Attempts @@ -361,6 +363,61 @@ func main() { | sdkerrors.WebhooksErrorResponse | default | application/json | | sdkerrors.SDKError | 4xx-5xx | */* | +## DeleteHook + +Set the status of one Hook as "DELETED" + +### Example Usage + +```go +package main + +import( + "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" + "github.com/formancehq/formance-sdk-go/v2" + "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" + "context" + "log" +) + +func main() { + s := v2.New( + v2.WithSecurity(shared.Security{ + Authorization: "", + }), + ) + + request := operations.DeleteHookRequest{ + HookID: "4997257d-dfb6-445b-929c-cbe2ab182818", + } + + ctx := context.Background() + res, err := s.Webhooks.DeleteHook(ctx, request) + if err != nil { + log.Fatal(err) + } + if res.V2HookResponse != nil { + // handle response + } +} +``` + +### Parameters + +| 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.DeleteHookRequest](../../pkg/models/operations/deletehookrequest.md) | :heavy_check_mark: | The request object to use for the request. | + + +### Response + +**[*operations.DeleteHookResponse](../../pkg/models/operations/deletehookresponse.md), error** +| Error Object | Status Code | Content Type | +| ------------------------------- | ------------------------------- | ------------------------------- | +| sdkerrors.WebhooksErrorResponse | default | application/json | +| sdkerrors.SDKError | 4xx-5xx | */* | + ## GetAbortedAttempts Get Aborted Attempts @@ -414,6 +471,61 @@ func main() { | sdkerrors.WebhooksErrorResponse | default | application/json | | sdkerrors.SDKError | 4xx-5xx | */* | +## GetHook + +Get one Hook by its ID + +### Example Usage + +```go +package main + +import( + "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" + "github.com/formancehq/formance-sdk-go/v2" + "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" + "context" + "log" +) + +func main() { + s := v2.New( + v2.WithSecurity(shared.Security{ + Authorization: "", + }), + ) + + request := operations.GetHookRequest{ + HookID: "4997257d-dfb6-445b-929c-cbe2ab182818", + } + + ctx := context.Background() + res, err := s.Webhooks.GetHook(ctx, request) + if err != nil { + log.Fatal(err) + } + if res.V2HookResponse != nil { + // handle response + } +} +``` + +### Parameters + +| 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.GetHookRequest](../../pkg/models/operations/gethookrequest.md) | :heavy_check_mark: | The request object to use for the request. | + + +### Response + +**[*operations.GetHookResponse](../../pkg/models/operations/gethookresponse.md), error** +| Error Object | Status Code | Content Type | +| ------------------------------- | ------------------------------- | ------------------------------- | +| sdkerrors.WebhooksErrorResponse | default | application/json | +| sdkerrors.SDKError | 4xx-5xx | */* | + ## GetManyConfigs Sorted by updated date descending diff --git a/releases/sdks/go/formance.go b/releases/sdks/go/formance.go index 42e8120cdd..a2c3553434 100644 --- a/releases/sdks/go/formance.go +++ b/releases/sdks/go/formance.go @@ -206,105 +206,6 @@ func New(opts ...SDKOption) *Formance { return sdk } -// DeleteHook - Delete one Hook -// Set the status of one Hook as "DELETED" -func (s *Formance) DeleteHook(ctx context.Context, request operations.DeleteHookRequest) (*operations.DeleteHookResponse, error) { - hookCtx := hooks.HookContext{ - Context: ctx, - OperationID: "deleteHook", - OAuth2Scopes: []string{}, - SecuritySource: s.sdkConfiguration.Security, - } - - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - opURL, err := utils.GenerateURL(ctx, baseURL, "/api/webhooks/v2/hooks/{hookId}", request, nil) - if err != nil { - return nil, fmt.Errorf("error generating URL: %w", err) - } - - req, err := http.NewRequestWithContext(ctx, "DELETE", opURL, nil) - if err != nil { - return nil, fmt.Errorf("error creating request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - - if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { - return nil, err - } - - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) - if err != nil { - return nil, err - } - - httpRes, err := s.sdkConfiguration.Client.Do(req) - if err != nil || httpRes == nil { - if err != nil { - err = fmt.Errorf("error sending request: %w", err) - } else { - err = fmt.Errorf("error sending request: no response") - } - - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) - return nil, err - } else if utils.MatchStatusCodes([]string{"default"}, httpRes.StatusCode) { - _httpRes, err := s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) - if err != nil { - return nil, err - } else if _httpRes != nil { - httpRes = _httpRes - } - } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) - if err != nil { - return nil, err - } - } - - res := &operations.DeleteHookResponse{ - StatusCode: httpRes.StatusCode, - ContentType: httpRes.Header.Get("Content-Type"), - RawResponse: httpRes, - } - - rawBody, err := io.ReadAll(httpRes.Body) - if err != nil { - return nil, fmt.Errorf("error reading response body: %w", err) - } - httpRes.Body.Close() - httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - - switch { - case httpRes.StatusCode == 200: - switch { - case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): - var out shared.V2HookResponse - if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { - return nil, err - } - - res.V2HookResponse = &out - default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) - } - default: - switch { - case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): - var out sdkerrors.WebhooksErrorResponse - if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { - return nil, err - } - - return nil, &out - default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) - } - } - - return res, nil -} - // GetOIDCWellKnowns - Retrieve OpenID connect well-knowns. func (s *Formance) GetOIDCWellKnowns(ctx context.Context) (*operations.GetOIDCWellKnownsResponse, error) { hookCtx := hooks.HookContext{ diff --git a/releases/sdks/go/pkg/models/operations/gethook.go b/releases/sdks/go/pkg/models/operations/gethook.go new file mode 100644 index 0000000000..ded747af3b --- /dev/null +++ b/releases/sdks/go/pkg/models/operations/gethook.go @@ -0,0 +1,59 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" + "net/http" +) + +type GetHookRequest struct { + // Hook ID + HookID string `pathParam:"style=simple,explode=false,name=hookId"` +} + +func (o *GetHookRequest) GetHookID() string { + if o == nil { + return "" + } + return o.HookID +} + +type GetHookResponse struct { + // HTTP response content type for this operation + ContentType string + // HTTP response status code for this operation + StatusCode int + // Raw HTTP response; suitable for custom response parsing + RawResponse *http.Response + // The hook + V2HookResponse *shared.V2HookResponse +} + +func (o *GetHookResponse) GetContentType() string { + if o == nil { + return "" + } + return o.ContentType +} + +func (o *GetHookResponse) GetStatusCode() int { + if o == nil { + return 0 + } + return o.StatusCode +} + +func (o *GetHookResponse) GetRawResponse() *http.Response { + if o == nil { + return nil + } + return o.RawResponse +} + +func (o *GetHookResponse) GetV2HookResponse() *shared.V2HookResponse { + if o == nil { + return nil + } + return o.V2HookResponse +} diff --git a/releases/sdks/go/pkg/models/shared/v2attempt.go b/releases/sdks/go/pkg/models/shared/v2attempt.go index bcc4de92a7..039e654152 100644 --- a/releases/sdks/go/pkg/models/shared/v2attempt.go +++ b/releases/sdks/go/pkg/models/shared/v2attempt.go @@ -3,23 +3,54 @@ package shared import ( + "encoding/json" + "fmt" "github.com/formancehq/formance-sdk-go/v2/pkg/utils" "time" ) +type V2AttemptStatus string + +const ( + V2AttemptStatusWaiting V2AttemptStatus = "WAITING" + V2AttemptStatusSuccess V2AttemptStatus = "SUCCESS" + V2AttemptStatusAbort V2AttemptStatus = "ABORT" +) + +func (e V2AttemptStatus) ToPointer() *V2AttemptStatus { + return &e +} +func (e *V2AttemptStatus) UnmarshalJSON(data []byte) error { + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v { + case "WAITING": + fallthrough + case "SUCCESS": + fallthrough + case "ABORT": + *e = V2AttemptStatus(v) + return nil + default: + return fmt.Errorf("invalid value for V2AttemptStatus: %v", v) + } +} + type V2Attempt struct { - Comment string `json:"comment"` - DateOccured time.Time `json:"dateOccured"` - DateStatus time.Time `json:"dateStatus"` - Event string `json:"event"` - HookEndpoint string `json:"hookEndpoint"` - HookID string `json:"hookId"` - HookName string `json:"hookName"` - ID string `json:"id"` - NextRetryAfter time.Time `json:"nextRetryAfter"` - Payload string `json:"payload"` - Status string `json:"status"` - StatusCode int64 `json:"statusCode"` + Comment string `json:"comment"` + DateOccured time.Time `json:"dateOccured"` + DateStatus time.Time `json:"dateStatus"` + Event string `json:"event"` + HookEndpoint string `json:"hookEndpoint"` + HookID string `json:"hookId"` + HookName string `json:"hookName"` + ID string `json:"id"` + NextRetryAfter time.Time `json:"nextRetryAfter"` + Payload string `json:"payload"` + Status V2AttemptStatus `json:"status"` + StatusCode int64 `json:"statusCode"` } func (v V2Attempt) MarshalJSON() ([]byte, error) { @@ -103,9 +134,9 @@ func (o *V2Attempt) GetPayload() string { return o.Payload } -func (o *V2Attempt) GetStatus() string { +func (o *V2Attempt) GetStatus() V2AttemptStatus { if o == nil { - return "" + return V2AttemptStatus("") } return o.Status } diff --git a/releases/sdks/go/pkg/models/shared/v2attemptcursorresponse.go b/releases/sdks/go/pkg/models/shared/v2attemptcursorresponse.go index f26d1d334c..ff0cabe5fb 100644 --- a/releases/sdks/go/pkg/models/shared/v2attemptcursorresponse.go +++ b/releases/sdks/go/pkg/models/shared/v2attemptcursorresponse.go @@ -2,13 +2,56 @@ package shared +type V2AttemptCursorResponseCursor struct { + Data []V2Attempt `json:"data"` + HasMore bool `json:"hasMore"` + Next string `json:"next"` + PageSize int64 `json:"pageSize"` + Previous string `json:"previous"` +} + +func (o *V2AttemptCursorResponseCursor) GetData() []V2Attempt { + if o == nil { + return []V2Attempt{} + } + return o.Data +} + +func (o *V2AttemptCursorResponseCursor) GetHasMore() bool { + if o == nil { + return false + } + return o.HasMore +} + +func (o *V2AttemptCursorResponseCursor) GetNext() string { + if o == nil { + return "" + } + return o.Next +} + +func (o *V2AttemptCursorResponseCursor) GetPageSize() int64 { + if o == nil { + return 0 + } + return o.PageSize +} + +func (o *V2AttemptCursorResponseCursor) GetPrevious() string { + if o == nil { + return "" + } + return o.Previous +} + type V2AttemptCursorResponse struct { - Cursor V2Cursor `json:"cursor"` + Cursor V2AttemptCursorResponseCursor `json:"cursor"` } -func (o *V2AttemptCursorResponse) GetCursor() V2Cursor { +func (o *V2AttemptCursorResponse) GetCursor() V2AttemptCursorResponseCursor { if o == nil { - return V2Cursor{} + return V2AttemptCursorResponseCursor{} } return o.Cursor } diff --git a/releases/sdks/go/pkg/models/shared/v2cursor.go b/releases/sdks/go/pkg/models/shared/v2cursor.go deleted file mode 100644 index 475f01f7af..0000000000 --- a/releases/sdks/go/pkg/models/shared/v2cursor.go +++ /dev/null @@ -1,49 +0,0 @@ -// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. - -package shared - -type V2CursorData struct { -} - -type V2Cursor struct { - Data []V2CursorData `json:"data"` - HasMore bool `json:"hasMore"` - Next string `json:"next"` - PageSize int64 `json:"pageSize"` - Previous string `json:"previous"` -} - -func (o *V2Cursor) GetData() []V2CursorData { - if o == nil { - return []V2CursorData{} - } - return o.Data -} - -func (o *V2Cursor) GetHasMore() bool { - if o == nil { - return false - } - return o.HasMore -} - -func (o *V2Cursor) GetNext() string { - if o == nil { - return "" - } - return o.Next -} - -func (o *V2Cursor) GetPageSize() int64 { - if o == nil { - return 0 - } - return o.PageSize -} - -func (o *V2Cursor) GetPrevious() string { - if o == nil { - return "" - } - return o.Previous -} diff --git a/releases/sdks/go/pkg/models/shared/v2hook.go b/releases/sdks/go/pkg/models/shared/v2hook.go index 7fa2518df1..de9c26312e 100644 --- a/releases/sdks/go/pkg/models/shared/v2hook.go +++ b/releases/sdks/go/pkg/models/shared/v2hook.go @@ -3,20 +3,51 @@ package shared import ( + "encoding/json" + "fmt" "github.com/formancehq/formance-sdk-go/v2/pkg/utils" "time" ) +type V2HookStatus string + +const ( + V2HookStatusEnabled V2HookStatus = "ENABLED" + V2HookStatusDisabled V2HookStatus = "DISABLED" + V2HookStatusDeleted V2HookStatus = "DELETED" +) + +func (e V2HookStatus) ToPointer() *V2HookStatus { + return &e +} +func (e *V2HookStatus) UnmarshalJSON(data []byte) error { + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v { + case "ENABLED": + fallthrough + case "DISABLED": + fallthrough + case "DELETED": + *e = V2HookStatus(v) + return nil + default: + return fmt.Errorf("invalid value for V2HookStatus: %v", v) + } +} + type V2Hook struct { - DateCreation time.Time `json:"dateCreation"` - DateStatus time.Time `json:"dateStatus"` - Endpoint string `json:"endpoint"` - Events []string `json:"events"` - ID string `json:"id"` - Name string `json:"name"` - Retry bool `json:"retry"` - Secret string `json:"secret"` - Status string `json:"status"` + DateCreation time.Time `json:"dateCreation"` + DateStatus time.Time `json:"dateStatus"` + Endpoint string `json:"endpoint"` + Events []string `json:"events"` + ID string `json:"id"` + Name string `json:"name"` + Retry bool `json:"retry"` + Secret string `json:"secret"` + Status V2HookStatus `json:"status"` } func (v V2Hook) MarshalJSON() ([]byte, error) { @@ -86,9 +117,9 @@ func (o *V2Hook) GetSecret() string { return o.Secret } -func (o *V2Hook) GetStatus() string { +func (o *V2Hook) GetStatus() V2HookStatus { if o == nil { - return "" + return V2HookStatus("") } return o.Status } diff --git a/releases/sdks/go/pkg/models/shared/v2hookcursorresponse.go b/releases/sdks/go/pkg/models/shared/v2hookcursorresponse.go index 2a3f5195dd..ad077c0b0d 100644 --- a/releases/sdks/go/pkg/models/shared/v2hookcursorresponse.go +++ b/releases/sdks/go/pkg/models/shared/v2hookcursorresponse.go @@ -2,13 +2,56 @@ package shared +type V2HookCursorResponseCursor struct { + Data []V2Hook `json:"data"` + HasMore bool `json:"hasMore"` + Next string `json:"next"` + PageSize int64 `json:"pageSize"` + Previous string `json:"previous"` +} + +func (o *V2HookCursorResponseCursor) GetData() []V2Hook { + if o == nil { + return []V2Hook{} + } + return o.Data +} + +func (o *V2HookCursorResponseCursor) GetHasMore() bool { + if o == nil { + return false + } + return o.HasMore +} + +func (o *V2HookCursorResponseCursor) GetNext() string { + if o == nil { + return "" + } + return o.Next +} + +func (o *V2HookCursorResponseCursor) GetPageSize() int64 { + if o == nil { + return 0 + } + return o.PageSize +} + +func (o *V2HookCursorResponseCursor) GetPrevious() string { + if o == nil { + return "" + } + return o.Previous +} + type V2HookCursorResponse struct { - Cursor V2Cursor `json:"cursor"` + Cursor V2HookCursorResponseCursor `json:"cursor"` } -func (o *V2HookCursorResponse) GetCursor() V2Cursor { +func (o *V2HookCursorResponse) GetCursor() V2HookCursorResponseCursor { if o == nil { - return V2Cursor{} + return V2HookCursorResponseCursor{} } return o.Cursor } diff --git a/releases/sdks/go/pkg/models/shared/webhookserrorsenum.go b/releases/sdks/go/pkg/models/shared/webhookserrorsenum.go index 7490f3331d..48cebcfa18 100644 --- a/releases/sdks/go/pkg/models/shared/webhookserrorsenum.go +++ b/releases/sdks/go/pkg/models/shared/webhookserrorsenum.go @@ -10,9 +10,9 @@ import ( type WebhooksErrorsEnum string const ( - WebhooksErrorsEnumInternal WebhooksErrorsEnum = "INTERNAL" - WebhooksErrorsEnumValidation WebhooksErrorsEnum = "VALIDATION" - WebhooksErrorsEnumNotFound WebhooksErrorsEnum = "NOT_FOUND" + WebhooksErrorsEnumInternalType WebhooksErrorsEnum = "INTERNAL_TYPE" + WebhooksErrorsEnumValidationType WebhooksErrorsEnum = "VALIDATION_TYPE" + WebhooksErrorsEnumNotFound WebhooksErrorsEnum = "NOT_FOUND" ) func (e WebhooksErrorsEnum) ToPointer() *WebhooksErrorsEnum { @@ -24,9 +24,9 @@ func (e *WebhooksErrorsEnum) UnmarshalJSON(data []byte) error { return err } switch v { - case "INTERNAL": + case "INTERNAL_TYPE": fallthrough - case "VALIDATION": + case "VALIDATION_TYPE": fallthrough case "NOT_FOUND": *e = WebhooksErrorsEnum(v) diff --git a/releases/sdks/go/webhooks.go b/releases/sdks/go/webhooks.go index 04bd82e0a7..80351b644a 100644 --- a/releases/sdks/go/webhooks.go +++ b/releases/sdks/go/webhooks.go @@ -618,6 +618,105 @@ func (s *Webhooks) DeleteConfig(ctx context.Context, request operations.DeleteCo return res, nil } +// DeleteHook - Delete one Hook +// Set the status of one Hook as "DELETED" +func (s *Webhooks) DeleteHook(ctx context.Context, request operations.DeleteHookRequest) (*operations.DeleteHookResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "deleteHook", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := utils.GenerateURL(ctx, baseURL, "/api/webhooks/v2/hooks/{hookId}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "DELETE", opURL, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json") + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) + + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err + } + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{"default"}, httpRes.StatusCode) { + _httpRes, err := s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } else if _httpRes != nil { + httpRes = _httpRes + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.DeleteHookResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): + var out shared.V2HookResponse + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { + return nil, err + } + + res.V2HookResponse = &out + default: + return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) + } + default: + switch { + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): + var out sdkerrors.WebhooksErrorResponse + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { + return nil, err + } + + return nil, &out + default: + return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) + } + } + + return res, nil +} + // GetAbortedAttempts - Get aborted Attempts // Get Aborted Attempts func (s *Webhooks) GetAbortedAttempts(ctx context.Context, request operations.GetAbortedAttemptsRequest) (*operations.GetAbortedAttemptsResponse, error) { @@ -721,6 +820,105 @@ func (s *Webhooks) GetAbortedAttempts(ctx context.Context, request operations.Ge return res, nil } +// GetHook - Get one Hook by its ID +// Get one Hook by its ID +func (s *Webhooks) GetHook(ctx context.Context, request operations.GetHookRequest) (*operations.GetHookResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getHook", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := utils.GenerateURL(ctx, baseURL, "/api/webhooks/v2/hooks/{hookId}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json") + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) + + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err + } + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{"default"}, httpRes.StatusCode) { + _httpRes, err := s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } else if _httpRes != nil { + httpRes = _httpRes + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetHookResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): + var out shared.V2HookResponse + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { + return nil, err + } + + res.V2HookResponse = &out + default: + return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) + } + default: + switch { + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): + var out sdkerrors.WebhooksErrorResponse + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { + return nil, err + } + + return nil, &out + default: + return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) + } + } + + return res, nil +} + // GetManyConfigs - Get many configs // Sorted by updated date descending func (s *Webhooks) GetManyConfigs(ctx context.Context, request operations.GetManyConfigsRequest) (*operations.GetManyConfigsResponse, error) { diff --git a/tests/integration/suite/webhooks-configs-activation.go b/tests/integration/suite/webhooks-configs-activation.go index 3a705d8bf1..1c4df40cc5 100644 --- a/tests/integration/suite/webhooks-configs-activation.go +++ b/tests/integration/suite/webhooks-configs-activation.go @@ -15,6 +15,7 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { var ( secret = webhooks.NewSecret() insertResp *shared.ConfigResponse + ) BeforeEach(func() { @@ -33,9 +34,10 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { Expect(response.StatusCode).To(Equal(200)) insertResp = response.ConfigResponse + }) - Context("deactivating the inserted one", func() { + Context("Config: deactivating the inserted one", func() { BeforeEach(func() { response, err := Client().Webhooks.DeactivateConfig( TestContext(), @@ -49,7 +51,7 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { Expect(response.ConfigResponse.Data.Active).To(BeFalse()) }) - Context("getting all configs", func() { + Context("Config: getting all configs", func() { It("should return 1 deactivated config", func() { response, err := Client().Webhooks.GetManyConfigs( TestContext(), @@ -64,7 +66,7 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { }) }) - Context("deactivating the inserted one, then reactivating it", func() { + Context("Config: deactivating the inserted one, then reactivating it", func() { BeforeEach(func() { response, err := Client().Webhooks.DeactivateConfig( TestContext(), @@ -87,7 +89,7 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { Expect(activateConfigResponse.ConfigResponse.Data.Active).To(BeTrue()) }) - Context("getting all configs", func() { + Context("Config: getting all configs", func() { It("should return 1 activated config", func() { response, err := Client().Webhooks.GetManyConfigs( TestContext(), @@ -102,7 +104,7 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { }) }) - Context("trying to deactivate an unknown ID", func() { + Context("Config : trying to deactivate an unknown ID", func() { It("should fail", func() { _, err := Client().Webhooks.DeactivateConfig( TestContext(), diff --git a/tests/integration/suite/webhooks-configs-delete.go b/tests/integration/suite/webhooks-configs-delete.go index 945036a2fd..259c62e9cc 100644 --- a/tests/integration/suite/webhooks-configs-delete.go +++ b/tests/integration/suite/webhooks-configs-delete.go @@ -18,6 +18,7 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { var ( secret = webhooks.NewSecret() insertResp *shared.ConfigResponse + ) BeforeEach(func() { @@ -36,9 +37,10 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { Expect(response.StatusCode).To(Equal(http.StatusOK)) insertResp = response.ConfigResponse + }) - Context("deleting the inserted one", func() { + Context("Config: deleting the inserted one", func() { BeforeEach(func() { response, err := Client().Webhooks.DeleteConfig( TestContext(), @@ -60,23 +62,15 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { Expect(response.StatusCode).To(Equal(http.StatusOK)) Expect(response.ConfigsResponse.Cursor.HasMore).To(BeFalse()) - Expect(response.ConfigsResponse.Cursor.Data).To(BeEmpty()) + for _, data := range response.ConfigsResponse.Cursor.Data { + Expect(data.ID).ToNot(Equal(insertResp.Data.ID)) + } + }) }) - - AfterEach(func() { - _, err := Client().Webhooks.DeleteConfig( - TestContext(), - operations.DeleteConfigRequest{ - ID: insertResp.Data.ID, - }, - ) - Expect(err).To(HaveOccurred()) - Expect(err.(*sdkerrors.WebhooksErrorResponse).ErrorCode).To(Equal(shared.WebhooksErrorsEnumNotFound)) - }) }) - Context("trying to delete an unknown ID", func() { + Context("Config trying to delete an unknown ID", func() { It("should fail", func() { _, err := Client().Webhooks.DeleteConfig( TestContext(), diff --git a/tests/integration/suite/webhooks-configs-get.go b/tests/integration/suite/webhooks-configs-get.go index ea418639b1..4b14f05296 100644 --- a/tests/integration/suite/webhooks-configs-get.go +++ b/tests/integration/suite/webhooks-configs-get.go @@ -1,7 +1,6 @@ package suite import ( - "fmt" "net/http" "github.com/formancehq/stack/tests/integration/internal/modules" @@ -26,10 +25,13 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { Expect(response.ConfigsResponse.Cursor.Data).To(BeEmpty()) }) + + When("inserting 2 configs", func() { var ( insertResp1 *shared.ConfigResponse insertResp2 *shared.ConfigResponse + ) BeforeEach(func() { @@ -47,6 +49,8 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { "ledger.saved_metadata", }, } + + ) response, err := Client().Webhooks.InsertConfig( @@ -64,9 +68,11 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { Expect(err).ToNot(HaveOccurred()) Expect(response.StatusCode).To(Equal(http.StatusOK)) insertResp2 = response.ConfigResponse + Expect(insertResp2.Data.Endpoint).To(Equal("https://example2.com")) + }) - Context("getting all configs without filters", func() { + Context("getting all configs and V2 hooks without filters", func() { It("should return 2 configs", func() { response, err := Client().Webhooks.GetManyConfigs( TestContext(), @@ -78,16 +84,13 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { resp := response.ConfigsResponse Expect(resp.Cursor.HasMore).To(BeFalse()) Expect(resp.Cursor.Data).To(HaveLen(2)) - fmt.Println(resp.Cursor.Data[0].Endpoint) - fmt.Println(resp.Cursor.Data[1].Endpoint) - Expect(resp.Cursor.Data[0].Endpoint).To(Equal(insertResp2.Data.Endpoint)) - Expect(resp.Cursor.Data[1].Endpoint).To(Equal(insertResp1.Data.Endpoint)) }) + }) Context("getting all configs with known endpoint filter", func() { - It("should return 1 config with the same endpoint", func() { + It("should return 1 configs with the same endpoint", func() { response, err := Client().Webhooks.GetManyConfigs( TestContext(), operations.GetManyConfigsRequest{ @@ -102,6 +105,7 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { Expect(resp.Cursor.Data).To(HaveLen(1)) Expect(resp.Cursor.Data[0].Endpoint).To(Equal(insertResp1.Data.Endpoint)) }) + }) Context("getting all configs with unknown endpoint filter", func() { @@ -119,6 +123,7 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { Expect(resp.Cursor.HasMore).To(BeFalse()) Expect(resp.Cursor.Data).To(BeEmpty()) }) + }) Context("getting all configs with known ID filter", func() { diff --git a/tests/integration/suite/webhooks-configs-insert.go b/tests/integration/suite/webhooks-configs-insert.go index 81c7af08b3..2d6cf98254 100644 --- a/tests/integration/suite/webhooks-configs-insert.go +++ b/tests/integration/suite/webhooks-configs-insert.go @@ -66,7 +66,7 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { cfg, ) Expect(err).To(HaveOccurred()) - Expect(err.(*sdkerrors.WebhooksErrorResponse).ErrorCode).To(Equal(shared.WebhooksErrorsEnumValidation)) + Expect(err.(*sdkerrors.WebhooksErrorResponse).ErrorCode).To(Equal(shared.WebhooksErrorsEnumValidationType)) }) It("inserting an invalid config with invalid secret", func() { @@ -85,89 +85,6 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { Expect(err).To(HaveOccurred()) }) - It("inserting a valid V2 Hook", func(){ - name := "ValidV2Hook" - retry := true - hookBodyParams := shared.V2HookBodyParams{ - Endpoint: "https://example.com", - Events: []string{ - "ledger.committed_transactions", - }, - Name: &name, - Retry: &retry, - } - - response , err := Client().Webhooks.InsertHook( - TestContext(), - hookBodyParams, - ) - Expect(err).ToNot(HaveOccurred()) - Expect(response.StatusCode).To(Equal(http.StatusOK)) - newV2Hook := response.V2HookResponse.Data - Expect(newV2Hook.Endpoint).To(Equal(hookBodyParams.Endpoint)) - Expect(newV2Hook.Events).To(Equal(hookBodyParams.Events)) - Expect(newV2Hook.Status).To(Equal("DISABLED")) - }) - - It("inserting an invalid V2 Hook", func(){ - name := "ValidV2Hook" - retry := true - hookBodyParams := shared.V2HookBodyParams{ - Endpoint: "https://example.com", - Events: []string{ - }, - Name: &name, - Retry: &retry, - } - - _ , err := Client().Webhooks.InsertHook( - TestContext(), - hookBodyParams, - ) - Expect(err).To(HaveOccurred()) - - }) - - It("inserting a V2 Hook without endpoint", func(){ - name := "ValidV2Hook" - retry := true - hookBodyParams := shared.V2HookBodyParams{ - Endpoint: "", - Events: []string{ - }, - Name: &name, - Retry: &retry, - } - - _ , err := Client().Webhooks.InsertHook( - TestContext(), - hookBodyParams, - ) - Expect(err).To(HaveOccurred()) - - }) - - It("inserting a V2 Hook with invalid secret", func(){ - name := "ValidV2Hook" - retry := true - secret := "invalid" - hookBodyParams := shared.V2HookBodyParams{ - Endpoint: "", - Events: []string{ - }, - Name: &name, - Retry: &retry, - Secret: &secret, - } - - _ , err := Client().Webhooks.InsertHook( - TestContext(), - hookBodyParams, - ) - Expect(err).To(HaveOccurred()) - - }) - }) diff --git a/tests/integration/suite/webhooks-configs-test.go b/tests/integration/suite/webhooks-configs-test.go index a253bd81bc..8c7ec83bc4 100644 --- a/tests/integration/suite/webhooks-configs-test.go +++ b/tests/integration/suite/webhooks-configs-test.go @@ -12,8 +12,8 @@ import ( "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" . "github.com/formancehq/stack/tests/integration/internal" - webhooksUtils "github.com/formancehq/webhooks/pkg/utils" webhookSecurity "github.com/formancehq/webhooks/pkg/security" + webhooksUtils "github.com/formancehq/webhooks/pkg/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) @@ -33,24 +33,29 @@ var _ = WithModules([]*Module{modules.Webhooks}, func() { id := r.Header.Get("formance-webhook-id") ts := r.Header.Get("formance-webhook-timestamp") signatures := r.Header.Get("formance-webhook-signature") + timeInt, err := strconv.ParseInt(ts, 10, 64) + if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } payload, err := io.ReadAll(r.Body) + if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } - + ok, err := webhookSecurity.Verify(signatures, id, timeInt, secret, payload) if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) return } if !ok { + http.Error(w, "WEBHOOKS SIGNATURE VERIFICATION NOK", http.StatusBadRequest) return } diff --git a/tests/integration/suite/webhooks-retries.go b/tests/integration/suite/webhooks-retries.go deleted file mode 100644 index 4641e0e561..0000000000 --- a/tests/integration/suite/webhooks-retries.go +++ /dev/null @@ -1,147 +0,0 @@ -package suite - -// -// import ( -// "database/sql" -// "math/big" -// "net/http" -// "net/http/httptest" -// "time" - -// "github.com/formancehq/stack/tests/integration/internal/modules" - -// "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" -// "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" -// . "github.com/formancehq/stack/tests/integration/internal" -// . "github.com/onsi/ginkgo/v2" -// . "github.com/onsi/gomega" -// "github.com/spf13/viper" -// "github.com/uptrace/bun" -// "github.com/uptrace/bun/dialect/pgdialect" -// "github.com/uptrace/bun/driver/pgdriver" -// ) - -// var _ = WithModules([]*Module{modules.Ledger, modules.Webhooks}, func() { -// BeforeEach(func() { -// createLedgerResponse, err := Client().Ledger.V2CreateLedger(TestContext(), operations.V2CreateLedgerRequest{ -// Ledger: "default", -// }) -// Expect(err).To(BeNil()) -// Expect(createLedgerResponse.StatusCode).To(Equal(http.StatusNoContent)) -// }) -// Context("the endpoint only returning errors", func() { -// It("with an exponential backoff starting at 1s with a 3s timeout, 3 attempts have to be made and all should have a failed status", func() { -// httpServer := httptest.NewServer(http.HandlerFunc( -// func(w http.ResponseWriter, _ *http.Request) { -// http.Error(w, "error", http.StatusNotFound) -// })) -// defer func() { -// httpServer.Close() -// }() -// //TODO: Remove viper usage -// sqldb := sql.OpenDB( -// pgdriver.NewConnector( -// pgdriver.WithDSN(viper.GetString("postgres-uri")))) -// db := bun.NewDB(sqldb, pgdialect.New()) -// defer func() { -// _ = db.Close() -// }() - -// response, err := Client().Webhooks.InsertConfig( -// TestContext(), -// shared.ConfigUser{ -// Endpoint: httpServer.URL, -// EventTypes: []string{ -// "ledger.committed_transactions", -// }, -// }, -// ) -// Expect(err).ToNot(HaveOccurred()) -// Expect(response.StatusCode).To(Equal(http.StatusOK)) - -// createTransactionResponse, err := Client().Ledger.V2CreateTransaction( -// TestContext(), -// operations.V2CreateTransactionRequest{ -// V2PostTransaction: shared.V2PostTransaction{ -// Metadata: map[string]string{}, -// Postings: []shared.V2Posting{ -// { -// Amount: big.NewInt(100), -// Asset: "USD", -// Source: "world", -// Destination: "alice", -// }, -// }, -// }, -// Ledger: "default", -// }, -// ) -// Expect(err).ToNot(HaveOccurred()) -// Expect(createTransactionResponse.StatusCode).To(Equal(http.StatusOK)) - -// Eventually(db.Ping()). -// WithTimeout(5 * time.Second).Should(Succeed()) - -// Eventually(getNumAttemptsToRetry).WithArguments(db). -// WithTimeout(5 * time.Second). -// Should(BeNumerically(">", 0)) - -// Eventually(getNumFailedAttempts).WithArguments(db). -// WithTimeout(5 * time.Second). -// Should(BeNumerically(">=", 3)) - -// toRetry, err := getNumAttemptsToRetry(db) -// Expect(err).ToNot(HaveOccurred()) -// Expect(toRetry).To(Equal(0)) -// }) -// }) -// }) - -// func getNumAttempts(db *bun.DB) (int, error) { -// var results []webhooks.Attempt -// if err := db.NewSelect().Model(&results).Scan(TestContext()); err != nil { -// return 0, err -// } -// return len(results), nil -// } - -// func getNumAttemptsToRetry(db *bun.DB) (int, error) { -// var results []webhooks.Attempt -// err := db.NewSelect().Model(&results). -// Where("status = ?", "to retry"). -// Scan(TestContext()) -// if err != nil { -// return 0, err -// } -// return len(results), nil -// } - -// func getNumFailedAttempts(db *bun.DB) (int, error) { -// var results []webhooks.Attempt -// err := db.NewSelect().Model(&results). -// Where("status = ?", "failed"). -// Scan(TestContext()) -// if err != nil { -// return 0, err -// } -// return len(results), nil -// } - -// func getAttempts(db *bun.DB) ([]webhooks.Attempt, error) { -// var results []webhooks.Attempt -// if err := db.NewSelect().Model(&results).Scan(TestContext()); err != nil { -// return []webhooks.Attempt{}, err -// } -// return results, nil -// } - -// func getAttemptsStatus(db *bun.DB) (string, error) { -// atts, err := getAttempts(db) -// if err != nil { -// return "", err -// } -// if len(atts) == 0 { -// return "", nil -// } -// return atts[0].Status, nil -// } diff --git a/tests/integration/suite/webhooks-v2-hooks-activation.go b/tests/integration/suite/webhooks-v2-hooks-activation.go new file mode 100644 index 0000000000..3696e721a5 --- /dev/null +++ b/tests/integration/suite/webhooks-v2-hooks-activation.go @@ -0,0 +1,96 @@ +package suite + +import ( + "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/v2/pkg/models/sdkerrors" + "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" + . "github.com/formancehq/stack/tests/integration/internal" + "github.com/formancehq/stack/tests/integration/internal/modules" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = WithModules([]*Module{modules.Webhooks}, func() { + var ( + hook1 shared.V2Hook + ) + + BeforeEach(func() { + + hookBodyParams := shared.V2HookBodyParams{ + Endpoint: "https://example.com", + Name: ptr("Hook1"), + Events: []string{ + "ledger.committed_transactions", + }, + } + + resp1, err := Client().Webhooks.InsertHook( + TestContext(), + hookBodyParams, + ) + Expect(err).NotTo(HaveOccurred()) + hook1 = resp1.V2HookResponse.Data + + }) + + Context("Activate and Deactive Hook1", func(){ + It("should be activate", func(){ + response, err := Client().Webhooks.ActivateHook( + TestContext(), + operations.ActivateHookRequest{ + HookID: hook1.ID, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(response.V2HookResponse.Data.Status).To(Equal(shared.V2HookStatusEnabled)) + }) + + It("should be deactivate", func(){ + response, err := Client().Webhooks.DeactivateHook( + TestContext(), + operations.DeactivateHookRequest{ + HookID: hook1.ID, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(response.V2HookResponse.Data.Status).To(Equal(shared.V2HookStatusDisabled)) + }) + + It("Should return One hook deactivate", func(){ + response, err := Client().Webhooks.GetManyHooks( + TestContext(), + operations.GetManyHooksRequest{}, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(response.V2HookCursorResponse.Cursor.Data[0].Status).To(Equal(shared.V2HookStatusDisabled)) + }) + + + }) + + Context("Activate and Deactivate a bad ID Hook", func(){ + It("Activate should failed", func(){ + _, err := Client().Webhooks.ActivateHook( + TestContext(), + operations.ActivateHookRequest{ + HookID: "UNKNOWN", + }, + ) + Expect(err).To(HaveOccurred()) + Expect(err.(*sdkerrors.WebhooksErrorResponse).ErrorCode).To(Equal(shared.WebhooksErrorsEnumNotFound)) + }) + + It("Deactivate should failed", func(){ + _, err := Client().Webhooks.DeactivateHook( + TestContext(), + operations.DeactivateHookRequest{ + HookID: "UNKNOWN", + }, + ) + Expect(err).To(HaveOccurred()) + Expect(err.(*sdkerrors.WebhooksErrorResponse).ErrorCode).To(Equal(shared.WebhooksErrorsEnumNotFound)) + }) + }) + +}) diff --git a/tests/integration/suite/webhooks-v2-hooks-delete.go b/tests/integration/suite/webhooks-v2-hooks-delete.go new file mode 100644 index 0000000000..ab53529724 --- /dev/null +++ b/tests/integration/suite/webhooks-v2-hooks-delete.go @@ -0,0 +1,87 @@ +package suite + +import ( + "net/http" + + "github.com/formancehq/formance-sdk-go/v2/pkg/models/sdkerrors" + "github.com/formancehq/stack/tests/integration/internal/modules" + + "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" + . "github.com/formancehq/stack/tests/integration/internal" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = WithModules([]*Module{modules.Webhooks}, func() { + var ( + hook *shared.V2Hook + ) + + BeforeEach(func() { + + hookBodyParam := shared.V2HookBodyParams{ + Endpoint: "https://example1.com", + Name: ptr("Test1"), + Events: []string{ + "ledger.committed_transactions", + }, + } + + resp, err := Client().Webhooks.InsertHook( + TestContext(), + hookBodyParam, + ) + Expect(err).ToNot(HaveOccurred()) + hook = &resp.V2HookResponse.Data + + + }) + + Context("Hook: deleting the inserted one", func() { + BeforeEach(func() { + + + + response, err := Client().Webhooks.DeleteHook( + TestContext(), + operations.DeleteHookRequest{ + HookID: hook.ID, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + Expect(response.V2HookResponse.Data.Status).To(Equal(shared.V2HookStatusDeleted)) + }) + + Context("Hook : getting all hooks", func() { + It("None should be Hook1", func() { + response, err := Client().Webhooks.GetManyHooks( + TestContext(), + operations.GetManyHooksRequest{}, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + + Expect(response.V2HookCursorResponse.Cursor.HasMore).To(BeFalse()) + for _, data := range response.V2HookCursorResponse.Cursor.Data { + Expect(data.ID).ToNot(Equal(hook.ID)) + } + + }) + }) + }) + + Context("Hook: trying to delete an unknown ID", func() { + It("should fail", func() { + _, err := Client().Webhooks.DeleteHook( + TestContext(), + operations.DeleteHookRequest{ + HookID: "unknown", + }, + ) + Expect(err).To(HaveOccurred()) + Expect(err.(*sdkerrors.WebhooksErrorResponse).ErrorCode).To(Equal(shared.WebhooksErrorsEnumNotFound)) + }) + }) +}) diff --git a/tests/integration/suite/webhooks-v2-hooks-endpoint.go b/tests/integration/suite/webhooks-v2-hooks-endpoint.go new file mode 100644 index 0000000000..165262c0fa --- /dev/null +++ b/tests/integration/suite/webhooks-v2-hooks-endpoint.go @@ -0,0 +1,58 @@ +package suite + +import ( + "net/http" + + "github.com/formancehq/stack/tests/integration/internal/modules" + + "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" + . "github.com/formancehq/stack/tests/integration/internal" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = WithModules([]*Module{modules.Webhooks}, func() { + var ( + endpoint2 = "https://example2.com" + hook1 shared.V2Hook + ) + + BeforeEach(func() { + hookBodyParam := shared.V2HookBodyParams{ + Endpoint: "https://example.com", + Events: []string{ + "ledger.committed_transactions", + }, + } + response, err := Client().Webhooks.InsertHook( + TestContext(), + hookBodyParam, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + + hook1 = response.V2HookResponse.Data + }) + + Context("changing the endpoint of the inserted one", func() { + + It("should work ",func() { + response, err := Client().Webhooks.UpdateEndpointHook( + TestContext(), + operations.UpdateEndpointHookRequest{ + RequestBody: operations.UpdateEndpointHookRequestBody{ + Endpoint: &endpoint2, + }, + HookID: hook1.ID, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + Expect(response.V2HookResponse.Data.Endpoint).To(Equal(endpoint2)) + }) + + + + }) +}) diff --git a/tests/integration/suite/webhooks-v2-hooks-get.go b/tests/integration/suite/webhooks-v2-hooks-get.go new file mode 100644 index 0000000000..40b806ae38 --- /dev/null +++ b/tests/integration/suite/webhooks-v2-hooks-get.go @@ -0,0 +1,200 @@ +package suite + +import ( + "net/http" + + "github.com/formancehq/stack/tests/integration/internal/modules" + + "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/v2/pkg/models/sdkerrors" + "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" + . "github.com/formancehq/stack/tests/integration/internal" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = WithModules([]*Module{modules.Webhooks}, func() { + + It("should return 0 hook", func() { + response, err := Client().Webhooks.GetManyHooks( + TestContext(), + operations.GetManyHooksRequest{}, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + + Expect(response.V2HookCursorResponse.Cursor.HasMore).To(BeFalse()) + Expect(response.V2HookCursorResponse.Cursor.Data).To(BeEmpty()) + }) + + + When("inserting 4 Hooks", func() { + var ( + + hook1 *shared.V2Hook + hook2 *shared.V2Hook + hook3 *shared.V2Hook + hook4 *shared.V2Hook + + ) + + BeforeEach(func() { + var ( + err error + + name1 = "Hook1" + name2 = "Hook2" + name3 = "Hook3" + name4 = "Hook4" + + hookBody1 = shared.V2HookBodyParams{ + Name: &name1, + Endpoint: "https://example1.com", + Events: []string{ + "ledger.committed_transactions", + }, + } + hookBody2 = shared.V2HookBodyParams{ + Name: &name2, + Endpoint: "https://example.hook2", + Events: []string{ + "ledger.committed_transactions", + }, + } + hookBody3 = shared.V2HookBodyParams{ + Name: &name3, + Endpoint: "https://example.hook2", + Events: []string{ + "ledger.committed_transactions", + }, + } + hookBody4 = shared.V2HookBodyParams{ + Name: &name4, + Endpoint: "https://example.hook4", + Events: []string{ + "ledger.committed_transactions", + }, + } + ) + + + response1, err := Client().Webhooks.InsertHook( + TestContext(), + hookBody1, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(response1.StatusCode).To(Equal(http.StatusOK)) + hook1 = &response1.V2HookResponse.Data + Expect(hook1.Name).To(Equal(*(hookBody1.Name))) + + response2, err := Client().Webhooks.InsertHook( + TestContext(), + hookBody2, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(response2.StatusCode).To(Equal(http.StatusOK)) + hook2 = &response2.V2HookResponse.Data + Expect(hook2.Name).To(Equal(*(hookBody2.Name))) + + response3, err := Client().Webhooks.InsertHook( + TestContext(), + hookBody3, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(response3.StatusCode).To(Equal(http.StatusOK)) + hook3 = &response3.V2HookResponse.Data + Expect(hook3.Name).To(Equal(*(hookBody3.Name))) + + response4, err := Client().Webhooks.InsertHook( + TestContext(), + hookBody4, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(response4.StatusCode).To(Equal(http.StatusOK)) + hook4 = &response4.V2HookResponse.Data + Expect(hook4.Name).To(Equal(*(hookBody4.Name))) + }) + + Context("getting all V2 hooks without filters", func() { + + It("should return 4 Hooks", func() { + response, err := Client().Webhooks.GetManyHooks( + TestContext(), + operations.GetManyHooksRequest{}, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + + resp := response.V2HookCursorResponse + Expect(resp.Cursor.HasMore).To(BeFalse()) + Expect(resp.Cursor.Data).To(HaveLen(4)) + + }) + }) + + Context("getting all chooks with known endpoint filter", func() { + + It("should return 2 hooks with the same endpoint", func() { + response, err := Client().Webhooks.GetManyHooks( + TestContext(), + operations.GetManyHooksRequest{ + Endpoint: ptr(hook2.Endpoint), + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + + cursor := response.V2HookCursorResponse.Cursor + Expect(cursor.HasMore).To(BeFalse()) + Expect(cursor.Data).To(HaveLen(2)) + Expect(cursor.Data[0].Endpoint).To(Equal(hook2.Endpoint)) + }) + }) + + Context("getting all hooks with unknown endpoint filter", func() { + + It("should return 0 hook", func() { + response, err := Client().Webhooks.GetManyHooks( + TestContext(), + operations.GetManyHooksRequest{ + Endpoint: ptr("https://unknown.com"), + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + + resp := response.V2HookCursorResponse.Cursor + Expect(resp.HasMore).To(BeFalse()) + Expect(resp.Data).To(BeEmpty()) + }) + }) + + + Context("getting ONE hook", func() { + It("should return 1 Hook with the same ID", func() { + response, err := Client().Webhooks.GetHook( + TestContext(), + operations.GetHookRequest{ + HookID: hook1.ID, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + + resp := response.V2HookResponse + Expect(resp.Data.ID).To(Equal(hook1.ID)) + }) + It("should return error because false ID", func(){ + _, err := Client().Webhooks.GetHook( + TestContext(), + operations.GetHookRequest{ + HookID: "BADID", + }, + ) + Expect(err).To(HaveOccurred()) + Expect(err.(*sdkerrors.WebhooksErrorResponse).ErrorCode).To(Equal(shared.WebhooksErrorsEnumNotFound)) + }) + }) + + }) +}) diff --git a/tests/integration/suite/webhooks-v2-hooks-insert.go b/tests/integration/suite/webhooks-v2-hooks-insert.go new file mode 100644 index 0000000000..4d603af328 --- /dev/null +++ b/tests/integration/suite/webhooks-v2-hooks-insert.go @@ -0,0 +1,103 @@ +package suite + +import ( + "net/http" + "github.com/formancehq/stack/tests/integration/internal/modules" + + "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" + . "github.com/formancehq/stack/tests/integration/internal" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = WithModules([]*Module{modules.Webhooks}, func() { + + When("Trying to insert Hooks", func(){ + It("inserting a valid V2 Hook", func(){ + name := "ValidV2Hook" + retry := true + hookBodyParams := shared.V2HookBodyParams{ + Endpoint: "https://example.com", + Events: []string{ + "ledger.committed_transactions", + }, + Name: &name, + Retry: &retry, + } + + response , err := Client().Webhooks.InsertHook( + TestContext(), + hookBodyParams, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + newV2Hook := response.V2HookResponse.Data + Expect(newV2Hook.Endpoint).To(Equal(hookBodyParams.Endpoint)) + Expect(newV2Hook.Events).To(Equal(hookBodyParams.Events)) + Expect(newV2Hook.Status).To(Equal(shared.V2HookStatusDisabled)) + }) + + It("inserting an invalid V2 Hook", func(){ + name := "ValidV2Hook" + retry := true + hookBodyParams := shared.V2HookBodyParams{ + Endpoint: "https://example.com", + Events: []string{ + }, + Name: &name, + Retry: &retry, + } + + _ , err := Client().Webhooks.InsertHook( + TestContext(), + hookBodyParams, + ) + Expect(err).To(HaveOccurred()) + + }) + + It("inserting a V2 Hook without endpoint", func(){ + name := "ValidV2Hook" + retry := true + hookBodyParams := shared.V2HookBodyParams{ + Endpoint: "", + Events: []string{ + }, + Name: &name, + Retry: &retry, + } + + _ , err := Client().Webhooks.InsertHook( + TestContext(), + hookBodyParams, + ) + Expect(err).To(HaveOccurred()) + + }) + + It("inserting a V2 Hook with invalid secret", func(){ + name := "ValidV2Hook" + retry := true + secret := "invalid" + hookBodyParams := shared.V2HookBodyParams{ + Endpoint: "", + Events: []string{ + }, + Name: &name, + Retry: &retry, + Secret: &secret, + } + + _ , err := Client().Webhooks.InsertHook( + TestContext(), + hookBodyParams, + ) + Expect(err).To(HaveOccurred()) + + }) + + }) + + +}) diff --git a/tests/integration/suite/webhooks-v2-hooks-secret.go b/tests/integration/suite/webhooks-v2-hooks-secret.go new file mode 100644 index 0000000000..45530078b3 --- /dev/null +++ b/tests/integration/suite/webhooks-v2-hooks-secret.go @@ -0,0 +1,91 @@ +package suite + +import ( + "net/http" + + "github.com/formancehq/stack/tests/integration/internal/modules" + + "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/v2/pkg/models/sdkerrors" + "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" + . "github.com/formancehq/stack/tests/integration/internal" + webhooks "github.com/formancehq/webhooks/pkg/utils" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = WithModules([]*Module{modules.Webhooks}, func() { + var ( + secret1 = webhooks.NewSecret() + secret2 = webhooks.NewSecret() + hook1 shared.V2Hook + ) + + BeforeEach(func() { + hookBodyParam := shared.V2HookBodyParams{ + Endpoint: "https://example.com", + Secret: &secret1, + Events: []string{ + "ledger.committed_transactions", + }, + } + response, err := Client().Webhooks.InsertHook( + TestContext(), + hookBodyParam, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + + hook1 = response.V2HookResponse.Data + }) + + Context("changing the secret of the inserted one", func() { + + It("should work with no secret provided",func() { + response, err := Client().Webhooks.UpdateSecretHook( + TestContext(), + operations.UpdateSecretHookRequest{ + RequestBody: operations.UpdateSecretHookRequestBody{ + }, + HookID: hook1.ID, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + Expect(response.V2HookResponse.Data.Secret).To(Not(Equal(hook1.Secret))) + }) + + + It("should work with a secret provided",func() { + response, err := Client().Webhooks.UpdateSecretHook( + TestContext(), + operations.UpdateSecretHookRequest{ + RequestBody: operations.UpdateSecretHookRequestBody{ + Secret: &secret2, + }, + HookID: hook1.ID, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + Expect(response.V2HookResponse.Data.Secret).To(Equal(secret2)) + }) + + It("should not work with an invalid secret provided",func() { + _, err := Client().Webhooks.UpdateSecretHook( + TestContext(), + operations.UpdateSecretHookRequest{ + RequestBody: operations.UpdateSecretHookRequestBody{ + Secret: ptr("invalid_secret"), + }, + HookID: hook1.ID, + }, + ) + Expect(err).To(HaveOccurred()) + Expect(err.(*sdkerrors.WebhooksErrorResponse).ErrorCode).To(Equal(shared.WebhooksErrorsEnumValidationType)) + + }) + + + }) +}) diff --git a/tests/integration/suite/webhooks-v2-hooks-test.go b/tests/integration/suite/webhooks-v2-hooks-test.go new file mode 100644 index 0000000000..cc21c3522f --- /dev/null +++ b/tests/integration/suite/webhooks-v2-hooks-test.go @@ -0,0 +1,172 @@ +package suite + +import ( + "io" + "net/http" + "net/http/httptest" + "strconv" + + "github.com/formancehq/formance-sdk-go/v2/pkg/models/sdkerrors" + "github.com/formancehq/stack/tests/integration/internal/modules" + + "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" + . "github.com/formancehq/stack/tests/integration/internal" + webhookSecurity "github.com/formancehq/webhooks/pkg/security" + webhooksUtils "github.com/formancehq/webhooks/pkg/utils" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = WithModules([]*Module{modules.Webhooks}, func() { + When("testing Hooks", func() { + Context("inserting a HOok with an endpoint to a success handler", func() { + var ( + httpServer *httptest.Server + hook shared.V2Hook + secret = webhooksUtils.NewSecret() + payload = "{\"Data\":\"payload_test\"}" + ) + + BeforeEach(func() { + httpServer = httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + id := r.Header.Get("formance-webhook-id") + ts := r.Header.Get("formance-webhook-timestamp") + signatures := r.Header.Get("formance-webhook-signature") + + timeInt, err := strconv.ParseInt(ts, 10, 64) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + payload, err := io.ReadAll(r.Body) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + ok, err := webhookSecurity.Verify(signatures, id, timeInt, secret, payload) + if err != nil { + + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if !ok { + + http.Error(w, "WEBHOOKS SIGNATURE VERIFICATION NOK", http.StatusBadRequest) + return + } + })) + + hookBodyParam := shared.V2HookBodyParams{ + Endpoint: httpServer.URL, + Secret: &secret, + Events: []string{ + "ledger.committed_transactions", + }, + } + response, err := Client().Webhooks.InsertHook( + TestContext(), + hookBodyParam, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + hook = response.V2HookResponse.Data + DeferCleanup(func() { + httpServer.Close() + }) + }) + + Context("testing the inserted one", func() { + It("should return a successful attempt", func() { + response, err := Client().Webhooks.TestHook( + TestContext(), + operations.TestHookRequest{ + HookID: hook.ID, + RequestBody: operations.TestHookRequestBody{ + Payload: &payload, + }, + + }, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + + attemptResp := response.V2AttemptResponse + Expect(attemptResp.Data.HookID).To(Equal(hook.ID)) + Expect(attemptResp.Data.Payload).To(Equal(payload)) + Expect(int(attemptResp.Data.StatusCode)).To(Equal(http.StatusOK)) + Expect(attemptResp.Data.Status).To(Equal(shared.V2AttemptStatusSuccess)) + }) + }) + }) + + Context("inserting a hook with an endpoint to a fail handler", func() { + var hook2 *shared.V2Hook + var payload = "{\"Data\":\"payload_test\"}" + BeforeEach(func() { + httpServer := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, + "WEBHOOKS RECEIVED: MOCK ERROR RESPONSE", http.StatusNotFound) + })) + + hookBodyParam := shared.V2HookBodyParams{ + Endpoint: httpServer.URL, + Events: []string{ + "ledger.committed_transactions", + }, + } + response, err := Client().Webhooks.InsertHook( + TestContext(), + hookBodyParam, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + hook2 = &response.V2HookResponse.Data + DeferCleanup(func() { + httpServer.Close() + }) + }) + + Context("testing the inserted one", func() { + It("should return a failed attempt", func() { + response, err := Client().Webhooks.TestHook( + TestContext(), + operations.TestHookRequest{ + HookID: hook2.ID, + RequestBody: operations.TestHookRequestBody{ + Payload: ptr(payload), + }, + }, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + + attemptResp := response.V2AttemptResponse + Expect(attemptResp.Data.HookID).To(Equal(hook2.ID)) + Expect(attemptResp.Data.Payload).To(Equal(payload)) + Expect(int(attemptResp.Data.StatusCode)).To(Equal(http.StatusNotFound)) + Expect(attemptResp.Data.Status).To(Equal(shared.V2AttemptStatusAbort)) + }) + }) + }) + + Context("testing an unknown ID", func() { + It("should fail", func() { + _, err := Client().Webhooks.TestHook( + TestContext(), + operations.TestHookRequest{ + HookID: "unknown", + }, + ) + Expect(err).To(HaveOccurred()) + Expect(err.(*sdkerrors.WebhooksErrorResponse).ErrorCode).To(Equal(shared.WebhooksErrorsEnumNotFound)) + }) + }) + }) +}) diff --git a/tests/integration/suite/webhooks-v2-ledger-committed-transaction.go b/tests/integration/suite/webhooks-v2-ledger-committed-transaction.go new file mode 100644 index 0000000000..ebdb4b17a4 --- /dev/null +++ b/tests/integration/suite/webhooks-v2-ledger-committed-transaction.go @@ -0,0 +1,97 @@ +package suite + +import ( + "fmt" + "math/big" + "net/http" + "net/http/httptest" + + "github.com/formancehq/stack/tests/integration/internal/modules" + + "github.com/formancehq/formance-sdk-go/v2/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/v2/pkg/models/shared" + . "github.com/formancehq/stack/tests/integration/internal" + webhooks "github.com/formancehq/webhooks/pkg/utils" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = WithModules([]*Module{modules.Ledger, modules.Webhooks}, func() { + BeforeEach(func() { + createLedgerResponse, err := Client().Ledger.V2CreateLedger(TestContext(), operations.V2CreateLedgerRequest{ + Ledger: "default", + }) + Expect(err).To(BeNil()) + Expect(createLedgerResponse.StatusCode).To(Equal(http.StatusNoContent)) + }) + var ( + httpServer *httptest.Server + called chan struct{} + secret = webhooks.NewSecret() + hook1 shared.V2Hook + ) + + BeforeEach(func() { + called = make(chan struct{}) + httpServer = httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Println("INSIDE SERVER ;") + defer close(called) + return + })) + DeferCleanup(func() { + httpServer.Close() + }) + + + + response, err := Client().Webhooks.InsertHook( + TestContext(), + shared.V2HookBodyParams{ + Endpoint: httpServer.URL, + Secret: &secret, + Events: []string{ + "ledger.committed_transactions", + }, + }, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + hook1 = response.V2HookResponse.Data + _, err = Client().Webhooks.ActivateHook( + TestContext(), + operations.ActivateHookRequest{ + HookID: hook1.ID, + }, + ) + Expect(err).ToNot(HaveOccurred()) + }) + + When("creating a transaction", func() { + BeforeEach(func() { + response, err := Client().Ledger.V2CreateTransaction( + TestContext(), + operations.V2CreateTransactionRequest{ + V2PostTransaction: shared.V2PostTransaction{ + Metadata: map[string]string{}, + Postings: []shared.V2Posting{ + { + Amount: big.NewInt(100), + Asset: "USD", + Source: "world", + Destination: "alice", + }, + }, + }, + Ledger: "default", + }, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + }) + + It("should trigger a call to the webhook endpoint", func() { + Eventually(ChanClosed(called)).Should(BeTrue()) + }) + }) +})