refactor(api): remove duplicates of TxResponse + HandlerError detection (#1117)

pull/11580/merge
LP B 2025-09-11 11:33:30 +02:00 committed by GitHub
parent 26a0c4e809
commit 326fdcf6ea
36 changed files with 381 additions and 299 deletions

View File

@ -5,15 +5,14 @@ import (
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/database/models" "github.com/portainer/portainer/api/database/models"
"github.com/stretchr/testify/require"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
func TestStoreCreation(t *testing.T) { func TestStoreCreation(t *testing.T) {
_, store := MustNewTestStore(t, true, true) _, store := MustNewTestStore(t, true, true)
if store == nil { require.NotNil(t, store)
t.Fatal("Expect to create a store")
}
v, err := store.VersionService.Version() v, err := store.VersionService.Version()
if err != nil { if err != nil {

View File

@ -1,20 +0,0 @@
package errors
import (
"errors"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
)
func TxResponse(err error, validResponse func() *httperror.HandlerError) *httperror.HandlerError {
if err != nil {
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)
}
return validResponse()
}

View File

@ -12,7 +12,6 @@ import (
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/docker/stats" "github.com/portainer/portainer/api/docker/stats"
"github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/api/http/handler/docker/utils" "github.com/portainer/portainer/api/http/handler/docker/utils"
"github.com/portainer/portainer/api/http/middlewares" "github.com/portainer/portainer/api/http/middlewares"
"github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/http/security"
@ -164,7 +163,5 @@ func (h *Handler) dashboard(w http.ResponseWriter, r *http.Request) *httperror.H
return nil return nil
}) })
return errors.TxResponse(err, func() *httperror.HandlerError { return response.TxResponse(w, resp, err)
return response.JSON(w, resp)
})
} }

View File

@ -10,6 +10,7 @@ import (
"github.com/portainer/portainer/api/roar" "github.com/portainer/portainer/api/roar"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request" "github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
) )
type edgeGroupCreatePayload struct { type edgeGroupCreatePayload struct {
@ -111,5 +112,5 @@ func (handler *Handler) edgeGroupCreate(w http.ResponseWriter, r *http.Request)
return nil return nil
}) })
return txResponse(w, shadowedEdgeGroup{EdgeGroup: *edgeGroup}, err) return response.TxResponse(w, shadowedEdgeGroup{EdgeGroup: *edgeGroup}, err)
} }

View File

