1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-04-27 13:48:49 +08:00

MF-862 - Add boostrap CRUD to SDK and CLI (#1114)

* MF-862 - Add boostrap CRUD to SDK and CLI

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Revert ReaderPrefix

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix reviews

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix typo

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
This commit is contained in:
Manuel Imperiale 2020-04-15 17:38:20 +02:00 committed by GitHub
parent b8bd011f9d
commit 4c970a8079
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 354 additions and 0 deletions

137
cli/bootstrap.go Normal file
View File

@ -0,0 +1,137 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
package cli
import (
"encoding/json"
mfxsdk "github.com/mainflux/mainflux/sdk/go"
"github.com/spf13/cobra"
)
var cmdBootstrap = []cobra.Command{
cobra.Command{
Use: "add",
Short: "add <JSON_config> <user_auth_token>",
Long: `Adds new Thing Bootstrap Config to the user identified by the provided key`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 2 {
logUsage(cmd.Short)
return
}
var cfg mfxsdk.BoostrapConfig
if err := json.Unmarshal([]byte(args[0]), &cfg); err != nil {
logError(err)
return
}
id, err := sdk.AddBootstrap(args[1], cfg)
if err != nil {
logError(err)
return
}
logCreated(id)
},
},
cobra.Command{
Use: "view",
Short: "view <thing_id> <user_auth_token>",
Long: `Returns Thing Config with given ID belonging to the user identified by the given key`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 2 {
logUsage(cmd.Short)
return
}
c, err := sdk.ViewBoostrap(args[1], args[0])
if err != nil {
logError(err)
return
}
logJSON(c)
},
},
cobra.Command{
Use: "update",
Short: "update <JSON_config> <user_auth_token>",
Long: `Updates editable fields of the provided Config`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 2 {
logUsage(cmd.Short)
return
}
var cfg mfxsdk.BoostrapConfig
if err := json.Unmarshal([]byte(args[0]), &cfg); err != nil {
logError(err)
return
}
if err := sdk.UpdateBoostrap(args[1], cfg); err != nil {
logError(err)
return
}
logOK()
},
},
cobra.Command{
Use: "remove",
Short: "remove <thing_id> <user_auth_token>",
Long: `Removes Config with specified key that belongs to the user identified by the given key`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 2 {
logUsage(cmd.Short)
return
}
if err := sdk.RemoveBoostrap(args[1], args[0]); err != nil {
logError(err)
return
}
logOK()
},
},
cobra.Command{
Use: "bootstrap",
Short: "bootstrap <external_id> <external_key>",
Long: `Returns Config to the Thing with provided external ID using external key`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 2 {
logUsage(cmd.Short)
return
}
c, err := sdk.Boostrap(args[1], args[0])
if err != nil {
logError(err)
return
}
logJSON(c)
},
},
}
// NewBootstrapCmd returns bootstrap command.
func NewBootstrapCmd() *cobra.Command {
cmd := cobra.Command{
Use: "bootstrap",
Short: "Bootstrap management",
Long: `Bootstrap management: create, get, update or delete Bootstrap config`,
Run: func(cmd *cobra.Command, args []string) {
logUsage("bootstrap [add | view | update | remove | bootstrap]")
},
}
for i := range cmdBootstrap {
cmd.AddCommand(&cmdBootstrap[i])
}
return &cmd
}

View File

