can get schema

This commit is contained in:
Kasper Juul Hermansen 2023-01-28 13:33:30 +01:00
parent 83ecbd238a
commit 06fd730a1c
Signed by: kjuulh
GPG Key ID: 57B6E1465221F912
6 changed files with 333 additions and 49 deletions

View File

@ -1,9 +1,11 @@
use core::time;
use std::{ use std::{
fs::canonicalize, fs::canonicalize,
io::{BufRead, BufReader}, io::{BufRead, BufReader},
path::PathBuf, path::PathBuf,
process::Stdio, process::{Child, Stdio},
sync::Arc, sync::{mpsc::sync_channel, Arc},
thread::sleep,
}; };
use crate::{config::Config, connect_params::ConnectParams}; use crate::{config::Config, connect_params::ConnectParams};
@ -20,7 +22,11 @@ impl CliSession {
} }
} }
pub fn connect(&self, config: &Config, cli_path: &PathBuf) -> eyre::Result<ConnectParams> { pub fn connect(
&self,
config: &Config,
cli_path: &PathBuf,
) -> eyre::Result<(ConnectParams, Child)> {
self.inner.connect(config, cli_path) self.inner.connect(config, cli_path)
} }
} }
@ -29,9 +35,13 @@ impl CliSession {
struct InnerCliSession {} struct InnerCliSession {}
impl InnerCliSession { impl InnerCliSession {
pub fn connect(&self, config: &Config, cli_path: &PathBuf) -> eyre::Result<ConnectParams> { pub fn connect(
let mut proc = self.start(config, cli_path)?; &self,
let params = self.get_conn(&mut proc)?; config: &Config,
cli_path: &PathBuf,
) -> eyre::Result<(ConnectParams, Child)> {
let proc = self.start(config, cli_path)?;
let params = self.get_conn(proc)?;
Ok(params) Ok(params)
} }
@ -62,7 +72,10 @@ impl InnerCliSession {
return Ok(proc); return Ok(proc);
} }
fn get_conn(&self, proc: &mut std::process::Child) -> eyre::Result<ConnectParams> { fn get_conn(
&self,
mut proc: std::process::Child,
) -> eyre::Result<(ConnectParams, std::process::Child)> {
let stdout = proc let stdout = proc
.stdout .stdout
.take() .take()
@ -73,31 +86,28 @@ impl InnerCliSession {
.take() .take()
.ok_or(eyre::anyhow!("could not acquire stderr from child process"))?; .ok_or(eyre::anyhow!("could not acquire stderr from child process"))?;
let mut conn: Option<ConnectParams> = None; let (sender, receiver) = sync_channel(1);
std::thread::scope(|s| { std::thread::spawn(move || {
s.spawn(|| {
let stdout_bufr = BufReader::new(stdout); let stdout_bufr = BufReader::new(stdout);
let mut res_conn: Option<ConnectParams> = None;
for line in stdout_bufr.lines() { for line in stdout_bufr.lines() {
let out = line.unwrap(); let out = line.unwrap();
let conn: ConnectParams = serde_json::from_str(&out).unwrap(); if let Ok(conn) = serde_json::from_str::<ConnectParams>(&out) {
res_conn = Some(conn); sender.send(conn).unwrap();
break; }
} }
conn = res_conn;
}); });
//s.spawn(|| { std::thread::spawn(|| {
// let stderr_bufr = BufReader::new(stderr); let stderr_bufr = BufReader::new(stderr);
// for line in stderr_bufr.lines() { for line in stderr_bufr.lines() {
// let out = line.unwrap(); let out = line.unwrap();
// panic!("could not start dagger session: {}", out) panic!("could not start dagger session: {}", out)
// } }
//});
}); });
Ok(conn.ok_or(eyre::anyhow!("could not connect to dagger"))?) let conn = receiver.recv()?;
Ok((conn, proc))
} }
} }

View File

