mirror of
https://github.com/mainflux/mainflux.git
synced 2025-04-24 13:48:49 +08:00
MF-1340 - Add CLI config TOML file (#1858)
* Add config Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Change key names Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Add config file path Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Add config file path Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Configure TOML parsing Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Add cli config command Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Remove debug log Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Use snake case Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Change from interactive command Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * use map for keys Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Add cli logger level Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Use mainflux logger Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix cli Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Remove unnecessary comments Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix cli error handling Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Remove fmt Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Update config Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix cli Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix cli Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix cli Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix cli Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Modify CLI Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix errors Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix cli Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix errors Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Remove user token Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Remove unused variables Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Add empty line Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Add url parsing Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * TEsts Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Make config path configurable Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix ci Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Remove empty toml Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Change url key identification Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Remove url parsing functions Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Handle parse error Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Handle url error Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Add marshal Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Update config Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix cli Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Handle file error Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Handle file error Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Modify url parsing Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Add usertoken Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix user token Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix errors Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix errors Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Remove string init Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix error Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix errors Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Remove config.toml from root Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Add empty line to config.toml Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Add empty line to config.toml Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Inline error handling Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Remove unnecessary type conversion Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix error handling Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Fix error handling Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> * Remove dynamic filters Signed-off-by: WashingtonKK <washingtonkigan@gmail.com> --------- Signed-off-by: WashingtonKK <washingtonkigan@gmail.com>
This commit is contained in:
parent
ca52a5a38d
commit
0f0d761a1b
@ -48,8 +48,8 @@ var cmdBootstrap = []cobra.Command{
|
||||
return
|
||||
}
|
||||
pageMetadata := mfxsdk.PageMetadata{
|
||||
Offset: uint64(Offset),
|
||||
Limit: uint64(Limit),
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
State: State,
|
||||
Name: Name,
|
||||
}
|
||||
|
@ -57,8 +57,8 @@ var cmdChannels = []cobra.Command{
|
||||
}
|
||||
pageMetadata := mfxsdk.PageMetadata{
|
||||
Name: "",
|
||||
Offset: uint64(Offset),
|
||||
Limit: uint64(Limit),
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
@ -116,8 +116,8 @@ var cmdChannels = []cobra.Command{
|
||||
return
|
||||
}
|
||||
pm := mfxsdk.PageMetadata{
|
||||
Offset: uint64(Offset),
|
||||
Limit: uint64(Limit),
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
}
|
||||
cl, err := sdk.ThingsByChannel(args[0], pm, args[1])
|
||||
if err != nil {
|
||||
|
231
cli/config.go
231
cli/config.go
@ -4,65 +4,240 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
mfxsdk "github.com/mainflux/mainflux/pkg/sdk/go"
|
||||
"github.com/pelletier/go-toml"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Offset uint `toml:"offset"`
|
||||
Limit uint `toml:"limit"`
|
||||
Name string `toml:"name"`
|
||||
RawOutput bool `toml:"raw_output"`
|
||||
type remotes struct {
|
||||
ThingsURL string `toml:"things_url"`
|
||||
UsersURL string `toml:"users_url"`
|
||||
ReaderURL string `toml:"reader_url"`
|
||||
HTTPAdapterURL string `toml:"http_adapter_url"`
|
||||
BootstrapURL string `toml:"bootstrap_url"`
|
||||
CertsURL string `toml:"certs_url"`
|
||||
TLSVerification bool `toml:"tls_verification"`
|
||||
}
|
||||
|
||||
// read - retrieve config from a file.
|
||||
func read(file string) (Config, error) {
|
||||
data, err := os.ReadFile(file)
|
||||
c := Config{}
|
||||
type filter struct {
|
||||
Offset string `toml:"offset"`
|
||||
Limit string `toml:"limit"`
|
||||
Topic string `toml:"topic"`
|
||||
}
|
||||
|
||||
type config struct {
|
||||
Remotes remotes `toml:"remotes"`
|
||||
Filter filter `toml:"filter"`
|
||||
UserToken string `toml:"user_token"`
|
||||
RawOutput string `toml:"raw_output"`
|
||||
}
|
||||
|
||||
// Readable by all user groups but writeable by the user only.
|
||||
const filePermission = 0644
|
||||
|
||||
var (
|
||||
errReadFail = errors.New("failed to read config file")
|
||||
errNoKey = errors.New("no such key")
|
||||
errUnsupportedKeyValue = errors.New("unsupported data type for key")
|
||||
errWritingConfig = errors.New("error in writing the updated config to file")
|
||||
errInvalidURL = errors.New("invalid url")
|
||||
errURLParseFail = errors.New("failed to parse url")
|
||||
defaultConfigPath = "./config.toml"
|
||||
)
|
||||
|
||||
func read(file string) (config, error) {
|
||||
c := config{}
|
||||
data, err := os.Open(file)
|
||||
if err != nil {
|
||||
return c, errors.New(fmt.Sprintf("failed to read config file: %s", err))
|
||||
return c, errors.Wrap(errReadFail, err)
|
||||
}
|
||||
defer data.Close()
|
||||
|
||||
buf, err := io.ReadAll(data)
|
||||
if err != nil {
|
||||
return c, errors.Wrap(errReadFail, err)
|
||||
}
|
||||
|
||||
if err := toml.Unmarshal(data, &c); err != nil {
|
||||
return Config{}, errors.New(fmt.Sprintf("failed to unmarshal config TOML: %s", err))
|
||||
if err := toml.Unmarshal(buf, &c); err != nil {
|
||||
return config{}, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func ParseConfig() error {
|
||||
// ParseConfig - parses the config file.
|
||||
func ParseConfig(sdkConf mfxsdk.Config) (mfxsdk.Config, error) {
|
||||
if ConfigPath == "" {
|
||||
// No config file
|
||||
return nil
|
||||
ConfigPath = defaultConfigPath
|
||||
}
|
||||
|
||||
if _, err := os.Stat(ConfigPath); os.IsNotExist(err) {
|
||||
errConfigNotFound := errors.Wrap(errors.New("config file was not found"), err)
|
||||
logError(errConfigNotFound)
|
||||
return nil
|
||||
_, err := os.Stat(ConfigPath)
|
||||
switch {
|
||||
// If the file does not exist, create it with default values.
|
||||
case os.IsNotExist(err):
|
||||
defaultConfig := config{
|
||||
Remotes: remotes{
|
||||
ThingsURL: "http://localhost:9000",
|
||||
UsersURL: "http://localhost:9002",
|
||||
ReaderURL: "http://localhost",
|
||||
HTTPAdapterURL: "http://localhost/http:9016",
|
||||
BootstrapURL: "http://localhost",
|
||||
CertsURL: "https://localhost:9019",
|
||||
TLSVerification: false,
|
||||
},
|
||||
}
|
||||
buf, err := toml.Marshal(defaultConfig)
|
||||
if err != nil {
|
||||
return sdkConf, err
|
||||
}
|
||||
if err = os.WriteFile(ConfigPath, buf, filePermission); err != nil {
|
||||
return sdkConf, errors.Wrap(errWritingConfig, err)
|
||||
}
|
||||
case err != nil:
|
||||
return sdkConf, err
|
||||
}
|
||||
|
||||
config, err := read(ConfigPath)
|
||||
if err != nil {
|
||||
return sdkConf, err
|
||||
}
|
||||
|
||||
if config.Filter.Offset != "" {
|
||||
offset, err := strconv.ParseUint(config.Filter.Offset, 10, 64)
|
||||
if err != nil {
|
||||
return sdkConf, err
|
||||
}
|
||||
Offset = offset
|
||||
}
|
||||
|
||||
if config.Filter.Limit != "" {
|
||||
limit, err := strconv.ParseUint(config.Filter.Limit, 10, 64)
|
||||
if err != nil {
|
||||
return sdkConf, err
|
||||
}
|
||||
Limit = limit
|
||||
}
|
||||
|
||||
if config.Filter.Topic != "" {
|
||||
Topic = config.Filter.Topic
|
||||
}
|
||||
|
||||
if config.RawOutput != "" {
|
||||
rawOutput, err := strconv.ParseBool(config.RawOutput)
|
||||
if err != nil {
|
||||
return sdkConf, err
|
||||
}
|
||||
RawOutput = rawOutput
|
||||
}
|
||||
|
||||
sdkConf.ThingsURL = config.Remotes.ThingsURL
|
||||
sdkConf.UsersURL = config.Remotes.UsersURL
|
||||
sdkConf.ReaderURL = config.Remotes.ReaderURL
|
||||
sdkConf.HTTPAdapterURL = config.Remotes.HTTPAdapterURL
|
||||
sdkConf.BootstrapURL = config.Remotes.BootstrapURL
|
||||
sdkConf.CertsURL = config.Remotes.CertsURL
|
||||
|
||||
return sdkConf, nil
|
||||
}
|
||||
|
||||
// New config command to store params to local TOML file.
|
||||
func NewConfigCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "config <key> <value>",
|
||||
Short: "CLI local config",
|
||||
Long: "Local param storage to prevent repetitive passing of keys",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
logUsage(cmd.Use)
|
||||
return
|
||||
}
|
||||
|
||||
if err := setConfigValue(args[0], args[1]); err != nil {
|
||||
logError(err)
|
||||
return
|
||||
}
|
||||
|
||||
logOK()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func setConfigValue(key string, value string) error {
|
||||
config, err := read(ConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.Offset != 0 {
|
||||
Offset = config.Offset
|
||||
if strings.Contains(key, "url") {
|
||||
u, err := url.Parse(value)
|
||||
if err != nil {
|
||||
return errors.Wrap(errInvalidURL, err)
|
||||
}
|
||||
if u.Scheme == "" || u.Host == "" {
|
||||
return errors.Wrap(errInvalidURL, err)
|
||||
}
|
||||
if u.Scheme != "http" && u.Scheme != "https" {
|
||||
return errors.Wrap(errURLParseFail, err)
|
||||
}
|
||||
}
|
||||
|
||||
if config.Limit != 0 {
|
||||
Limit = config.Limit
|
||||
var configKeyToField = map[string]interface{}{
|
||||
"things_url": &config.Remotes.ThingsURL,
|
||||
"users_url": &config.Remotes.UsersURL,
|
||||
"reader_url": &config.Remotes.ReaderURL,
|
||||
"http_adapter_url": &config.Remotes.HTTPAdapterURL,
|
||||
"bootstrap_url": &config.Remotes.BootstrapURL,
|
||||
"certs_url": &config.Remotes.CertsURL,
|
||||
"tls_verification": &config.Remotes.TLSVerification,
|
||||
"offset": &config.Filter.Offset,
|
||||
"limit": &config.Filter.Limit,
|
||||
"topic": &config.Filter.Topic,
|
||||
"raw_output": &config.RawOutput,
|
||||
"user_token": &config.UserToken,
|
||||
}
|
||||
|
||||
if config.Name != "" {
|
||||
Name = config.Name
|
||||
fieldPtr, ok := configKeyToField[key]
|
||||
if !ok {
|
||||
return errNoKey
|
||||
}
|
||||
|
||||
if config.RawOutput {
|
||||
RawOutput = config.RawOutput
|
||||
fieldValue := reflect.ValueOf(fieldPtr).Elem()
|
||||
|
||||
switch fieldValue.Kind() {
|
||||
case reflect.String:
|
||||
fieldValue.SetString(value)
|
||||
case reflect.Int:
|
||||
intValue, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fieldValue.SetUint(uint64(intValue))
|
||||
case reflect.Bool:
|
||||
boolValue, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fieldValue.SetBool(boolValue)
|
||||
default:
|
||||
return errors.Wrap(errUnsupportedKeyValue, err)
|
||||
}
|
||||
|
||||
buf, err := toml.Marshal(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(ConfigPath, buf, filePermission); err != nil {
|
||||
return errors.Wrap(errWritingConfig, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -40,8 +40,8 @@ var cmdSubscription = []cobra.Command{
|
||||
return
|
||||
}
|
||||
pageMetadata := mfxsdk.PageMetadata{
|
||||
Offset: uint64(Offset),
|
||||
Limit: uint64(Limit),
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
Topic: Topic,
|
||||
Contact: Contact,
|
||||
}
|
||||
|
@ -84,8 +84,8 @@ var cmdGroups = []cobra.Command{
|
||||
return
|
||||
}
|
||||
pm := mfxsdk.PageMetadata{
|
||||
Offset: uint64(Offset),
|
||||
Limit: uint64(Limit),
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
}
|
||||
l, err := sdk.Groups(pm, args[1])
|
||||
if err != nil {
|
||||
@ -101,8 +101,8 @@ var cmdGroups = []cobra.Command{
|
||||
return
|
||||
}
|
||||
pm := mfxsdk.PageMetadata{
|
||||
Offset: uint64(Offset),
|
||||
Limit: uint64(Limit),
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
}
|
||||
l, err := sdk.Children(args[1], pm, args[2])
|
||||
if err != nil {
|
||||
@ -118,8 +118,8 @@ var cmdGroups = []cobra.Command{
|
||||
return
|
||||
}
|
||||
pm := mfxsdk.PageMetadata{
|
||||
Offset: uint64(Offset),
|
||||
Limit: uint64(Limit),
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
}
|
||||
l, err := sdk.Parents(args[1], pm, args[2])
|
||||
if err != nil {
|
||||
@ -194,8 +194,8 @@ var cmdGroups = []cobra.Command{
|
||||
return
|
||||
}
|
||||
pm := mfxsdk.PageMetadata{
|
||||
Offset: uint64(Offset),
|
||||
Limit: uint64(Limit),
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
Status: Status,
|
||||
}
|
||||
up, err := sdk.Members(args[0], pm, args[1])
|
||||
@ -218,8 +218,8 @@ var cmdGroups = []cobra.Command{
|
||||
return
|
||||
}
|
||||
pm := mfxsdk.PageMetadata{
|
||||
Offset: uint64(Offset),
|
||||
Limit: uint64(Limit),
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
}
|
||||
up, err := sdk.Memberships(args[0], pm, args[1])
|
||||
if err != nil {
|
||||
|
@ -111,8 +111,8 @@ var cmdPolicies = []cobra.Command{
|
||||
return
|
||||
}
|
||||
pm := mfxsdk.PageMetadata{
|
||||
Offset: uint64(Offset),
|
||||
Limit: uint64(Limit),
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
}
|
||||
switch args[0] {
|
||||
case things:
|
||||
|
@ -58,9 +58,9 @@ var cmdThings = []cobra.Command{
|
||||
return
|
||||
}
|
||||
pageMetadata := mfxsdk.PageMetadata{
|
||||
Name: "",
|
||||
Offset: uint64(Offset),
|
||||
Limit: uint64(Limit),
|
||||
Name: Name,
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
Metadata: metadata,
|
||||
}
|
||||
if args[0] == all {
|
||||
@ -300,8 +300,8 @@ var cmdThings = []cobra.Command{
|
||||
return
|
||||
}
|
||||
pm := mfxsdk.PageMetadata{
|
||||
Offset: uint64(Offset),
|
||||
Limit: uint64(Limit),
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
}
|
||||
cl, err := sdk.ChannelsByThing(args[0], pm, args[1])
|
||||
if err != nil {
|
||||
|
@ -63,9 +63,9 @@ var cmdUsers = []cobra.Command{
|
||||
return
|
||||
}
|
||||
pageMetadata := mfxsdk.PageMetadata{
|
||||
Email: "",
|
||||
Offset: uint64(Offset),
|
||||
Limit: uint64(Limit),
|
||||
Email: Email,
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
Metadata: metadata,
|
||||
Status: Status,
|
||||
}
|
||||
|
@ -15,9 +15,9 @@ import (
|
||||
|
||||
var (
|
||||
// Limit query parameter.
|
||||
Limit uint = 10
|
||||
Limit uint64 = 10
|
||||
// Offset query parameter.
|
||||
Offset uint = 0
|
||||
Offset uint64 = 0
|
||||
// Name query parameter.
|
||||
Name string = ""
|
||||
// Email query parameter.
|
||||
|
@ -39,16 +39,17 @@ func main() {
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "mainflux-cli",
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
if err := cli.ParseConfig(); err != nil {
|
||||
log.Fatal(err)
|
||||
cliConf, err := cli.ParseConfig(sdkConf)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse config: %s", err)
|
||||
}
|
||||
|
||||
sdkConf.MsgContentType = sdk.ContentType(msgContentType)
|
||||
s := sdk.NewSDK(sdkConf)
|
||||
if cliConf.MsgContentType == "" {
|
||||
cliConf.MsgContentType = sdk.ContentType(msgContentType)
|
||||
}
|
||||
s := sdk.NewSDK(cliConf)
|
||||
cli.SetSDK(s)
|
||||
},
|
||||
}
|
||||
|
||||
// API commands
|
||||
healthCmd := cli.NewHealthCmd()
|
||||
usersCmd := cli.NewUsersCmd()
|
||||
@ -61,6 +62,7 @@ func main() {
|
||||
certsCmd := cli.NewCertsCmd()
|
||||
subscriptionsCmd := cli.NewSubscriptionCmd()
|
||||
policiesCmd := cli.NewPolicyCmd()
|
||||
configCmd := cli.NewConfigCmd()
|
||||
|
||||
// Root Commands
|
||||
rootCmd.AddCommand(healthCmd)
|
||||
@ -74,6 +76,7 @@ func main() {
|
||||
rootCmd.AddCommand(certsCmd)
|
||||
rootCmd.AddCommand(subscriptionsCmd)
|
||||
rootCmd.AddCommand(policiesCmd)
|
||||
rootCmd.AddCommand(configCmd)
|
||||
|
||||
// Root Flags
|
||||
rootCmd.PersistentFlags().StringVarP(
|
||||
@ -165,7 +168,7 @@ func main() {
|
||||
)
|
||||
|
||||
// Client and Channels Flags
|
||||
rootCmd.PersistentFlags().UintVarP(
|
||||
rootCmd.PersistentFlags().Uint64VarP(
|
||||
&cli.Limit,
|
||||
"limit",
|
||||
"l",
|
||||
@ -173,7 +176,7 @@ func main() {
|
||||
"Limit query parameter",
|
||||
)
|
||||
|
||||
rootCmd.PersistentFlags().UintVarP(
|
||||
rootCmd.PersistentFlags().Uint64VarP(
|
||||
&cli.Offset,
|
||||
"offset",
|
||||
"o",
|
||||
|
16
config.toml
Normal file
16
config.toml
Normal file
@ -0,0 +1,16 @@
|
||||
raw_output = ""
|
||||
user_token = ""
|
||||
|
||||
[filter]
|
||||
limit = ""
|
||||
offset = ""
|
||||
topic = ""
|
||||
|
||||
[remotes]
|
||||
bootstrap_url = "http://localhost"
|
||||
certs_url = "https://localhost:9019"
|
||||
http_adapter_url = "http://localhost/http:9016"
|
||||
reader_url = "http://localhost"
|
||||
things_url = "http://localhost:9000"
|
||||
tls_verification = false
|
||||
users_url = "http://localhost:9002"
|
Loading…
x
Reference in New Issue
Block a user