Skip to content

Commit ed1e7f2

Browse files
committed
add dynamic-files example
1 parent 59cfedf commit ed1e7f2

File tree

6 files changed

+154
-2
lines changed

6 files changed

+154
-2
lines changed

Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
[workspace]
2+
resolver = "2"
23
members = [
34
"models/starwars",
4-
"models/dynamic-starwars",
55
"models/books",
66
"models/files",
77
"models/token",
8+
"models/dynamic-starwars",
9+
"models/dynamic-files",
810

911
"poem/opentelemetry-basic",
1012
"poem/starwars",
@@ -15,6 +17,7 @@ members = [
1517
"poem/dynamic-schema",
1618
"poem/dynamic-starwars",
1719
"poem/dynamic-books",
20+
"poem/dynamic-upload",
1821

1922
"actix-web/token-from-header",
2023
"actix-web/subscription",

models/dynamic-files/Cargo.toml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "dynamic-files"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
async-graphql = { path = "../../.." }
8+
slab = "0.4.2"
9+
futures = "0.3.0"

models/dynamic-files/src/lib.rs

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use async_graphql::{
2+
dynamic::{Field, FieldFuture, FieldValue, InputValue, Object, Schema, SchemaError, TypeRef},
3+
Value,
4+
};
5+
use futures::lock::Mutex;
6+
use slab::Slab;
7+
8+
pub type Storage = Mutex<Slab<FileInfo>>;
9+
10+
#[derive(Clone)]
11+
pub struct FileInfo {
12+
pub id: String,
13+
url: String,
14+
}
15+
16+
pub fn schema() -> Result<Schema, SchemaError> {
17+
let file_info = Object::new("FileInfo")
18+
.field(Field::new("id", TypeRef::named_nn(TypeRef::ID), |ctx| {
19+
FieldFuture::new(async {
20+
let file_info = ctx.parent_value.try_downcast_ref::<FileInfo>()?;
21+
Ok(Some(Value::from(&file_info.id)))
22+
})
23+
}))
24+
.field(Field::new(
25+
"url",
26+
TypeRef::named_nn(TypeRef::STRING),
27+
|ctx| {
28+
FieldFuture::new(async {
29+
let file_info = ctx.parent_value.try_downcast_ref::<FileInfo>()?;
30+
Ok(Some(Value::from(&file_info.url)))
31+
})
32+
},
33+
));
34+
35+
let query = Object::new("Query").field(Field::new(
36+
"uploads",
37+
TypeRef::named_nn_list_nn(file_info.type_name()),
38+
|ctx| {
39+
FieldFuture::new(async move {
40+
let storage = ctx.data_unchecked::<Storage>().lock().await;
41+
Ok(Some(FieldValue::list(
42+
storage
43+
.iter()
44+
.map(|(_, file)| FieldValue::owned_any(file.clone())),
45+
)))
46+
})
47+
},
48+
));
49+
50+
let mutation = Object::new("Mutation")
51+
.field(
52+
Field::new(
53+
"singleUpload",
54+
TypeRef::named_nn(file_info.type_name()),
55+
|ctx| {
56+
FieldFuture::new(async move {
57+
let mut storage = ctx.data_unchecked::<Storage>().lock().await;
58+
let file = ctx.args.try_get("file")?.upload()?;
59+
let entry = storage.vacant_entry();
60+
let upload = file.value(&ctx).unwrap();
61+
let info = FileInfo {
62+
id: entry.key().to_string(),
63+
url: upload.filename.clone(),
64+
};
65+
entry.insert(info.clone());
66+
Ok(Some(FieldValue::owned_any(info)))
67+
})
68+
},
69+
)
70+
.argument(InputValue::new("file", TypeRef::named_nn(TypeRef::UPLOAD))),
71+
)
72+
.field(
73+
Field::new(
74+
"multipleUpload",
75+
TypeRef::named_nn_list_nn(file_info.type_name()),
76+
|ctx| {
77+
FieldFuture::new(async move {
78+
let mut infos = Vec::new();
79+
let mut storage = ctx.data_unchecked::<Storage>().lock().await;
80+
for item in ctx.args.try_get("files")?.list()?.iter() {
81+
let file = item.upload()?;
82+
let entry = storage.vacant_entry();
83+
let upload = file.value(&ctx).unwrap();
84+
let info = FileInfo {
85+
id: entry.key().to_string(),
86+
url: upload.filename.clone(),
87+
};
88+
entry.insert(info.clone());
89+
infos.push(FieldValue::owned_any(info))
90+
}
91+
Ok(Some(infos))
92+
})
93+
},
94+
)
95+
.argument(InputValue::new(
96+
"files",
97+
TypeRef::named_nn_list_nn(TypeRef::UPLOAD),
98+
)),
99+
);
100+
101+
Schema::build(query.type_name(), Some(mutation.type_name()), None)
102+
.enable_uploading()
103+
.register(file_info)
104+
.register(query)
105+
.register(mutation)
106+
.data(Storage::default())
107+
.finish()
108+
}

models/files/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ pub struct MutationRoot;
2828
impl MutationRoot {
2929
async fn single_upload(&self, ctx: &Context<'_>, file: Upload) -> FileInfo {
3030
let mut storage = ctx.data_unchecked::<Storage>().lock().await;
31-
println!("files count: {}", storage.len());
3231
let entry = storage.vacant_entry();
3332
let upload = file.value(ctx).unwrap();
3433
let info = FileInfo {

poem/dynamic-upload/Cargo.toml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "dynamic-upload"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
async-graphql = { path = "../../..", features = ["dynamic-schema"] }
8+
async-graphql-poem = { path = "../../../integrations/poem" }
9+
tokio = { version = "1.8", features = ["macros", "rt-multi-thread"] }
10+
dynamic-files = { path = "../../models/dynamic-files" }
11+
poem = "1.3.48"

poem/dynamic-upload/src/main.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use async_graphql::http::GraphiQLSource;
2+
use async_graphql_poem::GraphQL;
3+
use poem::{get, handler, listener::TcpListener, web::Html, IntoResponse, Route, Server};
4+
5+
#[handler]
6+
async fn graphiql() -> impl IntoResponse {
7+
Html(GraphiQLSource::build().endpoint("/").finish())
8+
}
9+
10+
#[tokio::main]
11+
async fn main() {
12+
let app = Route::new().at(
13+
"/",
14+
get(graphiql).post(GraphQL::new(dynamic_files::schema().unwrap())),
15+
);
16+
17+
println!("GraphiQL IDE: http://localhost:8000");
18+
Server::new(TcpListener::bind("0.0.0.0:8000"))
19+
.run(app)
20+
.await
21+
.unwrap();
22+
}

0 commit comments

Comments
 (0)