@ -32,16 +32,8 @@ func (handler *Handler) edgeGroupDelete(w http.ResponseWriter, r *http.Request)
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return deleteEdgeGroup(tx, portainer.EdgeGroupID(edgeGroupID)) return deleteEdgeGroup(tx, portainer.EdgeGroupID(edgeGroupID))
}) })
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err) return response.TxEmptyResponse(w, err)
}
return response.Empty(w)
} }
func deleteEdgeGroup(tx dataservices.DataStoreTx, ID portainer.EdgeGroupID) error { func deleteEdgeGroup(tx dataservices.DataStoreTx, ID portainer.EdgeGroupID) error {

View File

@ -8,6 +8,7 @@ import (
"github.com/portainer/portainer/api/roar" "github.com/portainer/portainer/api/roar"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request" "github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
) )
// @id EdgeGroupInspect // @id EdgeGroupInspect
@ -36,7 +37,7 @@ func (handler *Handler) edgeGroupInspect(w http.ResponseWriter, r *http.Request)
edgeGroup.Endpoints = edgeGroup.EndpointIDs.ToSlice() edgeGroup.Endpoints = edgeGroup.EndpointIDs.ToSlice()
return txResponse(w, shadowedEdgeGroup{EdgeGroup: *edgeGroup}, err) return response.TxResponse(w, shadowedEdgeGroup{EdgeGroup: *edgeGroup}, err)
} }
func getEdgeGroup(tx dataservices.DataStoreTx, ID portainer.EdgeGroupID) (*portainer.EdgeGroup, error) { func getEdgeGroup(tx dataservices.DataStoreTx, ID portainer.EdgeGroupID) (*portainer.EdgeGroup, error) {

View File

@ -9,6 +9,7 @@ import (
"github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/roar" "github.com/portainer/portainer/api/roar"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/response"
) )
type shadowedEdgeGroup struct { type shadowedEdgeGroup struct {
@ -44,7 +45,7 @@ func (handler *Handler) edgeGroupList(w http.ResponseWriter, r *http.Request) *h
return err return err
}) })
return txResponse(w, decoratedEdgeGroups, err) return response.TxResponse(w, decoratedEdgeGroups, err)
} }
func getEdgeGroupList(tx dataservices.DataStoreTx) ([]decoratedEdgeGroup, error) { func getEdgeGroupList(tx dataservices.DataStoreTx) ([]decoratedEdgeGroup, error) {

View File

@ -13,6 +13,7 @@ import (
"github.com/portainer/portainer/api/slicesx" "github.com/portainer/portainer/api/slicesx"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request" "github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
) )
type edgeGroupUpdatePayload struct { type edgeGroupUpdatePayload struct {
@ -158,7 +159,7 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request)
return nil return nil
}) })
return txResponse(w, shadowedEdgeGroup{EdgeGroup: *edgeGroup}, err) return response.TxResponse(w, shadowedEdgeGroup{EdgeGroup: *edgeGroup}, err)
} }
func (handler *Handler) updateEndpointStacks(tx dataservices.DataStoreTx, endpoint *portainer.Endpoint, edgeGroups []portainer.EdgeGroup, edgeStacks []portainer.EdgeStack) error { func (handler *Handler) updateEndpointStacks(tx dataservices.DataStoreTx, endpoint *portainer.Endpoint, edgeGroups []portainer.EdgeGroup, edgeStacks []portainer.EdgeStack) error {

View File

@ -1,14 +1,12 @@
package edgegroups package edgegroups
import ( import (
"errors"
"net/http" "net/http"
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
@ -38,16 +36,3 @@ func NewHandler(bouncer security.BouncerService) *Handler {
return h return h
} }
func txResponse(w http.ResponseWriter, r any, err error) *httperror.HandlerError {
if err != nil {
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)
}
return response.JSON(w, r)
}

View File

@ -15,6 +15,7 @@ import (
"github.com/portainer/portainer/api/internal/endpointutils" "github.com/portainer/portainer/api/internal/endpointutils"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request" "github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/portainer/portainer/pkg/validate" "github.com/portainer/portainer/pkg/validate"
) )
@ -85,19 +86,18 @@ func (payload *edgeJobCreateFromFileContentPayload) Validate(r *http.Request) er
// @router /edge_jobs/create/string [post] // @router /edge_jobs/create/string [post]
func (handler *Handler) createEdgeJobFromFileContent(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { func (handler *Handler) createEdgeJobFromFileContent(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
var payload edgeJobCreateFromFileContentPayload var payload edgeJobCreateFromFileContentPayload
err := request.DecodeAndValidateJSONPayload(r, &payload) if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
if err != nil {
return httperror.BadRequest("Invalid request payload", err) return httperror.BadRequest("Invalid request payload", err)
} }
var edgeJob *portainer.EdgeJob var edgeJob *portainer.EdgeJob
var err error
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
edgeJob, err = handler.createEdgeJob(tx, &payload.edgeJobBasePayload, []byte(payload.FileContent)) edgeJob, err = handler.createEdgeJob(tx, &payload.edgeJobBasePayload, []byte(payload.FileContent))
return err return err
}) })
return txResponse(w, edgeJob, err) return response.TxResponse(w, edgeJob, err)
} }
func (handler *Handler) createEdgeJob(tx dataservices.DataStoreTx, payload *edgeJobBasePayload, fileContent []byte) (*portainer.EdgeJob, error) { func (handler *Handler) createEdgeJob(tx dataservices.DataStoreTx, payload *edgeJobBasePayload, fileContent []byte) (*portainer.EdgeJob, error) {
@ -191,19 +191,18 @@ func (payload *edgeJobCreateFromFilePayload) Validate(r *http.Request) error {
// @router /edge_jobs/create/file [post] // @router /edge_jobs/create/file [post]
func (handler *Handler) createEdgeJobFromFile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { func (handler *Handler) createEdgeJobFromFile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
payload := &edgeJobCreateFromFilePayload{} payload := &edgeJobCreateFromFilePayload{}
err := payload.Validate(r) if err := payload.Validate(r); err != nil {
if err != nil {
return httperror.BadRequest("Invalid request payload", err) return httperror.BadRequest("Invalid request payload", err)
} }
var edgeJob *portainer.EdgeJob var edgeJob *portainer.EdgeJob
var err error
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
edgeJob, err = handler.createEdgeJob(tx, &payload.edgeJobBasePayload, payload.File) edgeJob, err = handler.createEdgeJob(tx, &payload.edgeJobBasePayload, payload.File)
return err return err
}) })
return txResponse(w, edgeJob, err) return response.TxResponse(w, edgeJob, err)
} }
func (handler *Handler) createEdgeJobObjectFromPayload(tx dataservices.DataStoreTx, payload *edgeJobBasePayload) *portainer.EdgeJob { func (handler *Handler) createEdgeJobObjectFromPayload(tx dataservices.DataStoreTx, payload *edgeJobBasePayload) *portainer.EdgeJob {

View File

@ -0,0 +1,158 @@
package edgejobs
import (
"bytes"
"encoding/json"
"io"
"mime/multipart"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/gorilla/mux"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/datastore"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
type mockFileService struct {
mock.Mock
portainer.FileService
}
func (m *mockFileService) StoreEdgeJobFileFromBytes(id string, file []byte) (string, error) {
args := m.Called(id, file)
return args.String(0), args.Error(1)
}
func (m *mockFileService) GetEdgeJobFolder(id string) string {
args := m.Called(id)
return args.String(0)
}
func (m *mockFileService) RemoveDirectory(path string) error {
args := m.Called(path)
return args.Error(0)
}
func initStore(t *testing.T) *datastore.Store {
_, store := datastore.MustNewTestStore(t, true, true)
require.NotNil(t, store)
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
require.NoError(t, tx.Endpoint().Create(&portainer.Endpoint{
ID: 1,
Name: "endpoint-1",
EdgeID: "edge-id-1",
GroupID: 1,
Type: portainer.EdgeAgentOnDockerEnvironment,
UserTrusted: true,
}))
require.NoError(t, tx.Endpoint().Create(&portainer.Endpoint{
ID: 2,
Name: "endpoint-2",
EdgeID: "edge-id-2",
GroupID: 1,
Type: portainer.EdgeAgentOnDockerEnvironment,
UserTrusted: false,
}))
return nil
}))
return store
}
func Test_edgeJobCreate_StringMethod_Success(t *testing.T) {
store := initStore(t)
fileService := &mockFileService{}
fileService.On("StoreEdgeJobFileFromBytes", mock.Anything, mock.Anything).Return("testfile.txt", nil)
handler := &Handler{
DataStore: store,
FileService: fileService,
}
payload := edgeJobCreateFromFileContentPayload{
edgeJobBasePayload: edgeJobBasePayload{
Name: "testjob",
CronExpression: "* * * * *",
Endpoints: []portainer.EndpointID{1, 2},
},
FileContent: "echo hello",
}
body, _ := json.Marshal(payload)
req := httptest.NewRequest(http.MethodPost, "/edge_jobs/create/string", bytes.NewReader(body))
req = mux.SetURLVars(req, map[string]string{"method": "string"})
w := httptest.NewRecorder()
// Call handler
errh := handler.edgeJobCreate(w, req)
require.Nil(t, errh)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
// Get edge job ID from response
var resp struct {
ID int `json:"Id"`
}
require.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
edgeJob, err := store.EdgeJob().Read(portainer.EdgeJobID(resp.ID))
require.NoError(t, err)
require.Len(t, edgeJob.Endpoints, 2)
require.Contains(t, edgeJob.Endpoints, portainer.EndpointID(1))
}
func Test_edgeJobCreate_FileMethod_Success(t *testing.T) {
store := initStore(t)
fileService := &mockFileService{}
fileService.On("StoreEdgeJobFileFromBytes", mock.Anything, mock.Anything).Return("testfile.txt", nil)
handler := &Handler{
DataStore: store,
FileService: fileService,
}
var body bytes.Buffer
writer := multipart.NewWriter(&body)
require.NoError(t, writer.WriteField("Name", "testjob"))
require.NoError(t, writer.WriteField("CronExpression", "* * * * *"))
require.NoError(t, writer.WriteField("Endpoints", "[1,2]"))
fileWriter, err := writer.CreateFormFile("file", "test.txt")
require.NoError(t, err)
_, err = io.Copy(fileWriter, strings.NewReader("echo hello"))
require.NoError(t, err)
require.NoError(t, writer.Close())
req := httptest.NewRequest(http.MethodPost, "/edge_jobs/create/file", &body)
req = mux.SetURLVars(req, map[string]string{"method": "file"})
req.Header.Set("Content-Type", writer.FormDataContentType())
w := httptest.NewRecorder()
handlerErr := handler.edgeJobCreate(w, req)
require.Nil(t, handlerErr)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
var resp struct {
ID int `json:"Id"`
}
require.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
edgeJob, err := store.EdgeJob().Read(portainer.EdgeJobID(resp.ID))
require.NoError(t, err)
require.Len(t, edgeJob.Endpoints, 2)
require.Contains(t, edgeJob.Endpoints, portainer.EndpointID(1))
}

