这是indexloc提供的服务,不要输入任何密码
Skip to content

rename "native query" to "native procedure"; remove read-only native procedures #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
27 changes: 13 additions & 14 deletions crates/configuration/src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,28 @@ use itertools::Itertools;
use schemars::JsonSchema;
use serde::Deserialize;

use crate::{native_queries::NativeQuery, read_directory, schema::ObjectType, Schema};
use crate::{native_procedure::NativeProcedure, read_directory, schema::ObjectType, Schema};

#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Configuration {
/// Descriptions of collections and types used in the database
pub schema: Schema,

/// Native queries allow arbitrary MongoDB aggregation pipelines where types of results are
/// Native procedures allow arbitrary MongoDB commands where types of results are
/// specified via user configuration.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub native_queries: BTreeMap<String, NativeQuery>,
pub native_procedures: BTreeMap<String, NativeProcedure>,
}

impl Configuration {
pub fn validate(
schema: Schema,
native_queries: BTreeMap<String, NativeQuery>,
native_procedures: BTreeMap<String, NativeProcedure>,
) -> anyhow::Result<Self> {
let config = Configuration {
schema,
native_queries,
native_procedures,
};

{
Expand Down Expand Up @@ -55,14 +55,14 @@ impl Configuration {
read_directory(configuration_dir).await
}

/// Returns object types collected from schema and native queries
/// Returns object types collected from schema and native procedures
pub fn object_types(&self) -> impl Iterator<Item = (&String, &ObjectType)> {
let object_types_from_schema = self.schema.object_types.iter();
let object_types_from_native_queries = self
.native_queries
let object_types_from_native_procedures = self
.native_procedures
.values()
.flat_map(|native_query| &native_query.object_types);
object_types_from_schema.chain(object_types_from_native_queries)
.flat_map(|native_procedure| &native_procedure.object_types);
object_types_from_schema.chain(object_types_from_native_procedures)
}
}

Expand All @@ -87,9 +87,9 @@ mod tests {
.into_iter()
.collect(),
};
let native_queries = [(
let native_procedures = [(
"hello".to_owned(),
NativeQuery {
NativeProcedure {
object_types: [(
"Album".to_owned(),
ObjectType {
Expand All @@ -104,12 +104,11 @@ mod tests {
arguments: Default::default(),
selection_criteria: Default::default(),
description: Default::default(),
mode: Default::default(),
},
)]
.into_iter()
.collect();
let result = Configuration::validate(schema, native_queries);
let result = Configuration::validate(schema, native_procedures);
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("multiple definitions"));
assert!(error_msg.contains("Album"));
Expand Down
10 changes: 5 additions & 5 deletions crates/configuration/src/directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use tokio_stream::wrappers::ReadDirStream;
use crate::{with_name::WithName, Configuration, Schema};

pub const SCHEMA_DIRNAME: &str = "schema";
pub const NATIVE_QUERIES_DIRNAME: &str = "native_queries";
pub const NATIVE_PROCEDURES_DIRNAME: &str = "native_procedures";

pub const CONFIGURATION_EXTENSIONS: [(&str, FileFormat); 3] =
[("json", JSON), ("yaml", YAML), ("yml", YAML)];
Expand All @@ -38,16 +38,16 @@ pub async fn read_directory(
.unwrap_or_default();
let schema = schemas.into_values().fold(Schema::default(), Schema::merge);

let native_queries = read_subdir_configs(&dir.join(NATIVE_QUERIES_DIRNAME))
let native_procedures = read_subdir_configs(&dir.join(NATIVE_PROCEDURES_DIRNAME))
.await?
.unwrap_or_default();

Configuration::validate(schema, native_queries)
Configuration::validate(schema, native_procedures)
}

/// Parse all files in a directory with one of the allowed configuration extensions according to
/// the given type argument. For example if `T` is `NativeQuery` this function assumes that all
/// json and yaml files in the given directory should be parsed as native query configurations.
/// the given type argument. For example if `T` is `NativeProcedure` this function assumes that all
/// json and yaml files in the given directory should be parsed as native procedure configurations.
///
/// Assumes that every configuration file has a `name` field.
async fn read_subdir_configs<T>(subdir: &Path) -> anyhow::Result<Option<BTreeMap<String, T>>>
Expand Down
2 changes: 1 addition & 1 deletion crates/configuration/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
mod configuration;
mod directory;
pub mod native_queries;
pub mod native_procedure;
pub mod schema;
mod with_name;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,23 @@ use crate::schema::{ObjectField, ObjectType, Type};

/// An arbitrary database command using MongoDB's runCommand API.
/// See https://www.mongodb.com/docs/manual/reference/method/db.runCommand/
///
/// Native Procedures appear as "procedures" in your data graph.
#[derive(Clone, Debug, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct NativeQuery {
pub struct NativeProcedure {
/// You may define object types here to reference in `result_type`. Any types defined here will
/// be merged with the definitions in `schema.json`. This allows you to maintain hand-written
/// types for native queries without having to edit a generated `schema.json` file.
/// types for native procedures without having to edit a generated `schema.json` file.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub object_types: BTreeMap<String, ObjectType>,

/// Type of data returned by the query. You may reference object types defined in the
/// Type of data returned by the procedure. You may reference object types defined in the
/// `object_types` list in this definition, or you may reference object types from
/// `schema.json`.
pub result_type: Type,

/// Arguments to be supplied for each query invocation. These will be substituted into the
/// Arguments to be supplied for each procedure invocation. These will be substituted into the
/// given `command`.
///
/// Argument values are standard JSON mapped from GraphQL input types, not Extended JSON.
Expand All @@ -38,7 +40,7 @@ pub struct NativeQuery {
/// See https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/
///
/// Keys and values in the command may contain placeholders of the form `{{variableName}}`
/// which will be substituted when the native query is executed according to the given
/// which will be substituted when the native procedure is executed according to the given
/// arguments.
///
/// Placeholders must be inside quotes so that the command can be stored in JSON format. If the
Expand Down Expand Up @@ -75,22 +77,6 @@ pub struct NativeQuery {

#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,

/// Set to `readWrite` if this native query might modify data in the database. When refreshing
/// a dataconnector native queries will appear in the corresponding `DataConnectorLink`
/// definition as `functions` if they are read-only, or as `procedures` if they are read-write.
/// Functions are intended to map to GraphQL Query fields, while procedures map to Mutation
/// fields.
#[serde(default)]
pub mode: Mode,
}

#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum Mode {
#[default]
ReadOnly,
ReadWrite,
}

type Object = serde_json::Map<String, serde_json::Value>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::BTreeMap;

use configuration::{native_queries::NativeQuery, schema::ObjectType};
use configuration::{native_procedure::NativeProcedure, schema::ObjectType};
use mongodb::Client;

#[derive(Clone, Debug)]
Expand All @@ -10,6 +10,6 @@ pub struct MongoConfig {
/// Name of the database to connect to
pub database: String,

pub native_queries: BTreeMap<String, NativeQuery>,
pub native_procedures: BTreeMap<String, NativeProcedure>,
pub object_types: BTreeMap<String, ObjectType>,
}
62 changes: 31 additions & 31 deletions crates/mongodb-agent-common/src/procedure/interpolated_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use super::ProcedureError;

type Result<T> = std::result::Result<T, ProcedureError>;

/// Parse native query commands, and interpolate arguments.
/// Parse native procedure commands, and interpolate arguments.
pub fn interpolated_command(
command: &bson::Document,
arguments: &BTreeMap<String, Bson>,
Expand Down Expand Up @@ -69,19 +69,19 @@ fn interpolate_document(
///
/// if the type of the variable `recordId` is `int`.
fn interpolate_string(string: &str, arguments: &BTreeMap<String, Bson>) -> Result<Bson> {
let parts = parse_native_query(string);
let parts = parse_native_procedure(string);
if parts.len() == 1 {
let mut parts = parts;
match parts.remove(0) {
NativeQueryPart::Text(string) => Ok(Bson::String(string)),
NativeQueryPart::Parameter(param) => resolve_argument(&param, arguments),
NativeProcedurePart::Text(string) => Ok(Bson::String(string)),
NativeProcedurePart::Parameter(param) => resolve_argument(&param, arguments),
}
} else {
let interpolated_parts: Vec<String> = parts
.into_iter()
.map(|part| match part {
NativeQueryPart::Text(string) => Ok(string),
NativeQueryPart::Parameter(param) => {
NativeProcedurePart::Text(string) => Ok(string),
NativeProcedurePart::Parameter(param) => {
let argument_value = resolve_argument(&param, arguments)?;
match argument_value {
Bson::String(string) => Ok(string),
Expand All @@ -101,30 +101,30 @@ fn resolve_argument(argument_name: &str, arguments: &BTreeMap<String, Bson>) ->
Ok(argument.clone())
}

/// A part of a Native Query text, either raw text or a parameter.
/// A part of a Native Procedure command text, either raw text or a parameter.
#[derive(Debug, Clone, PartialEq, Eq)]
enum NativeQueryPart {
enum NativeProcedurePart {
/// A raw text part
Text(String),
/// A parameter
Parameter(String),
}

/// Parse a string or key in a native query into parts where variables have the syntax
/// Parse a string or key in a native procedure into parts where variables have the syntax
/// `{{<variable>}}`.
fn parse_native_query(string: &str) -> Vec<NativeQueryPart> {
let vec: Vec<Vec<NativeQueryPart>> = string
fn parse_native_procedure(string: &str) -> Vec<NativeProcedurePart> {
let vec: Vec<Vec<NativeProcedurePart>> = string
.split("{{")
.filter(|part| !part.is_empty())
.map(|part| match part.split_once("}}") {
None => vec![NativeQueryPart::Text(part.to_string())],
None => vec![NativeProcedurePart::Text(part.to_string())],
Some((var, text)) => {
if text.is_empty() {
vec![NativeQueryPart::Parameter(var.trim().to_owned())]
vec![NativeProcedurePart::Parameter(var.trim().to_owned())]
} else {
vec![
NativeQueryPart::Parameter(var.trim().to_owned()),
NativeQueryPart::Text(text.to_string()),
NativeProcedurePart::Parameter(var.trim().to_owned()),
NativeProcedurePart::Text(text.to_string()),
]
}
}
Expand All @@ -135,7 +135,7 @@ fn parse_native_query(string: &str) -> Vec<NativeQueryPart> {

#[cfg(test)]
mod tests {
use configuration::native_queries::NativeQuery;
use configuration::native_procedure::NativeProcedure;
use pretty_assertions::assert_eq;
use serde_json::json;

Expand All @@ -148,7 +148,7 @@ mod tests {

#[test]
fn interpolates_non_string_type() -> anyhow::Result<()> {
let native_query_input = json!({
let native_procedure_input = json!({
"resultType": { "object": "InsertArtist" },
"arguments": {
"id": { "type": { "scalar": "int" } },
Expand All @@ -169,13 +169,13 @@ mod tests {
.into_iter()
.collect();

let native_query: NativeQuery = serde_json::from_value(native_query_input)?;
let native_procedure: NativeProcedure = serde_json::from_value(native_procedure_input)?;
let arguments = resolve_arguments(
&native_query.object_types,
&native_query.arguments,
&native_procedure.object_types,
&native_procedure.arguments,
input_arguments,
)?;
let command = interpolated_command(&native_query.command, &arguments)?;
let command = interpolated_command(&native_procedure.command, &arguments)?;

assert_eq!(
command,
Expand All @@ -192,7 +192,7 @@ mod tests {

#[test]
fn interpolates_array_argument() -> anyhow::Result<()> {
let native_query_input = json!({
let native_procedure_input = json!({
"name": "insertArtist",
"resultType": { "object": "InsertArtist" },
"objectTypes": {
Expand Down Expand Up @@ -221,13 +221,13 @@ mod tests {
.into_iter()
.collect();

let native_query: NativeQuery = serde_json::from_value(native_query_input)?;
let native_procedure: NativeProcedure = serde_json::from_value(native_procedure_input)?;
let arguments = resolve_arguments(
&native_query.object_types,
&native_query.arguments,
&native_procedure.object_types,
&native_procedure.arguments,
input_arguments,
)?;
let command = interpolated_command(&native_query.command, &arguments)?;
let command = interpolated_command(&native_procedure.command, &arguments)?;

assert_eq!(
command,
Expand All @@ -250,7 +250,7 @@ mod tests {

#[test]
fn interpolates_arguments_within_string() -> anyhow::Result<()> {
let native_query_input = json!({
let native_procedure_input = json!({
"name": "insert",
"resultType": { "object": "Insert" },
"arguments": {
Expand All @@ -269,13 +269,13 @@ mod tests {
.into_iter()
.collect();

let native_query: NativeQuery = serde_json::from_value(native_query_input)?;
let native_procedure: NativeProcedure = serde_json::from_value(native_procedure_input)?;
let arguments = resolve_arguments(
&native_query.object_types,
&native_query.arguments,
&native_procedure.object_types,
&native_procedure.arguments,
input_arguments,
)?;
let command = interpolated_command(&native_query.command, &arguments)?;
let command = interpolated_command(&native_procedure.command, &arguments)?;

assert_eq!(
command,
Expand Down
14 changes: 7 additions & 7 deletions crates/mongodb-agent-common/src/procedure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod interpolated_command;
use std::borrow::Cow;
use std::collections::BTreeMap;

use configuration::native_queries::NativeQuery;
use configuration::native_procedure::NativeProcedure;
use configuration::schema::{ObjectField, ObjectType, Type};
use mongodb::options::SelectionCriteria;
use mongodb::{bson, Database};
Expand All @@ -25,16 +25,16 @@ pub struct Procedure<'a> {
}

impl<'a> Procedure<'a> {
pub fn from_native_query(
native_query: &'a NativeQuery,
pub fn from_native_procedure(
native_procedure: &'a NativeProcedure,
arguments: BTreeMap<String, serde_json::Value>,
) -> Self {
Procedure {
arguments,
command: Cow::Borrowed(&native_query.command),
parameters: Cow::Borrowed(&native_query.arguments),
result_type: native_query.result_type.clone(),
selection_criteria: native_query.selection_criteria.as_ref().map(Cow::Borrowed),
command: Cow::Borrowed(&native_procedure.command),
parameters: Cow::Borrowed(&native_procedure.arguments),
result_type: native_procedure.result_type.clone(),
selection_criteria: native_procedure.selection_criteria.as_ref().map(Cow::Borrowed),
}
}

Expand Down
Loading