diff --git a/go.mod b/go.mod index b0603d77a..be44e1207 100644 --- a/go.mod +++ b/go.mod @@ -7,10 +7,10 @@ require ( github.com/diegoholiveira/jsonlogic v1.0.1-0.20200220175622-ab7989be08b9 github.com/eclipse/paho.mqtt.golang v1.2.0 github.com/edgexfoundry/go-mod-bootstrap v0.0.37 - github.com/edgexfoundry/go-mod-core-contracts v0.1.65 - github.com/edgexfoundry/go-mod-messaging v0.1.19 - github.com/edgexfoundry/go-mod-registry v0.1.20 - github.com/edgexfoundry/go-mod-secrets v0.0.18 + github.com/edgexfoundry/go-mod-core-contracts v0.1.66 + github.com/edgexfoundry/go-mod-messaging v0.1.21 + github.com/edgexfoundry/go-mod-registry v0.1.21 + github.com/edgexfoundry/go-mod-secrets v0.0.19 github.com/fxamacker/cbor/v2 v2.2.0 github.com/golang/snappy v0.0.1 // indirect github.com/gomodule/redigo v2.0.0+incompatible diff --git a/internal/v2/controller/http/controller.go b/internal/v2/controller/http/controller.go new file mode 100644 index 000000000..6c495a050 --- /dev/null +++ b/internal/v2/controller/http/controller.go @@ -0,0 +1,74 @@ +// +// Copyright (c) 2020 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package http + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/edgexfoundry/go-mod-core-contracts/clients" + "github.com/edgexfoundry/go-mod-core-contracts/clients/logger" + contractsV2 "github.com/edgexfoundry/go-mod-core-contracts/v2" + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common" + "github.com/google/uuid" +) + +// V2Controller controller for V2 REST APIs +type V2Controller struct { + lc logger.LoggingClient +} + +// NewV2Controller creates and initializes an V2Controller +func NewV2Controller(lc logger.LoggingClient) *V2Controller { + return &V2Controller{ + lc: lc, + } +} + +// Ping handles the request to /ping endpoint. Is used to test if the service is working +// It returns a response as specified by the V2 API swagger in openapi/v2 +func (v2c *V2Controller) Ping(w http.ResponseWriter, r *http.Request) { + pingResponse := common.NewPingResponse() + + v2c.sendResponse(w, contractsV2.ApiPingRoute, pingResponse, uuid.New().String()) + +} + +// sendResponse puts together the response packet for the V2 API +// api is the V2 API path +// item is the object or data that is sent back as part of the response +// correlationID is a unique identifier correlating a request to its associated response +func (v2c *V2Controller) sendResponse(w http.ResponseWriter, api string, item interface{}, correlationID string) { + data, err := json.Marshal(item) + if err != nil { + v2c.lc.Error(fmt.Sprintf("Unable to marshal %s response", api), "error", err.Error(), clients.CorrelationHeader, correlationID) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + _, err = w.Write(data) + if err != nil { + v2c.lc.Error(fmt.Sprintf("Unable to write %s response", api), "error", err.Error(), clients.CorrelationHeader, correlationID) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set(clients.CorrelationHeader, correlationID) + w.Header().Set(clients.ContentType, clients.ContentTypeJSON) + w.WriteHeader(http.StatusOK) +} diff --git a/internal/v2/controller/http/controller_test.go b/internal/v2/controller/http/controller_test.go new file mode 100644 index 000000000..e8b83bdb8 --- /dev/null +++ b/internal/v2/controller/http/controller_test.go @@ -0,0 +1,62 @@ +// +// Copyright (c) 2020 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package http + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/edgexfoundry/go-mod-core-contracts/clients" + "github.com/edgexfoundry/go-mod-core-contracts/clients/logger" + contractsV2 "github.com/edgexfoundry/go-mod-core-contracts/v2" + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPingRquest(t *testing.T) { + target := NewV2Controller(logger.NewMockClient()) + + req, err := http.NewRequest(http.MethodGet, contractsV2.ApiPingRoute, nil) + if err != nil { + t.Fatal(err) + } + + recorder := httptest.NewRecorder() + handler := http.HandlerFunc(target.Ping) + + handler.ServeHTTP(recorder, req) + + require.Equal(t, http.StatusOK, recorder.Code) + + assert.Equal(t, clients.ContentTypeJSON, recorder.HeaderMap.Get(clients.ContentType)) + assert.NotEmpty(t, recorder.HeaderMap.Get(clients.CorrelationHeader)) + + require.NotEmpty(t, recorder.Body.String()) + + actual := common.PingResponse{} + err = json.Unmarshal(recorder.Body.Bytes(), &actual) + require.NoError(t, err) + + _, err = time.Parse(time.UnixDate, actual.Timestamp) + assert.NoError(t, err) + + require.Equal(t, contractsV2.ApiVersion, actual.Versionable.ApiVersion) +} diff --git a/internal/v2/routes.go b/internal/v2/routes.go new file mode 100644 index 000000000..69aef5d8b --- /dev/null +++ b/internal/v2/routes.go @@ -0,0 +1,27 @@ +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package v2 + +import ( + "net/http" + + v2http "github.com/edgexfoundry/app-functions-sdk-go/internal/v2/controller/http" + "github.com/edgexfoundry/go-mod-core-contracts/clients/logger" + contractsV2 "github.com/edgexfoundry/go-mod-core-contracts/v2" + "github.com/gorilla/mux" +) + +// ConfigureStandardRoutes loads default V2 routes +func ConfigureStandardRoutes(router *mux.Router, lc logger.LoggingClient) { + controller := v2http.NewV2Controller(lc) + + router.HandleFunc(contractsV2.ApiPingRoute, controller.Ping).Methods(http.MethodGet) +} diff --git a/internal/webserver/server.go b/internal/webserver/server.go index 9031744d3..fc89b4e9d 100644 --- a/internal/webserver/server.go +++ b/internal/webserver/server.go @@ -27,6 +27,7 @@ import ( "github.com/edgexfoundry/app-functions-sdk-go/internal/common" "github.com/edgexfoundry/app-functions-sdk-go/internal/security" "github.com/edgexfoundry/app-functions-sdk-go/internal/telemetry" + v2 "github.com/edgexfoundry/app-functions-sdk-go/internal/v2" "github.com/edgexfoundry/go-mod-core-contracts/clients" "github.com/edgexfoundry/go-mod-core-contracts/clients/logger" @@ -293,6 +294,9 @@ func (webserver *WebServer) ConfigureStandardRoutes() { // Secrets webserver.router.HandleFunc(internal.SecretsAPIRoute, webserver.secretHandler).Methods(http.MethodPost) + + // V2 API routes + v2.ConfigureStandardRoutes(webserver.router, webserver.LoggingClient) } // SetupTriggerRoute adds a route to handle trigger pipeline from HTTP request