View File

@ -1,7 +1,6 @@
package edgejobs package edgejobs
import ( import (
"errors"
"maps" "maps"
"net/http" "net/http"
"strconv" "strconv"
@ -35,18 +34,11 @@ func (handler *Handler) edgeJobDelete(w http.ResponseWriter, r *http.Request) *h
return httperror.BadRequest("Invalid Edge job identifier route variable", err) return httperror.BadRequest("Invalid Edge job identifier route variable", err)
} }
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.deleteEdgeJob(tx, portainer.EdgeJobID(edgeJobID)) return handler.deleteEdgeJob(tx, portainer.EdgeJobID(edgeJobID))
}); err != nil { })
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err) return response.TxEmptyResponse(w, err)
}
return response.Empty(w)
} }
func (handler *Handler) deleteEdgeJob(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID) error { func (handler *Handler) deleteEdgeJob(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID) error {

View File

@ -1,7 +1,6 @@
package edgejobs package edgejobs
import ( import (
"errors"
"net/http" "net/http"
"slices" "slices"
"strconv" "strconv"
@ -54,7 +53,7 @@ func (handler *Handler) edgeJobTasksClear(w http.ResponseWriter, r *http.Request
} }
} }
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
updateEdgeJobFn := func(edgeJob *portainer.EdgeJob, endpointID portainer.EndpointID, endpointsFromGroups []portainer.EndpointID) error { updateEdgeJobFn := func(edgeJob *portainer.EdgeJob, endpointID portainer.EndpointID, endpointsFromGroups []portainer.EndpointID) error {
mutationFn(edgeJob, endpointID, endpointsFromGroups) mutationFn(edgeJob, endpointID, endpointsFromGroups)
@ -62,16 +61,9 @@ func (handler *Handler) edgeJobTasksClear(w http.ResponseWriter, r *http.Request
} }
return handler.clearEdgeJobTaskLogs(tx, portainer.EdgeJobID(edgeJobID), portainer.EndpointID(taskID), updateEdgeJobFn) return handler.clearEdgeJobTaskLogs(tx, portainer.EdgeJobID(edgeJobID), portainer.EndpointID(taskID), updateEdgeJobFn)
}); err != nil { })
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err) return response.TxEmptyResponse(w, err)
}
return response.Empty(w)
} }
func (handler *Handler) clearEdgeJobTaskLogs(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID, endpointID portainer.EndpointID, updateEdgeJob func(*portainer.EdgeJob, portainer.EndpointID, []portainer.EndpointID) error) error { func (handler *Handler) clearEdgeJobTaskLogs(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID, endpointID portainer.EndpointID, updateEdgeJob func(*portainer.EdgeJob, portainer.EndpointID, []portainer.EndpointID) error) error {

View File

@ -1,7 +1,6 @@
package edgejobs package edgejobs
import ( import (
"errors"
"net/http" "net/http"
"slices" "slices"
@ -39,7 +38,7 @@ func (handler *Handler) edgeJobTasksCollect(w http.ResponseWriter, r *http.Reque
return httperror.BadRequest("Invalid Task identifier route variable", err) return httperror.BadRequest("Invalid Task identifier route variable", err)
} }
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
edgeJob, err := tx.EdgeJob().Read(portainer.EdgeJobID(edgeJobID)) edgeJob, err := tx.EdgeJob().Read(portainer.EdgeJobID(edgeJobID))
if tx.IsErrObjectNotFound(err) { if tx.IsErrObjectNotFound(err) {
return httperror.NotFound("Unable to find an Edge job with the specified identifier inside the database", err) return httperror.NotFound("Unable to find an Edge job with the specified identifier inside the database", err)
@ -81,14 +80,7 @@ func (handler *Handler) edgeJobTasksCollect(w http.ResponseWriter, r *http.Reque
} }
return nil return nil
}); err != nil { })
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err) return response.TxEmptyResponse(w, err)
}
return response.Empty(w)
} }