@ -1,3 +1,5 @@
use std::process::Child;
use crate::{ use crate::{
cli_session::CliSession, config::Config, connect_params::ConnectParams, downloader::Downloader, cli_session::CliSession, config::Config, connect_params::ConnectParams, downloader::Downloader,
}; };
@ -9,7 +11,7 @@ impl Engine {
Self {} Self {}
} }
fn from_cli(&self, cfg: &Config) -> eyre::Result<ConnectParams> { fn from_cli(&self, cfg: &Config) -> eyre::Result<(ConnectParams, Child)> {
let cli = Downloader::new("0.3.10".into())?.get_cli()?; let cli = Downloader::new("0.3.10".into())?.get_cli()?;
let cli_session = CliSession::new(); let cli_session = CliSession::new();
@ -17,7 +19,7 @@ impl Engine {
Ok(cli_session.connect(cfg, &cli)?) Ok(cli_session.connect(cfg, &cli)?)
} }
pub fn start(&self, cfg: &Config) -> eyre::Result<ConnectParams> { pub fn start(&self, cfg: &Config) -> eyre::Result<(ConnectParams, Child)> {
// TODO: Add from existing session as well // TODO: Add from existing session as well
self.from_cli(cfg) self.from_cli(cfg)
} }
@ -36,7 +38,7 @@ mod tests {
let params = engine.start(&Config::new(None, None, None, None)).unwrap(); let params = engine.start(&Config::new(None, None, None, None)).unwrap();
assert_ne!( assert_ne!(
params, params.0,
ConnectParams { ConnectParams {
port: 123, port: 123,
session_token: "123".into() session_token: "123".into()

View File

@ -0,0 +1,99 @@
query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type {
...TypeRef
}
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,101 @@
schema {
query: Query
}
type Query {
__schema: __Schema
}
type __Schema {
types: [__Type!]!
queryType: __Type!
mutationType: __Type
subscriptionType: __Type
directives: [__Directive!]!
}
type __Type {
kind: __TypeKind!
name: String
description: String
# OBJECT and INTERFACE only
fields(includeDeprecated: Boolean = false): [__Field!]
# OBJECT only
interfaces: [__Type!]
# INTERFACE and UNION only
possibleTypes: [__Type!]
# ENUM only
enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
# INPUT_OBJECT only
inputFields: [__InputValue!]
# NON_NULL and LIST only
ofType: __Type
}
type __Field {
name: String!
description: String
args: [__InputValue!]!
type: __Type!
isDeprecated: Boolean!
deprecationReason: String
}
type __InputValue {
name: String!
description: String
type: __Type!
defaultValue: String
}
type __EnumValue {
name: String!
description: String
isDeprecated: Boolean!
deprecationReason: String
}
enum __TypeKind {
SCALAR
OBJECT
INTERFACE
UNION
ENUM
INPUT_OBJECT
LIST
NON_NULL
}
type __Directive {
name: String!
description: String
locations: [__DirectiveLocation!]!
args: [__InputValue!]!
}
enum __DirectiveLocation {
QUERY
MUTATION
SUBSCRIPTION
FIELD
FRAGMENT_DEFINITION
FRAGMENT_SPREAD
INLINE_FRAGMENT
SCHEMA
SCALAR
OBJECT
FIELD_DEFINITION
ARGUMENT_DEFINITION
INTERFACE
UNION
ENUM
ENUM_VALUE
INPUT_OBJECT
INPUT_FIELD_DEFINITION
}

View File

@ -0,0 +1,27 @@
use core::time;
use std::thread::sleep;
use graphql_introspection_query::introspection_response::IntrospectionResponse;
use crate::{config::Config, engine::Engine, session::Session};
pub fn get_schema() -> eyre::Result<IntrospectionResponse> {
//TODO: Implement cotext for proc
let cfg = Config::new(None, None, None, None);
let (conn, proc) = Engine::new().start(&cfg)?;
let session = Session::new();
let req_builder = session.start(cfg, &conn)?;
let schema = session.schema(req_builder)?;
Ok(schema)
}
#[cfg(test)]
mod tests {
use super::get_schema;
#[test]
fn can_get_schema() {
let _ = get_schema().unwrap();
}
}

View File

@ -1,34 +1,79 @@
use graphql_client::reqwest::post_graphql_blocking; use graphql_client::GraphQLQuery;
use graphql_introspection_query::introspection_response::IntrospectionResponse; use graphql_introspection_query::introspection_response::IntrospectionResponse;
use reqwest::blocking::Client; use reqwest::{
blocking::{Client, RequestBuilder},
header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE},
};
use crate::{config::Config, connect_params::ConnectParams}; use crate::{config::Config, connect_params::ConnectParams};
pub struct Session {} #[derive(GraphQLQuery)]
#[graphql(
schema_path = "src/graphql/introspection_schema.graphql",
query_path = "src/graphql/introspection_query.graphql",
responsive_path = "Serialize",
variable_derive = "Deserialize"
)]
struct IntrospectionQuery;
pub struct Session;
impl Session { impl Session {
pub fn new() -> Self { pub fn new() -> Self {
Self {} Self {}
} }
pub fn start(&self, cfg: Config, conn: &ConnectParams) -> eyre::Result<Client> { pub fn start(&self, cfg: Config, conn: &ConnectParams) -> eyre::Result<RequestBuilder> {
let client = Client::builder() let client = Client::builder()
.user_agent("graphql-rust/0.10.0") .user_agent("graphql-rust/0.10.0")
.default_headers( .connection_verbose(true)
std::iter::once(( //.danger_accept_invalid_certs(true)
reqwest::header::AUTHORIZATION,
reqwest::header::HeaderValue::from_str(&format!(
"Bearer {}",
conn.session_token
))
.unwrap(),
))
.collect(),
)
.build()?; .build()?;
let schema = post_graphql_blocking::<IntrospectionResponse, _>(&client, conn.url(), vec![]); let req_builder = client
.post(conn.url())
.headers(construct_headers())
.basic_auth::<String, String>(conn.session_token.to_string(), None);
Ok(client) Ok(req_builder)
}
pub fn schema(&self, req_builder: RequestBuilder) -> eyre::Result<IntrospectionResponse> {
let request_body: graphql_client::QueryBody<()> = graphql_client::QueryBody {
variables: (),
query: introspection_query::QUERY,
operation_name: introspection_query::OPERATION_NAME,
};
let res = req_builder.json(&request_body).send()?;
if res.status().is_success() {
// do nothing
} else if res.status().is_server_error() {
return Err(eyre::anyhow!("server error!"));
} else {
let status = res.status();
let error_message = match res.text() {
Ok(msg) => match serde_json::from_str::<serde_json::Value>(&msg) {
Ok(json) => {
format!("HTTP {}\n{}", status, serde_json::to_string_pretty(&json)?)
}
Err(_) => format!("HTTP {}: {}", status, msg),
},
Err(_) => format!("HTTP {}", status),
};
return Err(eyre::anyhow!(error_message));
}
let json: IntrospectionResponse = res.json()?;
Ok(json)
} }
} }
fn construct_headers() -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
headers
}