Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Fixed

- Fix serialization of optional values in request and response types ([#19](https://github.com/stjude-rust-labs/tes/pull/19)).

## 0.8.0 - 06-04-2025

### Changed
Expand Down
2 changes: 1 addition & 1 deletion examples/task-list-all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ async fn list_all_tasks(client: &Client) -> Result<()> {
loop {
let response = client
.list_tasks(Some(&ListTasksParams {
view: View::Full,
view: Some(View::Full),
page_token: last_token,
..Default::default()
}))
Expand Down
2 changes: 1 addition & 1 deletion src/v1/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl Client {
None => "tasks".to_string(),
};

match params.map(|p| p.view).unwrap_or_default() {
match params.and_then(|p| p.view).unwrap_or_default() {
View::Minimal => {
let results = self.get::<ListTasks<MinimalTask>>(url).await?;

Expand Down
22 changes: 16 additions & 6 deletions src/v1/types/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,41 +47,51 @@ pub struct ListTasksParams {
/// The filter for task name (prefixed).
///
/// If unspecified, no task name filtering is done.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub name_prefix: Option<String>,
/// The filter for task state.
///
/// If unspecified, no task state filtering is done.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub state: Option<State>,
/// The filter for task tag keys.
///
/// This is zipped with `tag_values`.
///
/// If empty, no task tags filtering is done.
#[cfg_attr(feature = "serde", serde(rename = "tag_key", default))]
pub tag_keys: Vec<String>,
#[cfg_attr(
feature = "serde",
serde(rename = "tag_key", default, skip_serializing_if = "Option::is_none")
)]
pub tag_keys: Option<Vec<String>>,
/// The filter for task tag values.
///
/// This is zipped with `tag_keys`.
///
/// If the value is empty, it matches all values.
///
/// It is an error if more values are supplied than keys.
#[cfg_attr(feature = "serde", serde(rename = "tag_value", default))]
pub tag_values: Vec<String>,
#[cfg_attr(
feature = "serde",
serde(rename = "tag_value", default, skip_serializing_if = "Option::is_none")
)]
pub tag_values: Option<Vec<String>>,
/// The number of tasks to return in one page.
///
/// Must be less than 2048. Defaults to 256.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub page_size: Option<u16>,
/// The page token to retrieve the next page of results.
///
/// If unspecified, returns the first page of results.
///
/// The value can be found in the `next_page_token`` field of the last
/// returned result of `list_tasks`.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub page_token: Option<String>,
/// The view of the returned task(s).
#[cfg_attr(feature = "serde", serde(default))]
pub view: View,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub view: Option<View>,
}

/// Represents the request body of the `CreateTask` endpoint.
Expand Down
20 changes: 19 additions & 1 deletion src/v1/types/responses/service_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,33 +65,50 @@ pub struct ServiceInfo {
ty: ServiceType,

/// An optional description of the service.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
description: Option<String>,

/// The organization running the service.
organization: Organization,

/// An optional contact URL.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
contact_url: Option<String>,

/// An optional documentation URL.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
documentation_url: Option<Url>,

/// Timestamp when the service was first available.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
created_at: Option<DateTime<Utc>>,

/// Timestamp when the service was last updated.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
updated_at: Option<DateTime<Utc>>,

/// An optional string describing the environment that the service is
/// running within.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
environment: Option<String>,

/// The version of the service.
version: String,

/// Lists some, but not necessarily all, storage locations supported by the
/// service.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
storage: Option<Vec<String>>,

/// Lists all resource.backend_parameters keys supported by the service.
#[cfg_attr(
feature = "serde",
serde(
rename = "tesResources_backend_parameters",
skip_serializing_if = "Option::is_none"
)
)]
backend_parameters: Option<Vec<String>>,
}

impl ServiceInfo {
Expand Down Expand Up @@ -256,12 +273,13 @@ mod tests {
String::from("file:///path/to/local/funnel-storage"),
String::from("s3://ohsu-compbio-funnel/storage"),
]),
backend_parameters: Some(vec!["foo".into(), "bar".into()]),
};

let serialized = serde_json::to_string(&info).unwrap();
assert_eq!(
serialized,
r#"{"id":"org.ga4gh.myservice","name":"My Server","type":{"group":"org.ga4gh","artifact":"tes","version":"1.0.0"},"description":"A description","organization":{"name":"My Organization","url":"https://example.com/"},"contactUrl":"mailto:foo@bar.com","documentationUrl":"https://docs.myservice.example.com/","createdAt":"2024-09-07T20:27:35.345673Z","updatedAt":"2024-09-07T20:27:35.345673Z","environment":"test","version":"1.5.0","storage":["file:///path/to/local/funnel-storage","s3://ohsu-compbio-funnel/storage"]}"#
r#"{"id":"org.ga4gh.myservice","name":"My Server","type":{"group":"org.ga4gh","artifact":"tes","version":"1.0.0"},"description":"A description","organization":{"name":"My Organization","url":"https://example.com/"},"contactUrl":"mailto:foo@bar.com","documentationUrl":"https://docs.myservice.example.com/","createdAt":"2024-09-07T20:27:35.345673Z","updatedAt":"2024-09-07T20:27:35.345673Z","environment":"test","version":"1.5.0","storage":["file:///path/to/local/funnel-storage","s3://ohsu-compbio-funnel/storage"],"tesResources_backend_parameters":["foo","bar"]}"#
);

let deserialized: ServiceInfo = serde_json::from_str(&serialized).unwrap();
Expand Down
15 changes: 15 additions & 0 deletions src/v1/types/responses/service_info/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ pub struct Builder {
///
/// This does not necessarily have to list _all_ storage locations.
storage: Option<Vec<String>>,

/// Lists all resource.backend_parameters keys supported by the service.
backend_parameters: Option<Vec<String>>,
}

impl Builder {
Expand Down Expand Up @@ -215,6 +218,17 @@ impl Builder {
self
}

/// Sets all resource.backend_parameters keys supported by the service.
///
/// # Notes
///
/// This silently overrides any previously set supported backend parameters
/// for the service.
pub fn backend_parameters(mut self, value: impl Into<Vec<String>>) -> Self {
self.backend_parameters = Some(value.into());
self
}

/// Consumes `self` and attempts to builde a [`ServiceInfo`].
pub fn try_build(self) -> Result<ServiceInfo> {
let id = self.id.ok_or(Error::Missing("id"))?;
Expand Down Expand Up @@ -249,6 +263,7 @@ impl Builder {
environment: self.environment,
version,
storage: self.storage,
backend_parameters: self.backend_parameters,
})
}
}
6 changes: 6 additions & 0 deletions src/v1/types/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,24 +208,30 @@ pub struct Executor {
pub command: Vec<String>,

/// The working directory.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub workdir: Option<String>,

/// The path from which to pipe the standard input stream.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub stdin: Option<String>,

/// The path to pipe the standard output stream to.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub stdout: Option<String>,

/// The path to pipe the standard error stream to.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub stderr: Option<String>,

/// The environment variables.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub env: Option<BTreeMap<String, String>>,

/// Default behavior of running an array of executors is that execution
/// stops on the first error.
///
/// If `ignore_error` is `true`, then the runner will record error exit
/// codes, but will continue on to the next executor.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub ignore_error: Option<bool>,
}