View File

@ -13,6 +13,7 @@ import (
"github.com/portainer/portainer/api/internal/edge" "github.com/portainer/portainer/api/internal/edge"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request" "github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
) )
type taskContainer struct { type taskContainer struct {
@ -49,31 +50,33 @@ func (handler *Handler) edgeJobTasksList(w http.ResponseWriter, r *http.Request)
return err return err
}) })
results := filters.SearchOrderAndPaginate(tasks, params, filters.Config[*taskContainer]{ return response.TxFuncResponse(err, func() *httperror.HandlerError {
SearchAccessors: []filters.SearchAccessor[*taskContainer]{ results := filters.SearchOrderAndPaginate(tasks, params, filters.Config[*taskContainer]{
func(tc *taskContainer) (string, error) { SearchAccessors: []filters.SearchAccessor[*taskContainer]{
switch tc.LogsStatus { func(tc *taskContainer) (string, error) {
case portainer.EdgeJobLogsStatusPending: switch tc.LogsStatus {
return "pending", nil case portainer.EdgeJobLogsStatusPending:
case 0, portainer.EdgeJobLogsStatusIdle: return "pending", nil
return "idle", nil case 0, portainer.EdgeJobLogsStatusIdle:
case portainer.EdgeJobLogsStatusCollected: return "idle", nil
return "collected", nil case portainer.EdgeJobLogsStatusCollected:
} return "collected", nil
return "", errors.New("unknown state") }
return "", errors.New("unknown state")
},
func(tc *taskContainer) (string, error) {
return tc.EndpointName, nil
},
}, },
func(tc *taskContainer) (string, error) { SortBindings: []filters.SortBinding[*taskContainer]{
return tc.EndpointName, nil {Key: "EndpointName", Fn: func(a, b *taskContainer) int { return strings.Compare(a.EndpointName, b.EndpointName) }},
}, },
}, })
SortBindings: []filters.SortBinding[*taskContainer]{
{Key: "EndpointName", Fn: func(a, b *taskContainer) int { return strings.Compare(a.EndpointName, b.EndpointName) }}, filters.ApplyFilterResultsHeaders(&w, results)
},
return response.JSON(w, results.Items)
}) })
filters.ApplyFilterResultsHeaders(&w, results)
return txResponse(w, results.Items, err)
} }
func listEdgeJobTasks(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID) ([]*taskContainer, error) { func listEdgeJobTasks(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID) ([]*taskContainer, error) {

View File

@ -14,6 +14,7 @@ import (
"github.com/portainer/portainer/api/internal/endpointutils" "github.com/portainer/portainer/api/internal/endpointutils"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request" "github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/portainer/portainer/pkg/validate" "github.com/portainer/portainer/pkg/validate"
) )
@ -66,7 +67,7 @@ func (handler *Handler) edgeJobUpdate(w http.ResponseWriter, r *http.Request) *h
return err return err
}) })
return txResponse(w, edgeJob, err) return response.TxResponse(w, edgeJob, err)
} }
func (handler *Handler) updateEdgeJob(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID, payload edgeJobUpdatePayload) (*portainer.EdgeJob, error) { func (handler *Handler) updateEdgeJob(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID, payload edgeJobUpdatePayload) (*portainer.EdgeJob, error) {

View File

@ -1,14 +1,12 @@
package edgejobs package edgejobs
import ( import (
"errors"
"net/http" "net/http"
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
@ -60,16 +58,3 @@ func convertEndpointsToMetaObject(endpoints []portainer.EndpointID) map[portaine
return endpointsMap return endpointsMap
} }
func txResponse(w http.ResponseWriter, r any, err error) *httperror.HandlerError {
if err != nil {
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)
}
return response.JSON(w, r)
}

View File

@ -1,7 +1,6 @@
package edgestacks package edgestacks
import ( import (
"errors"
"net/http" "net/http"
"strconv" "strconv"
@ -30,18 +29,11 @@ func (handler *Handler) edgeStackDelete(w http.ResponseWriter, r *http.Request)
return httperror.BadRequest("Invalid edge stack identifier route variable", err) return httperror.BadRequest("Invalid edge stack identifier route variable", err)
} }
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.deleteEdgeStack(tx, portainer.EdgeStackID(edgeStackID)) return handler.deleteEdgeStack(tx, portainer.EdgeStackID(edgeStackID))
}); err != nil { })
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err) return response.TxEmptyResponse(w, err)
}
return response.Empty(w)
} }
func (handler *Handler) deleteEdgeStack(tx dataservices.DataStoreTx, edgeStackID portainer.EdgeStackID) error { func (handler *Handler) deleteEdgeStack(tx dataservices.DataStoreTx, edgeStackID portainer.EdgeStackID) error {

View File

@ -96,12 +96,7 @@ func (handler *Handler) edgeStackStatusUpdate(w http.ResponseWriter, r *http.Req
return nil return nil
}); err != nil { }); err != nil {
var httpErr *httperror.HandlerError return response.TxErrorResponse(err)
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err)
} }
if ok, _ := strconv.ParseBool(r.Header.Get("X-Portainer-No-Body")); ok { if ok, _ := strconv.ParseBool(r.Header.Get("X-Portainer-No-Body")); ok {

View File

@ -66,12 +66,7 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request)
stack, err = handler.updateEdgeStack(tx, portainer.EdgeStackID(stackID), payload) stack, err = handler.updateEdgeStack(tx, portainer.EdgeStackID(stackID), payload)
return err return err
}); err != nil { }); err != nil {
var httpErr *httperror.HandlerError return response.TxErrorResponse(err)
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err)
} }
if err := fillEdgeStackStatus(handler.DataStore, stack); err != nil { if err := fillEdgeStackStatus(handler.DataStore, stack); err != nil {

View File

@ -49,26 +49,18 @@ func (payload *endpointGroupCreatePayload) Validate(r *http.Request) error {
// @router /endpoint_groups [post] // @router /endpoint_groups [post]
func (handler *Handler) endpointGroupCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { func (handler *Handler) endpointGroupCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
var payload endpointGroupCreatePayload var payload endpointGroupCreatePayload
err := request.DecodeAndValidateJSONPayload(r, &payload) if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
if err != nil {
return httperror.BadRequest("Invalid request payload", err) return httperror.BadRequest("Invalid request payload", err)
} }
var endpointGroup *portainer.EndpointGroup var endpointGroup *portainer.EndpointGroup
var err error
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
endpointGroup, err = handler.createEndpointGroup(tx, payload) endpointGroup, err = handler.createEndpointGroup(tx, payload)
return err return err
}) })
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err) return response.TxResponse(w, endpointGroup, err)
}
return response.JSON(w, endpointGroup)
} }
func (handler *Handler) createEndpointGroup(tx dataservices.DataStoreTx, payload endpointGroupCreatePayload) (*portainer.EndpointGroup, error) { func (handler *Handler) createEndpointGroup(tx dataservices.DataStoreTx, payload endpointGroupCreatePayload) (*portainer.EndpointGroup, error) {

View File

@ -37,16 +37,8 @@ func (handler *Handler) endpointGroupDelete(w http.ResponseWriter, r *http.Reque
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.deleteEndpointGroup(tx, portainer.EndpointGroupID(endpointGroupID)) return handler.deleteEndpointGroup(tx, portainer.EndpointGroupID(endpointGroupID))
}) })
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err) return response.TxEmptyResponse(w, err)
}
return response.Empty(w)
} }
func (handler *Handler) deleteEndpointGroup(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID) error { func (handler *Handler) deleteEndpointGroup(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID) error {

View File

@ -1,7 +1,6 @@
package endpointgroups package endpointgroups
import ( import (
"errors"
"net/http" "net/http"
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
@ -39,16 +38,8 @@ func (handler *Handler) endpointGroupAddEndpoint(w http.ResponseWriter, r *http.
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.addEndpoint(tx, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID)) return handler.addEndpoint(tx, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID))
}) })
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err) return response.TxEmptyResponse(w, err)
}
return response.Empty(w)
} }
func (handler *Handler) addEndpoint(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID, endpointID portainer.EndpointID) error { func (handler *Handler) addEndpoint(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID, endpointID portainer.EndpointID) error {

View File

@ -1,7 +1,6 @@
package endpointgroups package endpointgroups
import ( import (
"errors"
"net/http" "net/http"
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
@ -38,16 +37,8 @@ func (handler *Handler) endpointGroupDeleteEndpoint(w http.ResponseWriter, r *ht
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.removeEndpoint(tx, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID)) return handler.removeEndpoint(tx, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID))
}) })
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err) return response.TxEmptyResponse(w, err)
}
return response.Empty(w)
} }
func (handler *Handler) removeEndpoint(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID, endpointID portainer.EndpointID) error { func (handler *Handler) removeEndpoint(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID, endpointID portainer.EndpointID) error {

View File

@ -1,7 +1,6 @@
package endpointgroups package endpointgroups
import ( import (
"errors"
"net/http" "net/http"
"reflect" "reflect"
@ -61,20 +60,12 @@ func (handler *Handler) endpointGroupUpdate(w http.ResponseWriter, r *http.Reque
var endpointGroup *portainer.EndpointGroup var endpointGroup *portainer.EndpointGroup
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
endpointGroup, err = handler.updateEndpointGroup(tx, portainer.EndpointGroupID(endpointGroupID), payload) endpointGroup, err = handler.updateEndpointGroup(tx, portainer.EndpointGroupID(endpointGroupID), payload)
return err return err
}); err != nil { })
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err) return response.TxResponse(w, endpointGroup, err)
}
return response.JSON(w, endpointGroup)
} }
func (handler *Handler) updateEndpointGroup(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID, payload endpointGroupUpdatePayload) (*portainer.EndpointGroup, error) { func (handler *Handler) updateEndpointGroup(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID, payload endpointGroupUpdatePayload) (*portainer.EndpointGroup, error) {

View File

@ -62,18 +62,11 @@ func (handler *Handler) endpointDelete(w http.ResponseWriter, r *http.Request) *
return httperror.BadRequest("Invalid boolean query parameter", err) return httperror.BadRequest("Invalid boolean query parameter", err)
} }
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.deleteEndpoint(tx, portainer.EndpointID(endpointID), deleteCluster) return handler.deleteEndpoint(tx, portainer.EndpointID(endpointID), deleteCluster)
}); err != nil { })
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err) return response.TxEmptyResponse(w, err)
}
return response.Empty(w)
} }
// @id EndpointDeleteBatch // @id EndpointDeleteBatch

