tangled
alpha
login
or
join now
bad-example.com
/
microcosm-links
7
fork
atom
APIs for links and references in the ATmosphere
7
fork
atom
overview
issues
pulls
pipelines
com.bad-example.repo.getUriRecord
bad-example.com
7 months ago
0b1ba939
b894085d
+80
-6
3 changed files
expand all
collapse all
unified
split
Cargo.lock
slingshot
Cargo.toml
src
server.rs
+1
Cargo.lock
···
4924
4924
"foyer",
4925
4925
"hickory-resolver",
4926
4926
"jetstream",
4927
4927
+
"links",
4927
4928
"log",
4928
4929
"metrics",
4929
4930
"metrics-exporter-prometheus 0.17.2",
+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
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
···
4
4
};
5
5
use atrium_api::types::string::{Cid, Did, Handle, Nsid, RecordKey};
6
6
use foyer::HybridCache;
7
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
36
+
}
37
37
+
fn example_uri() -> String {
38
38
+
format!(
39
39
+
"at://{}/{}/{}",
40
40
+
example_did(),
41
41
+
example_collection(),
42
42
+
example_rkey()
43
43
+
)
35
44
}
36
45
37
46
#[derive(Object)]
···
84
93
impl Example for FoundRecordResponseObject {
85
94
fn example() -> Self {
86
95
Self {
87
87
-
uri: format!(
88
88
-
"at://{}/{}/{}",
89
89
-
example_did(),
90
90
-
example_collection(),
91
91
-
example_rkey()
92
92
-
),
96
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
164
+
) -> GetRecordResponse {
165
165
+
self.get_record_impl(repo, collection, rkey, cid).await
166
166
+
}
167
167
+
168
168
+
/// com.bad-example.repo.getUriRecord
169
169
+
///
170
170
+
/// Ergonomic complement to [`com.atproto.repo.getRecord`](https://docs.bsky.app/docs/api/com-atproto-repo-get-record)
171
171
+
/// which accepts an at-uri instead of individual rep/collection/rkey params
172
172
+
#[oai(path = "/com.bad-example.repo.getUriRecord", method = "get")]
173
173
+
async fn get_uri_record(
174
174
+
&self,
175
175
+
/// The at-uri of the record
176
176
+
///
177
177
+
/// The identifier can be a DID or an atproto handle, and the collection
178
178
+
/// and rkey segments must be present.
179
179
+
#[oai(example = "example_uri")]
180
180
+
Query(at_uri): Query<String>,
181
181
+
/// Optional: the CID of the version of the record.
182
182
+
///
183
183
+
/// If not specified, then return the most recent version.
184
184
+
///
185
185
+
/// If specified and a newer version of the record exists, returns 404 not
186
186
+
/// found. That is: slingshot only retains the most recent version of a
187
187
+
/// record.
188
188
+
Query(cid): Query<Option<String>>,
189
189
+
) -> GetRecordResponse {
190
190
+
let bad_at_uri = || {
191
191
+
GetRecordResponse::BadRequest(xrpc_error(
192
192
+
"InvalidRequest",
193
193
+
"at-uri does not appear to be valid",
194
194
+
))
195
195
+
};
196
196
+
197
197
+
let Some(normalized) = normalize_at_uri(&at_uri) else {
198
198
+
return bad_at_uri();
199
199
+
};
200
200
+
201
201
+
// TODO: move this to links
202
202
+
let Some(rest) = normalized.strip_prefix("at://") else {
203
203
+
return bad_at_uri();
204
204
+
};
205
205
+
let Some((repo, rest)) = rest.split_once('/') else {
206
206
+
return bad_at_uri();
207
207
+
};
208
208
+
let Some((collection, rest)) = rest.split_once('/') else {
209
209
+
return bad_at_uri();
210
210
+
};
211
211
+
let rkey = if let Some((rkey, _rest)) = rest.split_once('?') {
212
212
+
rkey
213
213
+
} else {
214
214
+
rest
215
215
+
};
216
216
+
217
217
+
self.get_record_impl(
218
218
+
repo.to_string(),
219
219
+
collection.to_string(),
220
220
+
rkey.to_string(),
221
221
+
cid,
222
222
+
)
223
223
+
.await
224
224
+
}
225
225
+
226
226
+
async fn get_record_impl(
227
227
+
&self,
228
228
+
repo: String,
229
229
+
collection: String,
230
230
+
rkey: String,
231
231
+
cid: Option<String>,
160
232
) -> GetRecordResponse {
161
233
let did = match Did::new(repo.clone()) {
162
234
Ok(did) => did,