Skip to content

Commit

Permalink
chore: introduce and use TargetRuntime (tailcallhq#1086)
Browse files Browse the repository at this point in the history
  • Loading branch information
mogery authored Feb 1, 2024
1 parent 78f90f6 commit 9d323a6
Show file tree
Hide file tree
Showing 16 changed files with 124 additions and 125 deletions.
33 changes: 7 additions & 26 deletions cloudflare/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ use tailcall::blueprint::Blueprint;
use tailcall::config::reader::ConfigReader;
use tailcall::config::ConfigSet;
use tailcall::http::{graphiql, handle_request, AppContext};
use tailcall::EnvIO;
use tailcall::target_runtime::TargetRuntime;

use crate::http::{to_request, to_response};
use crate::{init_cache, init_env, init_file, init_http};
use crate::init_runtime;

lazy_static! {
static ref APP_CTX: RwLock<Option<(String, Arc<AppContext>)>> = RwLock::new(None);
Expand Down Expand Up @@ -52,19 +52,8 @@ pub async fn fetch(req: worker::Request, env: worker::Env) -> anyhow::Result<wor
///
/// Reads the configuration from the CONFIG environment variable.
///
async fn get_config(
env_io: Arc<dyn EnvIO>,
env: Rc<worker::Env>,
file_path: &str,
) -> anyhow::Result<ConfigSet> {
let bucket_id = env_io
.get("BUCKET")
.ok_or(anyhow!("BUCKET var is not set"))?;
log::debug!("R2 Bucket ID: {}", bucket_id);
let file_io = init_file(env.clone(), bucket_id)?;
let http_io = init_http();

let reader = ConfigReader::init(file_io, http_io);
async fn get_config(runtime: TargetRuntime, file_path: &str) -> anyhow::Result<ConfigSet> {
let reader = ConfigReader::init(runtime);
let config = reader.read(&file_path).await?;
Ok(config)
}
Expand All @@ -82,21 +71,13 @@ async fn get_app_ctx(env: Rc<worker::Env>, file_path: &str) -> anyhow::Result<Ar
}
}
// Create new context
let env_io = init_env(env.clone());
let cfg = get_config(env_io.clone(), env.clone(), file_path).await?;
let runtime = init_runtime(env)?;
let cfg = get_config(runtime.clone(), file_path).await?;
log::info!("Configuration read ... ok");
log::debug!("\n{}", cfg.to_sdl());
let blueprint = Blueprint::try_from(&cfg)?;
log::info!("Blueprint generated ... ok");
let h_client = init_http();
let cache = init_cache(env);
let app_ctx = Arc::new(AppContext::new(
blueprint,
h_client.clone(),
h_client,
env_io,
cache,
));
let app_ctx = Arc::new(AppContext::new(blueprint, runtime));
*APP_CTX.write().unwrap() = Some((file_path.to_string(), app_ctx.clone()));
log::info!("Initialized new application context");
Ok(app_ctx)
Expand Down
16 changes: 16 additions & 0 deletions cloudflare/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::sync::Arc;

use anyhow::anyhow;
use async_graphql_value::ConstValue;
use tailcall::target_runtime::TargetRuntime;
use tailcall::{EnvIO, FileIO, HttpIO};

mod cache;
Expand All @@ -29,6 +30,21 @@ pub fn init_cache(env: Rc<worker::Env>) -> Arc<dyn tailcall::Cache<Key = u64, Va
Arc::new(cache::CloudflareChronoCache::init(env))
}

pub fn init_runtime(env: Rc<worker::Env>) -> anyhow::Result<TargetRuntime> {
let http = init_http();
let env_io = init_env(env.clone());
let bucket_id = env_io
.get("BUCKET")
.ok_or(anyhow!("BUCKET var is not set"))?;
Ok(TargetRuntime {
http: http.clone(),
http2_only: http.clone(),
env: init_env(env.clone()),
file: init_file(env.clone(), bucket_id)?,
cache: init_cache(env),
})
}