View File

@ -35,19 +35,12 @@ func (handler *Handler) endpointRegistriesList(w http.ResponseWriter, r *http.Re
} }
var registries []portainer.Registry var registries []portainer.Registry
if err := handler.DataStore.ViewTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.ViewTx(func(tx dataservices.DataStoreTx) error {
registries, err = handler.listRegistries(tx, r, portainer.EndpointID(endpointID)) registries, err = handler.listRegistries(tx, r, portainer.EndpointID(endpointID))
return err return err
}); err != nil { })
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err) return response.TxResponse(w, registries, err)
}
return response.JSON(w, registries)
} }
func (handler *Handler) listRegistries(tx dataservices.DataStoreTx, r *http.Request, endpointID portainer.EndpointID) ([]portainer.Registry, error) { func (handler *Handler) listRegistries(tx dataservices.DataStoreTx, r *http.Request, endpointID portainer.EndpointID) ([]portainer.Registry, error) {

View File

@ -1,7 +1,6 @@
package endpoints package endpoints
import ( import (
"errors"
"net/http" "net/http"
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
@ -53,16 +52,8 @@ func (handler *Handler) endpointRegistryAccess(w http.ResponseWriter, r *http.Re
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.updateRegistryAccess(tx, r, portainer.EndpointID(endpointID), portainer.RegistryID(registryID)) return handler.updateRegistryAccess(tx, r, portainer.EndpointID(endpointID), portainer.RegistryID(registryID))
}) })
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err) return response.TxEmptyResponse(w, err)
}
return response.Empty(w)
} }
func (handler *Handler) updateRegistryAccess(tx dataservices.DataStoreTx, r *http.Request, endpointID portainer.EndpointID, registryID portainer.RegistryID) error { func (handler *Handler) updateRegistryAccess(tx dataservices.DataStoreTx, r *http.Request, endpointID portainer.EndpointID, registryID portainer.RegistryID) error {

View File

@ -128,12 +128,7 @@ func (handler *Handler) settingsUpdate(w http.ResponseWriter, r *http.Request) *
return err return err
}); err != nil { }); err != nil {
var httpErr *httperror.HandlerError return response.TxErrorResponse(err)
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err)
} }
hideFields(settings) hideFields(settings)

