Skip to content
This repository has been archived by the owner on Oct 25, 2024. It is now read-only.

enhancement: integrate async-graphql into GraphQL API #877

Merged
merged 16 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Putting all of that together, we get the following query:
```graphql
query {
transactions: tx(
order: { desc: timestamp },
order: { timestamp: desc },
filter: { value: { gt: 0 } },
first: 2,
offset: 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Let's use the [block explorer](../../../examples/block-explorer.md) example to i

```graphql
query {
blocks: block(order: { asc: height }, first: 5) {
blocks: block(order: { height: asc }, first: 5) {
hash
height
timestamp
Expand Down Expand Up @@ -70,7 +70,7 @@ As you can see, we get the requested amount of blocks and the corresponding fiel
```graphql
query {
blocks: block(
order: { asc: height },
order: { height: asc },
first: 5,
offset: 5
) {
Expand Down
56 changes: 13 additions & 43 deletions packages/fuel-indexer-api-server/src/uses.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
api::{ApiError, ApiResult, HttpError},
models::{QueryResponse, VerifySignatureRequest},
models::VerifySignatureRequest,
};
use async_graphql::http::{playground_source, GraphQLPlaygroundConfig};
use async_graphql_axum::GraphQLRequest;
Expand All @@ -17,7 +17,7 @@ use fuel_indexer_database::{
types::{IndexAsset, IndexAssetType},
IndexerConnectionPool,
};
use fuel_indexer_graphql::graphql::GraphqlQueryBuilder;
use fuel_indexer_graphql::dynamic::{build_dynamic_schema, execute_query};
use fuel_indexer_lib::{
config::{
auth::{AuthenticationStrategy, Claims},
Expand All @@ -29,7 +29,7 @@ use fuel_indexer_lib::{
ServiceRequest, ServiceStatus,
},
};
use fuel_indexer_schema::db::{manager::SchemaManager, tables::Schema};
use fuel_indexer_schema::db::manager::SchemaManager;
use hyper::Client;
use hyper_rustls::HttpsConnectorBuilder;
use jsonwebtoken::{encode, EncodingKey, Header};
Expand Down Expand Up @@ -60,13 +60,15 @@ pub(crate) async fn query_graph(
.load_schema(&namespace, &identifier)
.await
{
Ok(schema) => match run_query(req.into_inner().query, schema, &pool).await {
Ok(query_res) => Ok(axum::Json(query_res)),
Err(e) => {
error!("query_graph error: {e}");
Err(e)
}
},
Ok(schema) => {
let dynamic_schema = build_dynamic_schema(schema.clone()).await?;
let user_query = req.0.query.clone();
let response =
execute_query(req.into_inner(), dynamic_schema, user_query, pool, schema)
.await?;
let json_res = axum::Json(response);
Ok(json_res)
}
Err(_e) => Err(ApiError::Http(HttpError::NotFound(format!(
"The graph '{namespace}.{identifier}' was not found."
)))),
Expand Down Expand Up @@ -366,44 +368,12 @@ pub(crate) async fn verify_signature(
unreachable!();
}

pub async fn run_query(
query: String,
schema: Schema,
pool: &IndexerConnectionPool,
) -> ApiResult<Value> {
let builder = GraphqlQueryBuilder::new(&schema, &query)?;
let query = builder.build()?;

let queries = query.as_sql(&schema, pool.database_type())?.join(";\n");

let mut conn = pool.acquire().await?;

match queries::run_query(&mut conn, queries).await {
Ok(ans) => {
let ans_json: Value = serde_json::from_value(ans)?;

// If the response is paginated, remove the array wrapping.
if ans_json[0].get("page_info").is_some() {
Ok(serde_json::json!(QueryResponse {
data: ans_json[0].clone()
}))
} else {
Ok(serde_json::json!(QueryResponse { data: ans_json }))
}
}
Err(e) => {
error!("Error querying database: {e}.");
Err(e.into())
}
}
}

pub async fn gql_playground(
Path((namespace, identifier)): Path<(String, String)>,
) -> ApiResult<impl IntoResponse> {
let html = playground_source(
GraphQLPlaygroundConfig::new(&format!("/api/graph/{namespace}/{identifier}"))
.with_setting("scehma.polling.enable", false),
.with_setting("schema.polling.enable", false),
);

let response = Response::builder()
Expand Down
3 changes: 3 additions & 0 deletions packages/fuel-indexer-graphql/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ rust-version = { workspace = true }
description = "Fuel Indexer GraphQL"

[dependencies]
async-graphql = { version = "5.0", features = ["dynamic-schema"] }
async-graphql-parser = "5.0"
async-graphql-value = "5.0"
fuel-indexer-database = { workspace = true }
fuel-indexer-database-types = { workspace = true }
fuel-indexer-schema = { workspace = true, features = ["db-models"] }
fuel-indexer-types = { workspace = true }
lazy_static = "1.4"
serde_json = "1.0"
thiserror = { workspace = true }

[dev-dependencies]
Expand Down
13 changes: 4 additions & 9 deletions packages/fuel-indexer-graphql/src/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,9 +398,9 @@ pub fn parse_argument_into_param(
)?))),
"order" => {
if let Value::Object(obj) = value {
if let Some((sort_order, predicate)) = obj.into_iter().next() {
if let Value::Enum(field) = predicate {
if schema.field_type(entity_type, field.as_str()).is_some() {
if let Some((field, sort_order)) = obj.into_iter().next() {
if schema.field_type(entity_type, field.as_str()).is_some() {
if let Value::Enum(sort_order) = sort_order {
match sort_order.as_str() {
"asc" => {
return Ok(ParamType::Sort(
Expand All @@ -420,15 +420,10 @@ pub fn parse_argument_into_param(
))
}
}
} else {
return Err(GraphqlError::UnrecognizedField(
entity_type.to_string(),
field.to_string(),
));
}
} else {
return Err(GraphqlError::UnsupportedValueType(
predicate.to_string(),
sort_order.to_string(),
));
}
}
Expand Down
Loading