Skip to content

Commit

Permalink
Steal ndc-postgres get_schema implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
danieljharvey committed Nov 9, 2023
1 parent 9454f66 commit 92e2421
Show file tree
Hide file tree
Showing 6 changed files with 3,221 additions and 13 deletions.
4 changes: 2 additions & 2 deletions crates/ndc-sqlserver/src/configuration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ pub mod introspection;
pub mod version1;

pub use version1::{
configure, create_state, validate_raw_configuration, Configuration, InitializationError,
RawConfiguration, State,
configure, create_state, occurring_scalar_types, validate_raw_configuration, Configuration,
InitializationError, RawConfiguration, State,
};
28 changes: 28 additions & 0 deletions crates/ndc-sqlserver/src/configuration/version1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::metrics;

use crate::configuration::introspection;
use ndc_sdk::connector;
use query_engine_metadata::metadata;
use query_engine_metadata::metadata::{database, Nullable};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -243,3 +244,30 @@ pub enum InitializationError {
#[error("error initializing Prometheus metrics: {0}")]
PrometheusError(prometheus::Error),
}

/// Collect all the types that can occur in the metadata. This is a bit circumstantial. A better
/// approach is likely to record scalar type names directly in the metadata via configuration.sql.
pub fn occurring_scalar_types(
tables: &metadata::TablesInfo,
native_queries: &metadata::NativeQueries,
) -> BTreeSet<metadata::ScalarType> {
let tables_column_types = tables
.0
.values()
.flat_map(|v| v.columns.values().map(|c| c.r#type.clone()));

let native_queries_column_types = native_queries
.0
.values()
.flat_map(|v| v.columns.values().map(|c| c.r#type.clone()));

let native_queries_arguments_types = native_queries
.0
.values()
.flat_map(|v| v.arguments.values().map(|c| c.r#type.clone()));

tables_column_types
.chain(native_queries_column_types)
.chain(native_queries_arguments_types)
.collect::<BTreeSet<metadata::ScalarType>>()
}
11 changes: 3 additions & 8 deletions crates/ndc-sqlserver/src/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use query_engine_execution::execution;
use query_engine_translation::translation;

use super::configuration;
use super::schema;

#[derive(Clone, Default)]
pub struct SQLServer {}
Expand Down Expand Up @@ -116,15 +117,9 @@ impl connector::Connector for SQLServer {
/// This function implements the [schema endpoint](https://hasura.github.io/ndc-spec/specification/schema/index.html)
/// from the NDC specification.
async fn get_schema(
_configuration: &Self::Configuration,
configuration: &Self::Configuration,
) -> Result<JsonResponse<models::SchemaResponse>, connector::SchemaError> {
Ok(JsonResponse::Value(models::SchemaResponse {
scalar_types: Default::default(),
collections: Default::default(),
functions: Default::default(),
object_types: Default::default(),
procedures: Default::default(),
}))
schema::get_schema(configuration).await.map(Into::into)
}

/// Explain a query by creating an execution plan
Expand Down
1 change: 1 addition & 0 deletions crates/ndc-sqlserver/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod configuration;
pub mod connector;
pub mod metrics;
pub mod schema;
210 changes: 210 additions & 0 deletions crates/ndc-sqlserver/src/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
//! Implement the `/schema` endpoint to return the connector's schema.
//! See the Hasura
//! [Native Data Connector Specification](https://hasura.github.io/ndc-spec/specification/schema/index.html)
//! for further details.
use std::collections::BTreeMap;

use ndc_sdk::connector;
use ndc_sdk::models;
use query_engine_metadata::metadata;

use super::configuration;

/// Get the connector's schema.
///
/// This function implements the [schema endpoint](https://hasura.github.io/ndc-spec/specification/schema/index.html)
/// from the NDC specification.
pub async fn get_schema(
configuration::Configuration { config, .. }: &configuration::Configuration,
) -> Result<models::SchemaResponse, connector::SchemaError> {
let configuration::RawConfiguration { metadata, .. } = config;
let scalar_types: BTreeMap<String, models::ScalarType> =
configuration::occurring_scalar_types(&metadata.tables, &metadata.native_queries)
.iter()
.map(|scalar_type| {
(
scalar_type.0.clone(),
models::ScalarType {
aggregate_functions: metadata
.aggregate_functions
.0
.get(scalar_type)
.unwrap_or(&BTreeMap::new())
.iter()
.map(|(function_name, function_definition)| {
(
function_name.clone(),
models::AggregateFunctionDefinition {
result_type: models::Type::Named {
name: function_definition.return_type.0.clone(),
},
},
)
})
.collect(),
comparison_operators: metadata
.comparison_operators
.0
.get(scalar_type)
.unwrap_or(&BTreeMap::new())
.iter()
.map(|(op_name, op_def)| {
(
op_name.clone(),
models::ComparisonOperatorDefinition {
argument_type: models::Type::Named {
name: op_def.argument_type.0.clone(),
},
},
)
})
.collect(),
update_operators: BTreeMap::new(),
},
)
})
.collect();

let tables: Vec<models::CollectionInfo> = metadata
.tables
.0
.iter()
.map(|(table_name, table)| models::CollectionInfo {
name: table_name.clone(),
description: table.description.clone(),
arguments: BTreeMap::new(),
collection_type: table_name.clone(),
insertable_columns: None,
updatable_columns: None,
deletable: false,
uniqueness_constraints: table
.uniqueness_constraints
.0
.iter()
.map(
|(constraint_name, metadata::UniquenessConstraint(constraint_columns))| {
(
constraint_name.clone(),
models::UniquenessConstraint {
unique_columns: constraint_columns.iter().cloned().collect(),
},
)
},
)
.collect(),
foreign_keys: table
.foreign_relations
.0
.iter()
.map(
|(
constraint_name,
metadata::ForeignRelation {
foreign_table,
column_mapping,
},
)| {
(
constraint_name.clone(),
models::ForeignKeyConstraint {
foreign_collection: foreign_table.clone(),
column_mapping: column_mapping.clone(),
},
)
},
)
.collect(),
})
.collect();

let native_queries: Vec<models::CollectionInfo> = metadata
.native_queries
.0
.iter()
.map(|(name, info)| models::CollectionInfo {
name: name.clone(),
description: info.description.clone(),
arguments: info
.arguments
.iter()
.map(|(name, column_info)| {
(
name.clone(),
models::ArgumentInfo {
description: column_info.description.clone(),
argument_type: column_to_type(column_info),
},
)
})
.collect(),
collection_type: name.clone(),
insertable_columns: None,
updatable_columns: None,
deletable: false,
uniqueness_constraints: BTreeMap::new(),
foreign_keys: BTreeMap::new(),
})
.collect();

let mut collections = tables;
collections.extend(native_queries);

let table_types = BTreeMap::from_iter(metadata.tables.0.iter().map(|(table_name, table)| {
let object_type = models::ObjectType {
description: table.description.clone(),
fields: BTreeMap::from_iter(table.columns.values().map(|column| {
(
column.name.clone(),
models::ObjectField {
description: column.description.clone(),
r#type: column_to_type(column),
},
)
})),
};
(table_name.clone(), object_type)
}));

let native_queries_types =
BTreeMap::from_iter(metadata.native_queries.0.iter().map(|(name, info)| {
let object_type = models::ObjectType {
description: info.description.clone(),
fields: BTreeMap::from_iter(info.columns.values().map(|column| {
(
column.name.clone(),
models::ObjectField {
description: column.description.clone(),
r#type: column_to_type(column),
},
)
})),
};
(name.clone(), object_type)
}));

let mut object_types = table_types;
object_types.extend(native_queries_types);

Ok(models::SchemaResponse {
collections,
procedures: vec![],
functions: vec![],
object_types,
scalar_types,
})
}

fn column_to_type(column: &metadata::ColumnInfo) -> models::Type {
match &column.nullable {
metadata::Nullable::NonNullable => models::Type::Named {
name: column.r#type.0.clone(),
},
metadata::Nullable::Nullable => models::Type::Nullable {
underlying_type: Box::new(models::Type::Named {
name: column.r#type.0.clone(),
}),
},

Check warning on line 207 in crates/ndc-sqlserver/src/schema.rs

View workflow job for this annotation

GitHub Actions / cargo fmt

Diff in /home/runner/work/ndc-sqlserver/ndc-sqlserver/crates/ndc-sqlserver/src/schema.rs
}
}

Loading

0 comments on commit 92e2421

Please sign in to comment.