Skip to content

Commit 09afbb6

Browse files
authored
Breaking API changes for SM.STATE and SM.TRANSITION (#6)
1 parent ff52441 commit 09afbb6

7 files changed

Lines changed: 88 additions & 113 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ target
88
libredis_state.so
99
libredis_state.Linux-ubuntu20.04-x86_64.1.0.1.zip
1010
results.xml
11+
redis

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# redis-state-machine
22

33
[![Latest Release](https://img.shields.io/github/v/release/redislabsmodules/redis-state-machine?label=latest)](https://github.com/redislabsmodules/redis-state-machine/releases/latest)
4+
[![Dockerhub](https://img.shields.io/badge/dockerhub-redislabs/redisstatemachine-blue)](https://hub.docker.com/r/redislabs/redisstatemachine/tags/)
45

56
A [Redis module](https://redis.io/docs/modules) that maintains a state machine on the server side.
67

@@ -69,15 +70,15 @@ r.execute_command("SM.SET", "mystatemachine", json.dumps(tmpl))
6970
If we try to change our statemachine to an invalid state, Redis returns a nil.
7071

7172
```python
72-
x = r.execute_command("SM.TRANSITION", "mystatemachine", "notastate")
73+
x = r.execute_command("SM.MUTATE", "mystatemachine", "notastate")
7374
print(x)
7475
>>> None
7576
```
7677

7778
If we try to change states to a valid state, we receive an ok.
7879

7980
```python
80-
x = r.execute_command("SM.TRANSITION", "mystatemachine", "blee")
81+
x = r.execute_command("SM.MUTATE", "mystatemachine", "blee")
8182
print(x)
8283
>>> OK
8384
```

commands.json

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,4 @@
11
{
2-
"SM.DEL": {
3-
"summary": "Deletes a state machine",
4-
"since": "0.1.0",
5-
"complexity": "O(N)",
6-
"group": "statemachine",
7-
"arguments": [
8-
{
9-
"name": "key",
10-
"type": "key"
11-
}
12-
]
13-
},
142
"SM.GET": {
153
"summary": "Gets a state machine, or the current state of a machine",
164
"since": "0.1.0",
@@ -43,18 +31,6 @@
4331
}
4432
]
4533
},
46-
"SM.STATES": {
47-
"summary": "Gets the current state associated with a state machine",
48-
"since": "0.1.0",
49-
"complexity": "O(N)",
50-
"group": "statemachine",
51-
"arguments": [
52-
{
53-
"name": "key",
54-
"type": "key"
55-
}
56-
]
57-
},
5834
"SM.RESET": {
5935
"summary": "Resets a state machine to its initial state",
6036
"since": "0.1.0",
@@ -67,7 +43,7 @@
6743
}
6844
]
6945
},
70-
"SM.CURRENT": {
46+
"SM.STATE": {
7147
"summary": "Retrieves the current state machine state",
7248
"since": "0.1.0",
7349
"complexity": "O(N)",
@@ -76,14 +52,28 @@
7652
{
7753
"name": "key",
7854
"type": "key"
55+
},
56+
{
57+
"name": "codition",
58+
"type": "string",
59+
"optional": true,
60+
"arguments": [
61+
{
62+
"name": "list",
63+
"type": "pure-token",
64+
"token": "LIST"
65+
}
66+
]
7967
}
68+
8069
]
8170
},
8271
"SM.CREATE": {
8372
"summary": "Creates a blank state machine and stores it in the named key",
8473
"since": "0.1.0",
8574
"complexity": "O(N)",
8675
"group": "statemachine",
76+
"arity": 2,
8777
"arguments": [
8878
{
8979
"name": "key",
@@ -95,9 +85,10 @@
9585
"summary": "Return the json template used for constructing a state machine",
9686
"since": "0.1.0",
9787
"complexity": "O(1)",
98-
"group": "statemachine"
88+
"group": "statemachine",
89+
"arity": 1
9990
},
100-
"SM.TRANSITION": {
91+
"SM.MUTATE": {
10192
"summary": "Transition the state machine to the specific state",
10293
"since": "0.1.0",
10394
"complexity": "O(1)",
@@ -119,7 +110,7 @@
119110
{
120111
"name": "force",
121112
"type": "pure-token",
122-
"token": "F"
113+
"token": "FORCE"
123114
}
124115
]
125116
}

src/function_delete.rs

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/function_state.rs

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,75 @@ use redis_module::{
55
Context, NextArg, RedisError, RedisResult, RedisString, RedisValue, REDIS_OK,
66
};
77

8-
pub(crate) fn current_state(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
9-
if args.len() != 2 {
8+
pub(crate) fn state(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
9+
if args.len() < 2 || args.len() > 3{
1010
return Err(RedisError::WrongArity);
1111
}
1212
let mut args = args.into_iter().skip(1);
1313

1414
let key = args.next_arg()?;
15+
let list = args.next_arg();
1516

1617
let rkey = RedisKey::open(ctx.ctx, &key);
1718
let value = rkey.get_value::<StateMachine>(&REDIS_SM_TYPE)?;
1819

19-
value.map_or_else(
20-
|| Ok(RedisValue::Null),
21-
|sm| Ok(RedisValue::SimpleString(sm.current().to_string())),
22-
)
23-
}
24-
25-
pub(crate) fn states(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
26-
if args.len() != 2 {
27-
return Err(RedisError::WrongArity);
20+
if value.is_none() {
21+
return Ok(RedisValue::Null);
2822
}
29-
let mut args = args.into_iter().skip(1);
30-
let key = args.next_arg()?;
3123

32-
let rkey = RedisKey::open(ctx.ctx, &key);
33-
let value = rkey.get_value::<StateMachine>(&REDIS_SM_TYPE)?;
24+
// return value.map_or_else(
25+
// || Ok(RedisValue::Null),
26+
// |sm| Ok(RedisValue::SimpleString(sm.current().to_string())),
27+
// );
28+
// }
3429

35-
if let Some(sm) = value {
36-
let mut keys: Vec<RedisValue> = Vec::new();
30+
let sm = value.unwrap();
31+
let mut keys: Vec<RedisValue> = Vec::new();
32+
if ! list.is_err() {
3733
for x in sm.map().keys() {
3834
keys.push(RedisValue::SimpleString(x.to_string()));
3935
}
40-
Ok(RedisValue::Array(keys))
36+
} else if list.unwrap().to_string().to_uppercase() == "LIST" {
37+
keys.push(RedisValue::SimpleString(sm.current().to_string()));
4138
} else {
42-
Ok(RedisValue::Null)
39+
return Ok(RedisValue::Null);
4340
}
41+
Ok(RedisValue::Array(keys))
42+
43+
// if let Some(sm) = value {
44+
// let mut keys: Vec<RedisValue> = Vec::new();
45+
// for x in sm.map().keys() {
46+
// keys.push(RedisValue::SimpleString(x.to_string()));
47+
// }
48+
// Ok(RedisValue::Array(keys))
49+
// } else {
50+
// Ok(RedisValue::Null)
51+
// }
52+
4453
}
4554

46-
pub(crate) fn transition(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
55+
// pub(crate) fn states(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
56+
// if args.len() != 2 {
57+
// return Err(RedisError::WrongArity);
58+
// }
59+
// let mut args = args.into_iter().skip(1);
60+
// let key = args.next_arg()?;
61+
62+
// let rkey = RedisKey::open(ctx.ctx, &key);
63+
// let value = rkey.get_value::<StateMachine>(&REDIS_SM_TYPE)?;
64+
65+
// if let Some(sm) = value {
66+
// let mut keys: Vec<RedisValue> = Vec::new();
67+
// for x in sm.map().keys() {
68+
// keys.push(RedisValue::SimpleString(x.to_string()));
69+
// }
70+
// Ok(RedisValue::Array(keys))
71+
// } else {
72+
// Ok(RedisValue::Null)
73+
// }
74+
// }
75+
76+
pub(crate) fn mutate(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
4777
if args.len() < 3 || args.len() > 5 {
4878
return Err(RedisError::WrongArity);
4979
}
@@ -67,7 +97,7 @@ pub(crate) fn transition(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
6797
return Ok(RedisValue::Null);
6898
}
6999
sm.set_current(target.to_string());
70-
} else if force.unwrap().to_string() == "F" {
100+
} else if force.unwrap().to_string().to_uppercase() == "FORCE" {
71101
sm.set_current(target.to_string());
72102
} else {
73103
return Ok(RedisValue::Null);

src/lib.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ pub const REDIS_SM_TYPE_VERSION: i32 = 1;
77
pub const MODULE_NAME: &str = "RedisStateMachine";
88
pub const MODULE_TYPE: &str = "RedisStateMachine";
99

10-
mod function_delete;
1110
mod function_get;
1211
mod function_set;
1312
mod function_state;
@@ -21,13 +20,11 @@ redis_module! {
2120
commands: [
2221
["SM.GET", function_get::get, "readonly", 0, 0, 0],
2322
["SM.SET", function_set::set, "write deny-oom", 1, 1, 1],
24-
["SM.CURRENT", function_state::current_state, "readonly", 0, 0, 0],
25-
["SM.STATES", function_state::states, "readonly", 0, 0, 0],
26-
["SM.DEL", function_delete::delete, "write", 1, 1, 1],
23+
["SM.STATE", function_state::state, "readonly", 0, 0, 0],
24+
// ["SM.STATES", function_state::states, "readonly", 0, 0, 0],
2725
["SM.CREATE", function_set::create, "write deny-oom", 1, 1, 1],
2826
["SM.TEMPLATE", function_get::template, "readonly", 0, 0, 0],
29-
// ["SM.FORCE", function_set::force_set, "write deny-oom", 1, 1, 1],
3027
["SM.RESET", function_set::reset, "write deny-oom", 1, 1, 1],
31-
["SM.TRANSITION", function_state::transition, "write deny-oom", 1, 1, 1],
28+
["SM.MUTATE", function_state::mutate, "write deny-oom", 1, 1, 1],
3229
],
3330
}

tests/test_commands.py

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def test_set_get(r):
4141
assert bar == valid_with_current
4242

4343

44-
def test_get_current(r):
44+
def test_get_current_state(r):
4545
r.flushdb()
4646
initial = "begin"
4747
mapstates = {
@@ -50,7 +50,7 @@ def test_get_current(r):
5050
}
5151
validmap = {"initial": initial, "map": mapstates, "current": "begin"}
5252
assert r.execute_command("SM.SET", "fooforcurrent", json.dumps(validmap))
53-
assert r.execute_command("SM.CURRENT", "fooforcurrent") == "begin"
53+
assert r.execute_command("SM.STATE", "fooforcurrent") == ["begin"]
5454

5555

5656
def test_get_states(r):
@@ -62,7 +62,7 @@ def test_get_states(r):
6262
}
6363
validmap = {"initial": initial, "map": mapstates, "current": "too"}
6464
assert r.execute_command("SM.SET", "foostates", json.dumps(validmap))
65-
states = r.execute_command("SM.STATES", "foostates")
65+
states = r.execute_command("SM.STATE", "foostates", "list")
6666

6767
mapkeys = list(mapstates.keys())
6868
mapkeys.sort()
@@ -79,7 +79,7 @@ def test_set_del(r):
7979
}
8080
validmap = {"initial": initial, "map": mapstates, "current": "shmm"}
8181
assert r.execute_command("SM.SET", "foostates", json.dumps(validmap))
82-
assert r.execute_command("SM.DEL", "foostates")
82+
assert r.delete("foostates")
8383

8484
keys = r.keys()
8585
assert "foostates" not in keys
@@ -95,46 +95,29 @@ def test_reset(r):
9595
validmap = {"initial": initial, "map": mapstates, "current": "too"}
9696
assert r.execute_command("SM.SET", "foostates", json.dumps(validmap))
9797
r.execute_command("SM.RESET", "foostates")
98-
assert r.execute_command("SM.CURRENT", "foostates") == initial
99-
100-
101-
# def test_force_set(r):
102-
# r.flushdb()
103-
# initial = "begin"
104-
# mapstates = {
105-
# "a": ["this", "maps", "states"],
106-
# "b": ["this", "too", "maps", "somewhere"],
107-
# }
108-
# validmap = {"initial": initial, "map": mapstates, "current": "maps"}
109-
# assert r.execute_command("SM.SET", "foostates", json.dumps(validmap))
110-
# assert r.execute_command("SM.FORCE", "foostates", "too")
111-
112-
# assert r.execute_command("SM.CURRENT", "foostates") == "too"
98+
assert r.execute_command("SM.STATE", "foostates") == [initial]
11399

114100

115101
def test_create(r):
116102
r.flushdb()
117103
key = "foo"
118104
assert r.execute_command("SM.CREATE", key)
119-
assert r.execute_command("SM.CURRENT", key) == ""
105+
assert r.execute_command("SM.STATE", key) == ['']
120106
res = r.execute_command("SM.GET", key)
121107
val = json.loads(res)
122108
assert val["initial"] == ""
123109
assert val["map"] == {}
124110
assert val["current"] == ""
125111

126112

127-
# r.execute_command("SM.FORCE", "foostates", "too")
128-
129-
130113
def test_template(r):
131114
res = r.execute_command("SM.TEMPLATE")
132115
val = json.loads(res)
133116
assert val["initial"] == ""
134117
assert val["map"] == {}
135118
assert val["current"] == ""
136119

137-
def test_transition(r):
120+
def test_mutate(r):
138121
r.flushdb()
139122
initial = "begin"
140123
current = "begin"
@@ -146,11 +129,11 @@ def test_transition(r):
146129

147130
valid_with_current = {"initial": initial, "map": mapstates, "current": current}
148131
assert r.execute_command("SM.SET", "bar", json.dumps(valid_with_current))
149-
assert r.execute_command("SM.TRANSITION", "bar", "smurfy") == None
150-
assert r.execute_command("SM.TRANSITION", "bar", "too")
132+
assert r.execute_command("SM.MUTATE", "bar", "smurfy") == None
133+
assert r.execute_command("SM.MUTATE", "bar", "too")
151134

152135
# force state
153136
r.flushdb()
154137
assert r.execute_command("SM.SET", "bar", json.dumps(valid_with_current))
155-
assert r.execute_command("SM.TRANSITION", "bar", "banna", "foo") == None
156-
assert r.execute_command("SM.TRANSITION", "bar", "banna", "F")
138+
assert r.execute_command("SM.MUTATE", "bar", "banna", "foo") == None
139+
assert r.execute_command("SM.MUTATE", "bar", "banna", "F")

0 commit comments

Comments
 (0)