Skip to content

Commit 18774c9

Browse files
Add id_attribute json list support
1 parent f572783 commit 18774c9

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Have a look at the [examples directory](examples) for some use cases
2424
- `password` (string, optional): When set, will use this password for BASIC auth to the API.
2525
- `headers` (hash of strings, optional): A map of header names and values to set on all outbound requests. This is useful if you want to use a script via the 'external' provider or provide a pre-approved token or change Content-Type from `application/json`. If `username` and `password` are set and Authorization is one of the headers defined here, the BASIC auth credentials take precedence.
2626
- `timeout` (integer, optional): When set, will cause requests taking longer than this time (in seconds) to be aborted. Default is `0` which means no timeout is set.
27-
- `id_attribute` (string, optional): Defaults to `id`. When set, this key will be used to operate on REST objects. For example, if the ID is set to 'name', changes to the API object will be to http://foo.com/bar/VALUE_OF_NAME. This value may also be a '/'-delimeted path to the id attribute if it is multple levels deep in the data (such as `attributes/id` in the case of an object `{ \"attributes\": { \"id\": 1234 }, \"config\": { \"name\": \"foo\", \"something\": \"bar\"}}`
27+
- `id_attribute` (string, optional): Defaults to `id`. When set, this key will be used to operate on REST objects. For example, if the ID is set to 'name', changes to the API object will be to http://foo.com/bar/VALUE_OF_NAME. This value may also be a '/'-delimeted path to the id attribute if it is multple levels deep in the data (such as `attributes/id` in the case of an object `{ \"attributes\": { \"id\": 1234 }, \"config\": { \"name\": \"foo\", \"something\": \"bar\"}}`. Lists are also supported, f.e. `attributes/items/0/id` in case of an object `{ \"attributes\": { \"items\": [{"id": 1234}] } }`.
2828
- `copy_keys` (array of strings, optional): When set, any `PUT` to the API for an object will copy these keys from the data the provider has gathered about the object. This is useful if internal API information must also be provided with updates, such as the revision of the object.
2929
- `write_returns_object` (boolean, optional): Set this when the API returns the object created on all write operations (`POST`, `PUT`). This is used by the provider to refresh internal data structures.
3030
- `create_returns_object` (boolean, optional): Set this when the API returns the object created only on creation operations (`POST`). This is used by the provider to refresh internal data structures.

restapi/common.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,14 @@ func GetObjectAtKey(data map[string]interface{}, path string, debug bool) (inter
119119
if tmp, ok := hash[part].(map[string]interface{});ok {
120120
if debug { log.Printf("common.go:GetObjectAtKey: %s - is a map", part) }
121121
hash = tmp
122+
} else if tmp, ok := hash[part].([]interface{}); ok {
123+
if debug { log.Printf("common.go:GetObjectAtKey: %s - is a list", part) }
124+
mapString := make(map[string]interface{})
125+
for key, value := range tmp {
126+
strKey := fmt.Sprintf("%v", key)
127+
mapString[strKey] = value
128+
}
129+
hash = mapString
122130
} else {
123131
if debug { log.Printf("common.go:GetObjectAtKey: %s - is a %T", part, hash[part]) }
124132
return nil, fmt.Errorf("GetObjectAtKey: Object at '%s' is not a map. Is this the right path?", seen)

restapi/common_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,50 @@ func TestGetStringAtKey(t *testing.T) {
104104
t.Fatalf("Error: Expected '1', but got %s", res)
105105
}
106106
}
107+
108+
func TestGetListStringAtKey(t *testing.T) {
109+
debug := false
110+
test_obj := make(map[string]interface{})
111+
err := json.Unmarshal([]byte(`
112+
{
113+
"rootFoo": "bar",
114+
"items": [
115+
{
116+
"foo": "bar",
117+
"number": 1,
118+
"list_numbers": [1, 2, 3],
119+
"test": [{"id": "3333"}, {"id": "1337"}],
120+
"resource": {
121+
"id": "123"
122+
}
123+
}
124+
]
125+
}
126+
`), &test_obj)
127+
if nil != err {
128+
t.Fatalf("Error unmarshalling JSON: %s", err)
129+
}
130+
131+
var res string
132+
133+
res, err = GetStringAtKey(test_obj, "items/0/resource/id", debug)
134+
if err != nil {
135+
t.Fatalf("Error extracting 'resource' from JSON payload: %s", err)
136+
} else if "123" != res {
137+
t.Fatalf("Error: Expected '123', but got %s", res)
138+
}
139+
140+
res, err = GetStringAtKey(test_obj, "items/0/test/1/id", debug)
141+
if err != nil {
142+
t.Fatalf("Error extracting 'resource' from JSON payload: %s", err)
143+
} else if "1337" != res {
144+
t.Fatalf("Error: Expected '1337', but got %s", res)
145+
}
146+
147+
res, err = GetStringAtKey(test_obj, "items/0/list_numbers/1", debug)
148+
if err != nil {
149+
t.Fatalf("Error extracting 'resource' from JSON payload: %s", err)
150+
} else if "2" != res {
151+
t.Fatalf("Error: Expected '2', but got %s", res)
152+
}
153+
}

0 commit comments

Comments
 (0)