diff --git a/graph/src/components/server/mod.rs b/graph/src/components/server/mod.rs index c092d7e211d..89323b9c8b1 100644 --- a/graph/src/components/server/mod.rs +++ b/graph/src/components/server/mod.rs @@ -1,4 +1,7 @@ /// Component for running GraphQL queries over HTTP. pub mod query; +/// Component for the index node server. +pub mod index_node; + pub mod server; diff --git a/graph/src/components/store/traits.rs b/graph/src/components/store/traits.rs index 7d6f2201879..063ca4aa010 100644 --- a/graph/src/components/store/traits.rs +++ b/graph/src/components/store/traits.rs @@ -9,6 +9,7 @@ use super::*; use crate::blockchain::block_stream::{EntitySourceOperation, FirehoseCursor}; use crate::blockchain::{BlockTime, ChainIdentifier, ExtendedBlockPtr}; use crate::components::metrics::stopwatch::StopwatchMetrics; +use crate::components::server::index_node::VersionInfo; use crate::components::subgraph::SubgraphVersionSwitchingMode; use crate::components::transaction_receipt; use crate::components::versions::ApiVersion; @@ -686,6 +687,25 @@ pub trait StatusStore: Send + Sync + 'static { fn status(&self, filter: status::Filter) -> Result, StoreError>; + /// Support for the explorer-specific API + fn version_info(&self, version_id: &str) -> Result; + + /// Support for the explorer-specific API; note that `subgraph_id` must be + /// the id of an entry in `subgraphs.subgraph`, not that of a deployment. + /// The return values are the ids of the `subgraphs.subgraph_version` for + /// the current and pending versions of the subgraph + fn versions_for_subgraph_id( + &self, + subgraph_id: &str, + ) -> Result<(Option, Option), StoreError>; + + /// Support for the explorer-specific API. Returns a vector of (name, version) of all + /// subgraphs for a given deployment hash. + fn subgraphs_for_deployment_hash( + &self, + deployment_hash: &str, + ) -> Result, StoreError>; + /// A value of None indicates that the table is not available. Re-deploying /// the subgraph fixes this. It is undesirable to force everything to /// re-sync from scratch, so existing deployments will continue without a diff --git a/graph/src/env/mod.rs b/graph/src/env/mod.rs index 6d611cf6102..b734302e2cf 100644 --- a/graph/src/env/mod.rs +++ b/graph/src/env/mod.rs @@ -168,6 +168,15 @@ pub struct EnvVars { /// Set by the flag `GRAPH_LOG_TRIGGER_DATA`. Off by /// default. pub log_trigger_data: bool, + /// Set by the environment variable `GRAPH_EXPLORER_TTL` + /// (expressed in seconds). The default value is 10s. + pub explorer_ttl: Duration, + /// Set by the environment variable `GRAPH_EXPLORER_LOCK_THRESHOLD` + /// (expressed in milliseconds). The default value is 100ms. + pub explorer_lock_threshold: Duration, + /// Set by the environment variable `GRAPH_EXPLORER_QUERY_THRESHOLD` + /// (expressed in milliseconds). The default value is 500ms. + pub explorer_query_threshold: Duration, /// Set by the environment variable `EXTERNAL_HTTP_BASE_URL`. No default /// value is provided. pub external_http_base_url: Option, @@ -319,6 +328,9 @@ impl EnvVars { postpone_attribute_index_creation: inner.postpone_attribute_index_creation.0 || cfg!(debug_assertions), log_trigger_data: inner.log_trigger_data.0, + explorer_ttl: Duration::from_secs(inner.explorer_ttl_in_secs), + explorer_lock_threshold: Duration::from_millis(inner.explorer_lock_threshold_in_msec), + explorer_query_threshold: Duration::from_millis(inner.explorer_query_threshold_in_msec), external_http_base_url: inner.external_http_base_url, external_ws_base_url: inner.external_ws_base_url, static_filters_threshold: inner.static_filters_threshold, @@ -480,6 +492,12 @@ struct Inner { postpone_attribute_index_creation: EnvVarBoolean, #[envconfig(from = "GRAPH_LOG_TRIGGER_DATA", default = "false")] log_trigger_data: EnvVarBoolean, + #[envconfig(from = "GRAPH_EXPLORER_TTL", default = "10")] + explorer_ttl_in_secs: u64, + #[envconfig(from = "GRAPH_EXPLORER_LOCK_THRESHOLD", default = "100")] + explorer_lock_threshold_in_msec: u64, + #[envconfig(from = "GRAPH_EXPLORER_QUERY_THRESHOLD", default = "500")] + explorer_query_threshold_in_msec: u64, #[envconfig(from = "EXTERNAL_HTTP_BASE_URL")] external_http_base_url: Option, #[envconfig(from = "EXTERNAL_WS_BASE_URL")] diff --git a/server/index-node/src/explorer.rs b/server/index-node/src/explorer.rs new file mode 100644 index 00000000000..da7d6354076 --- /dev/null +++ b/server/index-node/src/explorer.rs @@ -0,0 +1,218 @@ +//! Functionality to support the explorer in the hosted service. Everything +//! in this file is private API and experimental and subject to change at +//! any time +use graph::components::server::query::{ServerResponse, ServerResult}; +use graph::http_body_util::Full; +use graph::hyper::header::{ + ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, + CONTENT_TYPE, +}; +use graph::hyper::{Response, StatusCode}; +use graph::prelude::r; +use std::{sync::Arc, time::Instant}; + +use graph::{ + components::{ + server::{index_node::VersionInfo, query::ServerError}, + store::StatusStore, + }, + data::subgraph::status, + object, + prelude::{serde_json, warn, Logger, ENV_VARS}, + util::timed_cache::TimedCache, +}; + +// Do not implement `Clone` for this; the IndexNode service puts the `Explorer` +// behind an `Arc` so we don't have to put each `Cache` into an `Arc` +// +// We cache responses for a fixed amount of time with the time given by +// `GRAPH_EXPLORER_TTL` +#[derive(Debug)] +pub struct Explorer { + store: Arc, + versions: TimedCache, + version_infos: TimedCache, + entity_counts: TimedCache, +} + +impl Explorer +where + S: StatusStore, +{ + pub fn new(store: Arc) -> Self { + Self { + store, + versions: TimedCache::new(ENV_VARS.explorer_ttl), + version_infos: TimedCache::new(ENV_VARS.explorer_ttl), + entity_counts: TimedCache::new(ENV_VARS.explorer_ttl), + } + } + + pub fn handle(&self, logger: &Logger, req: &[&str]) -> ServerResult { + match req { + ["subgraph-versions", subgraph_id] => self.handle_subgraph_versions(subgraph_id), + ["subgraph-version", version] => self.handle_subgraph_version(version), + ["subgraph-repo", version] => self.handle_subgraph_repo(version), + ["entity-count", deployment] => self.handle_entity_count(logger, deployment), + ["subgraphs-for-deployment", deployment_hash] => { + self.handle_subgraphs_for_deployment(deployment_hash) + } + _ => handle_not_found(), + } + } + + fn handle_subgraph_versions(&self, subgraph_id: &str) -> ServerResult { + if let Some(value) = self.versions.get(subgraph_id) { + return Ok(as_http_response(value.as_ref())); + } + + let (current, pending) = self.store.versions_for_subgraph_id(subgraph_id)?; + + let value = object! { + currentVersion: current, + pendingVersion: pending + }; + + let resp = as_http_response(&value); + self.versions.set(subgraph_id.to_string(), Arc::new(value)); + Ok(resp) + } + + fn handle_subgraph_version(&self, version: &str) -> ServerResult { + let vi = self.version_info(version)?; + + let latest_ethereum_block_number = vi.latest_ethereum_block_number; + let total_ethereum_blocks_count = vi.total_ethereum_blocks_count; + let value = object! { + createdAt: vi.created_at.as_str(), + deploymentId: vi.deployment_id.as_str(), + latestEthereumBlockNumber: latest_ethereum_block_number, + totalEthereumBlocksCount: total_ethereum_blocks_count, + synced: vi.synced, + failed: vi.failed, + description: vi.description.as_deref(), + repository: vi.repository.as_deref(), + schema: vi.schema.document_string(), + network: vi.network.as_str() + }; + Ok(as_http_response(&value)) + } + + fn handle_subgraph_repo(&self, version: &str) -> ServerResult { + let vi = self.version_info(version)?; + + let value = object! { + createdAt: vi.created_at.as_str(), + deploymentId: vi.deployment_id.as_str(), + repository: vi.repository.as_deref() + }; + Ok(as_http_response(&value)) + } + + fn handle_entity_count(&self, logger: &Logger, deployment: &str) -> ServerResult { + let start = Instant::now(); + let count = self.entity_counts.get(deployment); + if start.elapsed() > ENV_VARS.explorer_lock_threshold { + let action = match count { + Some(_) => "cache_hit", + None => "cache_miss", + }; + warn!(logger, "Getting entity_count takes too long"; + "action" => action, + "deployment" => deployment, + "time_ms" => start.elapsed().as_millis()); + } + + if let Some(value) = count { + return Ok(as_http_response(value.as_ref())); + } + + let start = Instant::now(); + let infos = self + .store + .status(status::Filter::Deployments(vec![deployment.to_string()]))?; + if start.elapsed() > ENV_VARS.explorer_query_threshold { + warn!(logger, "Getting entity_count takes too long"; + "action" => "query_status", + "deployment" => deployment, + "time_ms" => start.elapsed().as_millis()); + } + let info = match infos.first() { + Some(info) => info, + None => { + return handle_not_found(); + } + }; + + let value = object! { + entityCount: info.entity_count as i32 + }; + let start = Instant::now(); + let resp = as_http_response(&value); + if start.elapsed() > ENV_VARS.explorer_lock_threshold { + warn!(logger, "Getting entity_count takes too long"; + "action" => "as_http_response", + "deployment" => deployment, + "time_ms" => start.elapsed().as_millis()); + } + let start = Instant::now(); + self.entity_counts + .set(deployment.to_string(), Arc::new(value)); + if start.elapsed() > ENV_VARS.explorer_lock_threshold { + warn!(logger, "Getting entity_count takes too long"; + "action" => "cache_set", + "deployment" => deployment, + "time_ms" => start.elapsed().as_millis()); + } + Ok(resp) + } + + fn version_info(&self, version: &str) -> Result, ServerError> { + match self.version_infos.get(version) { + Some(vi) => Ok(vi), + None => { + let vi = Arc::new(self.store.version_info(version)?); + self.version_infos.set(version.to_string(), vi.clone()); + Ok(vi) + } + } + } + + fn handle_subgraphs_for_deployment(&self, deployment_hash: &str) -> ServerResult { + let name_version_pairs: Vec = self + .store + .subgraphs_for_deployment_hash(deployment_hash)? + .into_iter() + .map(|(name, version)| { + object! { + name: name, + version: version + } + }) + .collect(); + let payload = r::Value::List(name_version_pairs); + Ok(as_http_response(&payload)) + } +} + +fn handle_not_found() -> ServerResult { + Ok(Response::builder() + .status(StatusCode::NOT_FOUND) + .header(CONTENT_TYPE, "text/plain") + .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") + .body(Full::from("Not found\n")) + .unwrap()) +} + +fn as_http_response(value: &r::Value) -> ServerResponse { + let status_code = StatusCode::OK; + let json = serde_json::to_string(&value).expect("Failed to serialize response to JSON"); + Response::builder() + .status(status_code) + .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") + .header(ACCESS_CONTROL_ALLOW_HEADERS, "Content-Type, User-Agent") + .header(ACCESS_CONTROL_ALLOW_METHODS, "GET, OPTIONS, POST") + .header(CONTENT_TYPE, "application/json") + .body(Full::from(json)) + .unwrap() +} diff --git a/server/index-node/src/lib.rs b/server/index-node/src/lib.rs index 11ec010fa5e..7ce6b03cb9d 100644 --- a/server/index-node/src/lib.rs +++ b/server/index-node/src/lib.rs @@ -1,4 +1,5 @@ mod auth; +mod explorer; mod resolver; mod schema; mod server; diff --git a/server/index-node/src/service.rs b/server/index-node/src/service.rs index 9f2a79df739..d07d9b9e5e3 100644 --- a/server/index-node/src/service.rs +++ b/server/index-node/src/service.rs @@ -23,6 +23,7 @@ use graph_graphql::prelude::{execute_query, Query as PreparedQuery, QueryExecuti use crate::auth::bearer_token; +use crate::explorer::Explorer; use crate::resolver::IndexNodeResolver; use crate::schema::SCHEMA; @@ -42,6 +43,7 @@ pub struct IndexNodeService { logger: Logger, blockchain_map: Arc, store: Arc, + explorer: Arc>, link_resolver: Arc, } @@ -55,10 +57,13 @@ where store: Arc, link_resolver: Arc, ) -> Self { + let explorer = Arc::new(Explorer::new(store.clone())); + IndexNodeService { logger, blockchain_map, store, + explorer, link_resolver, } } @@ -224,6 +229,8 @@ where } (Method::OPTIONS, ["graphql"]) => Ok(Self::handle_graphql_options(req)), + (Method::GET, ["explorer", rest @ ..]) => self.explorer.handle(&self.logger, rest), + _ => Ok(Self::handle_not_found()), } } diff --git a/store/postgres/src/deployment.rs b/store/postgres/src/deployment.rs index 13b54b2a6b5..4b3da58469d 100644 --- a/store/postgres/src/deployment.rs +++ b/store/postgres/src/deployment.rs @@ -341,6 +341,8 @@ pub fn schema(conn: &mut PgConnection, site: &Site) -> Result<(InputSchema, bool } pub struct ManifestInfo { + pub description: Option, + pub repository: Option, pub spec_version: String, pub instrument: bool, } @@ -348,8 +350,18 @@ pub struct ManifestInfo { impl ManifestInfo { pub fn load(conn: &mut PgConnection, site: &Site) -> Result { use subgraph_manifest as sm; - let (spec_version, features): (String, Vec) = sm::table - .select((sm::spec_version, sm::features)) + let (description, repository, spec_version, features): ( + Option, + Option, + String, + Vec, + ) = sm::table + .select(( + sm::description, + sm::repository, + sm::spec_version, + sm::features, + )) .filter(sm::id.eq(site.id)) .first(conn)?; @@ -359,6 +371,8 @@ impl ManifestInfo { let instrument = features.iter().any(|s| s == "instrument"); Ok(ManifestInfo { + description, + repository, spec_version, instrument, }) diff --git a/store/postgres/src/deployment_store.rs b/store/postgres/src/deployment_store.rs index ea78d1d2e09..7aaedf12895 100644 --- a/store/postgres/src/deployment_store.rs +++ b/store/postgres/src/deployment_store.rs @@ -82,6 +82,8 @@ pub(crate) struct SubgraphInfo { /// The deployment hash of the remote subgraph whose store /// will be GraphQL queried, for debugging purposes. pub(crate) debug_fork: Option, + pub(crate) description: Option, + pub(crate) repository: Option, pub(crate) poi_version: ProofOfIndexingVersion, pub(crate) instrument: bool, } @@ -497,6 +499,8 @@ impl DeploymentStore { api, graft_block, debug_fork, + description: manifest_info.description, + repository: manifest_info.repository, poi_version, instrument: manifest_info.instrument, }; diff --git a/store/postgres/src/lib.rs b/store/postgres/src/lib.rs index a05fc40d36f..0bbb261c154 100644 --- a/store/postgres/src/lib.rs +++ b/store/postgres/src/lib.rs @@ -48,7 +48,6 @@ pub mod layout_for_tests { make_dummy_site, Connection, Mirror, Namespace, EVENT_TAP, EVENT_TAP_ENABLED, }; pub use crate::relational::*; - pub use crate::store::VersionInfo; pub mod writable { pub use crate::writable::test_support::allow_steps; } diff --git a/store/postgres/src/primary.rs b/store/postgres/src/primary.rs index 50e358dc388..4ed8bada0a4 100644 --- a/store/postgres/src/primary.rs +++ b/store/postgres/src/primary.rs @@ -441,12 +441,13 @@ pub fn make_dummy_site(deployment: DeploymentHash, namespace: Namespace, network /// read-only mod queries { use diesel::data_types::PgTimestamp; - use diesel::dsl::exists; + use diesel::dsl::{exists, sql}; use diesel::pg::PgConnection; use diesel::prelude::{ - ExpressionMethods, JoinOnDsl, NullableExpressionMethods, OptionalExtension, QueryDsl, - RunQueryDsl, + BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, + OptionalExtension, QueryDsl, RunQueryDsl, }; + use diesel::sql_types::Text; use graph::prelude::NodeId; use graph::{ components::store::DeploymentId as GraphDeploymentId, @@ -713,16 +714,54 @@ mod queries { .transpose() } - pub(super) fn deployment_for_version( + pub(super) fn version_info( conn: &mut PgConnection, version: &str, - ) -> Result, StoreError> { + ) -> Result, StoreError> { Ok(v::table - .select(v::deployment) + .select((v::deployment, sql::("created_at::text"))) .filter(v::id.eq(version)) - .first::(conn) + .first::<(String, String)>(conn) .optional()?) } + + pub(super) fn versions_for_subgraph_id( + conn: &mut PgConnection, + subgraph_id: &str, + ) -> Result<(Option, Option), StoreError> { + Ok(s::table + .select((s::current_version.nullable(), s::pending_version.nullable())) + .filter(s::id.eq(subgraph_id)) + .first::<(Option, Option)>(conn) + .optional()? + .unwrap_or((None, None))) + } + + /// Returns all (subgraph_name, version) pairs for a given deployment hash. + pub fn subgraphs_by_deployment_hash( + conn: &mut PgConnection, + deployment_hash: &str, + ) -> Result, StoreError> { + v::table + .inner_join( + s::table.on(v::id + .nullable() + .eq(s::current_version) + .or(v::id.nullable().eq(s::pending_version))), + ) + .filter(v::deployment.eq(&deployment_hash)) + .select(( + s::name, + sql::( + "(case when subgraphs.subgraph.pending_version = subgraphs.subgraph_version.id then 'pending' + when subgraphs.subgraph.current_version = subgraphs.subgraph_version.id then 'current' + else 'unused' + end) as version", + ), + )) + .get_results(conn) + .map_err(Into::into) + } } /// A wrapper for a database connection that provides access to functionality @@ -2074,8 +2113,23 @@ impl Mirror { self.read(|conn| queries::fill_assignments(conn, infos)) } - pub fn deployment_for_version(&self, version: &str) -> Result, StoreError> { - self.read(|conn| queries::deployment_for_version(conn, version)) + pub fn version_info(&self, version: &str) -> Result, StoreError> { + self.read(|conn| queries::version_info(conn, version)) + } + + pub fn versions_for_subgraph_id( + &self, + subgraph_id: &str, + ) -> Result<(Option, Option), StoreError> { + self.read(|conn| queries::versions_for_subgraph_id(conn, subgraph_id)) + } + + /// Returns all (subgraph_name, version) pairs for a given deployment hash. + pub fn subgraphs_by_deployment_hash( + &self, + deployment_hash: &str, + ) -> Result, StoreError> { + self.read(|conn| queries::subgraphs_by_deployment_hash(conn, deployment_hash)) } pub fn find_site_in_shard( diff --git a/store/postgres/src/store.rs b/store/postgres/src/store.rs index 458b6db2c92..bda5b2da136 100644 --- a/store/postgres/src/store.rs +++ b/store/postgres/src/store.rs @@ -2,9 +2,12 @@ use async_trait::async_trait; use std::sync::Arc; use graph::{ - components::store::{ - BlockPtrForNumber, BlockStore as BlockStoreTrait, QueryPermit, QueryStoreManager, - StatusStore, Store as StoreTrait, + components::{ + server::index_node::VersionInfo, + store::{ + BlockPtrForNumber, BlockStore as BlockStoreTrait, QueryPermit, QueryStoreManager, + StatusStore, Store as StoreTrait, + }, }, data::subgraph::status, internal_error, @@ -16,14 +19,6 @@ use graph::{ use crate::{block_store::BlockStore, query_store::QueryStore, SubgraphStore}; -/// This is only needed for some tests -#[derive(Debug)] -pub struct VersionInfo { - pub deployment_id: String, - pub latest_ethereum_block_number: Option, - pub failed: bool, -} - /// The overall store of the system, consisting of a [`SubgraphStore`] and a /// [`BlockStore`], each of which multiplex across multiple database shards. /// The `SubgraphStore` is responsible for storing all data and metadata related @@ -55,12 +50,6 @@ impl Store { pub fn block_store(&self) -> Arc { self.block_store.cheap_clone() } - - pub fn version_info(&self, version_id: &str) -> Result { - let info = self.subgraph_store.version_info(version_id)?; - - Ok(info) - } } impl StoreTrait for Store { @@ -128,6 +117,29 @@ impl StatusStore for Store { Ok(infos) } + fn version_info(&self, version_id: &str) -> Result { + let mut info = self.subgraph_store.version_info(version_id)?; + + info.total_ethereum_blocks_count = self.block_store.chain_head_block(&info.network)?; + + Ok(info) + } + + fn versions_for_subgraph_id( + &self, + subgraph_id: &str, + ) -> Result<(Option, Option), StoreError> { + self.subgraph_store.versions_for_subgraph_id(subgraph_id) + } + + fn subgraphs_for_deployment_hash( + &self, + deployment_hash: &str, + ) -> Result, StoreError> { + self.subgraph_store + .subgraphs_for_deployment_hash(deployment_hash) + } + async fn get_proof_of_indexing( &self, subgraph_id: &DeploymentHash, diff --git a/store/postgres/src/subgraph_store.rs b/store/postgres/src/subgraph_store.rs index 5cfdcbc2f1f..2ba2a1a58f6 100644 --- a/store/postgres/src/subgraph_store.rs +++ b/store/postgres/src/subgraph_store.rs @@ -14,9 +14,12 @@ use std::{iter::FromIterator, time::Duration}; use graph::futures03::future::join_all; use graph::{ cheap_clone::CheapClone, - components::store::{ - self, BlockPtrForNumber, BlockStore, DeploymentLocator, EnsLookup as EnsLookupTrait, - PruneReporter, PruneRequest, SubgraphFork, + components::{ + server::index_node::VersionInfo, + store::{ + self, BlockPtrForNumber, BlockStore, DeploymentLocator, EnsLookup as EnsLookupTrait, + PruneReporter, PruneRequest, SubgraphFork, + }, }, data::query::QueryTarget, data::subgraph::{schema::DeploymentCreate, status, DeploymentFeatures}, @@ -41,7 +44,6 @@ use crate::{ index::{IndexList, Method}, Layout, }, - store::VersionInfo, writable::{SourceableStore, WritableStore}, ConnectionPool, NotificationSender, }; @@ -980,7 +982,7 @@ impl SubgraphStoreInner { } pub(crate) fn version_info(&self, version: &str) -> Result { - if let Some(deployment_id) = self.mirror.deployment_for_version(version)? { + if let Some((deployment_id, created_at)) = self.mirror.version_info(version)? { let id = DeploymentHash::new(deployment_id.clone()) .map_err(|id| internal_error!("illegal deployment id {}", id))?; let (store, site) = self.store(&id)?; @@ -994,11 +996,21 @@ impl SubgraphStoreInner { .ok_or_else(|| internal_error!("no chain info for {}", deployment_id))?; let latest_ethereum_block_number = chain.latest_block.as_ref().map(|block| block.number()); + let subgraph_info = store.subgraph_info(site.cheap_clone())?; + let layout = store.find_layout(site.cheap_clone())?; + let network = site.network.clone(); let info = VersionInfo { + created_at, deployment_id, latest_ethereum_block_number, + total_ethereum_blocks_count: None, + synced: status.synced, failed: status.health.is_failed(), + description: subgraph_info.description, + repository: subgraph_info.repository, + schema: layout.input_schema.cheap_clone(), + network, }; Ok(info) } else { @@ -1006,6 +1018,20 @@ impl SubgraphStoreInner { } } + pub(crate) fn versions_for_subgraph_id( + &self, + subgraph_id: &str, + ) -> Result<(Option, Option), StoreError> { + self.mirror.versions_for_subgraph_id(subgraph_id) + } + + pub(crate) fn subgraphs_for_deployment_hash( + &self, + deployment_hash: &str, + ) -> Result, StoreError> { + self.mirror.subgraphs_by_deployment_hash(deployment_hash) + } + #[cfg(debug_assertions)] pub fn error_count(&self, id: &DeploymentHash) -> Result { let (store, _) = self.store(id)?; diff --git a/store/test-store/tests/postgres/subgraph.rs b/store/test-store/tests/postgres/subgraph.rs index d041234e0c7..c66d34e27c7 100644 --- a/store/test-store/tests/postgres/subgraph.rs +++ b/store/test-store/tests/postgres/subgraph.rs @@ -1,6 +1,9 @@ use graph::futures03; use graph::{ - components::store::{DeploymentId, DeploymentLocator, StatusStore}, + components::{ + server::index_node::VersionInfo, + store::{DeploymentId, DeploymentLocator, StatusStore}, + }, data::query::QueryTarget, data::subgraph::{schema::SubgraphHealth, SubgraphFeature}, data::subgraph::{ @@ -19,7 +22,7 @@ use graph::{ schema::InputSchema, semver::Version, }; -use graph_store_postgres::layout_for_tests::{Connection as Primary, VersionInfo}; +use graph_store_postgres::layout_for_tests::Connection as Primary; use graph_store_postgres::SubgraphStore; use std::{collections::HashSet, marker::PhantomData, sync::Arc}; use test_store::*; @@ -470,6 +473,48 @@ fn status() { }) } +#[test] +fn version_info() { + const NAME: &str = "versionInfoSubgraph"; + + async fn setup() -> DeploymentLocator { + let id = DeploymentHash::new(NAME).unwrap(); + remove_subgraphs(); + block_store::set_chain(vec![], NETWORK_NAME).await; + create_test_subgraph(&id, SUBGRAPH_GQL).await + } + + run_test_sequentially(|store| async move { + let deployment = setup().await; + transact_and_wait( + &store.subgraph_store(), + &deployment, + BLOCK_ONE.clone(), + vec![], + ) + .await + .unwrap(); + + let vi = get_version_info(&store, NAME); + assert_eq!(NAME, vi.deployment_id.as_str()); + assert_eq!(false, vi.synced); + assert_eq!(false, vi.failed); + assert_eq!( + Some("manifest for versionInfoSubgraph"), + vi.description.as_deref() + ); + assert_eq!( + Some("repo for versionInfoSubgraph"), + vi.repository.as_deref() + ); + assert_eq!(NAME, vi.schema.id().as_str()); + assert_eq!(Some(1), vi.latest_ethereum_block_number); + assert_eq!(NETWORK_NAME, vi.network.as_str()); + // We set the head for the network to null in the test framework + assert_eq!(None, vi.total_ethereum_blocks_count); + }) +} + #[test] fn subgraph_features() { run_test_sequentially(|_store| async move {