-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
KVS functionality in the embedded web server (#161)
* Updated to savant-protobuf 0.2.2 * Implemented KVS handlers * Implemented tests for the embedded webserver * Implemented Python API/ABI samples for KVS * updated docs
- Loading branch information
Showing
23 changed files
with
1,089 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
savant_rs.webserver.kvs | ||
----------------------------- | ||
|
||
.. automodule:: savant_rs.webserver.kvs | ||
:members: | ||
:undoc-members: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import savant_rs.webserver as ws | ||
from savant_rs.primitives import Attribute, AttributeValue | ||
import savant_rs.webserver.kvs as kvs | ||
|
||
import requests | ||
from time import sleep | ||
|
||
attr = Attribute(namespace="some", name="attr", hint="x", values=[ | ||
AttributeValue.bytes(dims=[8, 3, 8, 8], blob=bytes(3 * 8 * 8), confidence=None), | ||
AttributeValue.bytes_from_list(dims=[4, 1], blob=[0, 1, 2, 3], confidence=None), | ||
AttributeValue.integer(1, confidence=0.5), | ||
AttributeValue.float(1.0, confidence=0.5), | ||
AttributeValue.floats([1.0, 2.0, 3.0]) | ||
]) | ||
|
||
|
||
def abi(): | ||
global attr | ||
|
||
kvs.set_attributes([attr], 1000) | ||
|
||
attributes = kvs.search_attributes("*", "*") | ||
assert len(attributes) == 1 | ||
|
||
attributes = kvs.search_attributes(None, "*") | ||
assert len(attributes) == 1 | ||
|
||
attribute = kvs.get_attribute("some", "attr") | ||
assert attribute.name == attr.name and attribute.namespace == attr.namespace | ||
|
||
nonexistent_attribute = kvs.get_attribute("some", "other") | ||
assert nonexistent_attribute is None | ||
|
||
removed_attribute = kvs.del_attribute("some", "attr") | ||
|
||
kvs.set_attributes([removed_attribute], 500) | ||
|
||
sleep(0.55) | ||
|
||
auto_removed_attribute = kvs.get_attribute("some", "attr") | ||
assert auto_removed_attribute is None | ||
|
||
kvs.del_attributes("*", "*") | ||
|
||
|
||
def api(base_url: str): | ||
global attr | ||
binary_attributes = kvs.serialize_attributes([attr]) | ||
|
||
response = requests.post(f'{base_url}/kvs/set', data=binary_attributes) | ||
assert response.status_code == 200 | ||
|
||
response = requests.post(f'{base_url}/kvs/set-with-ttl/1000', data=binary_attributes) | ||
assert response.status_code == 200 | ||
|
||
response = requests.post(f'{base_url}/kvs/delete/*/*') | ||
assert response.status_code == 200 | ||
|
||
response = requests.post(f'{base_url}/kvs/set', data=binary_attributes) | ||
assert response.status_code == 200 | ||
|
||
response = requests.post(f'{base_url}/kvs/delete-single/some/attr') | ||
assert response.status_code == 200 | ||
removed_attributes = kvs.deserialize_attributes(response.content) | ||
assert len(removed_attributes) == 1 | ||
|
||
response = requests.post(f'{base_url}/kvs/delete-single/some/attr') | ||
assert response.status_code == 200 | ||
removed_attributes = kvs.deserialize_attributes(response.content) | ||
assert len(removed_attributes) == 0 | ||
|
||
response = requests.post(f'{base_url}/kvs/set', data=binary_attributes) | ||
assert response.status_code == 200 | ||
|
||
response = requests.get(f'{base_url}/kvs/search/*/*') | ||
assert response.status_code == 200 | ||
attributes = kvs.deserialize_attributes(response.content) | ||
assert len(attributes) == 1 | ||
|
||
response = requests.get(f'{base_url}/kvs/search-keys/*/*') | ||
assert response.status_code == 200 | ||
attributes = response.json() | ||
assert attributes == [["some", "attr"]] | ||
|
||
response = requests.get(f'{base_url}/kvs/get/some/attr') | ||
assert response.status_code == 200 | ||
attributes = kvs.deserialize_attributes(response.content) | ||
assert len(attributes) == 1 | ||
|
||
|
||
if __name__ == "__main__": | ||
abi() | ||
port = 8080 | ||
ws.init_webserver(port) | ||
sleep(0.1) | ||
api(f'http://localhost:{port}') | ||
ws.stop_webserver() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
use crate::json_api::ToSerdeJsonValue; | ||
use crate::primitives::{Attribute, WithAttributes}; | ||
use crate::protobuf::from_pb; | ||
use savant_protobuf::generated; | ||
use serde_json::Value; | ||
|
||
#[derive(Debug, PartialEq, Clone, serde::Serialize, Default)] | ||
pub struct AttributeSet { | ||
pub attributes: Vec<Attribute>, | ||
} | ||
|
||
impl ToSerdeJsonValue for AttributeSet { | ||
fn to_serde_json_value(&self) -> Value { | ||
serde_json::json!(self) | ||
} | ||
} | ||
|
||
impl From<Vec<Attribute>> for AttributeSet { | ||
fn from(attributes: Vec<Attribute>) -> Self { | ||
Self { attributes } | ||
} | ||
} | ||
|
||
impl AttributeSet { | ||
pub fn new() -> Self { | ||
Self::default() | ||
} | ||
|
||
pub fn deserialize(bytes: &[u8]) -> anyhow::Result<Vec<Attribute>> { | ||
let deser = from_pb::<generated::AttributeSet, AttributeSet>(bytes)?; | ||
Ok(deser.attributes) | ||
} | ||
|
||
pub fn json(&self) -> String { | ||
serde_json::to_string(&self.to_serde_json_value()).unwrap() | ||
} | ||
|
||
pub fn json_pretty(&self) -> String { | ||
serde_json::to_string_pretty(&self.to_serde_json_value()).unwrap() | ||
} | ||
} | ||
|
||
impl WithAttributes for AttributeSet { | ||
fn with_attributes_ref<F, R>(&self, f: F) -> R | ||
where | ||
F: FnOnce(&Vec<Attribute>) -> R, | ||
{ | ||
f(&self.attributes) | ||
} | ||
|
||
fn with_attributes_mut<F, R>(&mut self, f: F) -> R | ||
where | ||
F: FnOnce(&mut Vec<Attribute>) -> R, | ||
{ | ||
f(&mut self.attributes) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
use crate::primitives::attribute_set::AttributeSet; | ||
use crate::primitives::Attribute; | ||
use crate::protobuf::serialize; | ||
use savant_protobuf::generated; | ||
|
||
impl From<&AttributeSet> for generated::AttributeSet { | ||
fn from(ud: &AttributeSet) -> Self { | ||
let attributes = ud | ||
.attributes | ||
.iter() | ||
.map(generated::Attribute::from) | ||
.collect(); | ||
|
||
generated::AttributeSet { attributes } | ||
} | ||
} | ||
|
||
impl TryFrom<&generated::AttributeSet> for AttributeSet { | ||
type Error = serialize::Error; | ||
|
||
fn try_from(value: &generated::AttributeSet) -> Result<Self, Self::Error> { | ||
let attributes = value | ||
.attributes | ||
.iter() | ||
.filter(|a| a.is_persistent) | ||
.map(Attribute::try_from) | ||
.collect::<Result<_, _>>()?; | ||
|
||
Ok(AttributeSet { attributes }) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::primitives::attribute_set::AttributeSet; | ||
use crate::primitives::attribute_value::AttributeValue; | ||
use crate::primitives::Attribute; | ||
use savant_protobuf::generated; | ||
|
||
#[test] | ||
fn test_attribute_set() { | ||
assert_eq!( | ||
AttributeSet { | ||
attributes: vec![ | ||
(Attribute::new( | ||
"namespace", | ||
"name", | ||
vec![AttributeValue::string("value", Some(1.0))], | ||
&Some("hint"), | ||
true, | ||
true | ||
)) | ||
] | ||
}, | ||
AttributeSet::try_from(&generated::AttributeSet { | ||
attributes: vec![generated::Attribute { | ||
namespace: "namespace".to_string(), | ||
name: "name".to_string(), | ||
hint: Some("hint".to_string()), | ||
is_persistent: true, | ||
values: vec![generated::AttributeValue { | ||
confidence: Some(1.0), | ||
value: Some(generated::attribute_value::Value::String( | ||
generated::StringAttributeValueVariant { | ||
data: "value".to_string() | ||
} | ||
)) | ||
}], | ||
is_hidden: true, | ||
}] | ||
.into_iter() | ||
.collect(), | ||
}) | ||
.unwrap() | ||
); | ||
assert_eq!( | ||
generated::AttributeSet { | ||
attributes: vec![generated::Attribute { | ||
namespace: "namespace".to_string(), | ||
name: "name".to_string(), | ||
hint: Some("hint".to_string()), | ||
is_persistent: true, | ||
values: vec![generated::AttributeValue { | ||
confidence: Some(1.0), | ||
value: Some(generated::attribute_value::Value::String( | ||
generated::StringAttributeValueVariant { | ||
data: "value".to_string() | ||
} | ||
)) | ||
}], | ||
is_hidden: true, | ||
}] | ||
.into_iter() | ||
.collect(), | ||
}, | ||
generated::AttributeSet::from(&AttributeSet { | ||
attributes: vec![ | ||
(Attribute::new( | ||
"namespace", | ||
"name", | ||
vec![AttributeValue::string("value", Some(1.0))], | ||
&Some("hint"), | ||
true, | ||
true | ||
)) | ||
] | ||
}) | ||
); | ||
} | ||
} |
Oops, something went wrong.