@ -16,10 +16,12 @@ func main() {
sdkConf := sdk.Config{
BaseURL: "http://localhost",
ReaderURL: "http://localhost:8905",
BootstrapURL: "http://localhost:8202",
ReaderPrefix: "",
UsersPrefix: "",
ThingsPrefix: "",
HTTPAdapterPrefix: "http",
BootstrapPrefix: "things",
MsgContentType: sdk.ContentType(msgContentType),
TLSVerification: false,
}
@ -41,6 +43,7 @@ func main() {
channelsCmd := cli.NewChannelsCmd()
messagesCmd := cli.NewMessagesCmd()
provisionCmd := cli.NewProvisionCmd()
bootstrapCmd := cli.NewBootstrapCmd()
// Root Commands
rootCmd.AddCommand(versionCmd)
@ -49,6 +52,7 @@ func main() {
rootCmd.AddCommand(channelsCmd)
rootCmd.AddCommand(messagesCmd)
rootCmd.AddCommand(provisionCmd)
rootCmd.AddCommand(bootstrapCmd)
// Root Flags
rootCmd.PersistentFlags().StringVarP(

192
sdk/go/bootstrap.go Normal file
View File

@ -0,0 +1,192 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
package sdk
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"github.com/mainflux/mainflux/errors"
)
const configsEndpoint = "configs"
const bootstrapEndpoint = "bootstrap"
// BoostrapConfig represents Configuration entity. It wraps information about external entity
// as well as info about corresponding Mainflux entities.
// MFThing represents corresponding Mainflux Thing ID.
// MFKey is key of corresponding Mainflux Thing.
// MFChannels is a list of Mainflux Channels corresponding Mainflux Thing connects to.
type BoostrapConfig 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"`
}
func (sdk mfSDK) AddBootstrap(key string, cfg BoostrapConfig) (string, error) {
data, err := json.Marshal(cfg)
if err != nil {
return "", errors.Wrap(ErrInvalidArgs, err)
}
url := createURL(sdk.bootstrapURL, sdk.bootstrapPrefix, configsEndpoint)
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(data))
if err != nil {
return "", err
}
resp, err := sdk.sendRequest(req, key, string(CTJSON))
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
if err := encodeError(resp.StatusCode); err != nil {
return "", err
}
return "", ErrFailedCreation
}
id := strings.TrimPrefix(resp.Header.Get("Location"), "/things/configs/")
return id, nil
}
func (sdk mfSDK) ViewBoostrap(key, id string) (BoostrapConfig, error) {
endpoint := fmt.Sprintf("%s/%s", configsEndpoint, id)
url := createURL(sdk.bootstrapURL, sdk.bootstrapPrefix, endpoint)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return BoostrapConfig{}, err
}
resp, err := sdk.sendRequest(req, key, string(CTJSON))
if err != nil {
return BoostrapConfig{}, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return BoostrapConfig{}, err
}
if resp.StatusCode != http.StatusOK {
if err := encodeError(resp.StatusCode); err != nil {
return BoostrapConfig{}, err
}
return BoostrapConfig{}, ErrFetchFailed
}
var bc BoostrapConfig
if err := json.Unmarshal(body, &bc); err != nil {
return BoostrapConfig{}, err
}
return bc, nil
}
func (sdk mfSDK) UpdateBoostrap(key string, cfg BoostrapConfig) error {
data, err := json.Marshal(cfg)
if err != nil {
return errors.Wrap(ErrInvalidArgs, err)
}
endpoint := fmt.Sprintf("%s/%s", configsEndpoint, cfg.MFThing)
url := createURL(sdk.bootstrapURL, sdk.bootstrapPrefix, endpoint)
req, err := http.NewRequest(http.MethodPut, url, bytes.NewReader(data))
if err != nil {
return err
}
resp, err := sdk.sendRequest(req, key, string(CTJSON))
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
if err := encodeError(resp.StatusCode); err != nil {
return err
}
return ErrFailedUpdate
}
return nil
}
func (sdk mfSDK) RemoveBoostrap(key, id string) error {
endpoint := fmt.Sprintf("%s/%s", configsEndpoint, id)
url := createURL(sdk.bootstrapURL, sdk.bootstrapPrefix, endpoint)
req, err := http.NewRequest(http.MethodDelete, url, nil)
if err != nil {
return err
}
resp, err := sdk.sendRequest(req, key, string(CTJSON))
if err != nil {
return err
}
if resp.StatusCode != http.StatusNoContent {
if err := encodeError(resp.StatusCode); err != nil {
return err
}
return ErrFailedRemoval
}
return nil
}
func (sdk mfSDK) Boostrap(key, id string) (BoostrapConfig, error) {
endpoint := fmt.Sprintf("%s/%s", bootstrapEndpoint, id)
url := createURL(sdk.bootstrapURL, sdk.bootstrapPrefix, endpoint)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return BoostrapConfig{}, err
}
resp, err := sdk.sendRequest(req, key, string(CTJSON))
if err != nil {
return BoostrapConfig{}, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return BoostrapConfig{}, err
}
if resp.StatusCode != http.StatusOK {
if err := encodeError(resp.StatusCode); err != nil {
return BoostrapConfig{}, err
}
return BoostrapConfig{}, ErrFetchFailed
}
var bc BoostrapConfig
if err := json.Unmarshal(body, &bc); err != nil {
return BoostrapConfig{}, err
}
return bc, nil
}

View File

@ -175,16 +175,33 @@ type SDK interface {
// Version returns used mainflux version.
Version() (string, error)
// AddBootstrap add boostrap configuration
AddBootstrap(key string, cfg BoostrapConfig) (string, error)
// View returns Thing Config with given ID belonging to the user identified by the given key.
ViewBoostrap(key, id string) (BoostrapConfig, error)
// Update updates editable fields of the provided Config.
UpdateBoostrap(key string, cfg BoostrapConfig) error
// Remove removes Config with specified key that belongs to the user identified by the given key.
RemoveBoostrap(key, id string) error
// View returns Thing Config with given ID belonging to the user identified by the given key.
Boostrap(key, id string) (BoostrapConfig, error)
}
type mfSDK struct {
baseURL string
readerURL string
bootstrapURL string
readerPrefix string
usersPrefix string
thingsPrefix string
channelsPrefix string
httpAdapterPrefix string
bootstrapPrefix string
msgContentType ContentType
client *http.Client
}
@ -193,10 +210,12 @@ type mfSDK struct {
type Config struct {
BaseURL string
ReaderURL string
BootstrapURL string
ReaderPrefix string
UsersPrefix string
ThingsPrefix string
HTTPAdapterPrefix string
BootstrapPrefix string
MsgContentType ContentType
TLSVerification bool
}
@ -206,10 +225,12 @@ func NewSDK(conf Config) SDK {
return &mfSDK{
baseURL: conf.BaseURL,
readerURL: conf.ReaderURL,
bootstrapURL: conf.BootstrapURL,
readerPrefix: conf.ReaderPrefix,
usersPrefix: conf.UsersPrefix,
thingsPrefix: conf.ThingsPrefix,
httpAdapterPrefix: conf.HTTPAdapterPrefix,
bootstrapPrefix: conf.BootstrapPrefix,
msgContentType: conf.MsgContentType,
client: &http.Client{
Transport: &http.Transport{