#[worker::event(fetch)]
async fn fetch(
req: worker::Request,
Expand Down
27 changes: 8 additions & 19 deletions src/app_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,24 @@ use crate::blueprint::Type::ListType;
use crate::blueprint::{Blueprint, Definition};
use crate::data_loader::DataLoader;
use crate::graphql::GraphqlDataLoader;
use crate::grpc;
use crate::grpc::data_loader::GrpcDataLoader;
use crate::http::{DataLoaderRequest, HttpDataLoader};
use crate::lambda::{DataLoaderId, Expression, IO};
use crate::{grpc, EntityCache, EnvIO, HttpIO};
use crate::target_runtime::TargetRuntime;

pub struct AppContext {
pub schema: dynamic::Schema,
pub universal_http_client: Arc<dyn HttpIO>,
pub http2_only_client: Arc<dyn HttpIO>,
pub runtime: TargetRuntime,
pub blueprint: Blueprint,
pub http_data_loaders: Arc<Vec<DataLoader<DataLoaderRequest, HttpDataLoader>>>,
pub gql_data_loaders: Arc<Vec<DataLoader<DataLoaderRequest, GraphqlDataLoader>>>,
pub grpc_data_loaders: Arc<Vec<DataLoader<grpc::DataLoaderRequest, GrpcDataLoader>>>,
pub cache: Arc<EntityCache>,
pub env_vars: Arc<dyn EnvIO>,
}

impl AppContext {
#[allow(clippy::too_many_arguments)]
pub fn new(
mut blueprint: Blueprint,
h_client: Arc<dyn HttpIO>,
h2_client: Arc<dyn HttpIO>,
env: Arc<dyn EnvIO>,
cache: Arc<EntityCache>,
) -> Self {
pub fn new(mut blueprint: Blueprint, runtime: TargetRuntime) -> Self {
let mut http_data_loaders = vec![];
let mut gql_data_loaders = vec![];
let mut grpc_data_loaders = vec![];
Expand All @@ -44,7 +36,7 @@ impl AppContext {
match expr {
IO::Http { req_template, group_by, .. } => {
let data_loader = HttpDataLoader::new(
h_client.clone(),
runtime.http.clone(),
group_by.clone(),
matches!(&field.of_type, ListType { .. }),
)
Expand All @@ -63,7 +55,7 @@ impl AppContext {

IO::GraphQLEndpoint { req_template, field_name, batch, .. } => {
let graphql_data_loader =
GraphqlDataLoader::new(h_client.clone(), *batch)
GraphqlDataLoader::new(runtime.http.clone(), *batch)
.to_data_loader(
blueprint.upstream.batch.clone().unwrap_or_default(),
);
Expand All @@ -80,7 +72,7 @@ impl AppContext {

IO::Grpc { req_template, group_by, .. } => {
let data_loader = GrpcDataLoader {
client: h2_client.clone(),
client: runtime.http2_only.clone(),
operation: req_template.operation.clone(),
group_by: group_by.clone(),
};
Expand All @@ -106,14 +98,11 @@ impl AppContext {

AppContext {
schema,
universal_http_client: h_client,
http2_only_client: h2_client,
runtime,
blueprint,
http_data_loaders: Arc::new(http_data_loaders),
gql_data_loaders: Arc::new(gql_data_loaders),
cache,
grpc_data_loaders: Arc::new(grpc_data_loaders),
env_vars: env,
}
}

Expand Down
11 changes: 11 additions & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub use tc::run;

use crate::cache::InMemoryCache;
use crate::config::Upstream;
use crate::target_runtime::TargetRuntime;
use crate::{blueprint, EnvIO, FileIO, HttpIO};

// Provides access to env in native rust environment
Expand Down Expand Up @@ -61,3 +62,13 @@ pub fn init_http2_only(upstream: &Upstream, script: Option<blueprint::Script>) -
pub fn init_in_memory_cache<K: Hash + Eq, V: Clone>() -> InMemoryCache<K, V> {
InMemoryCache::new()
}

pub fn init_runtime(upstream: &Upstream, script: Option<blueprint::Script>) -> TargetRuntime {
TargetRuntime {
http: init_http(upstream, script.clone()),
http2_only: init_http2_only(upstream, script),
env: init_env(),
file: init_file(),
cache: Arc::new(init_in_memory_cache()),
}
}
11 changes: 2 additions & 9 deletions src/cli/server/server_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc;

use crate::blueprint::{Blueprint, Http};
use crate::cli::{init_env, init_http, init_http2_only, init_in_memory_cache};
use crate::cli::init_runtime;
use crate::http::AppContext;

pub struct ServerConfig {
Expand All @@ -12,16 +12,9 @@ pub struct ServerConfig {

impl ServerConfig {
pub fn new(blueprint: Blueprint) -> Self {
let h_client = init_http(&blueprint.upstream, blueprint.server.script.clone());
let h2_client = init_http2_only(&blueprint.upstream, blueprint.server.script.clone());
let env = init_env();
let chrono_cache = Arc::new(init_in_memory_cache());
let server_context = Arc::new(AppContext::new(
blueprint.clone(),
h_client,
h2_client,
env,
chrono_cache,
init_runtime(&blueprint.upstream, blueprint.server.script.clone()),
));
Self { app_ctx: server_context, blueprint }
}
Expand Down
13 changes: 6 additions & 7 deletions src/cli/tc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::path::Path;
use std::sync::Arc;
use std::{env, fs};

use anyhow::Result;
Expand All @@ -13,11 +12,11 @@ use super::update_checker;
use crate::blueprint::{validate_operations, Blueprint, OperationQuery};
use crate::cli::fmt::Fmt;
use crate::cli::server::Server;
use crate::cli::{init_file, init_http, CLIError};
use crate::cli::{init_runtime, CLIError};
use crate::config::reader::ConfigReader;
use crate::config::{Config, Upstream};
use crate::print_schema;
use crate::valid::Validator;
use crate::{print_schema, FileIO};

const FILE_NAME: &str = ".tailcallrc.graphql";
const YML_FILE_NAME: &str = ".graphqlrc.yml";
Expand All @@ -26,9 +25,8 @@ pub async fn run() -> Result<()> {
let cli = Cli::parse();
logger_init();
update_checker::check_for_update().await;
let file_io: Arc<dyn FileIO> = init_file();
let default_http_io = init_http(&Upstream::default(), None);
let config_reader = ConfigReader::init(file_io.clone(), default_http_io);
let runtime = init_runtime(&Upstream::default(), None);
let config_reader = ConfigReader::init(runtime.clone());
match cli.command {
Command::Start { file_paths } => {
let config_set = config_reader.read_all(&file_paths).await?;
Expand All @@ -51,7 +49,8 @@ pub async fn run() -> Result<()> {

let ops: Vec<OperationQuery> =
futures_util::future::join_all(operations.iter().map(|op| async {
file_io
runtime
.file
.read(op)
.await
.map(|query| OperationQuery::new(query, op.clone()))
Expand Down
38 changes: 17 additions & 21 deletions src/config/reader.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::collections::{HashMap, VecDeque};
use std::sync::Arc;

use anyhow::Context;
use futures_util::future::join_all;
Expand All @@ -10,14 +9,13 @@ use url::Url;

use super::{ConfigSet, ExprBody, Extensions, Script, ScriptOptions};
use crate::config::{Config, Source};
use crate::{FileIO, HttpIO};
use crate::target_runtime::TargetRuntime;

const NULL_STR: &str = "\0\0\0\0\0\0\0";

/// Reads the configuration from a file or from an HTTP URL and resolves all linked extensions to create a ConfigSet.
pub struct ConfigReader {
file_io: Arc<dyn FileIO>,
http_io: Arc<dyn HttpIO>,
runtime: TargetRuntime,
}

/// Response of a file read operation
Expand All @@ -27,24 +25,25 @@ struct FileRead {
}

impl ConfigReader {
pub fn init(file_io: Arc<dyn FileIO>, http_io: Arc<dyn HttpIO>) -> Self {
Self { file_io, http_io }
pub fn init(runtime: TargetRuntime) -> Self {
Self { runtime }
}

/// Reads a file from the filesystem or from an HTTP URL
async fn read_file<T: ToString>(&self, file: T) -> anyhow::Result<FileRead> {
// Is an HTTP URL
let content = if let Ok(url) = Url::parse(&file.to_string()) {
let response = self
.http_io
.runtime
.http
.execute(reqwest::Request::new(reqwest::Method::GET, url))
.await?;

String::from_utf8(response.body.to_vec())?
} else {
// Is a file path

self.file_io.read(&file.to_string()).await?
self.runtime.file.read(&file.to_string()).await?
};

Ok(FileRead { content, path: file.to_string() })
Expand Down Expand Up @@ -187,13 +186,13 @@ mod test_proto_config {

use anyhow::{Context, Result};

use crate::cli::{init_file, init_http};
use crate::cli::init_runtime;
use crate::config::reader::ConfigReader;

#[tokio::test]
async fn test_resolve() {
// Skipping IO tests as they are covered in reader.rs
let reader = ConfigReader::init(init_file(), init_http(&Default::default(), None));
let reader = ConfigReader::init(init_runtime(&Default::default(), None));
reader
.read_proto("google/protobuf/empty.proto")
.await
Expand All @@ -219,7 +218,7 @@ mod test_proto_config {
assert!(test_file.exists());
let test_file = test_file.to_str().unwrap().to_string();

let reader = ConfigReader::init(init_file(), init_http(&Default::default(), None));
let reader = ConfigReader::init(init_runtime(&Default::default(), None));
let helper_map = reader
.resolve_descriptors(HashMap::new(), test_file)
.await?;
Expand Down Expand Up @@ -264,7 +263,7 @@ mod reader_tests {
use pretty_assertions::assert_eq;
use tokio::io::AsyncReadExt;

use crate::cli::{init_file, init_http};
use crate::cli::init_runtime;
use crate::config::reader::ConfigReader;
use crate::config::{Config, Script, ScriptOptions, Type, Upstream};

Expand All @@ -274,8 +273,7 @@ mod reader_tests {

#[tokio::test]
async fn test_all() {
let file_io = init_file();
let http_io = init_http(&Upstream::default(), None);
let runtime = init_runtime(&Upstream::default(), None);

let mut cfg = Config::default();
cfg.schema.query = Some("Test".to_string());
Expand Down Expand Up @@ -309,7 +307,7 @@ mod reader_tests {
.iter()
.map(|x| x.to_string())
.collect();
let cr = ConfigReader::init(file_io, http_io);
let cr = ConfigReader::init(runtime);
let c = cr.read_all(&files).await.unwrap();
assert_eq!(
["Post", "Query", "Test", "User"]
Expand All @@ -327,8 +325,7 @@ mod reader_tests {

#[tokio::test]
async fn test_local_files() {
let file_io = init_file();
let http_io = init_http(&Upstream::default(), None);
let runtime = init_runtime(&Upstream::default(), None);

let files: Vec<String> = [
"examples/jsonplaceholder.yml",
Expand All @@ -338,7 +335,7 @@ mod reader_tests {
.iter()
.map(|x| x.to_string())
.collect();
let cr = ConfigReader::init(file_io, http_io);
let cr = ConfigReader::init(runtime);
let c = cr.read_all(&files).await.unwrap();
assert_eq!(
["Post", "Query", "User"]
Expand All @@ -354,11 +351,10 @@ mod reader_tests {

#[tokio::test]
async fn test_script_loader() {
let file_io = init_file();
let http_io = init_http(&Upstream::default(), None);
let runtime = init_runtime(&Upstream::default(), None);

let cargo_manifest = std::env::var("CARGO_MANIFEST_DIR").unwrap();
let reader = ConfigReader::init(file_io, http_io);
let reader = ConfigReader::init(runtime);

let config = reader
.read(&format!(
Expand Down
Loading

0 comments on commit 9d323a6

Please sign in to comment.