From a08b9a556dbe3f7e9da808f5391cd0cac622cec8 Mon Sep 17 00:00:00 2001 From: Valentin Tolmer Date: Thu, 26 Aug 2021 21:46:00 +0200 Subject: [PATCH] cli: introduce the export_graphql_schema command Split the command line into subcommands `run` and `export_graphql_schema`. --- .github/workflows/rust.yml | 5 +++++ schema.graphql | 38 ++++++++++++++++++++++++++++++++++++++ src/infra/cli.rs | 24 ++++++++++++++++++++++++ src/infra/configuration.rs | 6 +++--- src/infra/graphql/api.rs | 21 +++++++++++++++++++++ src/main.rs | 17 ++++++++++++----- 6 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 schema.graphql diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 96e836a..5dfc386 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -21,6 +21,11 @@ jobs: run: cargo build --verbose --workspace - name: Run tests run: cargo test --verbose --workspace + - name: Generate GraphQL schema + run: cargo run -- export_graphql_schema -o generated_schema.graphql + - name: Check schema + run: diff schema.graphql generated_schema.graphql + clippy: name: cargo clippy diff --git a/schema.graphql b/schema.graphql new file mode 100644 index 0000000..b3765a5 --- /dev/null +++ b/schema.graphql @@ -0,0 +1,38 @@ +input EqualityConstraint { + field: String! + value: String! +} + +type Group { + id: String! + "The groups to which this user belongs." + users: [User!]! +} + +""" + A filter for requests, specifying a boolean expression based on field constraints. Only one of + the fields can be set at a time. +""" +input RequestFilter { + any: [RequestFilter!] + all: [RequestFilter!] + not: RequestFilter + eq: EqualityConstraint +} + +type Query { + apiVersion: String! + user(userId: String!): User! + users(filters: RequestFilter): [User!]! +} + +type User { + id: String! + email: String! + "The groups to which this user belongs." + groups: [Group!]! +} + +schema { + query: Query +} diff --git a/src/infra/cli.rs b/src/infra/cli.rs index 264751f..6a42119 100644 --- a/src/infra/cli.rs +++ b/src/infra/cli.rs @@ -4,6 +4,23 @@ use clap::Clap; #[derive(Debug, Clap, Clone)] #[clap(version = "0.1", author = "The LLDAP team")] pub struct CLIOpts { + /// Export + #[clap(subcommand)] + pub command: Command, +} + +#[derive(Debug, Clap, Clone)] +pub enum Command { + /// Export the GraphQL schema to *.graphql. + #[clap(name = "export_graphql_schema")] + ExportGraphQLSchema(ExportGraphQLSchemaOpts), + /// Run the LDAP and GraphQL server. + #[clap(name = "run")] + Run(RunOpts), +} + +#[derive(Debug, Clap, Clone)] +pub struct RunOpts { /// Change config file name #[clap(short, long, default_value = "lldap_config.toml")] pub config_file: String, @@ -21,6 +38,13 @@ pub struct CLIOpts { pub verbose: bool, } +#[derive(Debug, Clap, Clone)] +pub struct ExportGraphQLSchemaOpts { + /// Output to a file. If not specified, the config is printed to the standard output. + #[clap(short, long)] + pub output_file: Option, +} + pub fn init() -> CLIOpts { CLIOpts::parse() } diff --git a/src/infra/configuration.rs b/src/infra/configuration.rs index 730d98a..8da2d79 100644 --- a/src/infra/configuration.rs +++ b/src/infra/configuration.rs @@ -6,7 +6,7 @@ use figment::{ use lldap_model::opaque::{server::ServerSetup, KeyPair}; use serde::{Deserialize, Serialize}; -use crate::infra::cli::CLIOpts; +use crate::infra::cli::RunOpts; #[derive(Clone, Debug, Deserialize, Serialize, derive_builder::Builder)] #[builder( @@ -55,7 +55,7 @@ impl Configuration { self.get_server_setup().keypair() } - fn merge_with_cli(mut self: Configuration, cli_opts: CLIOpts) -> Configuration { + fn merge_with_cli(mut self: Configuration, cli_opts: RunOpts) -> Configuration { if cli_opts.verbose { self.verbose = true; } @@ -110,7 +110,7 @@ fn get_server_setup(file_path: &str) -> Result { } } -pub fn init(cli_opts: CLIOpts) -> Result { +pub fn init(cli_opts: RunOpts) -> Result { let config_file = cli_opts.config_file.clone(); let config: Configuration = Figment::from(Serialized::defaults(Configuration::default())) diff --git a/src/infra/graphql/api.rs b/src/infra/graphql/api.rs index c3ecc56..3396c68 100644 --- a/src/infra/graphql/api.rs +++ b/src/infra/graphql/api.rs @@ -2,6 +2,7 @@ use crate::{ domain::handler::BackendHandler, infra::{ auth_service::{check_if_token_is_valid, ValidationResults}, + cli::ExportGraphQLSchemaOpts, tcp_server::AppState, }, }; @@ -34,6 +35,26 @@ fn schema() -> Schema { ) } +pub fn export_schema(opts: ExportGraphQLSchemaOpts) -> anyhow::Result<()> { + use crate::domain::sql_backend_handler::SqlBackendHandler; + use anyhow::Context; + let output = schema::().as_schema_language(); + match opts.output_file { + None => println!("{}", output), + Some(path) => { + use std::fs::File; + use std::io::prelude::*; + use std::path::Path; + let path = Path::new(&path); + let mut file = + File::create(&path).context(format!("unable to open '{}'", path.display()))?; + file.write_all(output.as_bytes()) + .context(format!("unable to write in '{}'", path.display()))?; + } + } + Ok(()) +} + async fn graphiql_route() -> Result { graphiql_handler("/api/graphql", None).await } diff --git a/src/main.rs b/src/main.rs index a3f53a5..d691b3c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use crate::{ handler::BackendHandler, sql_backend_handler::SqlBackendHandler, sql_opaque_handler::register_password, sql_tables::PoolOptions, }, - infra::{configuration::Configuration, db_cleaner::Scheduler}, + infra::{cli::*, configuration::Configuration, db_cleaner::Scheduler}, }; use actix::Actor; use anyhow::{anyhow, Result}; @@ -65,14 +65,13 @@ async fn run_server(config: Configuration) -> Result<()> { Ok(()) } -fn main() -> Result<()> { - let cli_opts = infra::cli::init(); - let config = infra::configuration::init(cli_opts.clone())?; +fn run_server_command(opts: RunOpts) -> Result<()> { + let config = infra::configuration::init(opts.clone())?; infra::logging::init(config.clone())?; info!("Starting LLDAP...."); - debug!("CLI: {:#?}", cli_opts); + debug!("CLI: {:#?}", opts); debug!("Configuration: {:#?}", config); actix::run( @@ -82,3 +81,11 @@ fn main() -> Result<()> { info!("End."); Ok(()) } + +fn main() -> Result<()> { + let cli_opts = infra::cli::init(); + match cli_opts.command { + Command::ExportGraphQLSchema(opts) => infra::graphql::api::export_schema(opts), + Command::Run(opts) => run_server_command(opts), + } +}