View File

@ -1,13 +1,11 @@
package tags package tags
import ( import (
"errors"
"net/http" "net/http"
"github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
@ -32,16 +30,3 @@ func NewHandler(bouncer security.BouncerService) *Handler {
return h return h
} }
func txResponse(w http.ResponseWriter, r any, err error) *httperror.HandlerError {
if err != nil {
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)
}
return response.JSON(w, r)
}

View File

@ -8,6 +8,7 @@ import (
"github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/dataservices"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request" "github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
) )
type tagCreatePayload struct { type tagCreatePayload struct {
@ -38,18 +39,18 @@ func (payload *tagCreatePayload) Validate(r *http.Request) error {
// @router /tags [post] // @router /tags [post]
func (handler *Handler) tagCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { func (handler *Handler) tagCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
var payload tagCreatePayload var payload tagCreatePayload
err := request.DecodeAndValidateJSONPayload(r, &payload) if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
if err != nil {
return httperror.BadRequest("Invalid request payload", err) return httperror.BadRequest("Invalid request payload", err)
} }
var tag *portainer.Tag var tag *portainer.Tag
var err error
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
tag, err = createTag(tx, payload) tag, err = createTag(tx, payload)
return err return err
}) })
return txResponse(w, tag, err) return response.TxResponse(w, tag, err)
} }
func createTag(tx dataservices.DataStoreTx, payload tagCreatePayload) (*portainer.Tag, error) { func createTag(tx dataservices.DataStoreTx, payload tagCreatePayload) (*portainer.Tag, error) {

View File

@ -1,7 +1,6 @@
package tags package tags
import ( import (
"errors"
"net/http" "net/http"
"slices" "slices"
@ -37,16 +36,8 @@ func (handler *Handler) tagDelete(w http.ResponseWriter, r *http.Request) *httpe
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return deleteTag(tx, portainer.TagID(id)) return deleteTag(tx, portainer.TagID(id))
}) })
if err != nil {
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err) return response.TxEmptyResponse(w, err)
}
return response.Empty(w)
} }
func deleteTag(tx dataservices.DataStoreTx, tagID portainer.TagID) error { func deleteTag(tx dataservices.DataStoreTx, tagID portainer.TagID) error {

View File

@ -48,22 +48,13 @@ func (handler *Handler) teamCreate(w http.ResponseWriter, r *http.Request) *http
} }
var team *portainer.Team var team *portainer.Team
var err error
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
var err error
team, err = createTeam(tx, payload) team, err = createTeam(tx, payload)
return err return err
}); err != nil { })
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err) return response.TxResponse(w, team, err)
}
return response.JSON(w, team)
} }
func createTeam(tx dataservices.DataStoreTx, payload teamCreatePayload) (*portainer.Team, error) { func createTeam(tx dataservices.DataStoreTx, payload teamCreatePayload) (*portainer.Team, error) {

View File

@ -55,22 +55,13 @@ func (handler *Handler) userCreate(w http.ResponseWriter, r *http.Request) *http
} }
var user *portainer.User var user *portainer.User
var err error
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
var err error
user, err = handler.createUser(tx, payload) user, err = handler.createUser(tx, payload)
return err return err
}); err != nil { })
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
return httpErr
}
return httperror.InternalServerError("Unexpected error", err) return response.TxResponse(w, user, err)
}
return response.JSON(w, user)
} }
func (handler *Handler) createUser(tx dataservices.DataStoreTx, payload userCreatePayload) (*portainer.User, error) { func (handler *Handler) createUser(tx dataservices.DataStoreTx, payload userCreatePayload) (*portainer.User, error) {

View File

@ -0,0 +1,47 @@
package response
import (
"errors"
"net/http"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
)
func TxResponse(w http.ResponseWriter, r any, err error) *httperror.HandlerError {
return TxFuncResponse(err, func() *httperror.HandlerError { return JSON(w, r) })
}
func TxEmptyResponse(w http.ResponseWriter, err error) *httperror.HandlerError {
if err != nil {
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)
}
return Empty(w)
}
func TxFuncResponse(err error, validResponse func() *httperror.HandlerError) *httperror.HandlerError {
if err != nil {
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)
}
return validResponse()
}
func TxErrorResponse(err error) *httperror.HandlerError {
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)
}

