Skip to content

Commit

Permalink
remove Incoming pattern from the examples and add related tests
Browse files Browse the repository at this point in the history
  • Loading branch information
davepacheco committed Aug 5, 2020
1 parent 90a2234 commit 9d6347b
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 33 deletions.
28 changes: 11 additions & 17 deletions dropshot/examples/pagination-multiple-resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ impl_HasIdentity!(Instance);
*/
#[derive(Deserialize, Clone, ExtractedParameter, JsonSchema, Serialize)]
struct ExScanParams {
#[serde(default = "default_sort_mode")]
sort: ExSortMode,
}

#[derive(Deserialize, Clone, ExtractedParameter, JsonSchema, Serialize)]
struct ExScanParamsIncoming {
sort: Option<ExSortMode>,
fn default_sort_mode() -> ExSortMode {
ExSortMode::ByNameAscending
}

#[derive(Deserialize, Clone, ExtractedParameter, JsonSchema, Serialize)]
Expand Down Expand Up @@ -135,18 +135,12 @@ fn page_selector<T: HasIdentity>(
}
}

fn scan_params(
p: &WhichPage<ExScanParamsIncoming, ExPageSelector>,
) -> ExScanParams {
fn scan_params(p: &WhichPage<ExScanParams, ExPageSelector>) -> ExScanParams {
ExScanParams {
sort: match p {
WhichPage::First(ExScanParamsIncoming {
sort: None,
}) => ExSortMode::ByNameAscending,

WhichPage::First(ExScanParamsIncoming {
sort: Some(p),
}) => p.clone(),
WhichPage::First(ExScanParams {
sort,
}) => sort.clone(),

WhichPage::Next(ExPageSelector::Id(Ascending, ..)) => {
ExSortMode::ByIdAscending
Expand Down Expand Up @@ -178,7 +172,7 @@ fn scan_params(
}]
async fn example_list_projects(
rqctx: Arc<RequestContext>,
query: Query<PaginationParams<ExScanParamsIncoming, ExPageSelector>>,
query: Query<PaginationParams<ExScanParams, ExPageSelector>>,
) -> Result<HttpResponseOkObject<ResultsPage<Project>>, HttpError> {
let pag_params = query.into_inner();
let limit = rqctx.page_limit(&pag_params)?.get();
Expand Down Expand Up @@ -208,7 +202,7 @@ async fn example_list_projects(
}]
async fn example_list_disks(
rqctx: Arc<RequestContext>,
query: Query<PaginationParams<ExScanParamsIncoming, ExPageSelector>>,
query: Query<PaginationParams<ExScanParams, ExPageSelector>>,
) -> Result<HttpResponseOkObject<ResultsPage<Disk>>, HttpError> {
let pag_params = query.into_inner();
let limit = rqctx.page_limit(&pag_params)?.get();
Expand Down Expand Up @@ -238,7 +232,7 @@ async fn example_list_disks(
}]
async fn example_list_instances(
rqctx: Arc<RequestContext>,
query: Query<PaginationParams<ExScanParamsIncoming, ExPageSelector>>,
query: Query<PaginationParams<ExScanParams, ExPageSelector>>,
) -> Result<HttpResponseOkObject<ResultsPage<Instance>>, HttpError> {
let pag_params = query.into_inner();
let limit = rqctx.page_limit(&pag_params)?.get();
Expand All @@ -265,7 +259,7 @@ async fn example_list_instances(
fn do_list<'a, T>(
data: &'a Arc<DataCollection>,
scan_params: &ExScanParams,
p: &'a WhichPage<ExScanParamsIncoming, ExPageSelector>,
p: &'a WhichPage<ExScanParams, ExPageSelector>,
by_name: &'a BTreeMap<String, Arc<T>>,
by_id: &'a BTreeMap<Uuid, Arc<T>>,
) -> ItemIter<'a, T>
Expand Down
26 changes: 10 additions & 16 deletions dropshot/examples/pagination-multiple-sorts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,16 +149,16 @@ struct Project {
* serialize it using `serde_querystring`. That code could fail at runtime for
* certain types of values (e.g., enum variants that contain data).
*/
#[derive(Deserialize, Clone, ExtractedParameter)]
struct ProjectScanParamsIncoming {
sort: Option<ProjectSort>,
}

#[derive(JsonSchema, Serialize)]
#[derive(Clone, Deserialize, ExtractedParameter, JsonSchema, Serialize)]
struct ProjectScanParams {
#[serde(default = "default_project_sort")]
sort: ProjectSort,
}

fn default_project_sort() -> ProjectSort {
ProjectSort::ByNameAscending
}

#[derive(Deserialize, Clone, ExtractedParameter, JsonSchema, Serialize)]
#[serde(rename_all = "kebab-case")]
enum ProjectSort {
Expand Down Expand Up @@ -235,22 +235,16 @@ fn page_selector_for(
}]
async fn example_list_projects(
rqctx: Arc<RequestContext>,
query: Query<
PaginationParams<ProjectScanParamsIncoming, ProjectScanPageSelector>,
>,
query: Query<PaginationParams<ProjectScanParams, ProjectScanPageSelector>>,
) -> Result<HttpResponseOkObject<ResultsPage<Project>>, HttpError> {
let pag_params = query.into_inner();
let limit = rqctx.page_limit(&pag_params)?.get();
let data = rqctx_to_data(rqctx);
let scan_params = ProjectScanParams {
sort: match &pag_params.page {
WhichPage::First(ProjectScanParamsIncoming {
sort: None,
}) => ProjectSort::ByNameAscending,

WhichPage::First(ProjectScanParamsIncoming {
sort: Some(p),
}) => p.clone(),
WhichPage::First(ProjectScanParams {
sort,
}) => sort.clone(),

WhichPage::Next(ProjectScanPageSelector::Name(Ascending, ..)) => {
ProjectSort::ByNameAscending
Expand Down
94 changes: 94 additions & 0 deletions dropshot/tests/test_pagination.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ fn paginate_api() -> ApiDescription {
api.register(api_integers).unwrap();
api.register(api_empty).unwrap();
api.register(api_with_extra_params).unwrap();
api.register(api_with_required_params).unwrap();
api.register(api_dictionary).unwrap();
api
}
Expand Down Expand Up @@ -573,6 +574,93 @@ async fn test_paginate_extra_params() {
testctx.teardown().await;
}

/*
* Test an endpoint that requires scan parameters.
*/

#[derive(Deserialize)]
struct ReqScanParams {
/* Work around serde-rs/serde#1183 */
#[serde(with = "serde_with::rust::display_fromstr")]
doit: bool,
}

/**
* "/required": similar to "/intapi", but with a required start parameter
*/
#[endpoint {
method = GET,
path = "/required",
}]
async fn api_with_required_params(
rqctx: Arc<RequestContext>,
query: Query<PaginationParams<ReqScanParams, IntegersPageSelector>>,
) -> Result<HttpResponseOkObject<ResultsPage<u16>>, HttpError> {
let pag_params = query.into_inner();
let limit = rqctx.page_limit(&pag_params)?.get() as u16;

let start = match &pag_params.page {
WhichPage::First(ReqScanParams {
doit,
}) => {
if !doit {
return Err(HttpError::for_bad_request(
None,
String::from("you did not say to do it"),
));
}

0
}
WhichPage::Next(IntegersPageSelector {
last_seen,
}) => *last_seen,
};

Ok(HttpResponseOkObject(ResultsPage::new(
range_u16(start, limit),
&EmptyScanParams {},
page_selector_for,
)?))
}

#[tokio::test]
async fn test_paginate_with_required_params() {
let api = paginate_api();
let testctx = common::test_setup("required_params", api);
let client = &testctx.client_testctx;

/* Test that the extra query parameter is optional... */
let error = client
.make_request_error(
Method::GET,
"/required?limit=3",
StatusCode::BAD_REQUEST,
)
.await;
/*
* TODO-polish the message here is pretty poor. See comments in the
* automated tests in src/pagination.rs.
*/
assert!(error.message.starts_with("unable to parse query string"));

/* ... and that it's getting passed through to the handler function */
let error = client
.make_request_error(
Method::GET,
"/required?limit=3&doit=false",
StatusCode::BAD_REQUEST,
)
.await;
assert_eq!(error.message, "you did not say to do it");

let page =
objects_list_page::<u16>(&client, "/required?limit=3&doit=true").await;
assert_eq!(page.items.len(), 3);

testctx.teardown().await;
}

/*
* Test an endpoint with scan options that returns custom structures. Our
* endpoint will return a list of words, with the marker being the last word
Expand Down Expand Up @@ -1031,6 +1119,12 @@ async fn test_example_multiple_resources() {

let resources = ["/projects", "/disks", "/instances"];
for resource in &resources[..] {
/* Scan parameters are not necessary. */
let no_args =
objects_list_page::<ExampleObject>(&client, "/projects?limit=3")
.await;
assert_eq!(no_args.items.len(), 3);

let by_name_asc = assert_collection_iter::<ExampleObject>(
&client,
resource,
Expand Down

0 comments on commit 9d6347b

Please sign in to comment.