From b3c3affb931af91ce3824136d58f6713da561887 Mon Sep 17 00:00:00 2001 From: Ginny Guan Date: Fri, 14 Jul 2023 18:28:54 +0800 Subject: [PATCH] feat: Move all the common APIs into go-mod-bootstrap related: https://github.com/edgexfoundry/go-mod-bootstrap/issues/478 Signed-off-by: Ginny Guan --- internal/core/command/router.go | 8 +- internal/core/data/router.go | 8 +- internal/core/metadata/router.go | 8 +- internal/pkg/application/secret.go | 41 ------- internal/pkg/controller/http/common.go | 102 ---------------- internal/pkg/controller/http/secret.go | 41 ------- internal/pkg/controller/http/secret_test.go | 122 -------------------- internal/security/proxyauth/init.go | 10 +- internal/support/notifications/router.go | 9 +- internal/support/scheduler/router.go | 8 +- 10 files changed, 18 insertions(+), 339 deletions(-) delete mode 100644 internal/pkg/application/secret.go delete mode 100644 internal/pkg/controller/http/common.go delete mode 100644 internal/pkg/controller/http/secret.go delete mode 100644 internal/pkg/controller/http/secret_test.go diff --git a/internal/core/command/router.go b/internal/core/command/router.go index d0763baed3..30548941b3 100644 --- a/internal/core/command/router.go +++ b/internal/core/command/router.go @@ -9,14 +9,15 @@ package command import ( "net/http" + "github.com/edgexfoundry/edgex-go" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" + "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/controller" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/handlers" "github.com/edgexfoundry/go-mod-bootstrap/v3/di" "github.com/edgexfoundry/go-mod-core-contracts/v3/common" "github.com/gorilla/mux" commandController "github.com/edgexfoundry/edgex-go/internal/core/command/controller/http" - commonController "github.com/edgexfoundry/edgex-go/internal/pkg/controller/http" "github.com/edgexfoundry/edgex-go/internal/pkg/correlation" ) @@ -29,10 +30,7 @@ func LoadRestRoutes(r *mux.Router, dic *di.Container, serviceName string) { authenticationHook := handlers.AutoConfigAuthenticationFunc(secretProvider, lc) // Common - cc := commonController.NewCommonController(dic, serviceName) - r.HandleFunc(common.ApiPingRoute, cc.Ping).Methods(http.MethodGet) // Health check is always unauthenticated - r.HandleFunc(common.ApiVersionRoute, authenticationHook(cc.Version)).Methods(http.MethodGet) - r.HandleFunc(common.ApiConfigRoute, authenticationHook(cc.Config)).Methods(http.MethodGet) + _ = controller.NewCommonController(dic, r, serviceName, edgex.Version) // Command cmd := commandController.NewCommandController(dic) diff --git a/internal/core/data/router.go b/internal/core/data/router.go index ec03c847af..f03dd461f9 100644 --- a/internal/core/data/router.go +++ b/internal/core/data/router.go @@ -9,7 +9,9 @@ package data import ( "net/http" + "github.com/edgexfoundry/edgex-go" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" + "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/controller" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/handlers" "github.com/edgexfoundry/go-mod-bootstrap/v3/di" "github.com/gorilla/mux" @@ -17,7 +19,6 @@ import ( "github.com/edgexfoundry/go-mod-core-contracts/v3/common" dataController "github.com/edgexfoundry/edgex-go/internal/core/data/controller/http" - commonController "github.com/edgexfoundry/edgex-go/internal/pkg/controller/http" "github.com/edgexfoundry/edgex-go/internal/pkg/correlation" ) @@ -30,10 +31,7 @@ func LoadRestRoutes(r *mux.Router, dic *di.Container, serviceName string) { authenticationHook := handlers.AutoConfigAuthenticationFunc(secretProvider, lc) // Common - cc := commonController.NewCommonController(dic, serviceName) - r.HandleFunc(common.ApiPingRoute, cc.Ping).Methods(http.MethodGet) // Health check is always unauthenticated - r.HandleFunc(common.ApiVersionRoute, authenticationHook(cc.Version)).Methods(http.MethodGet) - r.HandleFunc(common.ApiConfigRoute, authenticationHook(cc.Config)).Methods(http.MethodGet) + _ = controller.NewCommonController(dic, r, serviceName, edgex.Version) // Events ec := dataController.NewEventController(dic) diff --git a/internal/core/metadata/router.go b/internal/core/metadata/router.go index 72e33671ec..a9c8651811 100644 --- a/internal/core/metadata/router.go +++ b/internal/core/metadata/router.go @@ -9,7 +9,9 @@ package metadata import ( "net/http" + "github.com/edgexfoundry/edgex-go" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" + "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/controller" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/handlers" "github.com/edgexfoundry/go-mod-bootstrap/v3/di" "github.com/gorilla/mux" @@ -17,7 +19,6 @@ import ( "github.com/edgexfoundry/go-mod-core-contracts/v3/common" metadataController "github.com/edgexfoundry/edgex-go/internal/core/metadata/controller/http" - commonController "github.com/edgexfoundry/edgex-go/internal/pkg/controller/http" "github.com/edgexfoundry/edgex-go/internal/pkg/correlation" ) @@ -30,10 +31,7 @@ func LoadRestRoutes(r *mux.Router, dic *di.Container, serviceName string) { authenticationHook := handlers.AutoConfigAuthenticationFunc(secretProvider, lc) // Common - cc := commonController.NewCommonController(dic, serviceName) - r.HandleFunc(common.ApiPingRoute, cc.Ping).Methods(http.MethodGet) // Health check is always unauthenticated - r.HandleFunc(common.ApiVersionRoute, authenticationHook(cc.Version)).Methods(http.MethodGet) - r.HandleFunc(common.ApiConfigRoute, authenticationHook(cc.Config)).Methods(http.MethodGet) + _ = controller.NewCommonController(dic, r, serviceName, edgex.Version) // Units of Measure uc := metadataController.NewUnitOfMeasureController(dic) diff --git a/internal/pkg/application/secret.go b/internal/pkg/application/secret.go deleted file mode 100644 index d9dc0bd2bb..0000000000 --- a/internal/pkg/application/secret.go +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (C) 2021 IOTech Ltd -// -// SPDX-License-Identifier: Apache-2.0 - -package application - -import ( - "strings" - - "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" - "github.com/edgexfoundry/go-mod-bootstrap/v3/di" - "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/common" - "github.com/edgexfoundry/go-mod-core-contracts/v3/errors" -) - -// AddSecret adds EdgeX Service exclusive secret to the Secret Store -func AddSecret(dic *di.Container, request common.SecretRequest) errors.EdgeX { - secretName, secret := prepareSecret(request) - - secretProvider := container.SecretProviderFrom(dic.Get) - if secretProvider == nil { - return errors.NewCommonEdgeX(errors.KindServerError, "secret provider is missing. Make sure it is specified to be used in bootstrap.Run()", nil) - } - - if err := secretProvider.StoreSecret(secretName, secret); err != nil { - return errors.NewCommonEdgeX(errors.Kind(err), "adding secret failed", err) - } - return nil -} - -func prepareSecret(request common.SecretRequest) (string, map[string]string) { - var secretsKV = make(map[string]string) - for _, secret := range request.SecretData { - secretsKV[secret.Key] = secret.Value - } - - secretName := strings.TrimSpace(request.SecretName) - - return secretName, secretsKV -} diff --git a/internal/pkg/controller/http/common.go b/internal/pkg/controller/http/common.go deleted file mode 100644 index 5e9a93bcad..0000000000 --- a/internal/pkg/controller/http/common.go +++ /dev/null @@ -1,102 +0,0 @@ -// -// Copyright (C) 2020 IOTech Ltd -// -// SPDX-License-Identifier: Apache-2.0 - -package http - -import ( - "encoding/json" - "fmt" - "net/http" - - "github.com/edgexfoundry/edgex-go" - - "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" - "github.com/edgexfoundry/go-mod-bootstrap/v3/di" - - "github.com/edgexfoundry/go-mod-core-contracts/v3/common" - commonDTO "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/common" - "github.com/edgexfoundry/go-mod-core-contracts/v3/errors" -) - -// CommonController controller for REST APIs -type CommonController struct { - dic *di.Container - serviceName string -} - -// NewCommonController creates and initializes an CommonController -func NewCommonController(dic *di.Container, serviceName string) *CommonController { - return &CommonController{ - dic: dic, - serviceName: serviceName, - } -} - -// Ping handles the request to /ping endpoint. Is used to test if the service is working -// It returns a response as specified by the API swagger in the openapi directory -func (c *CommonController) Ping(writer http.ResponseWriter, request *http.Request) { - response := commonDTO.NewPingResponse(c.serviceName) - c.sendResponse(writer, request, common.ApiPingRoute, response, http.StatusOK) -} - -// Version handles the request to /version endpoint. Is used to request the service's versions -// It returns a response as specified by the API swagger in the openapi directory -func (c *CommonController) Version(writer http.ResponseWriter, request *http.Request) { - response := commonDTO.NewVersionResponse(edgex.Version, c.serviceName) - c.sendResponse(writer, request, common.ApiVersionRoute, response, http.StatusOK) -} - -// Config handles the request to /config endpoint. Is used to request the service's configuration -// It returns a response as specified by the API swagger in the openapi directory -func (c *CommonController) Config(writer http.ResponseWriter, request *http.Request) { - response := commonDTO.NewConfigResponse(container.ConfigurationFrom(c.dic.Get), c.serviceName) - c.sendResponse(writer, request, common.ApiVersionRoute, response, http.StatusOK) -} - -// sendResponse puts together the response packet for the REST API -func (c *CommonController) sendResponse( - writer http.ResponseWriter, - request *http.Request, - api string, - response interface{}, - statusCode int) { - lc := container.LoggingClientFrom(c.dic.Get) - - correlationID := request.Header.Get(common.CorrelationHeader) - - writer.Header().Set(common.CorrelationHeader, correlationID) - writer.Header().Set(common.ContentType, common.ContentTypeJSON) - writer.WriteHeader(statusCode) - - data, err := json.Marshal(response) - if err != nil { - lc.Error(fmt.Sprintf("Unable to marshal %s response", api), "error", err.Error(), common.CorrelationHeader, correlationID) - http.Error(writer, err.Error(), http.StatusInternalServerError) - return - } - - _, err = writer.Write(data) - if err != nil { - lc.Error(fmt.Sprintf("Unable to write %s response", api), "error", err.Error(), common.CorrelationHeader, correlationID) - http.Error(writer, err.Error(), http.StatusInternalServerError) - return - } -} - -func (c *CommonController) sendError( - writer http.ResponseWriter, - request *http.Request, - errKind errors.ErrKind, - message string, - err error, - api string, - requestID string) { - lc := container.LoggingClientFrom(c.dic.Get) - edgeXerr := errors.NewCommonEdgeX(errKind, message, err) - lc.Error(edgeXerr.Error()) - lc.Debug(edgeXerr.DebugMessages()) - response := commonDTO.NewBaseResponse(requestID, edgeXerr.Message(), edgeXerr.Code()) - c.sendResponse(writer, request, api, response, edgeXerr.Code()) -} diff --git a/internal/pkg/controller/http/secret.go b/internal/pkg/controller/http/secret.go deleted file mode 100644 index 3c809641d4..0000000000 --- a/internal/pkg/controller/http/secret.go +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (C) 2021 IOTech Ltd -// -// SPDX-License-Identifier: Apache-2.0 - -package http - -import ( - "encoding/json" - "net/http" - - "github.com/edgexfoundry/edgex-go/internal/pkg/application" - - "github.com/edgexfoundry/go-mod-core-contracts/v3/common" - commonDTO "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/common" - "github.com/edgexfoundry/go-mod-core-contracts/v3/errors" -) - -// AddSecret handles the request to the /secret endpoint. Is used to add EdgeX Service exclusive secret to the Secret Store -// It returns a response as specified by the API swagger in the openapi directory -func (c *CommonController) AddSecret(writer http.ResponseWriter, request *http.Request) { - defer func() { - _ = request.Body.Close() - }() - - secretRequest := commonDTO.SecretRequest{} - err := json.NewDecoder(request.Body).Decode(&secretRequest) - if err != nil { - c.sendError(writer, request, errors.KindContractInvalid, "JSON decode failed", err, common.ApiSecretRoute, "") - return - } - - err = application.AddSecret(c.dic, secretRequest) - if err != nil { - c.sendError(writer, request, errors.Kind(err), err.Error(), err, common.ApiSecretRoute, secretRequest.RequestId) - return - } - - response := commonDTO.NewBaseResponse(secretRequest.RequestId, "", http.StatusCreated) - c.sendResponse(writer, request, common.ApiSecretRoute, response, http.StatusCreated) -} diff --git a/internal/pkg/controller/http/secret_test.go b/internal/pkg/controller/http/secret_test.go deleted file mode 100644 index 4e8b744a8f..0000000000 --- a/internal/pkg/controller/http/secret_test.go +++ /dev/null @@ -1,122 +0,0 @@ -// -// Copyright (C) 2021 IOTech Ltd -// -// SPDX-License-Identifier: Apache-2.0 - -package http - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/google/uuid" - - "github.com/edgexfoundry/edgex-go/internal/security/bootstrapper/config" - - "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" - "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/interfaces/mocks" - "github.com/edgexfoundry/go-mod-bootstrap/v3/di" - "github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v3/common" - commonDTO "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/common" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestAddSecret(t *testing.T) { - dic := mockDic() - - target := NewCommonController(dic, uuid.NewString()) - assert.NotNil(t, target) - - validRequest := commonDTO.NewSecretRequest( - "mqtt", - []commonDTO.SecretDataKeyValue{ - {Key: "username", Value: "username"}, - {Key: "password", Value: "password"}, - }, - ) - - NoPath := validRequest - NoPath.SecretName = "" - validNoRequestId := validRequest - validNoRequestId.RequestId = "" - badRequestId := validRequest - badRequestId.RequestId = "bad requestId" - noSecrets := validRequest - noSecrets.SecretData = []commonDTO.SecretDataKeyValue{} - missingSecretKey := validRequest - missingSecretKey.SecretData = []commonDTO.SecretDataKeyValue{ - {Key: "", Value: "username"}, - } - missingSecretValue := validRequest - missingSecretValue.SecretData = []commonDTO.SecretDataKeyValue{ - {Key: "username", Value: ""}, - } - - mockProvider := &mocks.SecretProvider{} - mockProvider.On("StoreSecret", validRequest.SecretName, map[string]string{"password": "password", "username": "username"}).Return(nil) - dic.Update(di.ServiceConstructorMap{ - container.SecretProviderName: func(get di.Get) interface{} { - return mockProvider - }, - }) - - tests := []struct { - Name string - Request commonDTO.SecretRequest - ErrorExpected bool - ExpectedStatusCode int - }{ - {"Valid - no requestId", validNoRequestId, false, http.StatusCreated}, - {"Invalid - no path", NoPath, true, http.StatusBadRequest}, - {"Invalid - bad requestId", badRequestId, true, http.StatusBadRequest}, - {"Invalid - no secrets", noSecrets, true, http.StatusBadRequest}, - {"Invalid - missing secret key", missingSecretKey, true, http.StatusBadRequest}, - {"Invalid - missing secret value", missingSecretValue, true, http.StatusBadRequest}, - } - - for _, testCase := range tests { - t.Run(testCase.Name, func(t *testing.T) { - jsonData, err := json.Marshal(testCase.Request) - require.NoError(t, err) - - reader := strings.NewReader(string(jsonData)) - req, err := http.NewRequest(http.MethodPost, common.ApiSecretRoute, reader) - require.NoError(t, err) - - recorder := httptest.NewRecorder() - handler := http.HandlerFunc(target.AddSecret) - handler.ServeHTTP(recorder, req) - - actualResponse := commonDTO.BaseResponse{} - err = json.Unmarshal(recorder.Body.Bytes(), &actualResponse) - require.NoError(t, err) - - assert.Equal(t, testCase.ExpectedStatusCode, recorder.Result().StatusCode, "HTTP status code not as expected") - assert.Equal(t, common.ApiVersion, actualResponse.ApiVersion, "Api Version not as expected") - assert.Equal(t, testCase.ExpectedStatusCode, actualResponse.StatusCode, "BaseResponse status code not as expected") - - if testCase.ErrorExpected { - assert.NotEmpty(t, actualResponse.Message, "Message is empty") - } else { - assert.Empty(t, actualResponse.Message, "Message not empty, as expected") - } - }) - } -} - -func mockDic() *di.Container { - return di.NewContainer(di.ServiceConstructorMap{ - container.ConfigurationInterfaceName: func(get di.Get) interface{} { - return &config.ConfigurationStruct{} - }, - container.LoggingClientInterfaceName: func(get di.Get) interface{} { - return logger.NewMockClient() - }, - }) -} diff --git a/internal/security/proxyauth/init.go b/internal/security/proxyauth/init.go index c367a8ca19..948e01825d 100644 --- a/internal/security/proxyauth/init.go +++ b/internal/security/proxyauth/init.go @@ -21,13 +21,12 @@ import ( "net/http" "sync" - commonController "github.com/edgexfoundry/edgex-go/internal/pkg/controller/http" - + "github.com/edgexfoundry/edgex-go" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" + "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/controller" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/handlers" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/startup" "github.com/edgexfoundry/go-mod-bootstrap/v3/di" - "github.com/edgexfoundry/go-mod-core-contracts/v3/common" "github.com/gorilla/mux" ) @@ -55,10 +54,7 @@ func (b *Bootstrap) BootstrapHandler(ctx context.Context, wg *sync.WaitGroup, _ authenticationHook := handlers.VaultAuthenticationHandlerFunc(secretProvider, lc) // Common - cc := commonController.NewCommonController(dic, b.serviceName) - b.router.HandleFunc(common.ApiPingRoute, cc.Ping).Methods(http.MethodGet) // Health check is always unauthenticated - b.router.HandleFunc(common.ApiVersionRoute, authenticationHook(cc.Version)).Methods(http.MethodGet) - b.router.HandleFunc(common.ApiConfigRoute, authenticationHook(cc.Config)).Methods(http.MethodGet) + _ = controller.NewCommonController(dic, b.router, b.serviceName, edgex.Version) // Run authentication hook for a nil route b.router.HandleFunc("/auth", authenticationHook(emptyHandler)) diff --git a/internal/support/notifications/router.go b/internal/support/notifications/router.go index c3c39c2784..c6931cf855 100644 --- a/internal/support/notifications/router.go +++ b/internal/support/notifications/router.go @@ -8,13 +8,14 @@ package notifications import ( "net/http" + "github.com/edgexfoundry/edgex-go" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" + "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/controller" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/handlers" "github.com/edgexfoundry/go-mod-bootstrap/v3/di" "github.com/edgexfoundry/go-mod-core-contracts/v3/common" "github.com/gorilla/mux" - commonController "github.com/edgexfoundry/edgex-go/internal/pkg/controller/http" "github.com/edgexfoundry/edgex-go/internal/pkg/correlation" notificationsController "github.com/edgexfoundry/edgex-go/internal/support/notifications/controller/http" ) @@ -25,11 +26,7 @@ func LoadRestRoutes(r *mux.Router, dic *di.Container, serviceName string) { authenticationHook := handlers.AutoConfigAuthenticationFunc(secretProvider, lc) // Common - cc := commonController.NewCommonController(dic, serviceName) - r.HandleFunc(common.ApiPingRoute, cc.Ping).Methods(http.MethodGet) // Health check is always unauthenticated - r.HandleFunc(common.ApiVersionRoute, authenticationHook(cc.Version)).Methods(http.MethodGet) - r.HandleFunc(common.ApiConfigRoute, authenticationHook(cc.Config)).Methods(http.MethodGet) - r.HandleFunc(common.ApiSecretRoute, authenticationHook(cc.AddSecret)).Methods(http.MethodPost) + _ = controller.NewCommonController(dic, r, serviceName, edgex.Version) // Subscription sc := notificationsController.NewSubscriptionController(dic) diff --git a/internal/support/scheduler/router.go b/internal/support/scheduler/router.go index be7516710f..b2d644da89 100644 --- a/internal/support/scheduler/router.go +++ b/internal/support/scheduler/router.go @@ -8,13 +8,14 @@ package scheduler import ( "net/http" + "github.com/edgexfoundry/edgex-go" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" + "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/controller" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/handlers" "github.com/edgexfoundry/go-mod-bootstrap/v3/di" "github.com/edgexfoundry/go-mod-core-contracts/v3/common" "github.com/gorilla/mux" - commonController "github.com/edgexfoundry/edgex-go/internal/pkg/controller/http" "github.com/edgexfoundry/edgex-go/internal/pkg/correlation" schedulerController "github.com/edgexfoundry/edgex-go/internal/support/scheduler/controller/http" ) @@ -25,10 +26,7 @@ func LoadRestRoutes(r *mux.Router, dic *di.Container, serviceName string) { authenticationHook := handlers.AutoConfigAuthenticationFunc(secretProvider, lc) // Common - cc := commonController.NewCommonController(dic, serviceName) - r.HandleFunc(common.ApiPingRoute, cc.Ping).Methods(http.MethodGet) // Health check is always unauthenticated - r.HandleFunc(common.ApiVersionRoute, authenticationHook(cc.Version)).Methods(http.MethodGet) - r.HandleFunc(common.ApiConfigRoute, authenticationHook(cc.Config)).Methods(http.MethodGet) + _ = controller.NewCommonController(dic, r, serviceName, edgex.Version) // Interval interval := schedulerController.NewIntervalController(dic)