View File

@ -0,0 +1,86 @@
package response
import (
"errors"
"net/http"
"net/http/httptest"
"testing"
httperrors "github.com/portainer/portainer/api/http/errors"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/stretchr/testify/require"
)
func TestTxResponse(t *testing.T) {
type sample struct {
Name string `json:"name"`
}
w := httptest.NewRecorder()
got := TxResponse(w, sample{Name: "Alice"}, nil)
require.Nil(t, got)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
w = httptest.NewRecorder()
got = TxResponse(w, sample{}, httperror.Forbidden("Access denied to resource", httperrors.ErrResourceAccessDenied))
require.NotNil(t, got)
require.Equal(t, http.StatusForbidden, got.StatusCode)
require.Equal(t, "Access denied to resource", got.Message)
w = httptest.NewRecorder()
got = TxResponse(w, sample{}, errors.New("Some error"))
require.NotNil(t, got)
require.Equal(t, http.StatusInternalServerError, got.StatusCode)
require.Equal(t, "Unexpected error", got.Message)
}
func TestTxEmptyResponse(t *testing.T) {
w := httptest.NewRecorder()
got := TxEmptyResponse(w, nil)
require.Nil(t, got)
require.Equal(t, http.StatusNoContent, w.Result().StatusCode)
w = httptest.NewRecorder()
got = TxEmptyResponse(w, httperror.Forbidden("Access denied to resource", httperrors.ErrResourceAccessDenied))
require.NotNil(t, got)
require.Equal(t, http.StatusForbidden, got.StatusCode)
require.Equal(t, "Access denied to resource", got.Message)
w = httptest.NewRecorder()
got = TxEmptyResponse(w, errors.New("Some error"))
require.NotNil(t, got)
require.Equal(t, http.StatusInternalServerError, got.StatusCode)
require.Equal(t, "Unexpected error", got.Message)
}
func TestTxFuncResponse(t *testing.T) {
got := TxFuncResponse(nil, func() *httperror.HandlerError { return nil })
require.Nil(t, got)
got = TxFuncResponse(httperror.Forbidden("Access denied to resource", httperrors.ErrResourceAccessDenied), func() *httperror.HandlerError { return nil })
require.NotNil(t, got)
require.Equal(t, http.StatusForbidden, got.StatusCode)
require.Equal(t, "Access denied to resource", got.Message)
got = TxFuncResponse(errors.New("Some error"), func() *httperror.HandlerError { return nil })
require.NotNil(t, got)
require.Equal(t, http.StatusInternalServerError, got.StatusCode)
require.Equal(t, "Unexpected error", got.Message)
}
func TestTxErrorResponse(t *testing.T) {
got := TxErrorResponse(nil)
require.NotNil(t, got)
require.Equal(t, http.StatusInternalServerError, got.StatusCode)
require.Equal(t, "Unexpected error", got.Message)
got = TxErrorResponse(httperror.Forbidden("Access denied to resource", httperrors.ErrResourceAccessDenied))
require.NotNil(t, got)
require.Equal(t, http.StatusForbidden, got.StatusCode)
require.Equal(t, "Access denied to resource", got.Message)
got = TxErrorResponse(errors.New("Some error"))
require.NotNil(t, got)
require.Equal(t, http.StatusInternalServerError, got.StatusCode)
require.Equal(t, "Unexpected error", got.Message)
}