Skip to content

Commit

Permalink
Merge pull request #418 from reubenmiller/feat-support-duplicate-quer…
Browse files Browse the repository at this point in the history
…y-parameters

feat: allow duplicate keys when using customQueryParam global flag
  • Loading branch information
reubenmiller authored Dec 11, 2024
2 parents 6da3dab + dd1940c commit 58f41f9
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ jobs:
chmod +x /home/runner/.local/bin/c8y
echo "/home/runner/.local/bin" >> $GITHUB_PATH
c8y version
c8y currentuser get --select id
c8y currentuser get --select id --debug
task test-cli
shell: bash
timeout-minutes: 20
Expand Down
20 changes: 18 additions & 2 deletions pkg/flags/getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ func WithQueryParameters(cmd *cobra.Command, query *QueryTemplate, inputIterator
query.SetVariable(key, url.EscapeQueryString(val))
}
}
case map[string][]string:
// Collect values
for key, values := range v {
encodedValues := make([]string, 0, len(values))
for _, value := range values {
encodedValues = append(encodedValues, url.EscapeQueryString(value))
}
query.SetVariable(key, encodedValues)
}
case AnyString:
query.SetVariable(name, string(v))
default:
Expand Down Expand Up @@ -483,7 +492,9 @@ func WithCustomStringSlice(valuesFunc func() ([]string, error), opts ...string)
dst = ""
}

outputValues := make(map[string]string)
// Support setting multiple values for query parameters
// e.g. foo=bar1&foo=bar2
outputValues := make(map[string][]string)
for _, v := range values {
parts := strings.Split(v, ":")
if len(parts) != 2 {
Expand All @@ -492,7 +503,12 @@ func WithCustomStringSlice(valuesFunc func() ([]string, error), opts ...string)
continue
}
}
outputValues[strings.TrimSpace(parts[0])] = strings.TrimSpace(applyFormatter(format, parts[1]))
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(applyFormatter(format, parts[1]))
if _, ok := outputValues[key]; !ok {
outputValues[key] = make([]string, 0, 1)
}
outputValues[key] = append(outputValues[key], value)
}

return dst, outputValues, err
Expand Down
10 changes: 10 additions & 0 deletions tests/manual/api/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@ tests:
path: /inventory/managedObjects
query: myValue=2&pageSize=1

It adds multiple custom query parameters to an outgoing request with the same key:
command: |
echo "/inventory/managedObjects?pageSize=1" |
c8y api --customQueryParam "myValue=2" --customQueryParam "myValue=3" --customQueryParam other=value
stdout:
json:
method: GET
path: /inventory/managedObjects
query: myValue=2&myValue=3&other=value&pageSize=1

It accepts positional arguments for method and path (not using pipeline):
command: |
c8y api GET "/alarm/alarms?pageSize=10&status=ACTIVE"
Expand Down
3 changes: 2 additions & 1 deletion tests/manual/common/select/select_context_shaping.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ tests:

It adds nested objects under a property name when using globstar **:
command: |
c8y devices list --select "id:id,links:**.self" --pageSize 1
echo '{"additionParents":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/additionParents"},"assetParents":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/assetParents"},"c8y_IsDevice":{},"c8y_SupportedLogs":["software-management"],"c8y_SupportedOperations":["c8y_LogfileRequest","c8y_Restart","c8y_SoftwareUpdate"],"childAdditions":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/childAdditions"},"childAssets":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/childAssets"},"childDevices":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/childDevices"},"com_cumulocity_model_Agent":{},"creationTime":"2022-02-15T17:43:55.737Z","deviceParents":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/deviceParents"},"id":"12345","lastUpdated":"2022-02-23T11:57:02.657Z","name":"test","owner":"device_test","self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345","type":"thin-edge.io"}' |
c8y util show --select "id:id,links:**.self"
stdout:
json:
id: r/\d+$
Expand Down
6 changes: 4 additions & 2 deletions tests/manual/common/select/select_flat_selection.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,16 @@ tests:

It returns only objects on the selected level with the self property (1 level):
command: |
c8y devices list --select "*.self" --pageSize 1
echo '{"additionParents":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/additionParents"},"assetParents":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/assetParents"},"c8y_IsDevice":{},"c8y_SupportedLogs":["software-management"],"c8y_SupportedOperations":["c8y_LogfileRequest","c8y_Restart","c8y_SoftwareUpdate"],"childAdditions":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/childAdditions"},"childAssets":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/childAssets"},"childDevices":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/childDevices"},"com_cumulocity_model_Agent":{},"creationTime":"2022-02-15T17:43:55.737Z","deviceParents":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/deviceParents"},"id":"12345","lastUpdated":"2022-02-23T11:57:02.657Z","name":"test","owner":"device_test","self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345","type":"thin-edge.io"}' |
c8y util show --select "*.self"
stdout:
# no globstar was used, so depth matching is explicit by number of dots
match-pattern: '{"additionParents":{"self":".+"},"assetParents":{"self":".+"},"childAdditions":{"self":".+"},"childAssets":{"self":".+"},"childDevices":{"self":".+"},"deviceParents":{"self":".+"}}'

It returns only objects on the selected level with the self property (multi level):
command: |
c8y devices list --select "**.self" --pageSize 1
echo '{"additionParents":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/additionParents"},"assetParents":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/assetParents"},"c8y_IsDevice":{},"c8y_SupportedLogs":["software-management"],"c8y_SupportedOperations":["c8y_LogfileRequest","c8y_Restart","c8y_SoftwareUpdate"],"childAdditions":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/childAdditions"},"childAssets":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/childAssets"},"childDevices":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/childDevices"},"com_cumulocity_model_Agent":{},"creationTime":"2022-02-15T17:43:55.737Z","deviceParents":{"references":[],"self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345/deviceParents"},"id":"12345","lastUpdated":"2022-02-23T11:57:02.657Z","name":"test","owner":"device_test","self":"https://t12345.example.cumulocity.com/inventory/managedObjects/12345","type":"thin-edge.io"}' |
c8y util show --select "**.self"
stdout:
# globstar was used, so matching can occur on any depth
match-pattern: '{"additionParents":{"self":".+"},"assetParents":{"self":".+"},"childAdditions":{"self":".+"},"childAssets":{"self":".+"},"childDevices":{"self":".+"},"deviceParents":{"self":".+"},"self":".+"}'
Expand Down

0 comments on commit 58f41f9

Please sign in to comment.