Skip to content

Commit

Permalink
Merge pull request #806 from weichou1229/issue-773
Browse files Browse the repository at this point in the history
feat: Use URL escape for device command name and resource name
  • Loading branch information
cloudxxx8 authored Mar 1, 2023
2 parents 1751cc4 + 1450102 commit 9073418
Show file tree
Hide file tree
Showing 12 changed files with 45 additions and 41 deletions.
10 changes: 5 additions & 5 deletions clients/http/command.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (C) 2021-2022 IOTech Ltd
// Copyright (C) 2021-2023 IOTech Ltd
// Copyright (C) 2023 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
Expand Down Expand Up @@ -62,7 +62,7 @@ func (client *CommandClient) IssueGetCommandByName(ctx context.Context, deviceNa
requestParams := url.Values{}
requestParams.Set(common.PushEvent, strconv.FormatBool(dsPushEvent))
requestParams.Set(common.ReturnEvent, strconv.FormatBool(dsReturnEvent))
requestPath := path.Join(common.ApiDeviceRoute, common.Name, deviceName, commandName)
requestPath := utils.EscapeAndJoinPath(common.ApiDeviceRoute, common.Name, deviceName, commandName)
err = utils.GetRequest(ctx, &res, client.baseUrl, requestPath, requestParams, client.authInjector)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
Expand All @@ -76,7 +76,7 @@ func (client *CommandClient) IssueGetCommandByNameWithQueryParams(ctx context.Co
requestParams.Set(k, v)
}

requestPath := path.Join(common.ApiDeviceRoute, common.Name, url.QueryEscape(deviceName), url.QueryEscape(commandName))
requestPath := utils.EscapeAndJoinPath(common.ApiDeviceRoute, common.Name, url.QueryEscape(deviceName), url.QueryEscape(commandName))
err = utils.GetRequest(ctx, &res, client.baseUrl, requestPath, requestParams, client.authInjector)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
Expand All @@ -86,7 +86,7 @@ func (client *CommandClient) IssueGetCommandByNameWithQueryParams(ctx context.Co

// IssueSetCommandByName issues the specified write command referenced by the command name to the device/sensor that is also referenced by name.
func (client *CommandClient) IssueSetCommandByName(ctx context.Context, deviceName string, commandName string, settings map[string]string) (res dtoCommon.BaseResponse, err errors.EdgeX) {
requestPath := path.Join(common.ApiDeviceRoute, common.Name, deviceName, commandName)
requestPath := utils.EscapeAndJoinPath(common.ApiDeviceRoute, common.Name, deviceName, commandName)
err = utils.PutRequest(ctx, &res, client.baseUrl, requestPath, nil, settings, client.authInjector)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
Expand All @@ -96,7 +96,7 @@ func (client *CommandClient) IssueSetCommandByName(ctx context.Context, deviceNa

// IssueSetCommandByNameWithObject issues the specified write command and the settings supports object value type
func (client *CommandClient) IssueSetCommandByNameWithObject(ctx context.Context, deviceName string, commandName string, settings map[string]interface{}) (res dtoCommon.BaseResponse, err errors.EdgeX) {
requestPath := path.Join(common.ApiDeviceRoute, common.Name, deviceName, commandName)
requestPath := utils.EscapeAndJoinPath(common.ApiDeviceRoute, common.Name, deviceName, commandName)
err = utils.PutRequest(ctx, &res, client.baseUrl, requestPath, nil, settings, client.authInjector)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
Expand Down
8 changes: 4 additions & 4 deletions clients/http/deviceprofile.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (C) 2020-2022 IOTech Ltd
// Copyright (C) 2020-2023 IOTech Ltd
// Copyright (C) 2023 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
Expand Down Expand Up @@ -162,7 +162,7 @@ func (client *DeviceProfileClient) DeviceResourceByProfileNameAndResourceName(ct
if exists {
return res, nil
}
requestPath := path.Join(common.ApiDeviceResourceRoute, common.Profile, profileName, common.Resource, resourceName)
requestPath := utils.EscapeAndJoinPath(common.ApiDeviceResourceRoute, common.Profile, profileName, common.Resource, resourceName)
err := utils.GetRequest(ctx, &res, client.baseUrl, requestPath, nil, client.authInjector)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
Expand Down Expand Up @@ -223,7 +223,7 @@ func (client *DeviceProfileClient) UpdateDeviceProfileResource(ctx context.Conte
// DeleteDeviceResourceByName deletes device resource by name
func (client *DeviceProfileClient) DeleteDeviceResourceByName(ctx context.Context, profileName string, resourceName string) (dtoCommon.BaseResponse, errors.EdgeX) {
var response dtoCommon.BaseResponse
requestPath := path.Join(common.ApiDeviceProfileRoute, common.Name, url.QueryEscape(profileName), common.Resource, url.QueryEscape(resourceName))
requestPath := utils.EscapeAndJoinPath(common.ApiDeviceProfileRoute, common.Name, url.QueryEscape(profileName), common.Resource, url.QueryEscape(resourceName))
err := utils.DeleteRequest(ctx, &response, client.baseUrl, requestPath, client.authInjector)
if err != nil {
return response, errors.NewCommonEdgeXWrapper(err)
Expand Down Expand Up @@ -254,7 +254,7 @@ func (client *DeviceProfileClient) UpdateDeviceProfileDeviceCommand(ctx context.
// DeleteDeviceCommandByName deletes device command by name
func (client *DeviceProfileClient) DeleteDeviceCommandByName(ctx context.Context, profileName string, commandName string) (dtoCommon.BaseResponse, errors.EdgeX) {
var response dtoCommon.BaseResponse
requestPath := path.Join(common.ApiDeviceProfileRoute, common.Name, url.QueryEscape(profileName), common.DeviceCommand, url.QueryEscape(commandName))
requestPath := utils.EscapeAndJoinPath(common.ApiDeviceProfileRoute, common.Name, url.QueryEscape(profileName), common.DeviceCommand, url.QueryEscape(commandName))
err := utils.DeleteRequest(ctx, &response, client.baseUrl, requestPath, client.authInjector)
if err != nil {
return response, errors.NewCommonEdgeXWrapper(err)
Expand Down
9 changes: 4 additions & 5 deletions clients/http/deviceservicecommand.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (C) 2021 IOTech Ltd
// Copyright (C) 2021-2023 IOTech Ltd
// Copyright (C) 2023 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
Expand All @@ -10,7 +10,6 @@ import (
"context"
"encoding/json"
"net/url"
"path"

"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/http/utils"
"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/interfaces"
Expand All @@ -35,7 +34,7 @@ func NewDeviceServiceCommandClient(authInjector interfaces.AuthenticationInjecto

// GetCommand sends HTTP request to execute the Get command
func (client *deviceServiceCommandClient) GetCommand(ctx context.Context, baseUrl string, deviceName string, commandName string, queryParams string) (*responses.EventResponse, errors.EdgeX) {
requestPath := path.Join(common.ApiDeviceRoute, common.Name, deviceName, commandName)
requestPath := utils.EscapeAndJoinPath(common.ApiDeviceRoute, common.Name, deviceName, commandName)
params, err := url.ParseQuery(queryParams)
if err != nil {
return nil, errors.NewCommonEdgeXWrapper(err)
Expand Down Expand Up @@ -65,7 +64,7 @@ func (client *deviceServiceCommandClient) GetCommand(ctx context.Context, baseUr
// SetCommand sends HTTP request to execute the Set command
func (client *deviceServiceCommandClient) SetCommand(ctx context.Context, baseUrl string, deviceName string, commandName string, queryParams string, settings map[string]string) (dtoCommon.BaseResponse, errors.EdgeX) {
var response dtoCommon.BaseResponse
requestPath := path.Join(common.ApiDeviceRoute, common.Name, deviceName, commandName)
requestPath := utils.EscapeAndJoinPath(common.ApiDeviceRoute, common.Name, deviceName, commandName)
params, err := url.ParseQuery(queryParams)
if err != nil {
return response, errors.NewCommonEdgeXWrapper(err)
Expand All @@ -80,7 +79,7 @@ func (client *deviceServiceCommandClient) SetCommand(ctx context.Context, baseUr
// SetCommandWithObject invokes device service's set command API and the settings supports object value type
func (client *deviceServiceCommandClient) SetCommandWithObject(ctx context.Context, baseUrl string, deviceName string, commandName string, queryParams string, settings map[string]interface{}) (dtoCommon.BaseResponse, errors.EdgeX) {
var response dtoCommon.BaseResponse
requestPath := path.Join(common.ApiDeviceRoute, common.Name, deviceName, commandName)
requestPath := utils.EscapeAndJoinPath(common.ApiDeviceRoute, common.Name, deviceName, commandName)
params, err := url.ParseQuery(queryParams)
if err != nil {
return response, errors.NewCommonEdgeXWrapper(err)
Expand Down
2 changes: 1 addition & 1 deletion clients/http/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func NewEventClient(baseUrl string, authInjector interfaces.AuthenticationInject

func (ec *eventClient) Add(ctx context.Context, serviceName string, req requests.AddEventRequest) (
dtoCommon.BaseWithIdResponse, errors.EdgeX) {
path := path.Join(common.ApiEventRoute, serviceName, req.Event.ProfileName, req.Event.DeviceName, req.Event.SourceName)
path := utils.EscapeAndJoinPath(common.ApiEventRoute, serviceName, req.Event.ProfileName, req.Event.DeviceName, req.Event.SourceName)
var br dtoCommon.BaseWithIdResponse

bytes, encoding, err := req.Encode()
Expand Down
12 changes: 6 additions & 6 deletions clients/http/reading.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (C) 2020-2021 IOTech Ltd
// Copyright (C) 2020-2023 IOTech Ltd
// Copyright (C) 2023 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
Expand Down Expand Up @@ -78,7 +78,7 @@ func (rc readingClient) ReadingsByDeviceName(ctx context.Context, name string, o
}

func (rc readingClient) ReadingsByResourceName(ctx context.Context, name string, offset, limit int) (responses.MultiReadingsResponse, errors.EdgeX) {
requestPath := path.Join(common.ApiReadingRoute, common.ResourceName, name)
requestPath := utils.EscapeAndJoinPath(common.ApiReadingRoute, common.ResourceName, name)
requestParams := url.Values{}
requestParams.Set(common.Offset, strconv.Itoa(offset))
requestParams.Set(common.Limit, strconv.Itoa(limit))
Expand All @@ -105,7 +105,7 @@ func (rc readingClient) ReadingsByTimeRange(ctx context.Context, start, end, off

// ReadingsByResourceNameAndTimeRange returns readings by resource name and specified time range. Readings are sorted in descending order of origin time.
func (rc readingClient) ReadingsByResourceNameAndTimeRange(ctx context.Context, name string, start, end, offset, limit int) (responses.MultiReadingsResponse, errors.EdgeX) {
requestPath := path.Join(common.ApiReadingRoute, common.ResourceName, name, common.Start, strconv.Itoa(start), common.End, strconv.Itoa(end))
requestPath := utils.EscapeAndJoinPath(common.ApiReadingRoute, common.ResourceName, name, common.Start, strconv.Itoa(start), common.End, strconv.Itoa(end))
requestParams := url.Values{}
requestParams.Set(common.Offset, strconv.Itoa(offset))
requestParams.Set(common.Limit, strconv.Itoa(limit))
Expand All @@ -118,7 +118,7 @@ func (rc readingClient) ReadingsByResourceNameAndTimeRange(ctx context.Context,
}

func (rc readingClient) ReadingsByDeviceNameAndResourceName(ctx context.Context, deviceName, resourceName string, offset, limit int) (responses.MultiReadingsResponse, errors.EdgeX) {
requestPath := path.Join(common.ApiReadingRoute, common.Device, common.Name, deviceName, common.ResourceName, resourceName)
requestPath := utils.EscapeAndJoinPath(common.ApiReadingRoute, common.Device, common.Name, deviceName, common.ResourceName, resourceName)
requestParams := url.Values{}
requestParams.Set(common.Offset, strconv.Itoa(offset))
requestParams.Set(common.Limit, strconv.Itoa(limit))
Expand All @@ -132,7 +132,7 @@ func (rc readingClient) ReadingsByDeviceNameAndResourceName(ctx context.Context,
}

func (rc readingClient) ReadingsByDeviceNameAndResourceNameAndTimeRange(ctx context.Context, deviceName, resourceName string, start, end, offset, limit int) (responses.MultiReadingsResponse, errors.EdgeX) {
requestPath := path.Join(common.ApiReadingRoute, common.Device, common.Name, deviceName, common.ResourceName, resourceName, common.Start, strconv.Itoa(start), common.End, strconv.Itoa(end))
requestPath := utils.EscapeAndJoinPath(common.ApiReadingRoute, common.Device, common.Name, deviceName, common.ResourceName, resourceName, common.Start, strconv.Itoa(start), common.End, strconv.Itoa(end))
requestParams := url.Values{}
requestParams.Set(common.Offset, strconv.Itoa(offset))
requestParams.Set(common.Limit, strconv.Itoa(limit))
Expand All @@ -145,7 +145,7 @@ func (rc readingClient) ReadingsByDeviceNameAndResourceNameAndTimeRange(ctx cont
}

func (rc readingClient) ReadingsByDeviceNameAndResourceNamesAndTimeRange(ctx context.Context, deviceName string, resourceNames []string, start, end, offset, limit int) (responses.MultiReadingsResponse, errors.EdgeX) {
requestPath := path.Join(common.ApiReadingRoute, common.Device, common.Name, deviceName, common.Start, strconv.Itoa(start), common.End, strconv.Itoa(end))
requestPath := utils.EscapeAndJoinPath(common.ApiReadingRoute, common.Device, common.Name, deviceName, common.Start, strconv.Itoa(start), common.End, strconv.Itoa(end))
requestParams := url.Values{}
requestParams.Set(common.Offset, strconv.Itoa(offset))
requestParams.Set(common.Limit, strconv.Itoa(limit))
Expand Down
27 changes: 16 additions & 11 deletions clients/http/utils/common.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (C) 2020-2022 IOTech Ltd
// Copyright (C) 2020-2023 IOTech Ltd
// Copyright (C) 2023 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
Expand Down Expand Up @@ -75,11 +75,10 @@ func makeRequest(req *http.Request, authInjector interfaces.AuthenticationInject
}

func createRequest(ctx context.Context, httpMethod string, baseUrl string, requestPath string, requestParams url.Values) (*http.Request, errors.EdgeX) {
u, err := url.Parse(baseUrl)
u, err := url.Parse(baseUrl + requestPath)
if err != nil {
return nil, errors.NewCommonEdgeX(errors.KindServerError, "fail to parse baseUrl", err)
}
u.Path = path.Join(u.Path, requestPath)
if requestParams != nil {
u.RawQuery = requestParams.Encode()
}
Expand All @@ -92,11 +91,10 @@ func createRequest(ctx context.Context, httpMethod string, baseUrl string, reque
}

func createRequestWithRawDataAndParams(ctx context.Context, httpMethod string, baseUrl string, requestPath string, requestParams url.Values, data interface{}) (*http.Request, errors.EdgeX) {
u, err := url.Parse(baseUrl)
u, err := url.Parse(baseUrl + requestPath)
if err != nil {
return nil, errors.NewCommonEdgeX(errors.KindServerError, "fail to parse baseUrl", err)
}
u.Path = path.Join(u.Path, requestPath)
if requestParams != nil {
u.RawQuery = requestParams.Encode()
}
Expand All @@ -120,11 +118,10 @@ func createRequestWithRawDataAndParams(ctx context.Context, httpMethod string, b
}

func createRequestWithRawData(ctx context.Context, httpMethod string, baseUrl string, requestPath string, requestParams url.Values, data interface{}) (*http.Request, errors.EdgeX) {
u, err := url.Parse(baseUrl)
u, err := url.Parse(baseUrl + requestPath)
if err != nil {
return nil, errors.NewCommonEdgeX(errors.KindServerError, "fail to parse baseUrl", err)
}
u.Path = path.Join(u.Path, requestPath)
if requestParams != nil {
u.RawQuery = requestParams.Encode()
}
Expand All @@ -149,11 +146,10 @@ func createRequestWithRawData(ctx context.Context, httpMethod string, baseUrl st
}

func createRequestWithEncodedData(ctx context.Context, httpMethod string, baseUrl string, requestPath string, data []byte, encoding string) (*http.Request, errors.EdgeX) {
u, err := url.Parse(baseUrl)
u, err := url.Parse(baseUrl + requestPath)
if err != nil {
return nil, errors.NewCommonEdgeX(errors.KindServerError, "fail to parse baseUrl", err)
}
u.Path = path.Join(u.Path, requestPath)

content := encoding
if content == "" {
Expand All @@ -171,11 +167,10 @@ func createRequestWithEncodedData(ctx context.Context, httpMethod string, baseUr

// createRequestFromFilePath creates multipart/form-data request with the specified file
func createRequestFromFilePath(ctx context.Context, httpMethod string, baseUrl string, requestPath string, filePath string) (*http.Request, errors.EdgeX) {
u, err := url.Parse(baseUrl)
u, err := url.Parse(baseUrl + requestPath)
if err != nil {
return nil, errors.NewCommonEdgeX(errors.KindServerError, "fail to parse baseUrl", err)
}
u.Path = path.Join(u.Path, requestPath)

fileContents, err := ioutil.ReadFile(filePath)
if err != nil {
Expand Down Expand Up @@ -226,3 +221,13 @@ func sendRequest(ctx context.Context, req *http.Request, authInjector interfaces
errKind := errors.KindMapping(resp.StatusCode)
return nil, errors.NewCommonEdgeX(errKind, msg, nil)
}

// EscapeAndJoinPath escape and join the path variables
func EscapeAndJoinPath(apiRoutePath string, pathVariables ...string) string {
elements := make([]string, len(pathVariables)+1)
elements[0] = apiRoutePath // we don't need to escape the route path like /device, /reading, ...,etc.
for i, e := range pathVariables {
elements[i+1] = url.QueryEscape(e)
}
return path.Join(elements...)
}
2 changes: 1 addition & 1 deletion dtos/corecommand.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type DeviceCoreCommand struct {
// CoreCommand and its properties are defined in the APIv2 specification:
// https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/core-command/2.1.0#/CoreCommand
type CoreCommand struct {
Name string `json:"name" validate:"required,edgex-dto-none-empty-string,edgex-dto-rfc3986-unreserved-chars"`
Name string `json:"name" validate:"required,edgex-dto-none-empty-string"`
Get bool `json:"get,omitempty" validate:"required_without=Set"`
Set bool `json:"set,omitempty" validate:"required_without=Get"`
Path string `json:"path,omitempty"`
Expand Down
4 changes: 2 additions & 2 deletions dtos/devicecommand.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import "github.com/edgexfoundry/go-mod-core-contracts/v3/models"
// DeviceCommand and its properties are defined in the APIv2 specification:
// https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/core-metadata/2.1.0#/DeviceCommand
type DeviceCommand struct {
Name string `json:"name" yaml:"name" validate:"required,edgex-dto-none-empty-string,edgex-dto-rfc3986-unreserved-chars"`
Name string `json:"name" yaml:"name" validate:"required,edgex-dto-none-empty-string"`
IsHidden bool `json:"isHidden" yaml:"isHidden"`
ReadWrite string `json:"readWrite" yaml:"readWrite" validate:"required,oneof='R' 'W' 'RW' 'WR'"`
ResourceOperations []ResourceOperation `json:"resourceOperations" yaml:"resourceOperations" validate:"gt=0,dive"`
Expand All @@ -20,7 +20,7 @@ type DeviceCommand struct {
// UpdateDeviceCommand and its properties are defined in the APIv2 specification:
// https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/core-metadata/2.2.0#/DeviceCommand
type UpdateDeviceCommand struct {
Name *string `json:"name" validate:"required,edgex-dto-none-empty-string,edgex-dto-rfc3986-unreserved-chars"`
Name *string `json:"name" validate:"required,edgex-dto-none-empty-string"`
IsHidden *bool `json:"isHidden"`
}

Expand Down
4 changes: 2 additions & 2 deletions dtos/deviceresource.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import "github.com/edgexfoundry/go-mod-core-contracts/v3/models"
// https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/core-metadata/2.1.0#/DeviceResource
type DeviceResource struct {
Description string `json:"description" yaml:"description"`
Name string `json:"name" yaml:"name" validate:"required,edgex-dto-none-empty-string,edgex-dto-rfc3986-unreserved-chars"`
Name string `json:"name" yaml:"name" validate:"required,edgex-dto-none-empty-string"`
IsHidden bool `json:"isHidden" yaml:"isHidden"`
Properties ResourceProperties `json:"properties" yaml:"properties"`
Attributes map[string]interface{} `json:"attributes" yaml:"attributes"`
Expand All @@ -22,7 +22,7 @@ type DeviceResource struct {
// https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/core-metadata/2.2.0#/DeviceResource
type UpdateDeviceResource struct {
Description *string `json:"description"`
Name *string `json:"name" validate:"required,edgex-dto-none-empty-string,edgex-dto-rfc3986-unreserved-chars"`
Name *string `json:"name" validate:"required,edgex-dto-none-empty-string"`
IsHidden *bool `json:"isHidden"`
}

Expand Down
2 changes: 1 addition & 1 deletion dtos/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type Event struct {
Id string `json:"id" validate:"required,uuid"`
DeviceName string `json:"deviceName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
ProfileName string `json:"profileName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
SourceName string `json:"sourceName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
SourceName string `json:"sourceName" validate:"required"`
Origin int64 `json:"origin" validate:"required"`
Readings []BaseReading `json:"readings" validate:"gt=0,dive,required"`
Tags Tags `json:"tags,omitempty"`
Expand Down
Loading

0 comments on commit 9073418

Please sign in to comment.