NOISSUE - Return certs on bootstrap view response (#1855)

* return certs on bootstrap view response

Signed-off-by: SammyOina <sammyoina@gmail.com>

* return updated certs when updated

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix test

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix test

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix test

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix test

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix tests

Signed-off-by: SammyOina <sammyoina@gmail.com>

* simplify tests

Signed-off-by: SammyOina <sammyoina@gmail.com>

* use named query

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix test

Signed-off-by: SammyOina <sammyoina@gmail.com>

* use named params

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix typo

Signed-off-by: SammyOina <sammyoina@gmail.com>

* use inline error checks
remove unrequired conditions

Signed-off-by: SammyOina <sammyoina@gmail.com>

* sort slices before comparison

Signed-off-by: SammyOina <sammyoina@gmail.com>

* rename mainflux_id to thing_id
rename MFThing to ThingID
rename MFKey to ThingKey
rename mainflux_key to thing_key

Signed-off-by: SammyOina <sammyoina@gmail.com>

* remove mainflux_channels

Signed-off-by: SammyOina <sammyoina@gmail.com>

* simplify unmarshaller

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
This commit is contained in:
Sammy Kerata Oina
2023-07-31 15:17:14 +03:00
committed by GitHub
parent 57d47fea66
commit b7b14cc8b6
24 changed files with 569 additions and 385 deletions
+42 -8
View File
@@ -123,6 +123,7 @@ paths:
responses:
'200':
description: Config updated.
$ref: "#/components/responses/ConfigUpdateCertsRes"
'400':
description: Failed due to malformed JSON.
'401':
@@ -239,7 +240,7 @@ components:
Config:
type: object
properties:
mainflux_id:
thing_id:
type: string
format: uuid
description: Corresponding Mainflux Thing ID.
@@ -247,7 +248,7 @@ components:
type: string
format: uuid
description: Corresponding Mainflux Thing key.
mainflux_channels:
channels:
type: array
minItems: 0
items:
@@ -274,6 +275,12 @@ components:
description: Free-form custom configuration.
state:
$ref: "#/components/schemas/State"
client_cert:
type: string
description: Client certificate.
ca_cert:
type: string
description: Issuing CA certificate.
required:
- external_id
- external_key
@@ -305,15 +312,15 @@ components:
BootstrapConfig:
type: object
properties:
mainflux_id:
thing_id:
type: string
format: uuid
description: Corresponding Mainflux Thing ID.
mainflux_key:
thing_key:
type: string
format: uuid
description: Corresponding Mainflux Thing key.
mainflux_channels:
channels:
type: array
minItems: 0
items:
@@ -331,9 +338,30 @@ components:
type: string
description: Issuing CA certificate.
required:
- mainflux_id
- mainflux_key
- mainflux_channels
- thing_id
- thing_key
- channels
- content
ConfigUpdateCerts:
type: object
properties:
thing_id:
type: string
format: uuid
description: Corresponding Mainflux Thing ID.
client_cert:
type: string
description: Client certificate.
client_key:
type: string
description: Key for the client_cert.
ca_cert:
type: string
description: Issuing CA certificate.
required:
- thing_id
- thing_key
- channels
- content
parameters:
@@ -511,6 +539,12 @@ components:
application/json:
schema:
$ref: "./schemas/HealthInfo.yml"
ConfigUpdateCertsRes:
description: Data retrieved. Config certs updated.
content:
application/json:
schema:
$ref: "#/components/schemas/ConfigUpdateCerts"
securitySchemes:
bearerAuth:
+19 -13
View File
@@ -23,10 +23,10 @@ func addEndpoint(svc bootstrap.Service) endpoint.Endpoint {
}
config := bootstrap.Config{
MFThing: req.ThingID,
ThingID: req.ThingID,
ExternalID: req.ExternalID,
ExternalKey: req.ExternalKey,
MFChannels: channels,
Channels: channels,
Name: req.Name,
ClientCert: req.ClientCert,
ClientKey: req.ClientKey,
@@ -40,7 +40,7 @@ func addEndpoint(svc bootstrap.Service) endpoint.Endpoint {
}
res := configRes{
id: saved.MFThing,
id: saved.ThingID,
created: true,
}
@@ -55,11 +55,17 @@ func updateCertEndpoint(svc bootstrap.Service) endpoint.Endpoint {
return nil, err
}
if err := svc.UpdateCert(ctx, req.token, req.thingID, req.ClientCert, req.ClientKey, req.CACert); err != nil {
cfg, err := svc.UpdateCert(ctx, req.token, req.thingID, req.ClientCert, req.ClientKey, req.CACert)
if err != nil {
return nil, err
}
res := configRes{}
res := updateConfigRes{
ThingID: cfg.ThingID,
ClientCert: cfg.ClientCert,
CACert: cfg.CACert,
ClientKey: cfg.ClientKey,
}
return res, nil
}
@@ -79,7 +85,7 @@ func viewEndpoint(svc bootstrap.Service) endpoint.Endpoint {
}
var channels []channelRes
for _, ch := range config.MFChannels {
for _, ch := range config.Channels {
channels = append(channels, channelRes{
ID: ch.ID,
Name: ch.Name,
@@ -88,8 +94,8 @@ func viewEndpoint(svc bootstrap.Service) endpoint.Endpoint {
}
res := viewRes{
MFThing: config.MFThing,
MFKey: config.MFKey,
ThingID: config.ThingID,
ThingKey: config.ThingKey,
Channels: channels,
ExternalID: config.ExternalID,
ExternalKey: config.ExternalKey,
@@ -111,7 +117,7 @@ func updateEndpoint(svc bootstrap.Service) endpoint.Endpoint {
}
config := bootstrap.Config{
MFThing: req.id,
ThingID: req.id,
Name: req.Name,
Content: req.Content,
}
@@ -121,7 +127,7 @@ func updateEndpoint(svc bootstrap.Service) endpoint.Endpoint {
}
res := configRes{
id: config.MFThing,
id: config.ThingID,
created: false,
}
@@ -171,7 +177,7 @@ func listEndpoint(svc bootstrap.Service) endpoint.Endpoint {
for _, cfg := range page.Configs {
var channels []channelRes
for _, ch := range cfg.MFChannels {
for _, ch := range cfg.Channels {
channels = append(channels, channelRes{
ID: ch.ID,
Name: ch.Name,
@@ -180,8 +186,8 @@ func listEndpoint(svc bootstrap.Service) endpoint.Endpoint {
}
view := viewRes{
MFThing: cfg.MFThing,
MFKey: cfg.MFKey,
ThingID: cfg.ThingID,
ThingKey: cfg.ThingKey,
Channels: channels,
ExternalID: cfg.ExternalID,
ExternalKey: cfg.ExternalKey,
+52 -52
View File
@@ -109,7 +109,7 @@ func newConfig(channels []bootstrap.Channel) bootstrap.Config {
return bootstrap.Config{
ExternalID: addExternalID,
ExternalKey: addExternalKey,
MFChannels: channels,
Channels: channels,
Name: addName,
Content: addContent,
ClientCert: "newcert",
@@ -355,7 +355,7 @@ func TestView(t *testing.T) {
mfChs := generateChannels()
for id, ch := range mfChs {
c.MFChannels = append(c.MFChannels, bootstrap.Channel{
c.Channels = append(c.Channels, bootstrap.Channel{
ID: ch.ID,
Name: fmt.Sprintf("%s%s", "name ", id),
Metadata: map[string]interface{}{"type": fmt.Sprintf("some type %s", id)},
@@ -366,13 +366,13 @@ func TestView(t *testing.T) {
assert.Nil(t, err, fmt.Sprintf("Saving config expected to succeed: %s.\n", err))
var channels []channel
for _, ch := range saved.MFChannels {
for _, ch := range saved.Channels {
channels = append(channels, channel{ID: ch.ID, Name: ch.Name, Metadata: ch.Metadata})
}
data := config{
MFThing: saved.MFThing,
MFKey: saved.MFKey,
ThingID: saved.ThingID,
ThingKey: saved.ThingKey,
State: saved.State,
Channels: channels,
ExternalID: saved.ExternalID,
@@ -391,14 +391,14 @@ func TestView(t *testing.T) {
{
desc: "view a config with invalid token",
auth: invalidToken,
id: saved.MFThing,
id: saved.ThingID,
status: http.StatusUnauthorized,
res: config{},
},
{
desc: "view a config",
auth: validToken,
id: saved.MFThing,
id: saved.ThingID,
status: http.StatusOK,
res: data,
},
@@ -412,7 +412,7 @@ func TestView(t *testing.T) {
{
desc: "view a config with an empty token",
auth: "",
id: saved.MFThing,
id: saved.ThingID,
status: http.StatusUnauthorized,
res: config{},
},
@@ -467,7 +467,7 @@ func TestUpdate(t *testing.T) {
{
desc: "update with invalid token",
req: data,
id: saved.MFThing,
id: saved.ThingID,
auth: invalidToken,
contentType: contentType,
status: http.StatusUnauthorized,
@@ -475,7 +475,7 @@ func TestUpdate(t *testing.T) {
{
desc: "update with an empty token",
req: data,
id: saved.MFThing,
id: saved.ThingID,
auth: "",
contentType: contentType,
status: http.StatusUnauthorized,
@@ -483,7 +483,7 @@ func TestUpdate(t *testing.T) {
{
desc: "update a valid config",
req: data,
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
contentType: contentType,
status: http.StatusOK,
@@ -491,7 +491,7 @@ func TestUpdate(t *testing.T) {
{
desc: "update a config with wrong content type",
req: data,
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
contentType: "",
status: http.StatusUnsupportedMediaType,
@@ -507,14 +507,14 @@ func TestUpdate(t *testing.T) {
{
desc: "update a config with invalid request format",
req: "}",
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
contentType: contentType,
status: http.StatusBadRequest,
},
{
desc: "update a config with an empty request",
id: saved.MFThing,
id: saved.ThingID,
req: "",
auth: validToken,
contentType: contentType,
@@ -561,7 +561,7 @@ func TestUpdateCert(t *testing.T) {
{
desc: "update with invalid token",
req: data,
id: saved.MFThing,
id: saved.ThingID,
auth: invalidToken,
contentType: contentType,
status: http.StatusUnauthorized,
@@ -569,7 +569,7 @@ func TestUpdateCert(t *testing.T) {
{
desc: "update with an empty token",
req: data,
id: saved.MFThing,
id: saved.ThingID,
auth: "",
contentType: contentType,
status: http.StatusUnauthorized,
@@ -577,7 +577,7 @@ func TestUpdateCert(t *testing.T) {
{
desc: "update a valid config",
req: data,
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
contentType: contentType,
status: http.StatusOK,
@@ -585,7 +585,7 @@ func TestUpdateCert(t *testing.T) {
{
desc: "update a config with wrong content type",
req: data,
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
contentType: "",
status: http.StatusUnsupportedMediaType,
@@ -601,14 +601,14 @@ func TestUpdateCert(t *testing.T) {
{
desc: "update a config with invalid request format",
req: "}",
id: saved.MFKey,
id: saved.ThingKey,
auth: validToken,
contentType: contentType,
status: http.StatusBadRequest,
},
{
desc: "update a config with an empty request",
id: saved.MFThing,
id: saved.ThingID,
req: "",
auth: validToken,
contentType: contentType,
@@ -661,7 +661,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections with invalid token",
req: data,
id: saved.MFThing,
id: saved.ThingID,
auth: invalidToken,
contentType: contentType,
status: http.StatusUnauthorized,
@@ -669,7 +669,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections with an empty token",
req: data,
id: saved.MFThing,
id: saved.ThingID,
auth: "",
contentType: contentType,
status: http.StatusUnauthorized,
@@ -677,7 +677,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections valid config",
req: data,
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
contentType: contentType,
status: http.StatusOK,
@@ -685,7 +685,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections with wrong content type",
req: data,
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
contentType: "",
status: http.StatusUnsupportedMediaType,
@@ -701,7 +701,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections with invalid channels",
req: wrongData,
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
contentType: contentType,
status: http.StatusBadRequest,
@@ -709,14 +709,14 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update a config with invalid request format",
req: "}",
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
contentType: contentType,
status: http.StatusBadRequest,
},
{
desc: "update a config with an empty request",
id: saved.MFThing,
id: saved.ThingID,
req: "",
auth: validToken,
contentType: contentType,
@@ -755,7 +755,7 @@ func TestList(t *testing.T) {
for i := 0; i < configNum; i++ {
c.ExternalID = strconv.Itoa(i)
c.MFKey = c.ExternalID
c.ThingKey = c.ExternalID
c.Name = fmt.Sprintf("%s-%d", addName, i)
c.ExternalKey = fmt.Sprintf("%s%s", addExternalKey, strconv.Itoa(i))
@@ -763,12 +763,12 @@ func TestList(t *testing.T) {
assert.Nil(t, err, fmt.Sprintf("Saving config expected to succeed: %s.\n", err))
var channels []channel
for _, ch := range saved.MFChannels {
for _, ch := range saved.Channels {
channels = append(channels, channel{ID: ch.ID, Name: ch.Name, Metadata: ch.Metadata})
}
s := config{
MFThing: saved.MFThing,
MFKey: saved.MFKey,
ThingID: saved.ThingID,
ThingKey: saved.ThingKey,
Channels: channels,
ExternalID: saved.ExternalID,
ExternalKey: saved.ExternalKey,
@@ -785,7 +785,7 @@ func TestList(t *testing.T) {
if i%2 == 0 {
state = bootstrap.Inactive
}
err := svc.ChangeState(context.Background(), validToken, list[i].MFThing, state)
err := svc.ChangeState(context.Background(), validToken, list[i].ThingID, state)
assert.Nil(t, err, fmt.Sprintf("Changing state expected to succeed: %s.\n", err))
list[i].State = state
if state == bootstrap.Inactive {
@@ -1009,12 +1009,12 @@ func TestRemove(t *testing.T) {
}{
{
desc: "remove with invalid token",
id: saved.MFThing,
id: saved.ThingID,
auth: invalidToken,
status: http.StatusUnauthorized,
}, {
desc: "remove with an empty token",
id: saved.MFThing,
id: saved.ThingID,
auth: "",
status: http.StatusUnauthorized,
},
@@ -1026,7 +1026,7 @@ func TestRemove(t *testing.T) {
},
{
desc: "remove config",
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
status: http.StatusNoContent,
},
@@ -1067,22 +1067,22 @@ func TestBootstrap(t *testing.T) {
assert.Nil(t, err, fmt.Sprintf("Encrypting config expected to succeed: %s.\n", err))
var channels []channel
for _, ch := range saved.MFChannels {
for _, ch := range saved.Channels {
channels = append(channels, channel{ID: ch.ID, Name: ch.Name, Metadata: ch.Metadata})
}
s := struct {
MFThing string `json:"mainflux_id"`
MFKey string `json:"mainflux_key"`
MFChannels []channel `json:"mainflux_channels"`
ThingID string `json:"thing_id"`
ThingKey string `json:"thing_key"`
Channels []channel `json:"channels"`
Content string `json:"content"`
ClientCert string `json:"client_cert"`
ClientKey string `json:"client_key"`
CACert string `json:"ca_cert"`
}{
MFThing: saved.MFThing,
MFKey: saved.MFKey,
MFChannels: channels,
ThingID: saved.ThingID,
ThingKey: saved.ThingKey,
Channels: channels,
Content: saved.Content,
ClientCert: saved.ClientCert,
ClientKey: saved.ClientKey,
@@ -1205,7 +1205,7 @@ func TestChangeState(t *testing.T) {
}{
{
desc: "change state with invalid token",
id: saved.MFThing,
id: saved.ThingID,
auth: invalidToken,
state: active,
contentType: contentType,
@@ -1213,7 +1213,7 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state with an empty token",
id: saved.MFThing,
id: saved.ThingID,
auth: "",
state: active,
contentType: contentType,
@@ -1221,7 +1221,7 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state with invalid content type",
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
state: active,
contentType: "",
@@ -1229,7 +1229,7 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state to active",
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
state: active,
contentType: contentType,
@@ -1237,7 +1237,7 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state to inactive",
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
state: inactive,
contentType: contentType,
@@ -1253,7 +1253,7 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state to invalid value",
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
state: fmt.Sprintf("{\"state\": %d}", -3),
contentType: contentType,
@@ -1261,7 +1261,7 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state with invalid data",
id: saved.MFThing,
id: saved.ThingID,
auth: validToken,
state: "",
contentType: contentType,
@@ -1291,9 +1291,9 @@ type channel struct {
}
type config struct {
MFThing string `json:"mainflux_id,omitempty"`
MFKey string `json:"mainflux_key,omitempty"`
Channels []channel `json:"mainflux_channels,omitempty"`
ThingID string `json:"thing_id,omitempty"`
ThingKey string `json:"thing_key,omitempty"`
Channels []channel `json:"channels,omitempty"`
ExternalID string `json:"external_id"`
ExternalKey string `json:"external_key,omitempty"`
Content string `json:"content,omitempty"`
+4 -4
View File
@@ -30,7 +30,7 @@ func LoggingMiddleware(svc bootstrap.Service, logger mflog.Logger) bootstrap.Ser
// If the request fails, it logs the error.
func (lm *loggingMiddleware) Add(ctx context.Context, token string, cfg bootstrap.Config) (saved bootstrap.Config, err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method add using token %s with thing %s took %s to complete", token, saved.MFThing, time.Since(begin))
message := fmt.Sprintf("Method add using token %s with thing %s took %s to complete", token, saved.ThingID, time.Since(begin))
if err != nil {
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
return
@@ -45,7 +45,7 @@ func (lm *loggingMiddleware) Add(ctx context.Context, token string, cfg bootstra
// If the request fails, it logs the error.
func (lm *loggingMiddleware) View(ctx context.Context, token, id string) (saved bootstrap.Config, err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method view using token %s with thing %s took %s to complete", token, saved.MFThing, time.Since(begin))
message := fmt.Sprintf("Method view using token %s with thing %s took %s to complete", token, saved.ThingID, time.Since(begin))
if err != nil {
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
return
@@ -60,7 +60,7 @@ func (lm *loggingMiddleware) View(ctx context.Context, token, id string) (saved
// If the request fails, it logs the error.
func (lm *loggingMiddleware) Update(ctx context.Context, token string, cfg bootstrap.Config) (err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method update using token %s with thing %s took %s to complete", token, cfg.MFThing, time.Since(begin))
message := fmt.Sprintf("Method update using token %s with thing %s took %s to complete", token, cfg.ThingID, time.Since(begin))
if err != nil {
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
return
@@ -73,7 +73,7 @@ func (lm *loggingMiddleware) Update(ctx context.Context, token string, cfg boots
// UpdateCert logs the update_cert request. It logs token, thing ID and the time it took to complete the request.
// If the request fails, it logs the error.
func (lm *loggingMiddleware) UpdateCert(ctx context.Context, token, thingID, clientCert, clientKey, caCert string) (err error) {
func (lm *loggingMiddleware) UpdateCert(ctx context.Context, token, thingID, clientCert, clientKey, caCert string) (cfg bootstrap.Config, err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method update_cert using token %s with thing id %s took %s to complete", token, thingID, time.Since(begin))
if err != nil {
+1 -1
View File
@@ -61,7 +61,7 @@ func (mm *metricsMiddleware) Update(ctx context.Context, token string, cfg boots
}
// UpdateCert instruments UpdateCert method with metrics.
func (mm *metricsMiddleware) UpdateCert(ctx context.Context, token, thingKey, clientCert, clientKey, caCert string) (err error) {
func (mm *metricsMiddleware) UpdateCert(ctx context.Context, token, thingKey, clientCert, clientKey, caCert string) (cfg bootstrap.Config, err error) {
defer func(begin time.Time) {
mm.counter.With("method", "update_cert").Add(1)
mm.latency.With("method", "update_cert").Observe(time.Since(begin).Seconds())
+24 -3
View File
@@ -67,14 +67,16 @@ type channelRes struct {
}
type viewRes struct {
MFThing string `json:"mainflux_id,omitempty"`
MFKey string `json:"mainflux_key,omitempty"`
Channels []channelRes `json:"mainflux_channels,omitempty"`
ThingID string `json:"thing_id,omitempty"`
ThingKey string `json:"thing_key,omitempty"`
Channels []channelRes `json:"channels,omitempty"`
ExternalID string `json:"external_id"`
ExternalKey string `json:"external_key,omitempty"`
Content string `json:"content,omitempty"`
Name string `json:"name,omitempty"`
State bootstrap.State `json:"state"`
ClientCert string `json:"client_cert,omitempty"`
CACert string `json:"ca_cert,omitempty"`
}
func (res viewRes) Code() int {
@@ -121,3 +123,22 @@ func (res stateRes) Headers() map[string]string {
func (res stateRes) Empty() bool {
return true
}
type updateConfigRes struct {
ThingID string `json:"thing_id,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
CACert string `json:"ca_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
}
func (res updateConfigRes) Code() int {
return http.StatusOK
}
func (res updateConfigRes) Headers() map[string]string {
return map[string]string{}
}
func (res updateConfigRes) Empty() bool {
return false
}
+1 -1
View File
@@ -30,7 +30,7 @@ const (
)
var (
fullMatch = []string{"state", "external_id", "mainflux_id", "mainflux_key"}
fullMatch = []string{"state", "external_id", "thing_id", "thing_key"}
partialMatch = []string{"name"}
)
+5 -5
View File
@@ -16,14 +16,14 @@ import (
// MFKey is key of corresponding Mainflux Thing.
// MFChannels is a list of Mainflux Channels corresponding Mainflux Thing connects to.
type Config struct {
MFThing string `json:"mainflux_thing"`
ThingID string `json:"thing_id"`
Owner string `json:"owner,omitempty"`
Name string `json:"name,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
CACert string `json:"ca_cert,omitempty"`
MFKey string `json:"mainflux_key"`
MFChannels []Channel `json:"mainflux_channels,omitempty"`
ThingKey string `json:"thing_key"`
Channels []Channel `json:"channels,omitempty"`
ExternalID string `json:"external_id"`
ExternalKey string `json:"external_key"`
Content string `json:"content,omitempty"`
@@ -80,9 +80,9 @@ type ConfigRepository interface {
// to indicate operation failure.
Update(ctx context.Context, cfg Config) error
// UpdateCerts updates an existing Config certificate and owner.
// UpdateCerts updates and returns an existing Config certificate and owner.
// A non-nil error is returned to indicate operation failure.
UpdateCert(ctx context.Context, owner, thingID, clientCert, clientKey, caCert string) error
UpdateCert(ctx context.Context, owner, thingID, clientCert, clientKey, caCert string) (Config, error)
// UpdateConnections updates a list of Channels the Config is connected to
// adding new Channels if needed.
+22 -22
View File
@@ -38,28 +38,28 @@ func (crm *configRepositoryMock) Save(_ context.Context, config bootstrap.Config
defer crm.mu.Unlock()
for _, v := range crm.configs {
if v.MFThing == config.MFThing || v.ExternalID == config.ExternalID {
if v.ThingID == config.ThingID || v.ExternalID == config.ExternalID {
return "", errors.ErrConflict
}
}
crm.counter++
config.MFThing = strconv.FormatUint(crm.counter, 10)
crm.configs[config.MFThing] = config
config.ThingID = strconv.FormatUint(crm.counter, 10)
crm.configs[config.ThingID] = config
for _, ch := range config.MFChannels {
for _, ch := range config.Channels {
crm.channels[ch.ID] = ch
}
config.MFChannels = []bootstrap.Channel{}
config.Channels = []bootstrap.Channel{}
for _, ch := range connections {
config.MFChannels = append(config.MFChannels, crm.channels[ch])
config.Channels = append(config.Channels, crm.channels[ch])
}
crm.configs[config.MFThing] = config
crm.configs[config.ThingID] = config
return config.MFThing, nil
return config.ThingID, nil
}
func (crm *configRepositoryMock) RetrieveByID(_ context.Context, token, id string) (bootstrap.Config, error) {
@@ -99,7 +99,7 @@ func (crm *configRepositoryMock) RetrieveAll(_ context.Context, token string, fi
var total uint64
for _, v := range crm.configs {
id, _ := strconv.ParseUint(v.MFThing, 10, 64)
id, _ := strconv.ParseUint(v.ThingID, 10, 64)
if (state == emptyState || v.State == state) &&
(name == "" || strings.Contains(strings.ToLower(v.Name), name)) &&
v.Owner == token {
@@ -111,7 +111,7 @@ func (crm *configRepositoryMock) RetrieveAll(_ context.Context, token string, fi
}
sort.SliceStable(configs, func(i, j int) bool {
return configs[i].MFThing < configs[j].MFThing
return configs[i].ThingID < configs[j].ThingID
})
return bootstrap.ConfigsPage{
@@ -139,37 +139,37 @@ func (crm *configRepositoryMock) Update(_ context.Context, config bootstrap.Conf
crm.mu.Lock()
defer crm.mu.Unlock()
cfg, ok := crm.configs[config.MFThing]
cfg, ok := crm.configs[config.ThingID]
if !ok || cfg.Owner != config.Owner {
return errors.ErrNotFound
}
cfg.Name = config.Name
cfg.Content = config.Content
crm.configs[config.MFThing] = cfg
crm.configs[config.ThingID] = cfg
return nil
}
func (crm *configRepositoryMock) UpdateCert(_ context.Context, owner, thingID, clientCert, clientKey, caCert string) error {
func (crm *configRepositoryMock) UpdateCert(_ context.Context, owner, thingID, clientCert, clientKey, caCert string) (bootstrap.Config, error) {
crm.mu.Lock()
defer crm.mu.Unlock()
var forUpdate bootstrap.Config
for _, v := range crm.configs {
if v.MFThing == thingID && v.Owner == owner {
if v.ThingID == thingID && v.Owner == owner {
forUpdate = v
break
}
}
if _, ok := crm.configs[forUpdate.MFThing]; !ok {
return errors.ErrNotFound
if _, ok := crm.configs[forUpdate.ThingID]; !ok {
return bootstrap.Config{}, errors.ErrNotFound
}
forUpdate.ClientCert = clientCert
forUpdate.ClientKey = clientKey
forUpdate.CACert = caCert
crm.configs[forUpdate.MFThing] = forUpdate
crm.configs[forUpdate.ThingID] = forUpdate
return nil
return forUpdate, nil
}
func (crm *configRepositoryMock) UpdateConnections(_ context.Context, token, id string, channels []bootstrap.Channel, connections []string) error {
@@ -185,13 +185,13 @@ func (crm *configRepositoryMock) UpdateConnections(_ context.Context, token, id
crm.channels[ch.ID] = ch
}
config.MFChannels = []bootstrap.Channel{}
config.Channels = []bootstrap.Channel{}
for _, conn := range connections {
ch, ok := crm.channels[conn]
if !ok {
return errors.ErrNotFound
}
config.MFChannels = append(config.MFChannels, ch)
config.Channels = append(config.Channels, ch)
}
crm.configs[id] = config
@@ -284,7 +284,7 @@ func (crm *configRepositoryMock) DisconnectThing(_ context.Context, channelID, t
idx := -1
if config, ok := crm.configs[thingID]; ok {
for i, ch := range config.MFChannels {
for i, ch := range config.Channels {
if ch.ID == channelID {
idx = i
break
@@ -292,7 +292,7 @@ func (crm *configRepositoryMock) DisconnectThing(_ context.Context, channelID, t
}
if idx != -1 {
config.MFChannels = append(config.MFChannels[0:idx], config.MFChannels[idx:]...)
config.Channels = append(config.Channels[0:idx], config.Channels[idx:]...)
}
crm.configs[thingID] = config
}
+81 -42
View File
@@ -67,7 +67,7 @@ func (cr configRepository) Save(ctx context.Context, cfg bootstrap.Config, chsCo
return "", errors.Wrap(errors.ErrCreateEntity, e)
}
if err := insertChannels(ctx, cfg.Owner, cfg.MFChannels, tx); err != nil {
if err := insertChannels(ctx, cfg.Owner, cfg.Channels, tx); err != nil {
cr.rollback("Failed to insert Channels", tx)
return "", errors.Wrap(errSaveChannels, err)
}
@@ -82,26 +82,33 @@ func (cr configRepository) Save(ctx context.Context, cfg bootstrap.Config, chsCo
return "", err
}
return cfg.MFThing, nil
return cfg.ThingID, nil
}
func (cr configRepository) RetrieveByID(ctx context.Context, owner, id string) (bootstrap.Config, error) {
q := `SELECT mainflux_thing, mainflux_key, external_id, external_key, name, content, state
q := `SELECT mainflux_thing, mainflux_key, external_id, external_key, name, content, state, client_cert, ca_cert
FROM configs
WHERE mainflux_thing = $1 AND owner = $2`
WHERE mainflux_thing = :mainflux_thing AND owner = :owner`
dbcfg := dbConfig{
MFThing: id,
Owner: owner,
}
if err := cr.db.QueryRowxContext(ctx, q, id, owner).StructScan(&dbcfg); err != nil {
empty := bootstrap.Config{}
row, err := cr.db.NamedQueryContext(ctx, q, dbcfg)
if err != nil {
if err == sql.ErrNoRows {
return empty, errors.Wrap(errors.ErrNotFound, err)
return bootstrap.Config{}, errors.Wrap(errors.ErrNotFound, err)
}
return empty, errors.Wrap(errors.ErrViewEntity, err)
return bootstrap.Config{}, errors.Wrap(errors.ErrViewEntity, err)
}
if ok := row.Next(); !ok {
return bootstrap.Config{}, errors.Wrap(errors.ErrNotFound, row.Err())
}
if err := row.StructScan(&dbcfg); err != nil {
return bootstrap.Config{}, err
}
q = `SELECT mainflux_channel, name, metadata FROM channels ch
@@ -133,7 +140,7 @@ func (cr configRepository) RetrieveByID(ctx context.Context, owner, id string) (
}
cfg := toConfig(dbcfg)
cfg.MFChannels = chans
cfg.Channels = chans
return cfg, nil
}
@@ -158,7 +165,7 @@ func (cr configRepository) RetrieveAll(ctx context.Context, owner string, filter
for rows.Next() {
c := bootstrap.Config{Owner: owner}
if err := rows.Scan(&c.MFThing, &c.MFKey, &c.ExternalID, &c.ExternalKey, &name, &content, &c.State); err != nil {
if err := rows.Scan(&c.ThingID, &c.ThingKey, &c.ExternalID, &c.ExternalKey, &name, &content, &c.State); err != nil {
cr.log.Error(fmt.Sprintf("Failed to read retrieved config due to %s", err))
return bootstrap.ConfigsPage{}
}
@@ -187,17 +194,25 @@ func (cr configRepository) RetrieveAll(ctx context.Context, owner string, filter
func (cr configRepository) RetrieveByExternalID(ctx context.Context, externalID string) (bootstrap.Config, error) {
q := `SELECT mainflux_thing, mainflux_key, external_key, owner, name, client_cert, client_key, ca_cert, content, state
FROM configs
WHERE external_id = $1`
WHERE external_id = :external_id`
dbcfg := dbConfig{
ExternalID: externalID,
}
if err := cr.db.QueryRowxContext(ctx, q, externalID).StructScan(&dbcfg); err != nil {
empty := bootstrap.Config{}
row, err := cr.db.NamedQueryContext(ctx, q, dbcfg)
if err != nil {
if err == sql.ErrNoRows {
return empty, errors.Wrap(errors.ErrNotFound, err)
return bootstrap.Config{}, errors.Wrap(errors.ErrNotFound, err)
}
return empty, errors.Wrap(errors.ErrViewEntity, err)
return bootstrap.Config{}, errors.Wrap(errors.ErrViewEntity, err)
}
if ok := row.Next(); !ok {
return bootstrap.Config{}, errors.Wrap(errors.ErrNotFound, row.Err())
}
if err := row.StructScan(&dbcfg); err != nil {
return bootstrap.Config{}, errors.Wrap(errors.ErrViewEntity, err)
}
q = `SELECT mainflux_channel, name, metadata FROM channels ch
@@ -230,18 +245,22 @@ func (cr configRepository) RetrieveByExternalID(ctx context.Context, externalID
}
cfg := toConfig(dbcfg)
cfg.MFChannels = channels
cfg.Channels = channels
return cfg, nil
}
func (cr configRepository) Update(ctx context.Context, cfg bootstrap.Config) error {
q := `UPDATE configs SET name = $1, content = $2 WHERE mainflux_thing = $3 AND owner = $4`
q := `UPDATE configs SET name = :name, content = :content WHERE mainflux_thing = :mainflux_thing AND owner = :owner `
content := nullString(cfg.Content)
name := nullString(cfg.Name)
dbcfg := dbConfig{
Name: nullString(cfg.Name),
Content: nullString(cfg.Content),
MFThing: cfg.ThingID,
Owner: cfg.Owner,
}
res, err := cr.db.ExecContext(ctx, q, name, content, cfg.MFThing, cfg.Owner)
res, err := cr.db.NamedExecContext(ctx, q, dbcfg)
if err != nil {
return errors.Wrap(errors.ErrUpdateEntity, err)
}
@@ -258,24 +277,33 @@ func (cr configRepository) Update(ctx context.Context, cfg bootstrap.Config) err
return nil
}
func (cr configRepository) UpdateCert(ctx context.Context, owner, thingID, clientCert, clientKey, caCert string) error {
q := `UPDATE configs SET client_cert = $1, client_key = $2, ca_cert = $3 WHERE mainflux_thing = $4 AND owner = $5`
func (cr configRepository) UpdateCert(ctx context.Context, owner, thingID, clientCert, clientKey, caCert string) (bootstrap.Config, error) {
q := `UPDATE configs SET client_cert = :client_cert, client_key = :client_key, ca_cert = :ca_cert WHERE mainflux_thing = :mainflux_thing AND owner = :owner
RETURNING mainflux_thing, client_cert, client_key, ca_cert`
res, err := cr.db.ExecContext(ctx, q, clientCert, clientKey, caCert, thingID, owner)
dbcfg := dbConfig{
MFThing: thingID,
ClientCert: nullString(clientCert),
Owner: owner,
ClientKey: nullString(clientKey),
CaCert: nullString(caCert),
}
row, err := cr.db.NamedQueryContext(ctx, q, dbcfg)
if err != nil {
return errors.Wrap(errors.ErrUpdateEntity, err)
return bootstrap.Config{}, errors.Wrap(errors.ErrUpdateEntity, err)
}
defer row.Close()
if ok := row.Next(); !ok {
return bootstrap.Config{}, errors.Wrap(errors.ErrNotFound, row.Err())
}
cnt, err := res.RowsAffected()
if err != nil {
return errors.Wrap(errors.ErrUpdateEntity, err)
if err := row.StructScan(&dbcfg); err != nil {
return bootstrap.Config{}, err
}
if cnt == 0 {
return errors.ErrNotFound
}
return nil
return toConfig(dbcfg), nil
}
func (cr configRepository) UpdateConnections(ctx context.Context, owner, id string, channels []bootstrap.Channel, connections []string) error {
@@ -308,8 +336,13 @@ func (cr configRepository) UpdateConnections(ctx context.Context, owner, id stri
}
func (cr configRepository) Remove(ctx context.Context, owner, id string) error {
q := `DELETE FROM configs WHERE mainflux_thing = $1 AND owner = $2`
if _, err := cr.db.ExecContext(ctx, q, id, owner); err != nil {
q := `DELETE FROM configs WHERE mainflux_thing = :mainflux_thing AND owner = :owner`
dbcfg := dbConfig{
MFThing: id,
Owner: owner,
}
if _, err := cr.db.NamedExecContext(ctx, q, dbcfg); err != nil {
return errors.Wrap(errors.ErrRemoveEntity, err)
}
@@ -321,9 +354,15 @@ func (cr configRepository) Remove(ctx context.Context, owner, id string) error {
}
func (cr configRepository) ChangeState(ctx context.Context, owner, id string, state bootstrap.State) error {
q := `UPDATE configs SET state = $1 WHERE mainflux_thing = $2 AND owner = $3;`
q := `UPDATE configs SET state = :state WHERE mainflux_thing = :mainflux_thing AND owner = :owner;`
res, err := cr.db.ExecContext(ctx, q, state, id, owner)
dbcfg := dbConfig{
MFThing: id,
State: state,
Owner: owner,
}
res, err := cr.db.NamedExecContext(ctx, q, dbcfg)
if err != nil {
return errors.Wrap(errors.ErrUpdateEntity, err)
}
@@ -485,7 +524,7 @@ func insertConnections(ctx context.Context, cfg bootstrap.Config, connections []
conns := []dbConnection{}
for _, conn := range connections {
dbconn := dbConnection{
Config: cfg.MFThing,
Config: cfg.ThingID,
Channel: conn,
ConfigOwner: cfg.Owner,
ChannelOwner: cfg.Owner,
@@ -586,13 +625,13 @@ type dbConfig struct {
func toDBConfig(cfg bootstrap.Config) dbConfig {
return dbConfig{
MFThing: cfg.MFThing,
MFThing: cfg.ThingID,
Owner: cfg.Owner,
Name: nullString(cfg.Name),
ClientCert: nullString(cfg.ClientCert),
ClientKey: nullString(cfg.ClientKey),
CaCert: nullString(cfg.CACert),
MFKey: cfg.MFKey,
MFKey: cfg.ThingKey,
ExternalID: cfg.ExternalID,
ExternalKey: cfg.ExternalKey,
Content: nullString(cfg.Content),
@@ -602,9 +641,9 @@ func toDBConfig(cfg bootstrap.Config) dbConfig {
func toConfig(dbcfg dbConfig) bootstrap.Config {
cfg := bootstrap.Config{
MFThing: dbcfg.MFThing,
ThingID: dbcfg.MFThing,
Owner: dbcfg.Owner,
MFKey: dbcfg.MFKey,
ThingKey: dbcfg.MFKey,
ExternalID: dbcfg.ExternalID,
ExternalKey: dbcfg.ExternalKey,
State: dbcfg.State,
+79 -69
View File
@@ -21,12 +21,12 @@ const numConfigs = 10
var (
config = bootstrap.Config{
MFThing: "mf-thing",
MFKey: "mf-key",
ThingID: "mf-thing",
ThingKey: "mf-key",
ExternalID: "external-id",
ExternalKey: "external-key",
Owner: "user@email.com",
MFChannels: []bootstrap.Channel{
Channels: []bootstrap.Channel{
{ID: "1", Name: "name 1", Metadata: map[string]interface{}{"meta": 1.0}},
{ID: "2", Name: "name 2", Metadata: map[string]interface{}{"meta": 2.0}},
},
@@ -46,18 +46,18 @@ func TestSave(t *testing.T) {
duplicateThing := config
duplicateThing.ExternalID = diff
duplicateThing.MFKey = diff
duplicateThing.MFChannels = []bootstrap.Channel{}
duplicateThing.ThingKey = diff
duplicateThing.Channels = []bootstrap.Channel{}
duplicateExternal := config
duplicateExternal.MFThing = diff
duplicateExternal.MFKey = diff
duplicateExternal.MFChannels = []bootstrap.Channel{}
duplicateExternal.ThingID = diff
duplicateExternal.ThingKey = diff
duplicateExternal.Channels = []bootstrap.Channel{}
duplicateChannels := config
duplicateChannels.ExternalID = diff
duplicateChannels.MFKey = diff
duplicateChannels.MFThing = diff
duplicateChannels.ThingKey = diff
duplicateChannels.ThingID = diff
cases := []struct {
desc string
@@ -94,7 +94,7 @@ func TestSave(t *testing.T) {
id, err := repo.Save(context.Background(), tc.config, tc.connections)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
if err == nil {
assert.Equal(t, id, tc.config.MFThing, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.config.MFThing, id))
assert.Equal(t, id, tc.config.ThingID, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.config.ThingID, id))
}
}
}
@@ -108,8 +108,8 @@ func TestRetrieveByID(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
require.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.MFKey = uid.String()
c.MFThing = uid.String()
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
id, err := repo.Save(context.Background(), c, channels)
@@ -168,15 +168,15 @@ func TestRetrieveAll(t *testing.T) {
require.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ExternalID = uid.String()
c.Name = fmt.Sprintf("name %d", i)
c.MFThing = uid.String()
c.MFKey = uid.String()
c.ThingID = uid.String()
c.ThingKey = uid.String()
if i%2 == 0 {
c.State = bootstrap.Active
}
if i > 0 {
c.MFChannels = nil
c.Channels = nil
}
_, err = repo.Save(context.Background(), c, channels)
@@ -245,8 +245,8 @@ func TestRetrieveByExternalID(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.MFKey = uid.String()
c.MFThing = uid.String()
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
@@ -283,8 +283,8 @@ func TestUpdate(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.MFKey = uid.String()
c.MFThing = uid.String()
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
@@ -328,8 +328,8 @@ func TestUpdateCert(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.MFKey = uid.String()
c.MFThing = uid.String()
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
@@ -342,36 +342,46 @@ func TestUpdateCert(t *testing.T) {
wrongOwner.Owner = "3"
cases := []struct {
desc string
thingID string
owner string
cert string
certKey string
ca string
err error
desc string
thingID string
owner string
cert string
certKey string
ca string
expectedConfig bootstrap.Config
err error
}{
{
desc: "update with wrong owner",
thingID: "",
cert: "cert",
certKey: "certKey",
ca: "",
owner: "wrong",
err: errors.ErrNotFound,
desc: "update with wrong owner",
thingID: "",
cert: "cert",
certKey: "certKey",
ca: "",
owner: "wrong",
expectedConfig: bootstrap.Config{},
err: errors.ErrNotFound,
},
{
desc: "update a config",
thingID: c.MFThing,
thingID: c.ThingID,
cert: "cert",
certKey: "certKey",
ca: "ca",
owner: c.Owner,
err: nil,
expectedConfig: bootstrap.Config{
ThingID: c.ThingID,
ClientCert: "cert",
CACert: "ca",
ClientKey: "certKey",
Owner: c.Owner,
},
err: nil,
},
}
for _, tc := range cases {
err := repo.UpdateCert(context.Background(), tc.owner, tc.thingID, tc.cert, tc.certKey, tc.ca)
cfg, err := repo.UpdateCert(context.Background(), tc.owner, tc.thingID, tc.cert, tc.certKey, tc.ca)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
assert.Equal(t, tc.expectedConfig, cfg, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.expectedConfig, cfg))
}
}
@@ -384,8 +394,8 @@ func TestUpdateConnections(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.MFKey = uid.String()
c.MFThing = uid.String()
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
@@ -393,11 +403,11 @@ func TestUpdateConnections(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err = uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.MFKey = uid.String()
c.MFThing = uid.String()
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
c.MFChannels = []bootstrap.Channel{}
c.Channels = []bootstrap.Channel{}
c2, err := repo.Save(context.Background(), c, []string{channels[0]})
assert.Nil(t, err, fmt.Sprintf("Saving a config expected to succeed: %s.\n", err))
@@ -420,7 +430,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections",
owner: config.Owner,
id: c.MFThing,
id: c.ThingID,
channels: nil,
connections: []string{channels[1]},
err: nil,
@@ -436,7 +446,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections no channels",
owner: config.Owner,
id: c.MFThing,
id: c.ThingID,
channels: nil,
connections: nil,
err: nil,
@@ -457,8 +467,8 @@ func TestRemove(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.MFKey = uid.String()
c.MFThing = uid.String()
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
id, err := repo.Save(context.Background(), c, channels)
@@ -484,8 +494,8 @@ func TestChangeState(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.MFKey = uid.String()
c.MFThing = uid.String()
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
saved, err := repo.Save(context.Background(), c, channels)
@@ -540,15 +550,15 @@ func TestListExisting(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.MFKey = uid.String()
c.MFThing = uid.String()
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
assert.Nil(t, err, fmt.Sprintf("Saving config expected to succeed: %s.\n", err))
var chs []bootstrap.Channel
chs = append(chs, config.MFChannels...)
chs = append(chs, config.Channels...)
cases := []struct {
desc string
@@ -591,8 +601,8 @@ func TestRemoveThing(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.MFKey = uid.String()
c.MFThing = uid.String()
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
saved, err := repo.Save(context.Background(), c, channels)
@@ -612,14 +622,14 @@ func TestUpdateChannel(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.MFKey = uid.String()
c.MFThing = uid.String()
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
assert.Nil(t, err, fmt.Sprintf("Saving config expected to succeed: %s.\n", err))
id := c.MFChannels[0].ID
id := c.Channels[0].ID
update := bootstrap.Channel{
ID: id,
Name: "update name",
@@ -628,10 +638,10 @@ func TestUpdateChannel(t *testing.T) {
err = repo.UpdateChannel(context.Background(), update)
assert.Nil(t, err, fmt.Sprintf("updating config expected to succeed: %s.\n", err))
cfg, err := repo.RetrieveByID(context.Background(), c.Owner, c.MFThing)
cfg, err := repo.RetrieveByID(context.Background(), c.Owner, c.ThingID)
assert.Nil(t, err, fmt.Sprintf("Retrieving config expected to succeed: %s.\n", err))
var retreved bootstrap.Channel
for _, c := range cfg.MFChannels {
for _, c := range cfg.Channels {
if c.ID == id {
retreved = c
break
@@ -649,19 +659,19 @@ func TestRemoveChannel(t *testing.T) {
c := config
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.MFKey = uid.String()
c.MFThing = uid.String()
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
assert.Nil(t, err, fmt.Sprintf("Saving config expected to succeed: %s.\n", err))
err = repo.RemoveChannel(context.Background(), c.MFChannels[0].ID)
err = repo.RemoveChannel(context.Background(), c.Channels[0].ID)
assert.Nil(t, err, fmt.Sprintf("Retrieving config expected to succeed: %s.\n", err))
cfg, err := repo.RetrieveByID(context.Background(), c.Owner, c.MFThing)
cfg, err := repo.RetrieveByID(context.Background(), c.Owner, c.ThingID)
assert.Nil(t, err, fmt.Sprintf("Retrieving config expected to succeed: %s.\n", err))
assert.NotContains(t, cfg.MFChannels, c.MFChannels[0], fmt.Sprintf("expected to remove channel %s from %s", c.MFChannels[0], cfg.MFChannels))
assert.NotContains(t, cfg.Channels, c.Channels[0], fmt.Sprintf("expected to remove channel %s from %s", c.Channels[0], cfg.Channels))
}
func TestDisconnectThing(t *testing.T) {
@@ -673,17 +683,17 @@ func TestDisconnectThing(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.MFKey = uid.String()
c.MFThing = uid.String()
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
saved, err := repo.Save(context.Background(), c, channels)
assert.Nil(t, err, fmt.Sprintf("Saving config expected to succeed: %s.\n", err))
err = repo.DisconnectThing(context.Background(), c.MFChannels[0].ID, saved)
err = repo.DisconnectThing(context.Background(), c.Channels[0].ID, saved)
assert.Nil(t, err, fmt.Sprintf("Retrieving config expected to succeed: %s.\n", err))
cfg, err := repo.RetrieveByID(context.Background(), c.Owner, c.MFThing)
cfg, err := repo.RetrieveByID(context.Background(), c.Owner, c.ThingID)
assert.Nil(t, err, fmt.Sprintf("Retrieving config expected to succeed: %s.\n", err))
assert.Equal(t, cfg.State, bootstrap.Inactive, fmt.Sprintf("expected ti be inactive when a connection is removed from %s", cfg))
}
+7 -7
View File
@@ -16,9 +16,9 @@ import (
// This is used as a response from ConfigReader and can easily be
// replace with any other response format.
type bootstrapRes struct {
MFThing string `json:"mainflux_id"`
MFKey string `json:"mainflux_key"`
MFChannels []channelRes `json:"mainflux_channels"`
ThingID string `json:"thing_id"`
ThingKey string `json:"thing_key"`
Channels []channelRes `json:"channels"`
Content string `json:"content,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
@@ -55,14 +55,14 @@ func NewConfigReader(encKey []byte) ConfigReader {
func (r reader) ReadConfig(cfg Config, secure bool) (interface{}, error) {
var channels []channelRes
for _, ch := range cfg.MFChannels {
for _, ch := range cfg.Channels {
channels = append(channels, channelRes{ID: ch.ID, Name: ch.Name, Metadata: ch.Metadata})
}
res := bootstrapRes{
MFKey: cfg.MFKey,
MFThing: cfg.MFThing,
MFChannels: channels,
ThingKey: cfg.ThingKey,
ThingID: cfg.ThingID,
Channels: channels,
Content: cfg.Content,
ClientCert: cfg.ClientCert,
ClientKey: cfg.ClientKey,
+9 -9
View File
@@ -24,9 +24,9 @@ type readChan struct {
}
type readResp struct {
MFThing string `json:"mainflux_id"`
MFKey string `json:"mainflux_key"`
MFChannels []readChan `json:"mainflux_channels"`
ThingID string `json:"thing_id"`
ThingKey string `json:"thing_key"`
Channels []readChan `json:"channels"`
Content string `json:"content,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
@@ -50,12 +50,12 @@ func dec(in []byte) ([]byte, error) {
func TestReadConfig(t *testing.T) {
cfg := bootstrap.Config{
MFThing: "mf_id",
ThingID: "mf_id",
ClientCert: "client_cert",
ClientKey: "client_key",
CACert: "ca_cert",
MFKey: "mf_key",
MFChannels: []bootstrap.Channel{
ThingKey: "mf_key",
Channels: []bootstrap.Channel{
{
ID: "mf_id",
Name: "mf_name",
@@ -65,9 +65,9 @@ func TestReadConfig(t *testing.T) {
Content: "content",
}
ret := readResp{
MFThing: "mf_id",
MFKey: "mf_key",
MFChannels: []readChan{
ThingID: "mf_id",
ThingKey: "mf_key",
Channels: []readChan{
{
ID: "mf_id",
Name: "mf_name",
+10 -10
View File
@@ -57,8 +57,8 @@ func (ce configEvent) encode() (map[string]interface{}, error) {
"state": ce.State.String(),
"operation": ce.operation,
}
if ce.MFThing != "" {
val["mainflux_thing"] = ce.MFThing
if ce.ThingID != "" {
val["thing_id"] = ce.ThingID
}
if ce.Content != "" {
val["content"] = ce.Content
@@ -72,9 +72,9 @@ func (ce configEvent) encode() (map[string]interface{}, error) {
if ce.ExternalID != "" {
val["external_id"] = ce.ExternalID
}
if len(ce.MFChannels) > 0 {
channels := make([]string, len(ce.MFChannels))
for i, ch := range ce.MFChannels {
if len(ce.Channels) > 0 {
channels := make([]string, len(ce.Channels))
for i, ch := range ce.Channels {
channels[i] = ch.ID
}
val["channels"] = fmt.Sprintf("[%s]", strings.Join(channels, ", "))
@@ -152,8 +152,8 @@ func (be bootstrapEvent) encode() (map[string]interface{}, error) {
"operation": thingBootstrap,
}
if be.MFThing != "" {
val["mainflux_thing"] = be.MFThing
if be.ThingID != "" {
val["thing_id"] = be.ThingID
}
if be.Content != "" {
val["content"] = be.Content
@@ -167,9 +167,9 @@ func (be bootstrapEvent) encode() (map[string]interface{}, error) {
if be.ExternalID != "" {
val["external_id"] = be.ExternalID
}
if len(be.MFChannels) > 0 {
channels := make([]string, len(be.MFChannels))
for i, ch := range be.MFChannels {
if len(be.Channels) > 0 {
channels := make([]string, len(be.Channels))
for i, ch := range be.Channels {
channels[i] = ch.ID
}
val["channels"] = fmt.Sprintf("[%s]", strings.Join(channels, ", "))
+5 -4
View File
@@ -77,9 +77,10 @@ func (es eventStore) Update(ctx context.Context, token string, cfg bootstrap.Con
return es.add(ctx, ev)
}
func (es eventStore) UpdateCert(ctx context.Context, token, thingKey, clientCert, clientKey, caCert string) error {
if err := es.svc.UpdateCert(ctx, token, thingKey, clientCert, clientKey, caCert); err != nil {
return err
func (es eventStore) UpdateCert(ctx context.Context, token, thingKey, clientCert, clientKey, caCert string) (bootstrap.Config, error) {
cfg, err := es.svc.UpdateCert(ctx, token, thingKey, clientCert, clientKey, caCert)
if err != nil {
return bootstrap.Config{}, err
}
ev := updateCertEvent{
@@ -89,7 +90,7 @@ func (es eventStore) UpdateCert(ctx context.Context, token, thingKey, clientCert
caCert: caCert,
}
return es.add(ctx, ev)
return cfg, es.add(ctx, ev)
}
func (es eventStore) UpdateConnections(ctx context.Context, token, id string, connections []string) error {
+26 -26
View File
@@ -66,7 +66,7 @@ var (
config = bootstrap.Config{
ExternalID: "external_id",
ExternalKey: "external_key",
MFChannels: []bootstrap.Channel{channel},
Channels: []bootstrap.Channel{channel},
Content: "config",
}
)
@@ -119,13 +119,13 @@ func TestAdd(t *testing.T) {
svc = producer.NewEventStoreMiddleware(svc, redisClient)
var channels []string
for _, ch := range config.MFChannels {
for _, ch := range config.Channels {
channels = append(channels, ch.ID)
}
invalidConfig := config
invalidConfig.MFChannels = []bootstrap.Channel{{ID: "empty"}}
invalidConfig.MFChannels = []bootstrap.Channel{{ID: "empty"}}
invalidConfig.Channels = []bootstrap.Channel{{ID: "empty"}}
invalidConfig.Channels = []bootstrap.Channel{{ID: "empty"}}
cases := []struct {
desc string
@@ -188,10 +188,10 @@ func TestView(t *testing.T) {
saved, err := svc.Add(context.Background(), validToken, config)
assert.Nil(t, err, fmt.Sprintf("Saving config expected to succeed: %s.\n", err))
svcConfig, svcErr := svc.View(context.Background(), validToken, saved.MFThing)
svcConfig, svcErr := svc.View(context.Background(), validToken, saved.ThingID)
svc = producer.NewEventStoreMiddleware(svc, redisClient)
esConfig, esErr := svc.View(context.Background(), validToken, saved.MFThing)
esConfig, esErr := svc.View(context.Background(), validToken, saved.ThingID)
assert.Equal(t, svcConfig, esConfig, fmt.Sprintf("event sourcing changed service behavior: expected %v got %v", svcConfig, esConfig))
assert.Equal(t, svcErr, esErr, fmt.Sprintf("event sourcing changed service behavior: expected %v got %v", svcErr, esErr))
@@ -210,7 +210,7 @@ func TestUpdate(t *testing.T) {
ch := channel
ch.ID = "2"
c.MFChannels = append(c.MFChannels, ch)
c.Channels = append(c.Channels, ch)
saved, err := svc.Add(context.Background(), validToken, c)
require.Nil(t, err, fmt.Sprintf("Saving config expected to succeed: %s.\n", err))
@@ -222,7 +222,7 @@ func TestUpdate(t *testing.T) {
modified.Name = "new name"
nonExisting := config
nonExisting.MFThing = "unknown"
nonExisting.ThingID = "unknown"
cases := []struct {
desc string
@@ -237,15 +237,15 @@ func TestUpdate(t *testing.T) {
token: validToken,
err: nil,
event: map[string]interface{}{
"name": modified.Name,
"content": modified.Content,
"timestamp": time.Now().Unix(),
"operation": configUpdate,
"channels": "[1, 2]",
"external_id": "external_id",
"mainflux_thing": "1",
"owner": email,
"state": "0",
"name": modified.Name,
"content": modified.Content,
"timestamp": time.Now().Unix(),
"operation": configUpdate,
"channels": "[1, 2]",
"external_id": "external_id",
"thing_id": "1",
"owner": email,
"state": "0",
},
},
{
@@ -304,12 +304,12 @@ func TestUpdateConnections(t *testing.T) {
}{
{
desc: "update connections successfully",
id: saved.MFThing,
id: saved.ThingID,
token: validToken,
connections: []string{"2"},
err: nil,
event: map[string]interface{}{
"thing_id": saved.MFThing,
"thing_id": saved.ThingID,
"channels": "2",
"timestamp": time.Now().Unix(),
"operation": thingUpdateConnections,
@@ -317,7 +317,7 @@ func TestUpdateConnections(t *testing.T) {
},
{
desc: "update connections unsuccessfully",
id: saved.MFThing,
id: saved.ThingID,
token: validToken,
connections: []string{"256"},
err: errors.ErrMalformedEntity,
@@ -388,18 +388,18 @@ func TestRemove(t *testing.T) {
}{
{
desc: "remove config successfully",
id: saved.MFThing,
id: saved.ThingID,
token: validToken,
err: nil,
event: map[string]interface{}{
"thing_id": saved.MFThing,
"thing_id": saved.ThingID,
"timestamp": time.Now().Unix(),
"operation": configRemove,
},
},
{
desc: "remove config with invalid credentials",
id: saved.MFThing,
id: saved.ThingID,
token: "",
err: errors.ErrAuthentication,
event: nil,
@@ -522,12 +522,12 @@ func TestChangeState(t *testing.T) {
}{
{
desc: "change state to active",
id: saved.MFThing,
id: saved.ThingID,
token: validToken,
state: bootstrap.Active,
err: nil,
event: map[string]interface{}{
"thing_id": saved.MFThing,
"thing_id": saved.ThingID,
"state": bootstrap.Active.String(),
"timestamp": time.Now().Unix(),
"operation": thingStateChange,
@@ -535,7 +535,7 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state invalid credentials",
id: saved.MFThing,
id: saved.ThingID,
token: "",
state: bootstrap.Inactive,
err: errors.ErrAuthentication,
+22 -21
View File
@@ -60,7 +60,7 @@ type Service interface {
// UpdateCert updates an existing Config certificate and token.
// A non-nil error is returned to indicate operation failure.
UpdateCert(ctx context.Context, token, thingID, clientCert, clientKey, caCert string) error
UpdateCert(ctx context.Context, token, thingID, clientCert, clientKey, caCert string) (Config, error)
// UpdateConnections updates list of Channels related to given Config.
UpdateConnections(ctx context.Context, token, id string, connections []string) error
@@ -125,7 +125,7 @@ func (bs bootstrapService) Add(ctx context.Context, token string, cfg Config) (C
return Config{}, err
}
toConnect := bs.toIDList(cfg.MFChannels)
toConnect := bs.toIDList(cfg.Channels)
// Check if channels exist. This is the way to prevent fetching channels that already exist.
existing, err := bs.configs.ListExisting(ctx, owner, toConnect)
@@ -133,35 +133,35 @@ func (bs bootstrapService) Add(ctx context.Context, token string, cfg Config) (C
return Config{}, errors.Wrap(errCheckChannels, err)
}
cfg.MFChannels, err = bs.connectionChannels(toConnect, bs.toIDList(existing), token)
cfg.Channels, err = bs.connectionChannels(toConnect, bs.toIDList(existing), token)
if err != nil {
return Config{}, errors.Wrap(errConnectionChannels, err)
}
id := cfg.MFThing
id := cfg.ThingID
mfThing, err := bs.thing(id, token)
if err != nil {
return Config{}, errors.Wrap(errThingNotFound, err)
}
cfg.MFThing = mfThing.ID
cfg.ThingID = mfThing.ID
cfg.Owner = owner
cfg.State = Inactive
cfg.MFKey = mfThing.Credentials.Secret
cfg.ThingKey = mfThing.Credentials.Secret
saved, err := bs.configs.Save(ctx, cfg, toConnect)
if err != nil {
if id == "" {
if _, errT := bs.sdk.DisableThing(cfg.MFThing, token); errT != nil {
if _, errT := bs.sdk.DisableThing(cfg.ThingID, token); errT != nil {
err = errors.Wrap(err, errT)
}
}
return Config{}, errors.Wrap(errAddBootstrap, err)
}
cfg.MFThing = saved
cfg.MFChannels = append(cfg.MFChannels, existing...)
cfg.ThingID = saved
cfg.Channels = append(cfg.Channels, existing...)
return cfg, nil
}
@@ -186,15 +186,16 @@ func (bs bootstrapService) Update(ctx context.Context, token string, cfg Config)
return bs.configs.Update(ctx, cfg)
}
func (bs bootstrapService) UpdateCert(ctx context.Context, token, thingID, clientCert, clientKey, caCert string) error {
func (bs bootstrapService) UpdateCert(ctx context.Context, token, thingID, clientCert, clientKey, caCert string) (Config, error) {
owner, err := bs.identify(ctx, token)
if err != nil {
return err
return Config{}, err
}
if err := bs.configs.UpdateCert(ctx, owner, thingID, clientCert, clientKey, caCert); err != nil {
return errors.Wrap(errUpdateCert, err)
cfg, err := bs.configs.UpdateCert(ctx, owner, thingID, clientCert, clientKey, caCert)
if err != nil {
return Config{}, errors.Wrap(errUpdateCert, err)
}
return nil
return cfg, nil
}
func (bs bootstrapService) UpdateConnections(ctx context.Context, token, id string, connections []string) error {
@@ -221,7 +222,7 @@ func (bs bootstrapService) UpdateConnections(ctx context.Context, token, id stri
return errors.Wrap(errUpdateConnections, err)
}
cfg.MFChannels = channels
cfg.Channels = channels
var connect, disconnect []string
if cfg.State == Active {
@@ -307,18 +308,18 @@ func (bs bootstrapService) ChangeState(ctx context.Context, token, id string, st
switch state {
case Active:
for _, c := range cfg.MFChannels {
for _, c := range cfg.Channels {
conIDs := mfsdk.ConnectionIDs{
ChannelIDs: []string{c.ID},
ThingIDs: []string{cfg.MFThing},
ThingIDs: []string{cfg.ThingID},
}
if err := bs.sdk.Connect(conIDs, token); err != nil {
return ErrThings
}
}
case Inactive:
for _, c := range cfg.MFChannels {
if err := bs.sdk.DisconnectThing(cfg.MFThing, c.ID, token); err != nil {
for _, c := range cfg.Channels {
if err := bs.sdk.DisconnectThing(cfg.ThingID, c.ID, token); err != nil {
if errors.Contains(err, errors.ErrNotFound) {
continue
}
@@ -433,8 +434,8 @@ func (bs bootstrapService) connectionChannels(channels, existing []string, token
// 2) IDs of Channels to be removed
// 3) IDs of common Channels for these two configs.
func (bs bootstrapService) updateList(cfg Config, connections []string) (add, remove []string) {
disconnect := make(map[string]bool, len(cfg.MFChannels))
for _, c := range cfg.MFChannels {
disconnect := make(map[string]bool, len(cfg.Channels))
for _, c := range cfg.Channels {
disconnect[c.ID] = true
}
+74 -50
View File
@@ -12,6 +12,7 @@ import (
"fmt"
"io"
"net/http/httptest"
"sort"
"strconv"
"testing"
@@ -57,7 +58,7 @@ var (
config = bootstrap.Config{
ExternalID: "external_id",
ExternalKey: "external_key",
MFChannels: []bootstrap.Channel{channel},
Channels: []bootstrap.Channel{channel},
Content: "config",
}
)
@@ -122,12 +123,12 @@ func TestAdd(t *testing.T) {
svc := newService(users, server.URL)
neID := config
neID.MFThing = "non-existent"
neID.ThingID = "non-existent"
wrongChannels := config
ch := channel
ch.ID = "invalid"
wrongChannels.MFChannels = append(wrongChannels.MFChannels, ch)
wrongChannels.Channels = append(wrongChannels.Channels, ch)
cases := []struct {
desc string
@@ -184,7 +185,7 @@ func TestView(t *testing.T) {
}{
{
desc: "view an existing config",
id: saved.MFThing,
id: saved.ThingID,
token: validToken,
err: nil,
},
@@ -196,7 +197,7 @@ func TestView(t *testing.T) {
},
{
desc: "view a config with wrong credentials",
id: config.MFThing,
id: config.ThingID,
token: invalidToken,
err: errors.ErrAuthentication,
},
@@ -217,7 +218,7 @@ func TestUpdate(t *testing.T) {
ch := channel
ch.ID = "2"
c.MFChannels = append(c.MFChannels, ch)
c.Channels = append(c.Channels, ch)
saved, err := svc.Add(context.Background(), validToken, c)
assert.Nil(t, err, fmt.Sprintf("Saving config expected to succeed: %s.\n", err))
@@ -226,7 +227,7 @@ func TestUpdate(t *testing.T) {
modifiedCreated.Name = "new name"
nonExisting := config
nonExisting.MFThing = unknown
nonExisting.ThingID = unknown
cases := []struct {
desc string
@@ -269,52 +270,75 @@ func TestUpdateCert(t *testing.T) {
ch := channel
ch.ID = "2"
c.MFChannels = append(c.MFChannels, ch)
c.Channels = append(c.Channels, ch)
saved, err := svc.Add(context.Background(), validToken, c)
assert.Nil(t, err, fmt.Sprintf("Saving config expected to succeed: %s.\n", err))
cases := []struct {
desc string
token string
thingKey string
clientCert string
clientKey string
caCert string
err error
desc string
token string
thingKey string
clientCert string
clientKey string
caCert string
expectedConfig bootstrap.Config
err error
}{
{
desc: "update certs for the valid config",
thingKey: saved.MFKey,
thingKey: saved.ThingKey,
clientCert: "newCert",
clientKey: "newKey",
caCert: "newCert",
token: validToken,
err: nil,
expectedConfig: bootstrap.Config{
Name: saved.Name,
ThingKey: saved.ThingKey,
Channels: saved.Channels,
ExternalID: saved.ExternalID,
ExternalKey: saved.ExternalKey,
Content: saved.Content,
State: saved.State,
Owner: saved.Owner,
ThingID: saved.ThingID,
ClientCert: "newCert",
CACert: "newCert",
ClientKey: "newKey",
},
err: nil,
},
{
desc: "update cert for a non-existing config",
thingKey: "empty",
clientCert: "newCert",
clientKey: "newKey",
caCert: "newCert",
token: validToken,
err: errors.ErrNotFound,
desc: "update cert for a non-existing config",
thingKey: "empty",
clientCert: "newCert",
clientKey: "newKey",
caCert: "newCert",
token: validToken,
expectedConfig: bootstrap.Config{},
err: errors.ErrNotFound,
},
{
desc: "update config cert with wrong credentials",
thingKey: saved.MFKey,
clientCert: "newCert",
clientKey: "newKey",
caCert: "newCert",
token: invalidToken,
err: errors.ErrAuthentication,
desc: "update config cert with wrong credentials",
thingKey: saved.ThingKey,
clientCert: "newCert",
clientKey: "newKey",
caCert: "newCert",
token: invalidToken,
expectedConfig: bootstrap.Config{},
err: errors.ErrAuthentication,
},
}
for _, tc := range cases {
err := svc.UpdateCert(context.Background(), tc.token, tc.thingKey, tc.clientCert, tc.clientKey, tc.caCert)
cfg, err := svc.UpdateCert(context.Background(), tc.token, tc.thingKey, tc.clientCert, tc.clientKey, tc.caCert)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
sort.Slice(cfg.Channels, func(i, j int) bool {
return cfg.Channels[i].ID < cfg.Channels[j].ID
})
sort.Slice(tc.expectedConfig.Channels, func(i, j int) bool {
return tc.expectedConfig.Channels[i].ID < tc.expectedConfig.Channels[j].ID
})
assert.Equal(t, tc.expectedConfig, cfg, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.expectedConfig, cfg))
}
}
@@ -327,7 +351,7 @@ func TestUpdateConnections(t *testing.T) {
ch := channel
ch.ID = "2"
c.MFChannels = append(c.MFChannels, ch)
c.Channels = append(c.Channels, ch)
created, err := svc.Add(context.Background(), validToken, c)
assert.Nil(t, err, fmt.Sprintf("Saving config expected to succeed: %s.\n", err))
@@ -336,11 +360,11 @@ func TestUpdateConnections(t *testing.T) {
c.ExternalID = externalID.String()
active, err := svc.Add(context.Background(), validToken, c)
assert.Nil(t, err, fmt.Sprintf("Saving config expected to succeed: %s.\n", err))
err = svc.ChangeState(context.Background(), validToken, active.MFThing, bootstrap.Active)
err = svc.ChangeState(context.Background(), validToken, active.ThingID, bootstrap.Active)
assert.Nil(t, err, fmt.Sprintf("Changing state expected to succeed: %s.\n", err))
nonExisting := config
nonExisting.MFThing = unknown
nonExisting.ThingID = unknown
cases := []struct {
desc string
@@ -352,14 +376,14 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections for config with state Inactive",
token: validToken,
id: created.MFThing,
id: created.ThingID,
connections: []string{"2"},
err: nil,
},
{
desc: "update connections for config with state Active",
token: validToken,
id: active.MFThing,
id: active.ThingID,
connections: []string{"3"},
err: nil,
},
@@ -373,14 +397,14 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections with invalid channels",
token: validToken,
id: created.MFThing,
id: created.ThingID,
connections: []string{"wrong"},
err: errors.ErrMalformedEntity,
},
{
desc: "update connections a config with wrong credentials",
token: invalidToken,
id: created.MFKey,
id: created.ThingKey,
connections: []string{"2", "3"},
err: errors.ErrAuthentication,
},
@@ -517,19 +541,19 @@ func TestRemove(t *testing.T) {
}{
{
desc: "view a config with wrong credentials",
id: saved.MFThing,
id: saved.ThingID,
token: invalidToken,
err: errors.ErrAuthentication,
},
{
desc: "remove an existing config",
id: saved.MFThing,
id: saved.ThingID,
token: validToken,
err: nil,
},
{
desc: "remove removed config",
id: saved.MFThing,
id: saved.ThingID,
token: validToken,
err: nil,
},
@@ -627,7 +651,7 @@ func TestChangeState(t *testing.T) {
{
desc: "change state with wrong credentials",
state: bootstrap.Active,
id: saved.MFThing,
id: saved.ThingID,
token: invalidToken,
err: errors.ErrAuthentication,
},
@@ -641,21 +665,21 @@ func TestChangeState(t *testing.T) {
{
desc: "change state to Active",
state: bootstrap.Active,
id: saved.MFThing,
id: saved.ThingID,
token: validToken,
err: nil,
},
{
desc: "change state to current state",
state: bootstrap.Active,
id: saved.MFThing,
id: saved.ThingID,
token: validToken,
err: nil,
},
{
desc: "change state to Inactive",
state: bootstrap.Inactive,
id: saved.MFThing,
id: saved.ThingID,
token: validToken,
err: nil,
},
@@ -752,7 +776,7 @@ func TestRemoveCoinfigHandler(t *testing.T) {
}{
{
desc: "remove an existing config",
id: saved.MFThing,
id: saved.ThingID,
err: nil,
},
{
@@ -786,13 +810,13 @@ func TestDisconnectThingsHandler(t *testing.T) {
{
desc: "disconnect",
channelID: channel.ID,
thingID: saved.MFThing,
thingID: saved.ThingID,
err: nil,
},
{
desc: "disconnect disconnected",
channelID: channel.ID,
thingID: saved.MFThing,
thingID: saved.ThingID,
err: nil,
},
}
+3 -3
View File
@@ -26,7 +26,7 @@ func New(svc bootstrap.Service, tracer trace.Tracer) bootstrap.Service {
// Add traces the "Add" operation of the wrapped bootstrap.Service.
func (tm *tracingMiddleware) Add(ctx context.Context, token string, cfg bootstrap.Config) (bootstrap.Config, error) {
ctx, span := tm.tracer.Start(ctx, "svc_register_client", trace.WithAttributes(
attribute.String("mainflux_thing", cfg.MFThing),
attribute.String("thing_id", cfg.ThingID),
attribute.String("owner", cfg.Owner),
attribute.String("name", cfg.Name),
attribute.String("external_id", cfg.ExternalID),
@@ -53,7 +53,7 @@ func (tm *tracingMiddleware) Update(ctx context.Context, token string, cfg boots
ctx, span := tm.tracer.Start(ctx, "svc_update_client", trace.WithAttributes(
attribute.String("name", cfg.Name),
attribute.String("content", cfg.Content),
attribute.String("mainflux_thing", cfg.MFThing),
attribute.String("thing_id", cfg.ThingID),
attribute.String("owner", cfg.Owner),
))
defer span.End()
@@ -62,7 +62,7 @@ func (tm *tracingMiddleware) Update(ctx context.Context, token string, cfg boots
}
// UpdateCert traces the "UpdateCert" operation of the wrapped bootstrap.Service.
func (tm *tracingMiddleware) UpdateCert(ctx context.Context, token, thingID, clientCert, clientKey, caCert string) error {
func (tm *tracingMiddleware) UpdateCert(ctx context.Context, token, thingID, clientCert, clientKey, caCert string) (bootstrap.Config, error) {
ctx, span := tm.tracer.Start(ctx, "svc_update_cert", trace.WithAttributes(
attribute.String("thing_id", thingID),
))
+1 -1
View File
@@ -343,7 +343,7 @@ mainflux-cli bootstrap get <thing_id> <user_token> -b <bootstrap-url>
#### Update configuration
```bash
mainflux-cli bootstrap update '{"mainflux_id":"<thing_id>", "name": "newName", "content": "newContent"}' <user_token> -b <bootstrap-url>
mainflux-cli bootstrap update '{"thing_id":"<thing_id>", "name": "newName", "content": "newContent"}' <user_token> -b <bootstrap-url>
```
#### Remove configuration
+3 -2
View File
@@ -115,12 +115,13 @@ var cmdBootstrap = []cobra.Command{
return
}
if args[0] == "certs" {
if err := sdk.UpdateBootstrapCerts(args[0], args[1], args[2], args[3], args[4]); err != nil {
cfg, err := sdk.UpdateBootstrapCerts(args[0], args[1], args[2], args[3], args[4])
if err != nil {
logError(err)
return
}
logOK()
logJSON(cfg)
return
}
logUsage(cmd.Use)
+72 -25
View File
@@ -27,25 +27,66 @@ const (
// MFKey is key of corresponding Mainflux Thing.
// MFChannels is a list of Mainflux Channels corresponding Mainflux Thing connects to.
type BootstrapConfig struct {
ThingID string `json:"thing_id,omitempty"`
Channels []string `json:"channels,omitempty"`
ExternalID string `json:"external_id,omitempty"`
ExternalKey string `json:"external_key,omitempty"`
MFThing string `json:"mainflux_id,omitempty"`
MFChannels []Channel `json:"mainflux_channels,omitempty"`
MFKey string `json:"mainflux_key,omitempty"`
Name string `json:"name,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
CACert string `json:"ca_cert,omitempty"`
Content string `json:"content,omitempty"`
State int `json:"state,omitempty"`
Channels interface{} `json:"channels,omitempty"`
ExternalID string `json:"external_id,omitempty"`
ExternalKey string `json:"external_key,omitempty"`
ThingID string `json:"thing_id,omitempty"`
ThingKey string `json:"thing_key,omitempty"`
Name string `json:"name,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
CACert string `json:"ca_cert,omitempty"`
Content string `json:"content,omitempty"`
State int `json:"state,omitempty"`
}
type ConfigUpdateCertReq struct {
ClientCert string `json:"client_cert"`
ClientKey string `json:"client_key"`
CACert string `json:"ca_cert"`
func (ts *BootstrapConfig) UnmarshalJSON(data []byte) error {
var rawData map[string]json.RawMessage
if err := json.Unmarshal(data, &rawData); err != nil {
return err
}
if channelData, ok := rawData["channels"]; ok {
var stringData []string
if err := json.Unmarshal(channelData, &stringData); err == nil {
ts.Channels = stringData
} else {
var channels []Channel
if err := json.Unmarshal(channelData, &channels); err == nil {
ts.Channels = channels
} else {
return fmt.Errorf("unsupported channel data type")
}
}
}
if err := json.Unmarshal(data, &struct {
ExternalID *string `json:"external_id,omitempty"`
ExternalKey *string `json:"external_key,omitempty"`
ThingID *string `json:"thing_id,omitempty"`
ThingKey *string `json:"thing_key,omitempty"`
Name *string `json:"name,omitempty"`
ClientCert *string `json:"client_cert,omitempty"`
ClientKey *string `json:"client_key,omitempty"`
CACert *string `json:"ca_cert,omitempty"`
Content *string `json:"content,omitempty"`
State *int `json:"state,omitempty"`
}{
ExternalID: &ts.ExternalID,
ExternalKey: &ts.ExternalKey,
ThingID: &ts.ThingID,
ThingKey: &ts.ThingKey,
Name: &ts.Name,
ClientCert: &ts.ClientCert,
ClientKey: &ts.ClientKey,
CACert: &ts.CACert,
Content: &ts.Content,
State: &ts.State,
}); err != nil {
return err
}
return nil
}
func (sdk mfSDK) AddBootstrap(cfg BootstrapConfig, token string) (string, errors.SDKError) {
@@ -91,11 +132,11 @@ func (sdk mfSDK) Whitelist(cfg BootstrapConfig, token string) errors.SDKError {
return errors.NewSDKError(err)
}
if cfg.MFThing == "" {
if cfg.ThingID == "" {
return errors.NewSDKError(errors.ErrNotFoundParam)
}
url := fmt.Sprintf("%s/%s/%s", sdk.bootstrapURL, whitelistEndpoint, cfg.MFThing)
url := fmt.Sprintf("%s/%s/%s", sdk.bootstrapURL, whitelistEndpoint, cfg.ThingID)
_, _, sdkerr := sdk.processRequest(http.MethodPut, url, token, string(CTJSON), data, http.StatusCreated, http.StatusOK)
@@ -123,15 +164,15 @@ func (sdk mfSDK) UpdateBootstrap(cfg BootstrapConfig, token string) errors.SDKEr
return errors.NewSDKError(err)
}
url := fmt.Sprintf("%s/%s/%s", sdk.bootstrapURL, configsEndpoint, cfg.MFThing)
url := fmt.Sprintf("%s/%s/%s", sdk.bootstrapURL, configsEndpoint, cfg.ThingID)
_, _, sdkerr := sdk.processRequest(http.MethodPut, url, token, string(CTJSON), data, http.StatusOK)
return sdkerr
}
func (sdk mfSDK) UpdateBootstrapCerts(id, clientCert, clientKey, ca, token string) errors.SDKError {
func (sdk mfSDK) UpdateBootstrapCerts(id, clientCert, clientKey, ca, token string) (BootstrapConfig, errors.SDKError) {
url := fmt.Sprintf("%s/%s/%s", sdk.bootstrapURL, bootstrapCertsEndpoint, id)
request := ConfigUpdateCertReq{
request := BootstrapConfig{
ClientCert: clientCert,
ClientKey: clientKey,
CACert: ca,
@@ -139,11 +180,17 @@ func (sdk mfSDK) UpdateBootstrapCerts(id, clientCert, clientKey, ca, token strin
data, err := json.Marshal(request)
if err != nil {
return errors.NewSDKError(err)
return BootstrapConfig{}, errors.NewSDKError(err)
}
_, _, sdkerr := sdk.processRequest(http.MethodPatch, url, token, string(CTJSON), data, http.StatusOK)
return sdkerr
_, body, sdkerr := sdk.processRequest(http.MethodPatch, url, token, string(CTJSON), data, http.StatusOK)
var bc BootstrapConfig
if err := json.Unmarshal(body, &bc); err != nil {
return BootstrapConfig{}, errors.NewSDKError(err)
}
return bc, sdkerr
}
func (sdk mfSDK) UpdateBootstrapConnection(id string, channels []string, token string) errors.SDKError {
+1 -1
View File
@@ -837,7 +837,7 @@ type SDK interface {
// example:
// err := sdk.UpdateBootstrapCerts("id", "clientCert", "clientKey", "ca", "token")
// fmt.Println(err)
UpdateBootstrapCerts(id string, clientCert, clientKey, ca string, token string) errors.SDKError
UpdateBootstrapCerts(id string, clientCert, clientKey, ca string, token string) (BootstrapConfig, errors.SDKError)
// UpdateBootstrapConnection updates connections performs update of the channel list corresponding Thing is connected to.
//
+6 -6
View File
@@ -226,7 +226,7 @@ func (ps *provisionService) Provision(token, name, externalID, externalKey strin
res.CACert = ""
if needsBootstrap(thing) {
if err = ps.sdk.UpdateBootstrapCerts(bsConfig.MFThing, cert.ClientCert, cert.ClientKey, "", token); err != nil {
if _, err = ps.sdk.UpdateBootstrapCerts(bsConfig.ThingID, cert.ClientCert, cert.ClientKey, "", token); err != nil {
return Result{}, errors.Wrap(ErrFailedCertCreation, err)
}
}
@@ -234,7 +234,7 @@ func (ps *provisionService) Provision(token, name, externalID, externalKey strin
if ps.conf.Bootstrap.AutoWhiteList {
wlReq := SDK.BootstrapConfig{
MFThing: thing.ID,
ThingID: thing.ID,
State: Active,
}
if err := ps.sdk.Whitelist(wlReq, token); err != nil {
@@ -310,10 +310,10 @@ func (ps *provisionService) updateGateway(token string, bs SDK.BootstrapConfig,
}
gw.ExternalID = bs.ExternalID
gw.ExternalKey = bs.ExternalKey
gw.CfgID = bs.MFThing
gw.CfgID = bs.ThingID
gw.Type = gateway
th, sdkerr := ps.sdk.Thing(bs.MFThing, token)
th, sdkerr := ps.sdk.Thing(bs.ThingID, token)
if sdkerr != nil {
return errors.Wrap(ErrGatewayUpdate, sdkerr)
}
@@ -383,7 +383,7 @@ func (ps *provisionService) recover(e *error, ths *[]SDK.Thing, chs *[]SDK.Chann
if needsBootstrap(th) {
bs, err := ps.sdk.ViewBootstrap(th.ID, token)
ps.errLog(errors.Wrap(ErrFailedBootstrapRetrieval, err))
ps.errLog(ps.sdk.RemoveBootstrap(bs.MFThing, token))
ps.errLog(ps.sdk.RemoveBootstrap(bs.ThingID, token))
}
}
}
@@ -398,7 +398,7 @@ func (ps *provisionService) recover(e *error, ths *[]SDK.Thing, chs *[]SDK.Chann
if needsBootstrap(th) {
bs, err := ps.sdk.ViewBootstrap(th.ID, token)
ps.errLog(errors.Wrap(ErrFailedBootstrapRetrieval, err))
ps.errLog(ps.sdk.RemoveBootstrap(bs.MFThing, token))
ps.errLog(ps.sdk.RemoveBootstrap(bs.ThingID, token))
}
}
return