NOISSUE - Add new endpoint to retrieve configuration to be used as a template. (#1242)
* add provision service Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix code style Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix test for provision Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * extra line Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * return map[string]interface instead of interface Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>
This commit is contained in:
parent
8ea26c5ab7
commit
9ed5f8334f
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
func doProvision(svc provision.Service) endpoint.Endpoint {
|
func doProvision(svc provision.Service) endpoint.Endpoint {
|
||||||
return func(_ context.Context, request interface{}) (interface{}, error) {
|
return func(_ context.Context, request interface{}) (interface{}, error) {
|
||||||
req := request.(addThingReq)
|
req := request.(provisionReq)
|
||||||
if err := req.validate(); err != nil {
|
if err := req.validate(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -34,3 +34,13 @@ func doProvision(svc provision.Service) endpoint.Endpoint {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMapping(svc provision.Service) endpoint.Endpoint {
|
||||||
|
return func(_ context.Context, request interface{}) (interface{}, error) {
|
||||||
|
req := request.(mappingReq)
|
||||||
|
if err := req.validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return svc.Mapping(req.token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,10 +24,10 @@ func (lm *loggingMiddleware) Provision(token, name, externalID, externalKey stri
|
||||||
defer func(begin time.Time) {
|
defer func(begin time.Time) {
|
||||||
message := fmt.Sprintf("Method provision for token: %s and things: %v took %s to complete", token, res.Things, time.Since(begin))
|
message := fmt.Sprintf("Method provision for token: %s and things: %v took %s to complete", token, res.Things, time.Since(begin))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
lm.logger.Warn(fmt.Sprintf("%s with error: %s", message, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
lm.logger.Info(fmt.Sprintf("%s without errors", message))
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
return lm.svc.Provision(token, name, externalID, externalKey)
|
return lm.svc.Provision(token, name, externalID, externalKey)
|
||||||
|
@ -37,11 +37,24 @@ func (lm *loggingMiddleware) Cert(token, thingID, duration string, keyBits int)
|
||||||
defer func(begin time.Time) {
|
defer func(begin time.Time) {
|
||||||
message := fmt.Sprintf("Method cert for token: %s and thing: %v took %s to complete", token, thingID, time.Since(begin))
|
message := fmt.Sprintf("Method cert for token: %s and thing: %v took %s to complete", token, thingID, time.Since(begin))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
lm.logger.Warn(fmt.Sprintf("%s with error: %s", message, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
lm.logger.Info(fmt.Sprintf("%s without errors", message))
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
return lm.svc.Cert(token, thingID, duration, keyBits)
|
return lm.svc.Cert(token, thingID, duration, keyBits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (lm *loggingMiddleware) Mapping(token string) (res map[string]interface{}, err error) {
|
||||||
|
defer func(begin time.Time) {
|
||||||
|
message := fmt.Sprintf("Method mapping for token: %s took %s to complete", token, time.Since(begin))
|
||||||
|
if err != nil {
|
||||||
|
lm.logger.Warn(fmt.Sprintf("%s with error: %s", message, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lm.logger.Info(fmt.Sprintf("%s without errors", message))
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
|
return lm.svc.Mapping(token)
|
||||||
|
}
|
||||||
|
|
|
@ -1,14 +1,25 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
type addThingReq struct {
|
type provisionReq struct {
|
||||||
token string
|
token string
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
ExternalID string `json:"external_id"`
|
ExternalID string `json:"external_id"`
|
||||||
ExternalKey string `json:"external_key"`
|
ExternalKey string `json:"external_key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (req addThingReq) validate() error {
|
func (req provisionReq) validate() error {
|
||||||
if req.ExternalID == "" || req.ExternalKey == "" {
|
if req.ExternalID == "" || req.ExternalKey == "" {
|
||||||
|
return errMalformedEntity
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type mappingReq struct {
|
||||||
|
token string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req mappingReq) validate() error {
|
||||||
|
if req.token == "" {
|
||||||
return errUnauthorized
|
return errUnauthorized
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -21,17 +21,17 @@ func TestValidate(t *testing.T) {
|
||||||
err: nil,
|
err: nil,
|
||||||
},
|
},
|
||||||
"external id for device empty": {
|
"external id for device empty": {
|
||||||
err: errUnauthorized,
|
err: errMalformedEntity,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for desc, tc := range cases {
|
for desc, tc := range cases {
|
||||||
req := addThingReq{
|
req := provisionReq{
|
||||||
ExternalID: tc.ExternalID,
|
ExternalID: tc.ExternalID,
|
||||||
ExternalKey: tc.ExternalKey,
|
ExternalKey: tc.ExternalKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := req.validate()
|
err := req.validate()
|
||||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected `%v` got `%v`", desc, err, tc.err))
|
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected `%v` got `%v`", desc, tc.err, err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,14 @@ func MakeHandler(svc provision.Service) http.Handler {
|
||||||
|
|
||||||
r.Post("/mapping", kithttp.NewServer(
|
r.Post("/mapping", kithttp.NewServer(
|
||||||
doProvision(svc),
|
doProvision(svc),
|
||||||
decodeThingCreation,
|
decodeProvisionRequest,
|
||||||
|
encodeResponse,
|
||||||
|
opts...,
|
||||||
|
))
|
||||||
|
|
||||||
|
r.Get("/mapping", kithttp.NewServer(
|
||||||
|
getMapping(svc),
|
||||||
|
decodeMappingRequest,
|
||||||
encodeResponse,
|
encodeResponse,
|
||||||
opts...,
|
opts...,
|
||||||
))
|
))
|
||||||
|
@ -66,12 +73,12 @@ func encodeResponse(_ context.Context, w http.ResponseWriter, response interface
|
||||||
return json.NewEncoder(w).Encode(response)
|
return json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeThingCreation(_ context.Context, r *http.Request) (interface{}, error) {
|
func decodeProvisionRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||||
if r.Header.Get("Content-Type") != contentType {
|
if r.Header.Get("Content-Type") != contentType {
|
||||||
return nil, errUnsupportedContentType
|
return nil, errUnsupportedContentType
|
||||||
}
|
}
|
||||||
|
|
||||||
req := addThingReq{token: r.Header.Get("Authorization")}
|
req := provisionReq{token: r.Header.Get("Authorization")}
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -79,6 +86,16 @@ func decodeThingCreation(_ context.Context, r *http.Request) (interface{}, error
|
||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeMappingRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||||
|
if r.Header.Get("Content-Type") != contentType {
|
||||||
|
return nil, errUnsupportedContentType
|
||||||
|
}
|
||||||
|
|
||||||
|
req := mappingReq{token: r.Header.Get("Authorization")}
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
|
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||||
w.Header().Set("Content-Type", contentType)
|
w.Header().Set("Content-Type", contentType)
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
ErrUnauthorized = errors.New("unauthorized access")
|
||||||
ErrFailedToCreateToken = errors.New("failed to create access token")
|
ErrFailedToCreateToken = errors.New("failed to create access token")
|
||||||
ErrEmptyThingsList = errors.New("things list in configuration empty")
|
ErrEmptyThingsList = errors.New("things list in configuration empty")
|
||||||
ErrEmptyChannelsList = errors.New("channels list in configuration is empty")
|
ErrEmptyChannelsList = errors.New("channels list in configuration is empty")
|
||||||
|
@ -47,6 +48,11 @@ type Service interface {
|
||||||
// - whitelist Thing in Bootstrap configuration == connect Thing to Channels
|
// - whitelist Thing in Bootstrap configuration == connect Thing to Channels
|
||||||
Provision(token, name, externalID, externalKey string) (Result, error)
|
Provision(token, name, externalID, externalKey string) (Result, error)
|
||||||
|
|
||||||
|
// Mapping returns current configuration used for provision
|
||||||
|
// useful for using in ui to create configuration that matches
|
||||||
|
// one created with Provision method.
|
||||||
|
Mapping(token string) (map[string]interface{}, error)
|
||||||
|
|
||||||
// Certs creates certificate for things that communicate over mTLS
|
// Certs creates certificate for things that communicate over mTLS
|
||||||
// A duration string is a possibly signed sequence of decimal numbers,
|
// A duration string is a possibly signed sequence of decimal numbers,
|
||||||
// each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m".
|
// each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m".
|
||||||
|
@ -81,6 +87,14 @@ func New(cfg Config, sdk SDK.SDK, logger logger.Logger) Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mapping retrieves current configuration
|
||||||
|
func (ps *provisionService) Mapping(token string) (map[string]interface{}, error) {
|
||||||
|
if _, err := ps.sdk.User(token); err != nil {
|
||||||
|
return map[string]interface{}{}, errors.Wrap(ErrUnauthorized, err)
|
||||||
|
}
|
||||||
|
return ps.conf.Bootstrap.Content, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Provision is provision method for creating setup according to
|
// Provision is provision method for creating setup according to
|
||||||
// provision layout specified in config.toml
|
// provision layout specified in config.toml
|
||||||
func (ps *provisionService) Provision(token, name, externalID, externalKey string) (res Result, err error) {
|
func (ps *provisionService) Provision(token, name, externalID, externalKey string) (res Result, err error) {
|
||||||
|
@ -118,6 +132,7 @@ func (ps *provisionService) Provision(token, name, externalID, externalKey strin
|
||||||
res.Error = err.Error()
|
res.Error = err.Error()
|
||||||
return res, errors.Wrap(ErrFailedThingCreation, err)
|
return res, errors.Wrap(ErrFailedThingCreation, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get newly created thing (in order to get the key).
|
// Get newly created thing (in order to get the key).
|
||||||
th, err = ps.sdk.Thing(thID, token)
|
th, err = ps.sdk.Thing(thID, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -15,7 +15,7 @@ paths:
|
||||||
summary: Adds new device to proxy
|
summary: Adds new device to proxy
|
||||||
description: Adds new device to proxy
|
description: Adds new device to proxy
|
||||||
tags:
|
tags:
|
||||||
- Thing to proxy
|
- provision
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: "#/parameters/Authorization"
|
- $ref: "#/parameters/Authorization"
|
||||||
- in: body
|
- in: body
|
||||||
|
@ -38,8 +38,35 @@ paths:
|
||||||
description: Created
|
description: Created
|
||||||
400:
|
400:
|
||||||
description: Failed due to malformed JSON.
|
description: Failed due to malformed JSON.
|
||||||
|
403:
|
||||||
|
description: Unauthorized.
|
||||||
500:
|
500:
|
||||||
description: Unexpected server-side error ocurred.
|
description: Unexpected server-side error ocurred.
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
summary: Gets current mapping.
|
||||||
|
description: Gets current mapping. This can be used in UI
|
||||||
|
so that when bootstrap config is created from UI matches
|
||||||
|
configuration created with provision service.
|
||||||
|
tags:
|
||||||
|
- provision
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/parameters/Authorization"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/Content"
|
||||||
|
description: retrieved
|
||||||
|
403:
|
||||||
|
description: Unauthorized.
|
||||||
|
500:
|
||||||
|
description: Unexpected server-side error ocurred.
|
||||||
|
|
||||||
|
definitions:
|
||||||
|
Content:
|
||||||
|
type: object
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
Authorization:
|
Authorization:
|
||||||
name: Authorization
|
name: Authorization
|
||||||
|
|
Loading…
Reference in New Issue