-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add the http clients/dtos of KVS and registry for Keeper
Signed-off-by: Jack Chen <[email protected]>
- Loading branch information
1 parent
a40ef71
commit e3dd702
Showing
22 changed files
with
1,113 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// | ||
// Copyright (C) 2024 IOTech Ltd | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package http | ||
|
||
import ( | ||
"context" | ||
"net/url" | ||
|
||
"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/http/utils" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/interfaces" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/common" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/requests" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/responses" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/errors" | ||
) | ||
|
||
// KVSClient is the REST client for invoking the key-value APIs(/kvs/*) from Core Keeper | ||
type KVSClient struct { | ||
baseUrl string | ||
authInjector interfaces.AuthenticationInjector | ||
} | ||
|
||
// NewKVSClient creates an instance of KVSClient | ||
func NewKVSClient(baseUrl string, authInjector interfaces.AuthenticationInjector) interfaces.KVSClient { | ||
return &KVSClient{ | ||
baseUrl: baseUrl, | ||
authInjector: authInjector, | ||
} | ||
} | ||
|
||
// UpdateValuesByKey updates values of the specified key and the child keys defined in the request payload. | ||
// If no key exists at the given path, the key(s) will be created. | ||
func (kc KVSClient) UpdateValuesByKey(ctx context.Context, key string, req requests.UpdateKeysRequest) (res responses.KeysResponse, err errors.EdgeX) { | ||
path := utils.EscapeAndJoinPath(common.ApiKVSRoute, common.Key, key) | ||
queryParams := url.Values{} | ||
queryParams.Set(common.Flatten, common.ValueTrue) | ||
err = utils.PutRequest(ctx, &res, kc.baseUrl, path, queryParams, req, kc.authInjector) | ||
if err != nil { | ||
return res, errors.NewCommonEdgeXWrapper(err) | ||
} | ||
return res, nil | ||
} | ||
|
||
// ValuesByKey returns the values of the specified key prefix. | ||
func (kc KVSClient) ValuesByKey(ctx context.Context, key string) (res responses.MultiKeyValueResponse, err errors.EdgeX) { | ||
path := utils.EscapeAndJoinPath(common.ApiKVSRoute, common.Key, key) | ||
queryParams := url.Values{} | ||
queryParams.Set(common.Plaintext, common.ValueTrue) | ||
err = utils.GetRequest(ctx, &res, kc.baseUrl, path, queryParams, kc.authInjector) | ||
if err != nil { | ||
return res, errors.NewCommonEdgeXWrapper(err) | ||
} | ||
return res, nil | ||
} | ||
|
||
// ListKeys returns the list of the keys with the specified key prefix. | ||
func (kc KVSClient) ListKeys(ctx context.Context, key string) (res responses.KeysResponse, err errors.EdgeX) { | ||
path := utils.EscapeAndJoinPath(common.ApiKVSRoute, common.Key, key) | ||
queryParams := url.Values{} | ||
queryParams.Set(common.KeyOnly, common.ValueTrue) | ||
err = utils.GetRequest(ctx, &res, kc.baseUrl, path, queryParams, kc.authInjector) | ||
if err != nil { | ||
return res, errors.NewCommonEdgeXWrapper(err) | ||
} | ||
return res, nil | ||
} | ||
|
||
// DeleteKey deletes the specified key. | ||
func (kc KVSClient) DeleteKey(ctx context.Context, key string) (res responses.KeysResponse, err errors.EdgeX) { | ||
path := utils.EscapeAndJoinPath(common.ApiKVSRoute, common.Key, key) | ||
err = utils.DeleteRequest(ctx, &res, kc.baseUrl, path, kc.authInjector) | ||
if err != nil { | ||
return res, errors.NewCommonEdgeXWrapper(err) | ||
} | ||
return res, nil | ||
} | ||
|
||
// DeleteKeysByPrefix deletes all keys with the specified prefix. | ||
func (kc KVSClient) DeleteKeysByPrefix(ctx context.Context, key string) (res responses.KeysResponse, err errors.EdgeX) { | ||
path := utils.EscapeAndJoinPath(common.ApiKVSRoute, common.Key, key) | ||
queryParams := url.Values{} | ||
queryParams.Set("prefixMatch", common.ValueTrue) | ||
err = utils.DeleteRequestWithParams(ctx, &res, kc.baseUrl, path, queryParams, kc.authInjector) | ||
if err != nil { | ||
return res, errors.NewCommonEdgeXWrapper(err) | ||
} | ||
return res, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// | ||
// Copyright (C) 2024 IOTech Ltd | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package http | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/edgexfoundry/go-mod-core-contracts/v3/common" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/requests" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/responses" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
const TestKey = "TestWritable" | ||
|
||
func TestUpdateValuesByKey(t *testing.T) { | ||
ts := newTestServer(http.MethodPut, common.ApiKVSRoute+"/"+common.Key+"/"+TestKey, responses.KeysResponse{}) | ||
defer ts.Close() | ||
|
||
client := NewKVSClient(ts.URL, NewNullAuthenticationInjector()) | ||
res, err := client.UpdateValuesByKey(context.Background(), TestKey, requests.UpdateKeysRequest{}) | ||
|
||
require.NoError(t, err) | ||
require.IsType(t, responses.KeysResponse{}, res) | ||
} | ||
|
||
func TestValuesByKey(t *testing.T) { | ||
ts := newTestServer(http.MethodGet, common.ApiKVSRoute+"/"+common.Key+"/"+TestKey, responses.MultiKeyValueResponse{}) | ||
defer ts.Close() | ||
|
||
client := NewKVSClient(ts.URL, NewNullAuthenticationInjector()) | ||
res, err := client.ValuesByKey(context.Background(), TestKey) | ||
|
||
require.NoError(t, err) | ||
require.IsType(t, responses.MultiKeyValueResponse{}, res) | ||
} | ||
|
||
func TestListKeys(t *testing.T) { | ||
ts := newTestServer(http.MethodGet, common.ApiKVSRoute+"/"+common.Key+"/"+TestKey, responses.KeysResponse{}) | ||
defer ts.Close() | ||
|
||
client := NewKVSClient(ts.URL, NewNullAuthenticationInjector()) | ||
res, err := client.ListKeys(context.Background(), TestKey) | ||
|
||
require.NoError(t, err) | ||
require.IsType(t, responses.KeysResponse{}, res) | ||
} | ||
|
||
func TestDeleteKeys(t *testing.T) { | ||
ts := newTestServer(http.MethodDelete, common.ApiKVSRoute+"/"+common.Key+"/"+TestKey, responses.KeysResponse{}) | ||
defer ts.Close() | ||
|
||
client := NewKVSClient(ts.URL, NewNullAuthenticationInjector()) | ||
res, err := client.DeleteKey(context.Background(), TestKey) | ||
|
||
require.NoError(t, err) | ||
require.IsType(t, responses.KeysResponse{}, res) | ||
} | ||
|
||
func TestDeleteKeysByPrefix(t *testing.T) { | ||
ts := newTestServer(http.MethodDelete, common.ApiKVSRoute+"/"+common.Key+"/"+TestKey, responses.KeysResponse{}) | ||
defer ts.Close() | ||
|
||
client := NewKVSClient(ts.URL, NewNullAuthenticationInjector()) | ||
res, err := client.DeleteKeysByPrefix(context.Background(), TestKey) | ||
|
||
require.NoError(t, err) | ||
require.IsType(t, responses.KeysResponse{}, res) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// | ||
// Copyright (C) 2024 IOTech Ltd | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package http | ||
|
||
import ( | ||
"context" | ||
"net/url" | ||
"strconv" | ||
|
||
"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/http/utils" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/interfaces" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/common" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/requests" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/responses" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/errors" | ||
) | ||
|
||
var emptyResponse any | ||
|
||
// RegistryClient is the REST client for invoking the registry APIs(/registry/*) from Core Keeper | ||
type registryClient struct { | ||
baseUrl string | ||
authInjector interfaces.AuthenticationInjector | ||
enableNameFieldEscape bool | ||
} | ||
|
||
// NewRegistryClient creates an instance of RegistryClient | ||
func NewRegistryClient(baseUrl string, authInjector interfaces.AuthenticationInjector, enableNameFieldEscape bool) interfaces.RegistryClient { | ||
return ®istryClient{ | ||
baseUrl: baseUrl, | ||
authInjector: authInjector, | ||
enableNameFieldEscape: enableNameFieldEscape, | ||
} | ||
} | ||
|
||
// Register registers a service instance | ||
func (rc *registryClient) Register(ctx context.Context, req requests.AddRegistrationRequest) errors.EdgeX { | ||
err := utils.PostRequestWithRawData(ctx, &emptyResponse, rc.baseUrl, common.ApiRegisterRoute, nil, req, rc.authInjector) | ||
if err != nil { | ||
return errors.NewCommonEdgeXWrapper(err) | ||
} | ||
return nil | ||
} | ||
|
||
// UpdateRegister updates the registration data of the service | ||
func (rc *registryClient) UpdateRegister(ctx context.Context, req requests.AddRegistrationRequest) errors.EdgeX { | ||
err := utils.PutRequest(ctx, &emptyResponse, rc.baseUrl, common.ApiRegisterRoute, nil, req, rc.authInjector) | ||
if err != nil { | ||
return errors.NewCommonEdgeXWrapper(err) | ||
} | ||
return nil | ||
} | ||
|
||
// RegistrationByServiceId returns the registration data by service id | ||
func (rc *registryClient) RegistrationByServiceId(ctx context.Context, serviceId string) (responses.RegistrationResponse, errors.EdgeX) { | ||
requestPath := common.NewPathBuilder().EnableNameFieldEscape(rc.enableNameFieldEscape). | ||
SetPath(common.ApiRegisterRoute).SetPath(common.ServiceId).SetNameFieldPath(serviceId).BuildPath() | ||
res := responses.RegistrationResponse{} | ||
err := utils.GetRequest(ctx, &res, rc.baseUrl, requestPath, nil, rc.authInjector) | ||
if err != nil { | ||
return res, errors.NewCommonEdgeXWrapper(err) | ||
} | ||
return res, nil | ||
} | ||
|
||
// AllRegistry returns the registration data of all registered service | ||
func (rc *registryClient) AllRegistry(ctx context.Context, deregistered bool) (responses.MultiRegistrationsResponse, errors.EdgeX) { | ||
requestParams := url.Values{} | ||
requestParams.Set(common.Deregistered, strconv.FormatBool(deregistered)) | ||
|
||
res := responses.MultiRegistrationsResponse{} | ||
err := utils.GetRequest(ctx, &res, rc.baseUrl, common.ApiAllRegistrationsRoute, requestParams, rc.authInjector) | ||
if err != nil { | ||
return res, errors.NewCommonEdgeXWrapper(err) | ||
} | ||
return res, nil | ||
} | ||
|
||
// Deregister deregisters a service by service id | ||
func (rc *registryClient) Deregister(ctx context.Context, serviceId string) errors.EdgeX { | ||
requestPath := common.NewPathBuilder().EnableNameFieldEscape(rc.enableNameFieldEscape). | ||
SetPath(common.ApiRegisterRoute).SetPath(common.ServiceId).SetNameFieldPath(serviceId).BuildPath() | ||
err := utils.DeleteRequest(ctx, &emptyResponse, rc.baseUrl, requestPath, rc.authInjector) | ||
if err != nil { | ||
return errors.NewCommonEdgeXWrapper(err) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// | ||
// Copyright (C) 2024 IOTech Ltd | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package http | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/edgexfoundry/go-mod-core-contracts/v3/common" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/requests" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/responses" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
const mockServiceId = "mock-service" | ||
|
||
func TestRegister(t *testing.T) { | ||
ts := newTestServer(http.MethodPost, common.ApiRegisterRoute, nil) | ||
defer ts.Close() | ||
|
||
client := NewRegistryClient(ts.URL, NewNullAuthenticationInjector(), false) | ||
err := client.Register(context.Background(), requests.AddRegistrationRequest{}) | ||
|
||
require.NoError(t, err) | ||
} | ||
|
||
func TestUpdateRegister(t *testing.T) { | ||
ts := newTestServer(http.MethodPut, common.ApiRegisterRoute, nil) | ||
defer ts.Close() | ||
|
||
client := NewRegistryClient(ts.URL, NewNullAuthenticationInjector(), false) | ||
err := client.UpdateRegister(context.Background(), requests.AddRegistrationRequest{}) | ||
|
||
require.NoError(t, err) | ||
} | ||
|
||
func TestRegistrationByServiceId(t *testing.T) { | ||
ts := newTestServer(http.MethodGet, common.ApiRegisterRoute+"/"+common.ServiceId+"/"+mockServiceId, responses.RegistrationResponse{}) | ||
defer ts.Close() | ||
|
||
client := NewRegistryClient(ts.URL, NewNullAuthenticationInjector(), false) | ||
res, err := client.RegistrationByServiceId(context.Background(), mockServiceId) | ||
|
||
require.NoError(t, err) | ||
require.IsType(t, responses.RegistrationResponse{}, res) | ||
} | ||
|
||
func TestAllRegistry(t *testing.T) { | ||
ts := newTestServer(http.MethodGet, common.ApiAllRegistrationsRoute, responses.MultiRegistrationsResponse{}) | ||
defer ts.Close() | ||
|
||
client := NewRegistryClient(ts.URL, NewNullAuthenticationInjector(), false) | ||
res, err := client.AllRegistry(context.Background(), false) | ||
|
||
require.NoError(t, err) | ||
require.IsType(t, responses.MultiRegistrationsResponse{}, res) | ||
} | ||
|
||
func TestDeregister(t *testing.T) { | ||
ts := newTestServer(http.MethodDelete, common.ApiRegisterRoute+"/"+common.ServiceId+"/"+mockServiceId, nil) | ||
defer ts.Close() | ||
|
||
client := NewRegistryClient(ts.URL, NewNullAuthenticationInjector(), false) | ||
err := client.Deregister(context.Background(), mockServiceId) | ||
|
||
require.NoError(t, err) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// | ||
// Copyright (C) 2024 IOTech Ltd | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package interfaces | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/requests" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/responses" | ||
"github.com/edgexfoundry/go-mod-core-contracts/v3/errors" | ||
) | ||
|
||
// KVSClient defines the interface for interactions with the kvs endpoint on the EdgeX core-keeper service. | ||
type KVSClient interface { | ||
// UpdateValuesByKey updates values of the specified key and the child keys defined in the request payload. | ||
// If no key exists at the given path, the key(s) will be created. | ||
UpdateValuesByKey(ctx context.Context, key string, reqs requests.UpdateKeysRequest) (responses.KeysResponse, errors.EdgeX) | ||
// ValuesByKey returns the values of the specified key prefix. | ||
ValuesByKey(ctx context.Context, key string) (responses.MultiKeyValueResponse, errors.EdgeX) | ||
// ListKeys returns the list of the keys with the specified key prefix. | ||
ListKeys(ctx context.Context, key string) (responses.KeysResponse, errors.EdgeX) | ||
// DeleteKey deletes the specified key. | ||
DeleteKey(ctx context.Context, key string) (responses.KeysResponse, errors.EdgeX) | ||
// DeleteKeysByPrefix deletes all keys with the specified prefix. | ||
DeleteKeysByPrefix(ctx context.Context, key string) (responses.KeysResponse, errors.EdgeX) | ||
} |
Oops, something went wrong.