mirror of
https://github.com/absmach/supermq.git
synced 2026-06-23 04:20:17 +00:00
60e256c267
Signed-off-by: dusan <borovcanindusan1@gmail.com>
353 lines
10 KiB
Go
353 lines
10 KiB
Go
// Copyright (c) Abstract Machines
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package sdk
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/tls"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/absmach/supermq/pkg/errors"
|
|
smqSDK "github.com/absmach/supermq/pkg/sdk"
|
|
"moul.io/http2curl"
|
|
)
|
|
|
|
var _ SDK = (*mgSDK)(nil)
|
|
|
|
type Metadata map[string]any
|
|
|
|
type PageMetadata struct {
|
|
Total uint64 `json:"total"`
|
|
Offset uint64 `json:"offset"`
|
|
Limit uint64 `json:"limit"`
|
|
Metadata Metadata `json:"metadata,omitempty"`
|
|
Topic string `json:"topic,omitempty"`
|
|
Contact string `json:"contact,omitempty"`
|
|
DomainID string `json:"domain_id,omitempty"`
|
|
Level uint64 `json:"level,omitempty"`
|
|
State string `json:"state,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
}
|
|
|
|
type MessagePageMetadata struct {
|
|
PageMetadata
|
|
Subtopic string `json:"subtopic,omitempty"`
|
|
Publisher string `json:"publisher,omitempty"`
|
|
Limit int `json:"limit,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
Comparator string `json:"comparator,omitempty"`
|
|
BoolValue *bool `json:"vb,omitempty"`
|
|
StringValue string `json:"vs,omitempty"`
|
|
DataValue string `json:"vd,omitempty"`
|
|
From float64 `json:"from,omitempty"`
|
|
To float64 `json:"to,omitempty"`
|
|
Aggregation string `json:"aggregation,omitempty"`
|
|
Interval string `json:"interval,omitempty"`
|
|
Value float64 `json:"value,omitempty"`
|
|
Protocol string `json:"protocol,omitempty"`
|
|
}
|
|
|
|
// SDK contains Magistrala API.
|
|
type SDK interface {
|
|
smqSDK.SDK
|
|
|
|
// AddBootstrap add bootstrap configuration
|
|
//
|
|
// example:
|
|
// cfg := sdk.BootstrapConfig{
|
|
// ClientID: "clientID",
|
|
// Name: "bootstrap",
|
|
// ExternalID: "externalID",
|
|
// ExternalKey: "externalKey",
|
|
// Channels: []string{"channel1", "channel2"},
|
|
// }
|
|
// id, _ := sdk.AddBootstrap(ctx, cfg, "domainID", "token")
|
|
// fmt.Println(id)
|
|
AddBootstrap(ctx context.Context, cfg BootstrapConfig, domainID, token string) (string, errors.SDKError)
|
|
|
|
// View returns Client Config with given ID belonging to the user identified by the given token.
|
|
//
|
|
// example:
|
|
// bootstrap, _ := sdk.ViewBootstrap(ctx, "id", "domainID", "token")
|
|
// fmt.Println(bootstrap)
|
|
ViewBootstrap(ctx context.Context, id, domainID, token string) (BootstrapConfig, errors.SDKError)
|
|
|
|
// Update updates editable fields of the provided Config.
|
|
//
|
|
// example:
|
|
// cfg := sdk.BootstrapConfig{
|
|
// ClientID: "clientID",
|
|
// Name: "bootstrap",
|
|
// ExternalID: "externalID",
|
|
// ExternalKey: "externalKey",
|
|
// Channels: []string{"channel1", "channel2"},
|
|
// }
|
|
// err := sdk.UpdateBootstrap(ctx, cfg, "domainID", "token")
|
|
// fmt.Println(err)
|
|
UpdateBootstrap(ctx context.Context, cfg BootstrapConfig, domainID, token string) errors.SDKError
|
|
|
|
// Update bootstrap config certificates.
|
|
//
|
|
// example:
|
|
// err := sdk.UpdateBootstrapCerts(ctx, "id", "clientCert", "clientKey", "ca", "domainID", "token")
|
|
// fmt.Println(err)
|
|
UpdateBootstrapCerts(ctx context.Context, id string, clientCert, clientKey, ca string, domainID, token string) (BootstrapConfig, errors.SDKError)
|
|
|
|
// UpdateBootstrapConnection updates connections performs update of the channel list corresponding Client is connected to.
|
|
//
|
|
// example:
|
|
// err := sdk.UpdateBootstrapConnection(ctx, "id", []string{"channel1", "channel2"}, "domainID", "token")
|
|
// fmt.Println(err)
|
|
UpdateBootstrapConnection(ctx context.Context, id string, channels []string, domainID, token string) errors.SDKError
|
|
|
|
// Remove removes Config with specified token that belongs to the user identified by the given token.
|
|
//
|
|
// example:
|
|
// err := sdk.RemoveBootstrap(ctx, "id", "domainID", "token")
|
|
// fmt.Println(err)
|
|
RemoveBootstrap(ctx context.Context, id, domainID, token string) errors.SDKError
|
|
|
|
// Bootstrap returns Config to the Client with provided external ID using external key.
|
|
//
|
|
// example:
|
|
// bootstrap, _ := sdk.Bootstrap(ctx, "externalID", "externalKey")
|
|
// fmt.Println(bootstrap)
|
|
Bootstrap(ctx context.Context, externalID, externalKey string) (BootstrapConfig, errors.SDKError)
|
|
|
|
// BootstrapSecure retrieves a configuration with given external ID and encrypted external key.
|
|
//
|
|
// example:
|
|
// bootstrap, _ := sdk.BootstrapSecure(ctx, "externalID", "externalKey", "cryptoKey")
|
|
// fmt.Println(bootstrap)
|
|
BootstrapSecure(ctx context.Context, externalID, externalKey, cryptoKey string) (BootstrapConfig, errors.SDKError)
|
|
|
|
// Bootstraps retrieves a list of managed configs.
|
|
//
|
|
// example:
|
|
// pm := sdk.PageMetadata{
|
|
// Offset: 0,
|
|
// Limit: 10,
|
|
// }
|
|
// bootstraps, _ := sdk.Bootstraps(ctx, pm, "domainID", "token")
|
|
// fmt.Println(bootstraps)
|
|
Bootstraps(ctx context.Context, pm PageMetadata, domainID, token string) (BootstrapPage, errors.SDKError)
|
|
|
|
// Whitelist updates Client state Config with given ID belonging to the user identified by the given token.
|
|
//
|
|
// example:
|
|
// err := sdk.Whitelist(ctx, "clientID", 1, "domainID", "token")
|
|
// fmt.Println(err)
|
|
Whitelist(ctx context.Context, clientID string, state int, domainID, token string) errors.SDKError
|
|
|
|
// ReadMessages read messages of specified channel.
|
|
//
|
|
// example:
|
|
// pm := sdk.MessagePageMetadata{
|
|
// Offset: 0,
|
|
// Limit: 10,
|
|
// }
|
|
// msgs, _ := sdk.ReadMessages(ctx, pm,"channelID", "domainID", "token")
|
|
// fmt.Println(msgs)
|
|
ReadMessages(ctx context.Context, pm MessagePageMetadata, chanID, domainID, token string) (MessagesPage, errors.SDKError)
|
|
|
|
// CreateSubscription creates a new subscription
|
|
//
|
|
// example:
|
|
// subscription, _ := sdk.CreateSubscription(ctx, "topic", "contact", "token")
|
|
// fmt.Println(subscription)
|
|
CreateSubscription(ctx context.Context, topic, contact, token string) (string, errors.SDKError)
|
|
|
|
// ListSubscriptions list subscriptions given list parameters.
|
|
//
|
|
// example:
|
|
// pm := sdk.PageMetadata{
|
|
// Offset: 0,
|
|
// Limit: 10,
|
|
// }
|
|
// subscriptions, _ := sdk.ListSubscriptions(ctx, pm, "token")
|
|
// fmt.Println(subscriptions)
|
|
ListSubscriptions(ctx context.Context, pm PageMetadata, token string) (SubscriptionPage, errors.SDKError)
|
|
|
|
// ViewSubscription retrieves a subscription with the provided id.
|
|
//
|
|
// example:
|
|
// subscription, _ := sdk.ViewSubscription(ctx, "id", "token")
|
|
// fmt.Println(subscription)
|
|
ViewSubscription(ctx context.Context, id, token string) (Subscription, errors.SDKError)
|
|
|
|
// DeleteSubscription removes a subscription with the provided id.
|
|
//
|
|
// example:
|
|
// err := sdk.DeleteSubscription(ctx, "id", "token")
|
|
// fmt.Println(err)
|
|
DeleteSubscription(ctx context.Context, id, token string) errors.SDKError
|
|
}
|
|
|
|
type mgSDK struct {
|
|
bootstrapURL string
|
|
readersURL string
|
|
usersURL string
|
|
client *http.Client
|
|
curlFlag bool
|
|
msgContentType smqSDK.ContentType
|
|
|
|
smqSDK.SDK
|
|
}
|
|
|
|
// Config contains sdk configuration parameters.
|
|
type Config struct {
|
|
BootstrapURL string
|
|
CertsURL string
|
|
HTTPAdapterURL string
|
|
ReaderURL string
|
|
ClientsURL string
|
|
UsersURL string
|
|
GroupsURL string
|
|
ChannelsURL string
|
|
DomainsURL string
|
|
JournalURL string
|
|
HostURL string
|
|
|
|
MsgContentType smqSDK.ContentType
|
|
TLSVerification bool
|
|
CurlFlag bool
|
|
}
|
|
|
|
// NewSDK returns new supermq SDK instance.
|
|
func NewSDK(conf Config) SDK {
|
|
smqSDK := smqSDK.NewSDK(smqSDK.Config{
|
|
CertsURL: conf.CertsURL,
|
|
HTTPAdapterURL: conf.HTTPAdapterURL,
|
|
ClientsURL: conf.ClientsURL,
|
|
UsersURL: conf.UsersURL,
|
|
GroupsURL: conf.GroupsURL,
|
|
ChannelsURL: conf.ChannelsURL,
|
|
DomainsURL: conf.DomainsURL,
|
|
JournalURL: conf.JournalURL,
|
|
HostURL: conf.HostURL,
|
|
|
|
MsgContentType: conf.MsgContentType,
|
|
TLSVerification: conf.TLSVerification,
|
|
CurlFlag: conf.CurlFlag,
|
|
})
|
|
|
|
return &mgSDK{
|
|
bootstrapURL: conf.BootstrapURL,
|
|
readersURL: conf.ReaderURL,
|
|
usersURL: conf.UsersURL,
|
|
msgContentType: conf.MsgContentType,
|
|
|
|
client: &http.Client{
|
|
Transport: &http.Transport{
|
|
TLSClientConfig: &tls.Config{
|
|
InsecureSkipVerify: !conf.TLSVerification,
|
|
},
|
|
},
|
|
},
|
|
curlFlag: conf.CurlFlag,
|
|
SDK: smqSDK,
|
|
}
|
|
}
|
|
|
|
// processRequest creates and send a new HTTP request, and checks for errors in the HTTP response.
|
|
// It then returns the response headers, the response body, and the associated error(s) (if any).
|
|
func (sdk mgSDK) processRequest(ctx context.Context, method, reqUrl, token string, data []byte, headers map[string]string, expectedRespCodes ...int) (http.Header, []byte, errors.SDKError) {
|
|
req, err := http.NewRequestWithContext(ctx, method, reqUrl, bytes.NewReader(data))
|
|
if err != nil {
|
|
return make(http.Header), []byte{}, errors.NewSDKError(err)
|
|
}
|
|
|
|
// Sets a default value for the Content-Type.
|
|
// Overridden if Content-Type is passed in the headers arguments.
|
|
req.Header.Add("Content-Type", string(smqSDK.CTJSON))
|
|
|
|
for key, value := range headers {
|
|
req.Header.Add(key, value)
|
|
}
|
|
|
|
if token != "" {
|
|
if !strings.Contains(token, smqSDK.ClientPrefix) {
|
|
token = smqSDK.BearerPrefix + token
|
|
}
|
|
req.Header.Set("Authorization", token)
|
|
}
|
|
|
|
if sdk.curlFlag {
|
|
curlCommand, err := http2curl.GetCurlCommand(req)
|
|
if err != nil {
|
|
return nil, nil, errors.NewSDKError(err)
|
|
}
|
|
log.Println(curlCommand.String())
|
|
}
|
|
|
|
resp, err := sdk.client.Do(req)
|
|
if err != nil {
|
|
return make(http.Header), []byte{}, errors.NewSDKError(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
sdkerr := errors.CheckError(resp, expectedRespCodes...)
|
|
if sdkerr != nil {
|
|
return make(http.Header), []byte{}, sdkerr
|
|
}
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return make(http.Header), []byte{}, errors.NewSDKError(err)
|
|
}
|
|
|
|
return resp.Header, body, nil
|
|
}
|
|
|
|
func (sdk mgSDK) withQueryParams(baseURL, endpoint string, pm PageMetadata) (string, error) {
|
|
q, err := pm.query()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return fmt.Sprintf("%s/%s?%s", baseURL, endpoint, q), nil
|
|
}
|
|
|
|
func (pm PageMetadata) query() (string, error) {
|
|
q := url.Values{}
|
|
if pm.Offset != 0 {
|
|
q.Add("offset", strconv.FormatUint(pm.Offset, 10))
|
|
}
|
|
if pm.Limit != 0 {
|
|
q.Add("limit", strconv.FormatUint(pm.Limit, 10))
|
|
}
|
|
if pm.Total != 0 {
|
|
q.Add("total", strconv.FormatUint(pm.Total, 10))
|
|
}
|
|
if pm.Metadata != nil {
|
|
md, err := json.Marshal(pm.Metadata)
|
|
if err != nil {
|
|
return "", errors.NewSDKError(err)
|
|
}
|
|
q.Add("metadata", string(md))
|
|
}
|
|
if pm.Topic != "" {
|
|
q.Add("topic", pm.Topic)
|
|
}
|
|
if pm.Contact != "" {
|
|
q.Add("contact", pm.Contact)
|
|
}
|
|
if pm.DomainID != "" {
|
|
q.Add("domain_id", pm.DomainID)
|
|
}
|
|
if pm.Level != 0 {
|
|
q.Add("level", strconv.FormatUint(pm.Level, 10))
|
|
}
|
|
|
|
return q.Encode(), nil
|
|
}
|