-
Notifications
You must be signed in to change notification settings - Fork 55
/
Copy pathcommonapi.go
177 lines (149 loc) · 6 KB
/
commonapi.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
//
// Copyright (C) 2023-2025 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0
package controller
import (
"encoding/json"
"net/http"
"strings"
"github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/container"
"github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/handlers"
"github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/interfaces"
"github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/utils"
"github.com/edgexfoundry/go-mod-bootstrap/v4/di"
"github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger"
"github.com/edgexfoundry/go-mod-core-contracts/v4/common"
commonDTO "github.com/edgexfoundry/go-mod-core-contracts/v4/dtos/common"
"github.com/edgexfoundry/go-mod-core-contracts/v4/errors"
"github.com/labstack/echo/v4"
"github.com/mitchellh/mapstructure"
)
// CommonController controller for common REST APIs
type CommonController struct {
dic *di.Container
serviceName string
router *echo.Echo
version version
config config
lc logger.LoggingClient
}
type version struct {
serviceVersion string
sdkVersion string
}
type config struct {
configuration interfaces.Configuration
customConfig interfaces.UpdatableConfig
}
func NewCommonController(dic *di.Container, r *echo.Echo, serviceName string, serviceVersion string) *CommonController {
lc := container.LoggingClientFrom(dic.Get)
authenticationHook := handlers.AutoConfigAuthenticationFunc(dic)
configuration := container.ConfigurationFrom(dic.Get)
c := CommonController{
dic: dic,
serviceName: serviceName,
router: r,
lc: lc,
version: version{
serviceVersion: serviceVersion,
},
config: config{
configuration: configuration,
},
}
r.GET(common.ApiPingRoute, c.Ping) // Health check is always unauthenticated
r.GET(common.ApiVersionRoute, c.Version, authenticationHook)
r.GET(common.ApiConfigRoute, c.Config, authenticationHook)
r.POST(common.ApiSecretRoute, c.AddSecret, authenticationHook)
return &c
}
// SetSDKVersion sets the service's skd version
func (c *CommonController) SetSDKVersion(sdkVersion string) {
c.version.sdkVersion = sdkVersion
}
// SetCustomConfigInfo sets the custom configuration, which is used to include the service's custom config in the /config endpoint response.
func (c *CommonController) SetCustomConfigInfo(customConfig interfaces.UpdatableConfig) {
c.config.customConfig = customConfig
}
// 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(e echo.Context) error {
request := e.Request()
writer := e.Response()
response := commonDTO.NewPingResponse(c.serviceName)
return utils.SendJsonResp(c.lc, writer, request, 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(e echo.Context) error {
request := e.Request()
writer := e.Response()
var response interface{}
if c.version.sdkVersion != "" {
response = commonDTO.NewVersionSdkResponse(c.version.serviceVersion, c.version.sdkVersion, c.serviceName)
} else {
response = commonDTO.NewVersionResponse(c.version.serviceVersion, c.serviceName)
}
return utils.SendJsonResp(c.lc, writer, request, 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 swagger in openapi/common
func (c *CommonController) Config(e echo.Context) error {
request := e.Request()
writer := e.Response()
var fullConfig interface{}
m := make(map[string]any)
err := mapstructure.Decode(c.config.configuration, &m)
if err != nil {
c.lc.Errorf("%v", err.Error())
return utils.SendJsonErrResp(c.lc, writer, request, errors.KindServerError, "config can not convert to map", err, "")
}
if c.config.customConfig != nil {
m["CustomConfiguration"] = c.config.customConfig
}
fullConfig = m
response := commonDTO.NewConfigResponse(fullConfig, c.serviceName)
return utils.SendJsonResp(c.lc, writer, request, response, http.StatusOK)
}
// 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(e echo.Context) error {
request := e.Request()
writer := e.Response()
defer func() {
_ = request.Body.Close()
}()
secretRequest := commonDTO.SecretRequest{}
err := json.NewDecoder(request.Body).Decode(&secretRequest)
if err != nil {
c.lc.Errorf("%v", err.Error())
return utils.SendJsonErrResp(c.lc, writer, request, errors.KindContractInvalid, "JSON decode failed", err, "")
}
err = addSecret(c.dic, secretRequest)
if err != nil {
return utils.SendJsonErrResp(c.lc, writer, request, errors.Kind(err), err.Error(), err, secretRequest.RequestId)
}
response := commonDTO.NewBaseResponse(secretRequest.RequestId, "", http.StatusCreated)
return utils.SendJsonResp(c.lc, writer, request, response, http.StatusCreated)
}
// addSecret adds EdgeX Service exclusive secret to the Secret Store
func addSecret(dic *di.Container, request commonDTO.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 commonDTO.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
}