Skip to content

Commit d4ff6f1

Browse files
committed
Add kv "entry" actions
An "entry" is a JSON object with properties. Multiple entries are stored under a JSON serialized object (as created by st2.kv.set_object) in the datastore. You can upsert or delete properties within an entry. And, you can define a fallback entry (which is named "default" by default) to allow getting properties from an entry, and using the fallback entry to provide defaults for any properties that are undfined in the entry. One use-case for objects like this is to store environment-specific (eg dev, stage, prod, etc) variables with global defaults. Each environment would be an entry and "global" could be the fallback entry. The variables would be properties inside the entries. Signed-off-by: Jacob Floyd <cognifloyd@gmail.com>
1 parent 0fc8c16 commit d4ff6f1

File tree

8 files changed

+210
-1
lines changed

8 files changed

+210
-1
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ You can also use dynamic values from the datastore. See the
4242
is serialized as JSON.
4343
* ``kv.grep_object`` - Find datastore items which name matches the provided query
4444
amd deserialize their values from JSON serialized objects.
45+
46+
* ``kv.get_entry`` - Retrieve entry object in a JSON serialized object from the
47+
datastore. An entry is a standard JSON object with properties.
48+
Fails if the datastore key does not exist.
49+
* ``kv.upsert_entry_property`` - Update or insert a property in the named entry
50+
of a JSON serialized object. Then, serialize and store the updated object.
51+
The property's value may be any json-serializable type.
52+
Fails if the datastore key does not exist.
53+
* ``kv.delete_entry_property`` - Delete a property from a named entry of a JSON
54+
serialized object in the datastore. If the entry is empty, delete it as well.
55+
Fails if the datastore key does not exist.
4556

4657
Note: ``kv.set`` and ``kv.get`` actions support compressing value before
4758
storing it in a datastore and decompressing it when retrieving it from
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import json
2+
3+
from lib.action import St2BaseAction
4+
5+
__all__ = [
6+
'St2KVPDeleteEntryPropertyAction'
7+
]
8+
9+
10+
class St2KVPDeleteEntryPropertyAction(St2BaseAction):
11+
# noinspection PyShadowingBuiltins
12+
def run(self, key, entry, property):
13+
# get and deserialize object or fail.
14+
_key = self.client.keys.get_by_name(key, decrypt=False)
15+
16+
if not _key:
17+
raise Exception("Key does not exist in datastore")
18+
19+
deserialized = json.loads(_key.value)
20+
21+
# delete object.entry.property
22+
_entry = deserialized.get(entry, {})
23+
try:
24+
del _entry[property]
25+
except KeyError:
26+
pass
27+
28+
# delete object.entry if entry is empty
29+
if not _entry:
30+
try:
31+
del deserialized[entry]
32+
except KeyError:
33+
pass
34+
35+
# re-serialize and save
36+
serialized = json.dumps(deserialized)
37+
kvp = self._kvp(name=key, value=serialized)
38+
kvp.id = key
39+
40+
self.client.keys.update(kvp)
41+
response = {
42+
'key': key,
43+
'entry_name': entry,
44+
'entry': _entry,
45+
'entry_deleted': not _entry,
46+
}
47+
return response
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
name: 'kv.delete_entry_property'
3+
enabled: true
4+
description: |
5+
Delete a property (if it exists) under an named entry (sub-object) of a
6+
serialized object in kv datastore. If the entry is empty after deleting
7+
the property, this deletes the entry as well.
8+
Fails if the key does not exist in the datastore.
9+
The kv object uses this structure: { entry: { property: value, ... }, entry: {}, ...}
10+
This action does not support encrypted datastore objects.
11+
12+
runner_type: python-script
13+
entry_point: kv_delete_entry_property.py
14+
parameters:
15+
key:
16+
required: true
17+
type: string
18+
entry:
19+
description: name of the key's entry
20+
required: false
21+
type: string
22+
default: default
23+
property:
24+
description: name of the entry's property to upsert
25+
required: true
26+
type: string

