Skip to content

Commit 39ecda8

Browse files
committed
Merge remote-tracking branch 'origin/main' into code-cov
2 parents 0dc66af + 818e72f commit 39ecda8

File tree

5 files changed

+270
-9
lines changed

5 files changed

+270
-9
lines changed

resources/metadata_table.json

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"data": {
3+
"id": "internal",
4+
"attributes": {
5+
"ancestors": [
6+
"4866611f-e6d9-4517-bedf-fc5526df57ad",
7+
"primary"
8+
],
9+
"structure_family": "table",
10+
"specs": [],
11+
"metadata": {
12+
"stage-x": {
13+
"dtype": "number",
14+
"shape": [],
15+
"dtype_numpy": "<f8",
16+
"source": "ca://BL01T-MO-SIMC-01:M1.RBV",
17+
"units": "degrees",
18+
"precision": 5,
19+
"limits": {
20+
"control": {
21+
"low": -20000,
22+
"high": 20000
23+
},
24+
"display": {
25+
"low": -20000,
26+
"high": 20000
27+
}
28+
},
29+
"object_name": "stage-x"
30+
}
31+
},
32+
"structure": {
33+
"arrow_schema": "data:application/vnd.apache.arrow.file;base64,/////xgBAAAQAAAAAAAKAAwABgAFAAgACgAAAAABBAAMAAAACAAIAAAABAAIAAAABAAAAAQAAACsAAAAaAAAADgAAAAEAAAAdP///wAAAQMQAAAAHAAAAAQAAAAAAAAACgAAAHRzX3N0YWdlLXgAAKr///8AAAIApP///wAAAQMQAAAAGAAAAAQAAAAAAAAABwAAAHN0YWdlLXgA1v///wAAAgDQ////AAABAxAAAAAcAAAABAAAAAAAAAAEAAAAdGltZQAABgAIAAYABgAAAAAAAgAQABQACAAGAAcADAAAABAAEAAAAAAAAQIQAAAAIAAAAAQAAAAAAAAABwAAAHNlcV9udW0ACAAMAAgABwAIAAAAAAAAAUAAAAAAAAAA",
34+
"npartitions": 1,
35+
"columns": [
36+
"seq_num",
37+
"time",
38+
"stage-x",
39+
"ts_stage-x"
40+
],
41+
"resizable": false
42+
},
43+
"access_blob": {},
44+
"sorting": null,
45+
"data_sources": null
46+
},
47+
"links": {
48+
"self": "http://127.0.0.1:8000/api/v1/metadata/4866611f-e6d9-4517-bedf-fc5526df57ad/primary/internal",
49+
"full": "http://127.0.0.1:8000/api/v1/table/full/4866611f-e6d9-4517-bedf-fc5526df57ad/primary/internal",
50+
"partition": "http://127.0.0.1:8000/api/v1/table/partition/4866611f-e6d9-4517-bedf-fc5526df57ad/primary/internal?partition={index}"
51+
},
52+
"meta": null
53+
},
54+
"error": null,
55+
"links": null,
56+
"meta": {}
57+
}

resources/table_full.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"seq_num": [
3+
1,
4+
2,
5+
3,
6+
4,
7+
5
8+
],
9+
"time": [
10+
1762787606.459906,
11+
1762787609.4694269,
12+
1762787612.4223065,
13+
1762787615.400967,
14+
1762787618.4348016
15+
],
16+
"stage-x": [
17+
0,
18+
2.5,
19+
5,
20+
7.5,
21+
10
22+
],
23+
"ts_stage-x": [
24+
631152000,
25+
1762787609.198412,
26+
1762787612.10868,
27+
1762787615.118978,
28+
1762787618.129375
29+
]
30+
}

src/clients.rs

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use std::fmt;
22

3+
use axum::http::HeaderMap;
34
use reqwest::Url;
45
use serde::de::DeserializeOwned;
56
use tracing::{info, instrument};
67
use uuid::Uuid;
78

8-
use crate::model::{app, event_stream, run};
9+
use crate::model::{app, event_stream, run, table};
910

1011
pub type ClientResult<T> = Result<T, ClientError>;
1112

