Crate django_query
source · [−]Expand description
django-query
This crate is a toolkit for assembling a mock instance of a Django-style API, although some of the parts may be useful beyond just mocking. The tools provided depend on the available features.
There are the following features:
"filter"
- Filtering values of a type using django filters."sort"
- Sort values of a type using django orderings."row"
- Convert a type into a row in tabular JSON output."wiremock"
- Wiremock endpoints to expose data; implies"filter"
,"row"
and"sort"
."persian-rug"
- Support for types built with thepersian-rug
crate."clone-replace"
- Use aCloneReplace
from theclone-replace
crate to provide data to mocks.
Filtering
The Filterable
trait, and its derive macro,
which allow you to use attribute markup to automatically parse
Django-style filter URLs into filter objects, when the "filter"
feature is enabled.
Example:
use django_query::filtering::{Filterable, OperatorSet};
#[derive(Filterable)]
struct Foo {
#[django(op(lt, gt))]
a: i32
}
let os = OperatorSet::<Foo>::new();
let filter = os.create_filter_from_query("a__lt=4").unwrap();
assert!(filter.filter_one(&Foo { a: 3}));
Sorting
The Sortable
trait, and its derive macro, which
allow you to use attribute markup to automatically parse Django-style
ordering URLs into sorter objects, when the "sort"
feature is
enabled.
Example:
use core::cmp::Ordering;
use django_query::sorting::{OrderingSet, Sortable};
#[derive(Sortable)]
struct Foo {
#[django(sort)]
a: i32
}
let os = OrderingSet::<Foo>::new();
let sort = os.create_sort("-a").unwrap();
assert_eq!(sort.compare(&Foo { a: 3}, &Foo {a: 4}), Ordering::Greater);
Tabular Output
The IntoRow
trait, and its derive macro, which allow
you to create Django-style JSON responses for your type; note that
this isn’t just standard serialization because complex objects are
replaced by one of their fields, which functions as a foreign
key. This is available when the "row"
feature is enabled.
Example:
use django_query::row::{IntoRow, Serializer};
use serde_json::json;
#[derive(IntoRow)]
struct Foo {
a: Vec<i32>
}
let ser = Foo::get_serializer();
let f = Foo { a: vec![2, 3]};
assert_eq!(ser.to_json(&f), json! {
{ "a": [2, 3] }
});
Mock Endpoints
The Endpoint
type, which implements
wiremock::Respond
, and can provide a mock endpoint for a
collection of objects whose type implements the preceding three
traits. This is available when the "wiremock"
feature is enabled.
Example:
use django_query::{filtering::Filterable, mock::Endpoint, row::IntoRow, sorting::Sortable};
use wiremock::{Mock, MockServer, matchers, http::Url};
#[derive(Clone, IntoRow, Filterable, Sortable)]
struct Foo {
name: String,
}
let server = MockServer::start().await;
let foos = vec![
Foo { name: "foo1".to_string() },
Foo { name: "foo2".to_string() }
];
Mock::given(matchers::method("GET"))
.respond_with(Endpoint::new(foos, Some(&server.uri())))
.mount(&server)
.await;
let url = Url::parse(&server.uri()).expect("failed to parse MockServer URL");
let body: serde_json::Value = reqwest::get(url)
.await
.expect("error getting response")
.json()
.await
.expect("error parsing response");
assert_eq!(body, serde_json::json!{
{
"count": 2,
"next": null,
"previous": null,
"results": [
{ "name": "foo1" },
{ "name": "foo2" },
]
}
});
Context support
There is support throughout this crate for types that require some context value in order to perform processing with them. Each module has an additional entry point with context support:
FilterableWithContext
infiltering
.SortableWithContext
insorting
.IntoRowWithContext
inrow
.EndpointWithContext
inmock
.
The context support makes no more than basic assumptions about what the context value is, or how it behaves. However, corresponding derive macros for the traits are not provided, for the same reason.
The "persian-rug"
feature provides derive macros for using a persian_rug::Context
:
for types that require it:
Note that there is no corresponding PersianRug
trait, only a derive
macro which produces an implementation for the generic (WithContext
)
trait.
Mutable collection support
Ideally, the data served by the mock endpoints using this crate should
be mutable, so that testing can examine how code reacts as the data
store evolves. This crate uses a RowSource
trait to
represent something that:
- Can be locked or queried to obtain a fixed view of the data.
- For which references to that retrieved fixed view are iterable.
This probably cannot be fully abstracted in stable Rust as it stands at the moment of writing, because of the lack of generic associated types.
The standard RowSource
implementations are for
Arc<Vec<T>>
and Vec<T>
, which clone
themselves on each request (the latter being provided because it is
convenient for small tests, even though it is expensive). Neither
permits mutability.
However, if the "clone-replace"
feature is enabled, a
CloneReplace
from the
clone-replace
crate can be used as a
RowSource
. Note that in addition to the
implementation for
CloneReplace<Vec<T>>
, there is also
a
CloneReplaceFieldSource
which allows you to extract a Vec
field from a structure which is
entirely contained within a
CloneReplace
.
If the "persian-rug"
feature is enabled, then in combination with
"clone-replace"
it allows you to use individual tables from a
persian_rug::Context
wrapped in a
CloneReplace
as data sources via
CloneReplacePersianRugTableSource
.