actions/kv_get_entry.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import json
2+
3+
from lib.action import St2BaseAction
4+
5+
__all__ = [
6+
'St2KVPGetEntryAction'
7+
]
8+
9+
10+
class St2KVPGetEntryAction(St2BaseAction):
11+
# noinspection PyShadowingBuiltins
12+
def run(self, key, fallback, entry):
13+
# get and deserialize object or fail.
14+
_key = self.client.keys.get_by_name(key, decrypt=False)
15+
16+
if not _key:
17+
raise Exception("Key does not exist in datastore")
18+
19+
deserialized = json.loads(_key.value)
20+
21+
# try get object.entry.property
22+
_entry = deserialized.get(fallback, {})
23+
_entry.update(deserialized.get(entry, {}))
24+
25+
return _entry

actions/kv_get_entry.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
name: 'kv.get_entry'
3+
enabled: true
4+
description: |
5+
Get a named entry (sub-object) of a serialized object in kv datastore
6+
merged on top of the fallback entry (ie the fallback provides defaults).
7+
Fails if the key does not exist in the datastore.
8+
Does not fail if the entry or fallback is missing.
9+
The kv object uses this structure: { fallback: { property: value, ... }, entry: {}, ...}
10+
This action does not support encrypted datastore objects.
11+
12+
runner_type: python-script
13+
entry_point: kv_get_entry.py
14+
parameters:
15+
key:
16+
required: true
17+
type: string
18+
fallback:
19+
description: name of the key's fallback entry
20+
required: false
21+
type: string
22+
default: default
23+
entry:
24+
description: name of the key's entry
25+
required: true
26+
type: string
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import json
2+
3+
from lib.action import St2BaseAction
4+
5+
__all__ = [
6+
'St2KVPUpsertEntryPropertyAction'
7+
]
8+
9+
10+
class St2KVPUpsertEntryPropertyAction(St2BaseAction):
11+
# noinspection PyShadowingBuiltins
12+
def run(self, key, entry, property, value):
13+
# get and deserialize object or fail.
14+
_key = self.client.keys.get_by_name(key, decrypt=False)
15+
16+
if not _key:
17+
raise Exception("Key does not exist in datastore")
18+
19+
# optimistically try to decode a json value
20+
try:
21+
value = json.loads(value)
22+
except (TypeError, ValueError):
23+
# assume it is either already decoded (TypeError)
24+
# or it is a plain string (ValueError)
25+
# (malformed JSON objects/arrays will be strings)
26+
pass
27+
28+
deserialized = json.loads(_key.value)
29+
30+
# update or insert object.entry.property
31+
_entry = deserialized.get(entry, {})
32+
_entry[property] = value
33+
deserialized[entry] = _entry
34+
35+
# re-serialize and save
36+
serialized = json.dumps(deserialized)
37+
kvp = self._kvp(name=key, value=serialized)
38+
kvp.id = key
39+
40+
self.client.keys.update(kvp)
41+
response = {
42+
'key': key,
43+
'entry_name': entry,
44+
'entry': _entry,
45+
}
46+
return response
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
name: 'kv.upsert_entry_property'
3+
enabled: true
4+
description: |
5+
Insert or update a property under an named entry (sub-object) of a serialized object in kv datastore.
6+
Fails if the key does not exist in the datastore, as a property inside it can't be upserted yet.
7+
The kv object uses this structure: { entry: { property: value, ... }, entry: {}, ...}
8+
This action does not support encrypted datastore objects.
9+
10+
runner_type: python-script
11+
entry_point: kv_upsert_entry_property.py
12+
parameters:
13+
key:
14+
required: true
15+
type: string
16+
entry:
17+
description: name of the key's entry
18+
required: false
19+
type: string
20+
default: default
21+
property:
22+
description: name of the entry's property to upsert
23+
required: true
24+
type: string
25+
value:
26+
description: the property value may be any json-serialible type (string, number, array, object)
27+
required: true
28+
# type: any

pack.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
ref: st2
33
name: st2
44
description: StackStorm utility actions and aliases
5-
version: 1.3.1
5+
version: 1.4.0
66
python_versions:
77
- "2"
88
- "3"

0 commit comments

Comments
 (0)