diff --git a/clients/http/utils/common.go b/clients/http/utils/common.go index a7276906..d1815d05 100644 --- a/clients/http/utils/common.go +++ b/clients/http/utils/common.go @@ -75,7 +75,7 @@ func makeRequest(req *http.Request, authInjector interfaces.AuthenticationInject return resp, nil } -func createRequest(ctx context.Context, httpMethod string, baseUrl string, requestPath string, requestParams url.Values) (*http.Request, errors.EdgeX) { +func CreateRequest(ctx context.Context, httpMethod string, baseUrl string, requestPath string, requestParams url.Values) (*http.Request, errors.EdgeX) { u, err := parseBaseUrlAndRequestPath(baseUrl, requestPath) if err != nil { return nil, errors.NewCommonEdgeX(errors.KindServerError, "failed to parse baseUrl and requestPath", err) @@ -91,7 +91,7 @@ func createRequest(ctx context.Context, httpMethod string, baseUrl string, reque return req, nil } -func createRequestWithRawDataAndParams(ctx context.Context, httpMethod string, baseUrl string, requestPath string, requestParams url.Values, data interface{}) (*http.Request, errors.EdgeX) { +func CreateRequestWithRawDataAndParams(ctx context.Context, httpMethod string, baseUrl string, requestPath string, requestParams url.Values, data interface{}) (*http.Request, errors.EdgeX) { u, err := parseBaseUrlAndRequestPath(baseUrl, requestPath) if err != nil { return nil, errors.NewCommonEdgeX(errors.KindServerError, "failed to parse baseUrl and requestPath", err) @@ -118,7 +118,7 @@ func createRequestWithRawDataAndParams(ctx context.Context, httpMethod string, b return req, nil } -func createRequestWithRawData(ctx context.Context, httpMethod string, baseUrl string, requestPath string, requestParams url.Values, data interface{}) (*http.Request, errors.EdgeX) { +func CreateRequestWithRawData(ctx context.Context, httpMethod string, baseUrl string, requestPath string, requestParams url.Values, data interface{}) (*http.Request, errors.EdgeX) { u, err := parseBaseUrlAndRequestPath(baseUrl, requestPath) if err != nil { return nil, errors.NewCommonEdgeX(errors.KindServerError, "failed to parse baseUrl and requestPath", err) @@ -146,7 +146,21 @@ func createRequestWithRawData(ctx context.Context, httpMethod string, baseUrl st return req, nil } -func createRequestWithEncodedData(ctx context.Context, httpMethod string, baseUrl string, requestPath string, data []byte, encoding string) (*http.Request, errors.EdgeX) { +func CreateRequestWithRawDataAndHeaders(ctx context.Context, httpMethod string, baseUrl string, requestPath string, requestParams url.Values, data any, headers map[string]string) (*http.Request, errors.EdgeX) { + req, err := CreateRequestWithRawData(ctx, httpMethod, baseUrl, requestPath, requestParams, data) + if err != nil { + return nil, errors.NewCommonEdgeXWrapper(err) + } + + // Add the additional headers from request + for name, value := range headers { + req.Header.Set(name, value) + } + + return req, nil +} + +func CreateRequestWithEncodedData(ctx context.Context, httpMethod string, baseUrl string, requestPath string, data []byte, encoding string) (*http.Request, errors.EdgeX) { u, err := parseBaseUrlAndRequestPath(baseUrl, requestPath) if err != nil { return nil, errors.NewCommonEdgeX(errors.KindServerError, "failed to parse baseUrl and requestPath", err) @@ -166,8 +180,8 @@ func createRequestWithEncodedData(ctx context.Context, httpMethod string, baseUr return req, nil } -// createRequestFromFilePath creates multipart/form-data request with the specified file -func createRequestFromFilePath(ctx context.Context, httpMethod string, baseUrl string, requestPath string, filePath string) (*http.Request, errors.EdgeX) { +// CreateRequestFromFilePath creates multipart/form-data request with the specified file +func CreateRequestFromFilePath(ctx context.Context, httpMethod string, baseUrl string, requestPath string, filePath string) (*http.Request, errors.EdgeX) { u, err := parseBaseUrlAndRequestPath(baseUrl, requestPath) if err != nil { return nil, errors.NewCommonEdgeX(errors.KindServerError, "failed to parse baseUrl and requestPath", err) @@ -199,9 +213,9 @@ func createRequestFromFilePath(ctx context.Context, httpMethod string, baseUrl s return req, nil } -// sendRequest will make a request with raw data to the specified URL. +// SendRequest will make a request with raw data to the specified URL. // It returns the body as a byte array if successful and an error otherwise. -func sendRequest(ctx context.Context, req *http.Request, authInjector interfaces.AuthenticationInjector) ([]byte, errors.EdgeX) { +func SendRequest(ctx context.Context, req *http.Request, authInjector interfaces.AuthenticationInjector) ([]byte, errors.EdgeX) { resp, err := makeRequest(req, authInjector) if err != nil { return nil, errors.NewCommonEdgeXWrapper(err) @@ -220,7 +234,7 @@ func sendRequest(ctx context.Context, req *http.Request, authInjector interfaces // Handle error response msg := fmt.Sprintf("request failed, status code: %d, err: %s", resp.StatusCode, string(bodyBytes)) errKind := errors.KindMapping(resp.StatusCode) - return nil, errors.NewCommonEdgeX(errKind, msg, nil) + return bodyBytes, errors.NewCommonEdgeX(errKind, msg, nil) } // EscapeAndJoinPath escape and join the path variables diff --git a/clients/http/utils/common_test.go b/clients/http/utils/common_test.go index 748edeb3..7ab96d7b 100644 --- a/clients/http/utils/common_test.go +++ b/clients/http/utils/common_test.go @@ -29,7 +29,7 @@ func TestCreateRequest(t *testing.T) { } for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { - _, err := createRequest(context.Background(), http.MethodGet, baseUrl, requestPath, nil) + _, err := CreateRequest(context.Background(), http.MethodGet, baseUrl, requestPath, nil) assert.NoError(t, err) }) } @@ -48,7 +48,7 @@ func TestCreateRequestWithRawData(t *testing.T) { } for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { - _, err := createRequestWithRawData(context.Background(), http.MethodGet, baseUrl, requestPath, nil, models.Event{}) + _, err := CreateRequestWithRawData(context.Background(), http.MethodGet, baseUrl, requestPath, nil, models.Event{}) assert.NoError(t, err) }) } @@ -67,7 +67,7 @@ func TestCreateRequestWithRawDataAndParams(t *testing.T) { } for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { - _, err := createRequestWithRawDataAndParams(context.Background(), http.MethodGet, baseUrl, requestPath, nil, models.Event{}) + _, err := CreateRequestWithRawDataAndParams(context.Background(), http.MethodGet, baseUrl, requestPath, nil, models.Event{}) assert.NoError(t, err) }) } @@ -86,7 +86,7 @@ func TestCreateRequestWithEncodedData(t *testing.T) { } for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { - _, err := createRequestWithEncodedData(context.Background(), http.MethodGet, baseUrl, requestPath, nil, "") + _, err := CreateRequestWithEncodedData(context.Background(), http.MethodGet, baseUrl, requestPath, nil, "") assert.NoError(t, err) }) } @@ -109,7 +109,7 @@ func TestCreateRequestFromFilePath(t *testing.T) { } for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { - _, err := createRequestFromFilePath(context.Background(), http.MethodGet, baseUrl, requestPath, f.Name()) + _, err := CreateRequestFromFilePath(context.Background(), http.MethodGet, baseUrl, requestPath, f.Name()) assert.NoError(t, err) }) } diff --git a/clients/http/utils/request.go b/clients/http/utils/request.go index d96f1bd3..916bb782 100644 --- a/clients/http/utils/request.go +++ b/clients/http/utils/request.go @@ -20,7 +20,7 @@ import ( // GetRequest makes the get request and return the body func GetRequest(ctx context.Context, returnValuePointer interface{}, baseUrl string, requestPath string, requestParams url.Values, authInjector interfaces.AuthenticationInjector) errors.EdgeX { - req, err := createRequest(ctx, http.MethodGet, baseUrl, requestPath, requestParams) + req, err := CreateRequest(ctx, http.MethodGet, baseUrl, requestPath, requestParams) if err != nil { return errors.NewCommonEdgeXWrapper(err) } @@ -30,7 +30,7 @@ func GetRequest(ctx context.Context, returnValuePointer interface{}, baseUrl str // GetRequestAndReturnBinaryRes makes the get request and return the binary response and content type(i.e., application/json, application/cbor, ... ) func GetRequestAndReturnBinaryRes(ctx context.Context, baseUrl string, requestPath string, requestParams url.Values, authInjector interfaces.AuthenticationInjector) (res []byte, contentType string, edgeXerr errors.EdgeX) { - req, edgeXerr := createRequest(ctx, http.MethodGet, baseUrl, requestPath, requestParams) + req, edgeXerr := CreateRequest(ctx, http.MethodGet, baseUrl, requestPath, requestParams) if edgeXerr != nil { return nil, "", errors.NewCommonEdgeXWrapper(edgeXerr) } @@ -60,7 +60,7 @@ func GetRequestAndReturnBinaryRes(ctx context.Context, baseUrl string, requestPa // GetRequestWithBodyRawData makes the GET request with JSON raw data as request body and return the response func GetRequestWithBodyRawData(ctx context.Context, returnValuePointer interface{}, baseUrl string, requestPath string, requestParams url.Values, data interface{}, authInjector interfaces.AuthenticationInjector) errors.EdgeX { - req, err := createRequestWithRawDataAndParams(ctx, http.MethodGet, baseUrl, requestPath, requestParams, data) + req, err := CreateRequestWithRawDataAndParams(ctx, http.MethodGet, baseUrl, requestPath, requestParams, data) if err != nil { return errors.NewCommonEdgeXWrapper(err) } @@ -76,7 +76,7 @@ func PostRequest( data []byte, encoding string, authInjector interfaces.AuthenticationInjector) errors.EdgeX { - req, err := createRequestWithEncodedData(ctx, http.MethodPost, baseUrl, requestPath, data, encoding) + req, err := CreateRequestWithEncodedData(ctx, http.MethodPost, baseUrl, requestPath, data, encoding) if err != nil { return errors.NewCommonEdgeXWrapper(err) } @@ -92,7 +92,24 @@ func PostRequestWithRawData( requestParams url.Values, data interface{}, authInjector interfaces.AuthenticationInjector) errors.EdgeX { - req, err := createRequestWithRawData(ctx, http.MethodPost, baseUrl, requestPath, requestParams, data) + req, err := CreateRequestWithRawData(ctx, http.MethodPost, baseUrl, requestPath, requestParams, data) + if err != nil { + return errors.NewCommonEdgeXWrapper(err) + } + + return processRequest(ctx, returnValuePointer, req, authInjector) +} + +// PostRequestWithRawDataAndHeaders makes the post JSON request with raw data and request headers, and returns the body +func PostRequestWithRawDataAndHeaders( + ctx context.Context, + returnValuePointer interface{}, + baseUrl string, requestPath string, + requestParams url.Values, + data interface{}, authInjector interfaces.AuthenticationInjector, + headers map[string]string) errors.EdgeX { + + req, err := CreateRequestWithRawDataAndHeaders(ctx, http.MethodPost, baseUrl, requestPath, requestParams, data, headers) if err != nil { return errors.NewCommonEdgeXWrapper(err) } @@ -108,7 +125,7 @@ func PutRequest( requestParams url.Values, data interface{}, authInjector interfaces.AuthenticationInjector) errors.EdgeX { - req, err := createRequestWithRawData(ctx, http.MethodPut, baseUrl, requestPath, requestParams, data) + req, err := CreateRequestWithRawData(ctx, http.MethodPut, baseUrl, requestPath, requestParams, data) if err != nil { return errors.NewCommonEdgeXWrapper(err) } @@ -124,7 +141,7 @@ func PatchRequest( requestParams url.Values, data interface{}, authInjector interfaces.AuthenticationInjector) errors.EdgeX { - req, err := createRequestWithRawData(ctx, http.MethodPatch, baseUrl, requestPath, requestParams, data) + req, err := CreateRequestWithRawData(ctx, http.MethodPatch, baseUrl, requestPath, requestParams, data) if err != nil { return errors.NewCommonEdgeXWrapper(err) } @@ -139,7 +156,7 @@ func PostByFileRequest( baseUrl string, requestPath string, filePath string, authInjector interfaces.AuthenticationInjector) errors.EdgeX { - req, err := createRequestFromFilePath(ctx, http.MethodPost, baseUrl, requestPath, filePath) + req, err := CreateRequestFromFilePath(ctx, http.MethodPost, baseUrl, requestPath, filePath) if err != nil { return errors.NewCommonEdgeXWrapper(err) } @@ -154,7 +171,7 @@ func PutByFileRequest( baseUrl string, requestPath string, filePath string, authInjector interfaces.AuthenticationInjector) errors.EdgeX { - req, err := createRequestFromFilePath(ctx, http.MethodPut, baseUrl, requestPath, filePath) + req, err := CreateRequestFromFilePath(ctx, http.MethodPut, baseUrl, requestPath, filePath) if err != nil { return errors.NewCommonEdgeXWrapper(err) } @@ -164,7 +181,7 @@ func PutByFileRequest( // DeleteRequest makes the delete request and return the body func DeleteRequest(ctx context.Context, returnValuePointer interface{}, baseUrl string, requestPath string, authInjector interfaces.AuthenticationInjector) errors.EdgeX { - req, err := createRequest(ctx, http.MethodDelete, baseUrl, requestPath, nil) + req, err := CreateRequest(ctx, http.MethodDelete, baseUrl, requestPath, nil) if err != nil { return errors.NewCommonEdgeXWrapper(err) } @@ -174,7 +191,7 @@ func DeleteRequest(ctx context.Context, returnValuePointer interface{}, baseUrl // DeleteRequestWithParams makes the delete request with URL query params and return the body func DeleteRequestWithParams(ctx context.Context, returnValuePointer interface{}, baseUrl string, requestPath string, requestParams url.Values, authInjector interfaces.AuthenticationInjector) errors.EdgeX { - req, err := createRequest(ctx, http.MethodDelete, baseUrl, requestPath, requestParams) + req, err := CreateRequest(ctx, http.MethodDelete, baseUrl, requestPath, requestParams) if err != nil { return errors.NewCommonEdgeXWrapper(err) } @@ -185,7 +202,7 @@ func DeleteRequestWithParams(ctx context.Context, returnValuePointer interface{} // processRequest is a helper function to process the request and get the return value func processRequest(ctx context.Context, returnValuePointer any, req *http.Request, authInjector interfaces.AuthenticationInjector) errors.EdgeX { - resp, err := sendRequest(ctx, req, authInjector) + resp, err := SendRequest(ctx, req, authInjector) if err != nil { return errors.NewCommonEdgeXWrapper(err) } diff --git a/errors/types.go b/errors/types.go index e72e7a8f..bfd2cb3f 100644 --- a/errors/types.go +++ b/errors/types.go @@ -36,6 +36,7 @@ const ( KindOverflowError ErrKind = "OverflowError" KindNaNError ErrKind = "NaNError" KindUnauthorized ErrKind = "Unauthorized" + KindForbidden ErrKind = "Forbidden" ) // EdgeX provides an abstraction for all internal EdgeX errors. @@ -209,7 +210,7 @@ func codeMapping(kind ErrKind) int { return http.StatusMethodNotAllowed case KindRangeNotSatisfiable: return http.StatusRequestedRangeNotSatisfiable - case KindIOError: + case KindIOError, KindForbidden: return http.StatusForbidden case KindUnauthorized: return http.StatusUnauthorized @@ -245,6 +246,8 @@ func KindMapping(code int) ErrKind { return KindRangeNotSatisfiable case http.StatusUnauthorized: return KindUnauthorized + case http.StatusForbidden: + return KindForbidden default: return KindUnknown }