@@ -15,35 +16,68 @@ pub struct TiledClient {
1516

1617
impl TiledClient {
1718
#[instrument(skip(self))]
18-
async fn request<T: DeserializeOwned>(&self, endpoint: &str) -> ClientResult<T> {
19+
async fn request<T: DeserializeOwned>(
20+
&self,
21+
endpoint: &str,
22+
headers: Option<HeaderMap>,
23+
) -> ClientResult<T> {
1924
info!("Requesting from tiled: {}", endpoint);
2025
let url = self.address.join(endpoint)?;
21-
let response = reqwest::get(url).await?.error_for_status()?;
26+
let client = reqwest::Client::new();
27+
let request = match headers {
28+
Some(headers) => client.get(url).headers(headers),
29+
None => client.get(url),
30+
};
31+
let response = request.send().await?.error_for_status()?;
2232
let body = response.text().await?;
2333
serde_json::from_str(&body).map_err(|e| ClientError::InvalidResponse(e, body))
2434
}
2535
pub async fn app_metadata(&self) -> ClientResult<app::AppMetadata> {
26-
self.request("/api/v1/").await
36+
self.request("/api/v1/", None).await
2737
}
2838
pub async fn run_metadata(&self, id: Uuid) -> ClientResult<run::RunMetadataRoot> {
29-
self.request(&format!("/api/v1/metadata/{id}")).await
39+
self.request(&format!("/api/v1/metadata/{id}"), None).await
3040
}
3141
pub async fn event_stream_metadata(
3242
&self,
3343
id: Uuid,
3444
stream: String,
3545
) -> ClientResult<event_stream::EventStreamMetadataRoot> {
36-
self.request(&format!("/api/v1/metadata/{id}/{stream}"))
46+
self.request(&format!("/api/v1/metadata/{id}/{stream}"), None)
47+
.await
48+
}
49+
pub async fn table_metadata(
50+
&self,
51+
id: Uuid,
52+
stream: String,
53+
table: String,
54+
) -> ClientResult<table::TableMetadataRoot> {
55+
self.request(&format!("/api/v1/metadata/{id}/{stream}/{table}"), None)
3756
.await
3857
}
58+
pub async fn table_full(
59+
&self,
60+
id: Uuid,
61+
stream: String,
62+
table: String,
63+
) -> ClientResult<table::Table> {
64+
let mut headers = HeaderMap::new();
65+
headers.insert("accept", "application/json".parse().unwrap());
66+
67+
self.request(
68+
&format!("/api/v1/table/full/{id}/{stream}/{table}"),
69+
Some(headers),
70+
)
71+
.await
72+
}
3973
pub async fn search_root(&self) -> ClientResult<run::RunRoot> {
40-
self.request("/api/v1/search/").await
74+
self.request("/api/v1/search/", None).await
4175
}
4276
pub async fn search_run_container(
4377
&self,
4478
id: Uuid,
4579
) -> ClientResult<event_stream::EventStreamRoot> {
46-
self.request(&format!("/api/v1/search/{id}")).await
80+
self.request(&format!("/api/v1/search/{id}"), None).await
4781
}
4882
}
4983

@@ -78,6 +112,7 @@ impl std::fmt::Display for ClientError {
78112

79113
#[cfg(test)]
80114
mod tests {
115+
use axum::http::HeaderMap;
81116
use httpmock::MockServer;
82117
use url::Url;
83118

@@ -96,7 +131,33 @@ mod tests {
96131
address: Url::parse(&server.base_url()).unwrap(),
97132
};
98133
assert_eq!(
99-
client.request::<Vec<u8>>("/demo/api").await.unwrap(),
134+
client.request::<Vec<u8>>("/demo/api", None).await.unwrap(),
135+
vec![1, 2, 3]
136+
);
137+
mock.assert();
138+
}
139+
#[tokio::test]
140+
async fn request_with_headers() {
141+
let server = MockServer::start();
142+
let mock = server
143+
.mock_async(|when, then| {
144+
when.method("GET")
145+
.path("/demo/api")
146+
.header("api-key", "foo");
147+
then.status(200).body("[1,2,3]");
148+
})
149+
.await;
150+
let client = TiledClient {
151+
address: Url::parse(&server.base_url()).unwrap(),
152+
};
153+
let mut headers = HeaderMap::new();
154+
headers.insert("api-key", "foo".parse().unwrap());
155+
156+
assert_eq!(
157+
client
158+
.request::<Vec<u8>>("/demo/api", Some(headers))
159+
.await
160+
.unwrap(),
100161
vec![1, 2, 3]
101162
);
102163
mock.assert();

src/model.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub(crate) mod container;
33
pub(crate) mod event_stream;
44
pub(crate) mod node;
55
pub(crate) mod run;
6+
pub(crate) mod table;
67

78
use async_graphql::Object;
89
use tracing::instrument;
@@ -31,6 +32,24 @@ impl TiledQuery {
3132
self.0.event_stream_metadata(id, stream).await
3233
}
3334
#[instrument(skip(self))]
35+
async fn table_metadata(
36+
&self,
37+
id: Uuid,
38+
stream: String,
39+
table: String,
40+
) -> Result<table::TableMetadataRoot, ClientError> {
41+
self.0.table_metadata(id, stream, table).await
42+
}
43+
#[instrument(skip(self))]
44+
async fn table_full(
45+
&self,
46+
id: Uuid,
47+
stream: String,
48+
table: String,
49+
) -> Result<table::Table, ClientError> {
50+
self.0.table_full(id, stream, table).await
51+
}
52+
#[instrument(skip(self))]
3453
async fn search_root(&self) -> Result<run::RunRoot, ClientError> {
3554
self.0.search_root().await
3655
}
@@ -104,6 +123,31 @@ mod tests {
104123
mock.assert();
105124
}
106125
#[tokio::test]
126+
async fn table_metadata() {
127+
let id = Uuid::parse_str("4866611f-e6d9-4517-bedf-fc5526df57ad").unwrap();
128+
let stream = "primary";
129+
let table = "internal";
130+
let server = MockServer::start();
131+
let mock = server
132+
.mock_async(|when, then| {
133+
when.method("GET")
134+
.path(format!("/api/v1/metadata/{id}/{stream}/{table}"));
135+
then.status(200)
136+
.body_from_file("resources/metadata_table.json");
137+
})
138+
.await;
139+
let schema = build_schema(&server.base_url());
140+
let query = r#"{ tableMetadata(id:"4866611f-e6d9-4517-bedf-fc5526df57ad", stream:"primary", table:"internal") {data {id}}}"#;
141+
let response = schema.execute(query).await;
142+
let exp = value! ({
143+
"tableMetadata": { "data": {"id": "internal"}}
144+
});
145+
146+
assert_eq!(response.data, exp);
147+
assert_eq!(response.errors, &[]);
148+
mock.assert();
149+
}
150+
#[tokio::test]
107151
async fn search_root() {
108152
let server = MockServer::start();
109153
let mock = server

src/model/table.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use std::collections::HashMap;
2+
3+
use async_graphql::SimpleObject;
4+
use serde::{Deserialize, Serialize};
5+
use serde_json::Value;
6+
7+
use crate::model::node;
8+
9+
pub type Table = HashMap<String, Vec<Value>>;
10+
11+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, SimpleObject)]
12+
pub struct TableMetadataRoot {
13+
pub data: TableData,
14+
pub error: Value,
15+
pub links: Option<node::Links>,
16+
pub meta: Value,
17+
}
18+
19+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, SimpleObject)]
20+
pub struct TableData {
21+
pub id: String,
22+
pub attributes: TableAttributes,
23+
pub links: TableLinks,
24+
pub meta: Value,
25+
}
26+
27+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, SimpleObject)]
28+
pub struct TableAttributes {
29+
pub ancestors: Vec<Value>,
30+
pub structure_family: String,
31+
pub specs: Option<Vec<Value>>,
32+
pub metadata: HashMap<String, Value>,
33+
pub structure: TableStructure,
34+
pub access_blob: Value,
35+
pub sorting: Value,
36+
pub data_sources: Value,
37+
}
38+
39+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, SimpleObject)]
40+
pub struct TableStructure {
41+
pub arrow_schema: String,
42+
pub npartitions: i64,
43+
pub columns: Vec<Value>,
44+
pub resizable: bool,
45+
}
46+
47+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, SimpleObject)]
48+
pub struct TableLinks {
49+
#[serde(rename = "self")]
50+
#[graphql(name = "self")]
51+
pub self_field: String,
52+
pub full: Option<String>,
53+
pub partition: Option<String>,
54+
}
55+
56+
#[cfg(test)]
57+
mod tests {
58+
use crate::model::table;
59+
use crate::test_utils::assert_readable_as;
60+
61+
#[test]
62+
fn table_metadata() {
63+
assert_readable_as::<table::TableMetadataRoot>("resources/metadata_table.json");
64+
}
65+
#[test]
66+
fn table_full() {
67+
assert_readable_as::<table::Table>("resources/table_full.json");
68+
}
69+
}

0 commit comments

Comments
 (0)