+1
Cargo.lock
+1
Cargo.lock
+1
slingshot/Cargo.toml
+1
slingshot/Cargo.toml
···
13
13
foyer = { version = "0.18.0", features = ["serde"] }
14
14
hickory-resolver = "0.25.2"
15
15
jetstream = { path = "../jetstream", features = ["metrics"] }
16
+
links = { path = "../links" }
16
17
log = "0.4.27"
17
18
metrics = "0.24.2"
18
19
metrics-exporter-prometheus = { version = "0.17.1", features = ["http-listener"] }
+78
-6
slingshot/src/server.rs
+78
-6
slingshot/src/server.rs
···
4
4
};
5
5
use atrium_api::types::string::{Cid, Did, Handle, Nsid, RecordKey};
6
6
use foyer::HybridCache;
7
+
use links::at_uri::parse_at_uri as normalize_at_uri;
7
8
use serde::Serialize;
8
9
use std::path::PathBuf;
9
10
use std::str::FromStr;
···
32
33
}
33
34
fn example_rkey() -> String {
34
35
"3lv4ouczo2b2a".to_string()
36
+
}
37
+
fn example_uri() -> String {
38
+
format!(
39
+
"at://{}/{}/{}",
40
+
example_did(),
41
+
example_collection(),
42
+
example_rkey()
43
+
)
35
44
}
36
45
37
46
#[derive(Object)]
···
84
93
impl Example for FoundRecordResponseObject {
85
94
fn example() -> Self {
86
95
Self {
87
-
uri: format!(
88
-
"at://{}/{}/{}",
89
-
example_did(),
90
-
example_collection(),
91
-
example_rkey()
92
-
),
96
+
uri: example_uri(),
93
97
cid: Some("bafyreialv3mzvvxaoyrfrwoer3xmabbmdchvrbyhayd7bga47qjbycy74e".to_string()),
94
98
value: serde_json::json!({
95
99
"$type": "app.bsky.feed.like",
···
157
161
/// found. That is: slingshot only retains the most recent version of a
158
162
/// record. (TODO: verify bsky behaviour for mismatched/old CID)
159
163
Query(cid): Query<Option<String>>,
164
+
) -> GetRecordResponse {
165
+
self.get_record_impl(repo, collection, rkey, cid).await
166
+
}
167
+
168
+
/// com.bad-example.repo.getUriRecord
169
+
///
170
+
/// Ergonomic complement to [`com.atproto.repo.getRecord`](https://docs.bsky.app/docs/api/com-atproto-repo-get-record)
171
+
/// which accepts an at-uri instead of individual rep/collection/rkey params
172
+
#[oai(path = "/com.bad-example.repo.getUriRecord", method = "get")]
173
+
async fn get_uri_record(
174
+
&self,
175
+
/// The at-uri of the record
176
+
///
177
+
/// The identifier can be a DID or an atproto handle, and the collection
178
+
/// and rkey segments must be present.
179
+
#[oai(example = "example_uri")]
180
+
Query(at_uri): Query<String>,
181
+
/// Optional: the CID of the version of the record.
182
+
///
183
+
/// If not specified, then return the most recent version.
184
+
///
185
+
/// If specified and a newer version of the record exists, returns 404 not
186
+
/// found. That is: slingshot only retains the most recent version of a
187
+
/// record.
188
+
Query(cid): Query<Option<String>>,
189
+
) -> GetRecordResponse {
190
+
let bad_at_uri = || {
191
+
GetRecordResponse::BadRequest(xrpc_error(
192
+
"InvalidRequest",
193
+
"at-uri does not appear to be valid",
194
+
))
195
+
};
196
+
197
+
let Some(normalized) = normalize_at_uri(&at_uri) else {
198
+
return bad_at_uri();
199
+
};
200
+
201
+
// TODO: move this to links
202
+
let Some(rest) = normalized.strip_prefix("at://") else {
203
+
return bad_at_uri();
204
+
};
205
+
let Some((repo, rest)) = rest.split_once('/') else {
206
+
return bad_at_uri();
207
+
};
208
+
let Some((collection, rest)) = rest.split_once('/') else {
209
+
return bad_at_uri();
210
+
};
211
+
let rkey = if let Some((rkey, _rest)) = rest.split_once('?') {
212
+
rkey
213
+
} else {
214
+
rest
215
+
};
216
+
217
+
self.get_record_impl(
218
+
repo.to_string(),
219
+
collection.to_string(),
220
+
rkey.to_string(),
221
+
cid,
222
+
)
223
+
.await
224
+
}
225
+
226
+
async fn get_record_impl(
227
+
&self,
228
+
repo: String,
229
+
collection: String,
230
+
rkey: String,
231
+
cid: Option<String>,
160
232
) -> GetRecordResponse {
161
233
let did = match Did::new(repo.clone()) {
162
234
Ok(did) => did,