Skip to content

Commit f5bf2e2

Browse files
committed
wip: Service definition
1 parent c0fabf6 commit f5bf2e2

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/proof_system/global_merkle_root/api/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ version = { workspace = true }
1414
anyhow = { workspace = true }
1515
async-trait = { workspace = true }
1616
async-graphql = { workspace = true }
17+
axum = { workspace = true }
1718
derive_more = { workspace = true }
1819
fuel-core-global-merkle-root-storage = { workspace = true }
1920
fuel-core-services = { workspace = true }
@@ -23,6 +24,7 @@ fuel-core-types = { workspace = true, default-features = false, features = [
2324
"alloc",
2425
] }
2526
hex = { workspace = true }
27+
hyper = { workspace = true }
2628
tracing = { workspace = true }
2729

2830
[dev-dependencies]
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
use std::{
2+
future::{
3+
Future,
4+
IntoFuture,
5+
},
6+
marker::PhantomData,
7+
net::{
8+
TcpListener,
9+
ToSocketAddrs,
10+
},
11+
pin::Pin,
12+
};
13+
14+
use async_graphql::http::GraphiQLSource;
15+
use axum::{
16+
response::{
17+
Html,
18+
IntoResponse,
19+
},
20+
routing,
21+
Router,
22+
};
23+
use fuel_core_services::{
24+
RunnableService,
25+
RunnableTask,
26+
ServiceRunner,
27+
StateWatcher,
28+
TaskNextAction,
29+
};
30+
31+
pub fn new_service<Storage>(
32+
storage: Storage,
33+
network_address: impl ToSocketAddrs,
34+
) -> anyhow::Result<ServiceRunner<StateRootApiService>> {
35+
Ok(ServiceRunner::new(StateRootApiService::new(
36+
storage,
37+
network_address,
38+
)?))
39+
}
40+
41+
pub struct StateRootApiService {
42+
router: Router<hyper::Body>,
43+
listener: TcpListener,
44+
}
45+
46+
impl StateRootApiService {
47+
#[tracing::instrument(skip(storage, network_address))]
48+
fn new<Storage>(
49+
storage: Storage,
50+
network_address: impl ToSocketAddrs,
51+
) -> anyhow::Result<Self> {
52+
let graphql_endpoint = "/graphql";
53+
54+
let graphql_playground = || render_graphql_playground(graphql_endpoint);
55+
56+
// TODO: Create schema, and serve it
57+
58+
let router = Router::<hyper::Body>::new()
59+
.route("/playground", routing::get(graphql_playground));
60+
61+
let listener = TcpListener::bind(network_address)?;
62+
63+
Ok(Self { router, listener })
64+
}
65+
}
66+
67+
#[async_trait::async_trait]
68+
impl RunnableService for StateRootApiService {
69+
const NAME: &'static str = "StateRootGraphQL";
70+
71+
type SharedData = ();
72+
type Task = StateRootApiTask;
73+
type TaskParams = ();
74+
75+
fn shared_data(&self) -> Self::SharedData {}
76+
77+
#[tracing::instrument(skip(self, state, _params))]
78+
async fn into_task(
79+
self,
80+
state: &StateWatcher,
81+
_params: Self::TaskParams,
82+
) -> anyhow::Result<Self::Task> {
83+
let mut state = state.clone();
84+
85+
let graceful_shutdown_signal = async move {
86+
state.while_started().await.expect("unexpected termination");
87+
};
88+
89+
let bound_address = self.listener.local_addr()?;
90+
tracing::info!(%bound_address, "listening for GraphQL requests");
91+
92+
let server = Box::pin(
93+
axum::Server::from_tcp(self.listener)?
94+
.serve(self.router.into_make_service())
95+
.with_graceful_shutdown(graceful_shutdown_signal),
96+
);
97+
98+
Ok(StateRootApiTask { server })
99+
}
100+
}
101+
102+
pub struct StateRootApiTask {
103+
server: Pin<Box<dyn Future<Output = hyper::Result<()>> + Send + 'static>>,
104+
}
105+
106+
impl RunnableTask for StateRootApiTask {
107+
async fn run(
108+
&mut self,
109+
watcher: &mut fuel_core_services::StateWatcher,
110+
) -> TaskNextAction {
111+
match self.server.as_mut().await {
112+
Ok(()) => {
113+
// The `axum::Server` has stopped, and so should we
114+
TaskNextAction::Stop
115+
}
116+
Err(error) => {
117+
tracing::error!(%error, "state root axum server returned error");
118+
TaskNextAction::Stop
119+
}
120+
}
121+
}
122+
123+
async fn shutdown(self) -> anyhow::Result<()> {
124+
Ok(())
125+
}
126+
}
127+
128+
async fn render_graphql_playground(graphql_endpoint: &str) -> impl IntoResponse {
129+
Html(
130+
GraphiQLSource::build()
131+
.endpoint(graphql_endpoint)
132+
.title("Fuel Graphql Playground")
133+
.finish(),
134+
)
135+
}

0 commit comments

Comments
 (0)