Skip to content
Open
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
4 changes: 2 additions & 2 deletions engine/crates/engine-config-builder/src/from_sdl_config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,7 +44,7 @@ pub fn build_with_sdl_config(config: &FederatedGraphConfig, graph: FederatedGrap
rate_limit: context.rate_limit,
timeout: config.timeout,
entity_caching: match config.entity_caching {
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl },
_ => EntityCaching::Disabled,
},
})
Expand DownExpand Up@@ -211,7 +211,7 @@ impl<'a> BuildContext<'a> {
retry,
entity_caching: entity_caching.as_ref().map(|config| match config {
EntityCachingConfig::Disabled => EntityCaching::Disabled,
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl: *ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl: *ttl },

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Ensure that the handling of additional fields in the pattern matching is consistent across all usages to avoid potential bugs.

}),
},
);
Expand Down
1 change: 1 addition & 0 deletions engine/crates/engine/response/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@ mod streaming;
pub struct GraphqlOperationAnalyticsAttributes {
pub name: Option<String>,
pub r#type: common_types::OperationType,
#[serde(default)]
pub used_fields: String,
}

Expand Down
69 changes: 58 additions & 11 deletions engine/crates/parser-sdl/src/federation.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,20 +61,62 @@ pub enum EntityCachingConfig {
Disabled,
Enabled {
ttl: Option<Duration>,
storage: EntityCacheStorage,
},
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum EntityCacheStorage {
#[default]
Memory,
Redis(RedisConfig),
}

impl From<gateway_config::EntityCachingConfig> for EntityCachingConfig {
fn from(config: gateway_config::EntityCachingConfig) -> Self {
match (config.enabled, config.ttl) {
(Some(false), _) => EntityCachingConfig::Disabled,
(Some(true), ttl) => EntityCachingConfig::Enabled { ttl },
(_, Some(ttl)) => EntityCachingConfig::Enabled { ttl: Some(ttl) },
(Some(true), ttl) => EntityCachingConfig::Enabled {
ttl,
storage: entity_cache_storage(config.storage, config.redis),
},
(_, Some(ttl)) => EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: entity_cache_storage(config.storage, config.redis),
},
_ => EntityCachingConfig::Disabled,
}
}
}

fn entity_cache_storage(
storage: gateway_config::EntityCachingStorage,
redis: Option<gateway_config::EntityCachingRedisConfig>,
) -> EntityCacheStorage {
match storage {
gateway_config::EntityCachingStorage::Memory => EntityCacheStorage::Memory,
gateway_config::EntityCachingStorage::Redis => EntityCacheStorage::Redis(redis.unwrap_or_default().into()),
}
}

impl From<gateway_config::EntityCachingRedisConfig> for RedisConfig {
fn from(value: gateway_config::EntityCachingRedisConfig) -> Self {
let gateway_config::EntityCachingRedisConfig { url, key_prefix, tls } = value;
RedisConfig {
url,
key_prefix,
tls: tls.map(Into::into),
}
}
}

impl From<gateway_config::EntityCachingRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::EntityCachingRedisTlsConfig) -> Self {
let gateway_config::EntityCachingRedisTlsConfig { cert, key, ca } = value;
RedisTlsConfig { cert, key, ca }
}
}

impl From<(String, ConnectorHeaderValue)> for SubgraphHeaderRule {
fn from((name, value): (String, ConnectorHeaderValue)) -> Self {
match value {
Expand All@@ -100,7 +142,7 @@ pub struct GraphRateLimit {
pub struct RateLimitConfig {
pub global: Option<GraphRateLimit>,
pub storage: RateLimitStorage,
pub redis: RateLimitRedisConfig,
pub redis: RedisConfig,
}

impl From<gateway_config::RateLimitConfig> for RateLimitConfig {
Expand DownExpand Up@@ -131,7 +173,7 @@ impl From<gateway_config::RateLimitStorage> for RateLimitStorage {
}
}

impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
impl From<gateway_config::RateLimitRedisConfig> for RedisConfig {
fn from(value: gateway_config::RateLimitRedisConfig) -> Self {
Self {
url: value.url,
Expand All@@ -141,7 +183,7 @@ impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
}
}

impl From<gateway_config::RateLimitRedisTlsConfig> for RateLimitRedisTlsConfig {
impl From<gateway_config::RateLimitRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::RateLimitRedisTlsConfig) -> Self {
Self {
cert: value.cert,
Expand All@@ -158,14 +200,14 @@ pub enum RateLimitStorage {
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisConfig {
pub struct RedisConfig {
pub url: url::Url,
pub key_prefix: String,
pub tls: Option<RateLimitRedisTlsConfig>,
pub tls: Option<RedisTlsConfig>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisTlsConfig {
pub struct RedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
Expand DownExpand Up@@ -201,7 +243,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.entity_caching),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default(),
}
)
}
Expand All@@ -218,7 +261,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default()
}
)
}
Expand All@@ -234,7 +278,10 @@ mod tests {

assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled { ttl: None }
EntityCachingConfig::Enabled {
ttl: None,
storage: Default::default()
}
)
}

Expand Down
24 changes: 12 additions & 12 deletions engine/crates/parser-sdl/src/rules/subgraph_directive.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -190,18 +190,18 @@ impl Visitor<'_> for SubgraphDirectiveVisitor {
subgraph.development_url = Some(url.to_string())
}

if let Some(enabled) = directive.entity_caching_enabled {
if enabled {
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: None });
} else {
subgraph.entity_caching = Some(EntityCachingConfig::Disabled);
}
}

if let Some(ttl) = directive.entity_caching_ttl {
// If there's a ttl we always enable
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: Some(ttl) });
}
subgraph.entity_caching = match (directive.entity_caching_enabled, directive.entity_caching_ttl) {
(Some(false), _) => Some(EntityCachingConfig::Disabled),
(Some(true), ttl) => Some(EntityCachingConfig::Enabled {
ttl,
storage: Default::default(),
}),
(_, Some(ttl)) => Some(EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: Default::default(),
}),
_ => None,
};

subgraph.header_rules.extend(
directive
Expand Down
1 change: 1 addition & 0 deletions engine/crates/runtime-local/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ mod kv;
mod log;
mod pg;
pub mod rate_limiting;
#[cfg(feature = "redis")]
pub mod redis;
mod ufd_invoker;

Expand Down
54 changes: 53 additions & 1 deletion gateway/crates/config/src/entity_caching.rs
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,62 @@
use std::time::Duration;
use std::{path::PathBuf, time::Duration};

#[derive(Debug, Default, serde::Deserialize, Clone, PartialEq)]
pub struct EntityCachingConfig {
pub enabled: Option<bool>,

#[serde(default)]
pub storage: EntityCachingStorage,

#[serde(default)]
pub redis: Option<EntityCachingRedisConfig>,

/// The ttl to store cache entries with. Defaults to 60s
#[serde(deserialize_with = "duration_str::deserialize_option_duration", default)]
pub ttl: Option<Duration>,
}

#[derive(Debug, Clone, Default, PartialEq, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EntityCachingStorage {
#[default]
Memory,
Redis,
}

#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisConfig {

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding a method to EntityCachingRedisConfig to validate the configuration, ensuring that the url is correctly formatted and the key_prefix is not empty, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_url")]
pub url: url::Url,

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding validation logic in the EntityCachingRedisConfig struct to ensure that the url is a valid Redis URL when deserializing, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_key_prefix")]
pub key_prefix: String,
pub tls: Option<EntityCachingRedisTlsConfig>,
}

impl Default for EntityCachingRedisConfig {
fn default() -> Self {
Self {
url: Self::default_url(),
key_prefix: Self::default_key_prefix(),
tls: None,
}
}
}

impl EntityCachingRedisConfig {
fn default_url() -> url::Url {
url::Url::parse("redis://localhost:6379").expect("must be correct")
}

fn default_key_prefix() -> String {
String::from("grafbase-cache")
}
}

#[derive(Debug, Clone, PartialEq, Default, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
}
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
prepare PR 1 by devslovecoffee · Pull Request #1 · CallstackAI/grafbase · GitHub
Skip to content
Open
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
4 changes: 2 additions & 2 deletions engine/crates/engine-config-builder/src/from_sdl_config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,7 +44,7 @@ pub fn build_with_sdl_config(config: &FederatedGraphConfig, graph: FederatedGrap
rate_limit: context.rate_limit,
timeout: config.timeout,
entity_caching: match config.entity_caching {
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl },
_ => EntityCaching::Disabled,
},
})
Expand DownExpand Up@@ -211,7 +211,7 @@ impl<'a> BuildContext<'a> {
retry,
entity_caching: entity_caching.as_ref().map(|config| match config {
EntityCachingConfig::Disabled => EntityCaching::Disabled,
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl: *ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl: *ttl },

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Ensure that the handling of additional fields in the pattern matching is consistent across all usages to avoid potential bugs.

}),
},
);
Expand Down
1 change: 1 addition & 0 deletions engine/crates/engine/response/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@ mod streaming;
pub struct GraphqlOperationAnalyticsAttributes {
pub name: Option<String>,
pub r#type: common_types::OperationType,
#[serde(default)]
pub used_fields: String,
}

Expand Down
69 changes: 58 additions & 11 deletions engine/crates/parser-sdl/src/federation.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,20 +61,62 @@ pub enum EntityCachingConfig {
Disabled,
Enabled {
ttl: Option<Duration>,
storage: EntityCacheStorage,
},
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum EntityCacheStorage {
#[default]
Memory,
Redis(RedisConfig),
}

impl From<gateway_config::EntityCachingConfig> for EntityCachingConfig {
fn from(config: gateway_config::EntityCachingConfig) -> Self {
match (config.enabled, config.ttl) {
(Some(false), _) => EntityCachingConfig::Disabled,
(Some(true), ttl) => EntityCachingConfig::Enabled { ttl },
(_, Some(ttl)) => EntityCachingConfig::Enabled { ttl: Some(ttl) },
(Some(true), ttl) => EntityCachingConfig::Enabled {
ttl,
storage: entity_cache_storage(config.storage, config.redis),
},
(_, Some(ttl)) => EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: entity_cache_storage(config.storage, config.redis),
},
_ => EntityCachingConfig::Disabled,
}
}
}

fn entity_cache_storage(
storage: gateway_config::EntityCachingStorage,
redis: Option<gateway_config::EntityCachingRedisConfig>,
) -> EntityCacheStorage {
match storage {
gateway_config::EntityCachingStorage::Memory => EntityCacheStorage::Memory,
gateway_config::EntityCachingStorage::Redis => EntityCacheStorage::Redis(redis.unwrap_or_default().into()),
}
}

impl From<gateway_config::EntityCachingRedisConfig> for RedisConfig {
fn from(value: gateway_config::EntityCachingRedisConfig) -> Self {
let gateway_config::EntityCachingRedisConfig { url, key_prefix, tls } = value;
RedisConfig {
url,
key_prefix,
tls: tls.map(Into::into),
}
}
}

impl From<gateway_config::EntityCachingRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::EntityCachingRedisTlsConfig) -> Self {
let gateway_config::EntityCachingRedisTlsConfig { cert, key, ca } = value;
RedisTlsConfig { cert, key, ca }
}
}

impl From<(String, ConnectorHeaderValue)> for SubgraphHeaderRule {
fn from((name, value): (String, ConnectorHeaderValue)) -> Self {
match value {
Expand All@@ -100,7 +142,7 @@ pub struct GraphRateLimit {
pub struct RateLimitConfig {
pub global: Option<GraphRateLimit>,
pub storage: RateLimitStorage,
pub redis: RateLimitRedisConfig,
pub redis: RedisConfig,
}

impl From<gateway_config::RateLimitConfig> for RateLimitConfig {
Expand DownExpand Up@@ -131,7 +173,7 @@ impl From<gateway_config::RateLimitStorage> for RateLimitStorage {
}
}

impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
impl From<gateway_config::RateLimitRedisConfig> for RedisConfig {
fn from(value: gateway_config::RateLimitRedisConfig) -> Self {
Self {
url: value.url,
Expand All@@ -141,7 +183,7 @@ impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
}
}

impl From<gateway_config::RateLimitRedisTlsConfig> for RateLimitRedisTlsConfig {
impl From<gateway_config::RateLimitRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::RateLimitRedisTlsConfig) -> Self {
Self {
cert: value.cert,
Expand All@@ -158,14 +200,14 @@ pub enum RateLimitStorage {
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisConfig {
pub struct RedisConfig {
pub url: url::Url,
pub key_prefix: String,
pub tls: Option<RateLimitRedisTlsConfig>,
pub tls: Option<RedisTlsConfig>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisTlsConfig {
pub struct RedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
Expand DownExpand Up@@ -201,7 +243,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.entity_caching),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default(),
}
)
}
Expand All@@ -218,7 +261,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default()
}
)
}
Expand All@@ -234,7 +278,10 @@ mod tests {

assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled { ttl: None }
EntityCachingConfig::Enabled {
ttl: None,
storage: Default::default()
}
)
}

Expand Down
24 changes: 12 additions & 12 deletions engine/crates/parser-sdl/src/rules/subgraph_directive.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -190,18 +190,18 @@ impl Visitor<'_> for SubgraphDirectiveVisitor {
subgraph.development_url = Some(url.to_string())
}

if let Some(enabled) = directive.entity_caching_enabled {
if enabled {
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: None });
} else {
subgraph.entity_caching = Some(EntityCachingConfig::Disabled);
}
}

if let Some(ttl) = directive.entity_caching_ttl {
// If there's a ttl we always enable
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: Some(ttl) });
}
subgraph.entity_caching = match (directive.entity_caching_enabled, directive.entity_caching_ttl) {
(Some(false), _) => Some(EntityCachingConfig::Disabled),
(Some(true), ttl) => Some(EntityCachingConfig::Enabled {
ttl,
storage: Default::default(),
}),
(_, Some(ttl)) => Some(EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: Default::default(),
}),
_ => None,
};

subgraph.header_rules.extend(
directive
Expand Down
1 change: 1 addition & 0 deletions engine/crates/runtime-local/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ mod kv;
mod log;
mod pg;
pub mod rate_limiting;
#[cfg(feature = "redis")]
pub mod redis;
mod ufd_invoker;

Expand Down
54 changes: 53 additions & 1 deletion gateway/crates/config/src/entity_caching.rs
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,62 @@
use std::time::Duration;
use std::{path::PathBuf, time::Duration};

#[derive(Debug, Default, serde::Deserialize, Clone, PartialEq)]
pub struct EntityCachingConfig {
pub enabled: Option<bool>,

#[serde(default)]
pub storage: EntityCachingStorage,

#[serde(default)]
pub redis: Option<EntityCachingRedisConfig>,

/// The ttl to store cache entries with. Defaults to 60s
#[serde(deserialize_with = "duration_str::deserialize_option_duration", default)]
pub ttl: Option<Duration>,
}

#[derive(Debug, Clone, Default, PartialEq, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EntityCachingStorage {
#[default]
Memory,
Redis,
}

#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisConfig {

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding a method to EntityCachingRedisConfig to validate the configuration, ensuring that the url is correctly formatted and the key_prefix is not empty, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_url")]
pub url: url::Url,

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding validation logic in the EntityCachingRedisConfig struct to ensure that the url is a valid Redis URL when deserializing, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_key_prefix")]
pub key_prefix: String,
pub tls: Option<EntityCachingRedisTlsConfig>,
}

impl Default for EntityCachingRedisConfig {
fn default() -> Self {
Self {
url: Self::default_url(),
key_prefix: Self::default_key_prefix(),
tls: None,
}
}
}

impl EntityCachingRedisConfig {
fn default_url() -> url::Url {
url::Url::parse("redis://localhost:6379").expect("must be correct")
}

fn default_key_prefix() -> String {
String::from("grafbase-cache")
}
}

#[derive(Debug, Clone, PartialEq, Default, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
}
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' prepare PR 1 by devslovecoffee · Pull Request #1 · CallstackAI/grafbase · GitHub
Skip to content
Open
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
4 changes: 2 additions & 2 deletions engine/crates/engine-config-builder/src/from_sdl_config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,7 +44,7 @@ pub fn build_with_sdl_config(config: &FederatedGraphConfig, graph: FederatedGrap
rate_limit: context.rate_limit,
timeout: config.timeout,
entity_caching: match config.entity_caching {
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl },
_ => EntityCaching::Disabled,
},
})
Expand DownExpand Up@@ -211,7 +211,7 @@ impl<'a> BuildContext<'a> {
retry,
entity_caching: entity_caching.as_ref().map(|config| match config {
EntityCachingConfig::Disabled => EntityCaching::Disabled,
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl: *ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl: *ttl },

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Ensure that the handling of additional fields in the pattern matching is consistent across all usages to avoid potential bugs.

}),
},
);
Expand Down
1 change: 1 addition & 0 deletions engine/crates/engine/response/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@ mod streaming;
pub struct GraphqlOperationAnalyticsAttributes {
pub name: Option<String>,
pub r#type: common_types::OperationType,
#[serde(default)]
pub used_fields: String,
}

Expand Down
69 changes: 58 additions & 11 deletions engine/crates/parser-sdl/src/federation.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,20 +61,62 @@ pub enum EntityCachingConfig {
Disabled,
Enabled {
ttl: Option<Duration>,
storage: EntityCacheStorage,
},
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum EntityCacheStorage {
#[default]
Memory,
Redis(RedisConfig),
}

impl From<gateway_config::EntityCachingConfig> for EntityCachingConfig {
fn from(config: gateway_config::EntityCachingConfig) -> Self {
match (config.enabled, config.ttl) {
(Some(false), _) => EntityCachingConfig::Disabled,
(Some(true), ttl) => EntityCachingConfig::Enabled { ttl },
(_, Some(ttl)) => EntityCachingConfig::Enabled { ttl: Some(ttl) },
(Some(true), ttl) => EntityCachingConfig::Enabled {
ttl,
storage: entity_cache_storage(config.storage, config.redis),
},
(_, Some(ttl)) => EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: entity_cache_storage(config.storage, config.redis),
},
_ => EntityCachingConfig::Disabled,
}
}
}

fn entity_cache_storage(
storage: gateway_config::EntityCachingStorage,
redis: Option<gateway_config::EntityCachingRedisConfig>,
) -> EntityCacheStorage {
match storage {
gateway_config::EntityCachingStorage::Memory => EntityCacheStorage::Memory,
gateway_config::EntityCachingStorage::Redis => EntityCacheStorage::Redis(redis.unwrap_or_default().into()),
}
}

impl From<gateway_config::EntityCachingRedisConfig> for RedisConfig {
fn from(value: gateway_config::EntityCachingRedisConfig) -> Self {
let gateway_config::EntityCachingRedisConfig { url, key_prefix, tls } = value;
RedisConfig {
url,
key_prefix,
tls: tls.map(Into::into),
}
}
}

impl From<gateway_config::EntityCachingRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::EntityCachingRedisTlsConfig) -> Self {
let gateway_config::EntityCachingRedisTlsConfig { cert, key, ca } = value;
RedisTlsConfig { cert, key, ca }
}
}

impl From<(String, ConnectorHeaderValue)> for SubgraphHeaderRule {
fn from((name, value): (String, ConnectorHeaderValue)) -> Self {
match value {
Expand All@@ -100,7 +142,7 @@ pub struct GraphRateLimit {
pub struct RateLimitConfig {
pub global: Option<GraphRateLimit>,
pub storage: RateLimitStorage,
pub redis: RateLimitRedisConfig,
pub redis: RedisConfig,
}

impl From<gateway_config::RateLimitConfig> for RateLimitConfig {
Expand DownExpand Up@@ -131,7 +173,7 @@ impl From<gateway_config::RateLimitStorage> for RateLimitStorage {
}
}

impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
impl From<gateway_config::RateLimitRedisConfig> for RedisConfig {
fn from(value: gateway_config::RateLimitRedisConfig) -> Self {
Self {
url: value.url,
Expand All@@ -141,7 +183,7 @@ impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
}
}

impl From<gateway_config::RateLimitRedisTlsConfig> for RateLimitRedisTlsConfig {
impl From<gateway_config::RateLimitRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::RateLimitRedisTlsConfig) -> Self {
Self {
cert: value.cert,
Expand All@@ -158,14 +200,14 @@ pub enum RateLimitStorage {
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisConfig {
pub struct RedisConfig {
pub url: url::Url,
pub key_prefix: String,
pub tls: Option<RateLimitRedisTlsConfig>,
pub tls: Option<RedisTlsConfig>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisTlsConfig {
pub struct RedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
Expand DownExpand Up@@ -201,7 +243,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.entity_caching),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default(),
}
)
}
Expand All@@ -218,7 +261,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default()
}
)
}
Expand All@@ -234,7 +278,10 @@ mod tests {

assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled { ttl: None }
EntityCachingConfig::Enabled {
ttl: None,
storage: Default::default()
}
)
}

Expand Down
24 changes: 12 additions & 12 deletions engine/crates/parser-sdl/src/rules/subgraph_directive.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -190,18 +190,18 @@ impl Visitor<'_> for SubgraphDirectiveVisitor {
subgraph.development_url = Some(url.to_string())
}

if let Some(enabled) = directive.entity_caching_enabled {
if enabled {
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: None });
} else {
subgraph.entity_caching = Some(EntityCachingConfig::Disabled);
}
}

if let Some(ttl) = directive.entity_caching_ttl {
// If there's a ttl we always enable
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: Some(ttl) });
}
subgraph.entity_caching = match (directive.entity_caching_enabled, directive.entity_caching_ttl) {
(Some(false), _) => Some(EntityCachingConfig::Disabled),
(Some(true), ttl) => Some(EntityCachingConfig::Enabled {
ttl,
storage: Default::default(),
}),
(_, Some(ttl)) => Some(EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: Default::default(),
}),
_ => None,
};

subgraph.header_rules.extend(
directive
Expand Down
1 change: 1 addition & 0 deletions engine/crates/runtime-local/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ mod kv;
mod log;
mod pg;
pub mod rate_limiting;
#[cfg(feature = "redis")]
pub mod redis;
mod ufd_invoker;

Expand Down
54 changes: 53 additions & 1 deletion gateway/crates/config/src/entity_caching.rs
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,62 @@
use std::time::Duration;
use std::{path::PathBuf, time::Duration};

#[derive(Debug, Default, serde::Deserialize, Clone, PartialEq)]
pub struct EntityCachingConfig {
pub enabled: Option<bool>,

#[serde(default)]
pub storage: EntityCachingStorage,

#[serde(default)]
pub redis: Option<EntityCachingRedisConfig>,

/// The ttl to store cache entries with. Defaults to 60s
#[serde(deserialize_with = "duration_str::deserialize_option_duration", default)]
pub ttl: Option<Duration>,
}

#[derive(Debug, Clone, Default, PartialEq, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EntityCachingStorage {
#[default]
Memory,
Redis,
}

#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisConfig {

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding a method to EntityCachingRedisConfig to validate the configuration, ensuring that the url is correctly formatted and the key_prefix is not empty, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_url")]
pub url: url::Url,

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding validation logic in the EntityCachingRedisConfig struct to ensure that the url is a valid Redis URL when deserializing, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_key_prefix")]
pub key_prefix: String,
pub tls: Option<EntityCachingRedisTlsConfig>,
}

impl Default for EntityCachingRedisConfig {
fn default() -> Self {
Self {
url: Self::default_url(),
key_prefix: Self::default_key_prefix(),
tls: None,
}
}
}

impl EntityCachingRedisConfig {
fn default_url() -> url::Url {
url::Url::parse("redis://localhost:6379").expect("must be correct")
}

fn default_key_prefix() -> String {
String::from("grafbase-cache")
}
}

#[derive(Debug, Clone, PartialEq, Default, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
}
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' prepare PR 1 by devslovecoffee · Pull Request #1 · CallstackAI/grafbase · GitHub
Skip to content
Open
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
4 changes: 2 additions & 2 deletions engine/crates/engine-config-builder/src/from_sdl_config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,7 +44,7 @@ pub fn build_with_sdl_config(config: &FederatedGraphConfig, graph: FederatedGrap
rate_limit: context.rate_limit,
timeout: config.timeout,
entity_caching: match config.entity_caching {
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl },
_ => EntityCaching::Disabled,
},
})
Expand DownExpand Up@@ -211,7 +211,7 @@ impl<'a> BuildContext<'a> {
retry,
entity_caching: entity_caching.as_ref().map(|config| match config {
EntityCachingConfig::Disabled => EntityCaching::Disabled,
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl: *ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl: *ttl },

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Ensure that the handling of additional fields in the pattern matching is consistent across all usages to avoid potential bugs.

}),
},
);
Expand Down
1 change: 1 addition & 0 deletions engine/crates/engine/response/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@ mod streaming;
pub struct GraphqlOperationAnalyticsAttributes {
pub name: Option<String>,
pub r#type: common_types::OperationType,
#[serde(default)]
pub used_fields: String,
}

Expand Down
69 changes: 58 additions & 11 deletions engine/crates/parser-sdl/src/federation.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,20 +61,62 @@ pub enum EntityCachingConfig {
Disabled,
Enabled {
ttl: Option<Duration>,
storage: EntityCacheStorage,
},
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum EntityCacheStorage {
#[default]
Memory,
Redis(RedisConfig),
}

impl From<gateway_config::EntityCachingConfig> for EntityCachingConfig {
fn from(config: gateway_config::EntityCachingConfig) -> Self {
match (config.enabled, config.ttl) {
(Some(false), _) => EntityCachingConfig::Disabled,
(Some(true), ttl) => EntityCachingConfig::Enabled { ttl },
(_, Some(ttl)) => EntityCachingConfig::Enabled { ttl: Some(ttl) },
(Some(true), ttl) => EntityCachingConfig::Enabled {
ttl,
storage: entity_cache_storage(config.storage, config.redis),
},
(_, Some(ttl)) => EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: entity_cache_storage(config.storage, config.redis),
},
_ => EntityCachingConfig::Disabled,
}
}
}

fn entity_cache_storage(
storage: gateway_config::EntityCachingStorage,
redis: Option<gateway_config::EntityCachingRedisConfig>,
) -> EntityCacheStorage {
match storage {
gateway_config::EntityCachingStorage::Memory => EntityCacheStorage::Memory,
gateway_config::EntityCachingStorage::Redis => EntityCacheStorage::Redis(redis.unwrap_or_default().into()),
}
}

impl From<gateway_config::EntityCachingRedisConfig> for RedisConfig {
fn from(value: gateway_config::EntityCachingRedisConfig) -> Self {
let gateway_config::EntityCachingRedisConfig { url, key_prefix, tls } = value;
RedisConfig {
url,
key_prefix,
tls: tls.map(Into::into),
}
}
}

impl From<gateway_config::EntityCachingRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::EntityCachingRedisTlsConfig) -> Self {
let gateway_config::EntityCachingRedisTlsConfig { cert, key, ca } = value;
RedisTlsConfig { cert, key, ca }
}
}

impl From<(String, ConnectorHeaderValue)> for SubgraphHeaderRule {
fn from((name, value): (String, ConnectorHeaderValue)) -> Self {
match value {
Expand All@@ -100,7 +142,7 @@ pub struct GraphRateLimit {
pub struct RateLimitConfig {
pub global: Option<GraphRateLimit>,
pub storage: RateLimitStorage,
pub redis: RateLimitRedisConfig,
pub redis: RedisConfig,
}

impl From<gateway_config::RateLimitConfig> for RateLimitConfig {
Expand DownExpand Up@@ -131,7 +173,7 @@ impl From<gateway_config::RateLimitStorage> for RateLimitStorage {
}
}

impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
impl From<gateway_config::RateLimitRedisConfig> for RedisConfig {
fn from(value: gateway_config::RateLimitRedisConfig) -> Self {
Self {
url: value.url,
Expand All@@ -141,7 +183,7 @@ impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
}
}

impl From<gateway_config::RateLimitRedisTlsConfig> for RateLimitRedisTlsConfig {
impl From<gateway_config::RateLimitRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::RateLimitRedisTlsConfig) -> Self {
Self {
cert: value.cert,
Expand All@@ -158,14 +200,14 @@ pub enum RateLimitStorage {
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisConfig {
pub struct RedisConfig {
pub url: url::Url,
pub key_prefix: String,
pub tls: Option<RateLimitRedisTlsConfig>,
pub tls: Option<RedisTlsConfig>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisTlsConfig {
pub struct RedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
Expand DownExpand Up@@ -201,7 +243,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.entity_caching),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default(),
}
)
}
Expand All@@ -218,7 +261,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default()
}
)
}
Expand All@@ -234,7 +278,10 @@ mod tests {

assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled { ttl: None }
EntityCachingConfig::Enabled {
ttl: None,
storage: Default::default()
}
)
}

Expand Down
24 changes: 12 additions & 12 deletions engine/crates/parser-sdl/src/rules/subgraph_directive.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -190,18 +190,18 @@ impl Visitor<'_> for SubgraphDirectiveVisitor {
subgraph.development_url = Some(url.to_string())
}

if let Some(enabled) = directive.entity_caching_enabled {
if enabled {
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: None });
} else {
subgraph.entity_caching = Some(EntityCachingConfig::Disabled);
}
}

if let Some(ttl) = directive.entity_caching_ttl {
// If there's a ttl we always enable
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: Some(ttl) });
}
subgraph.entity_caching = match (directive.entity_caching_enabled, directive.entity_caching_ttl) {
(Some(false), _) => Some(EntityCachingConfig::Disabled),
(Some(true), ttl) => Some(EntityCachingConfig::Enabled {
ttl,
storage: Default::default(),
}),
(_, Some(ttl)) => Some(EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: Default::default(),
}),
_ => None,
};

subgraph.header_rules.extend(
directive
Expand Down
1 change: 1 addition & 0 deletions engine/crates/runtime-local/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ mod kv;
mod log;
mod pg;
pub mod rate_limiting;
#[cfg(feature = "redis")]
pub mod redis;
mod ufd_invoker;

Expand Down
54 changes: 53 additions & 1 deletion gateway/crates/config/src/entity_caching.rs
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,62 @@
use std::time::Duration;
use std::{path::PathBuf, time::Duration};

#[derive(Debug, Default, serde::Deserialize, Clone, PartialEq)]
pub struct EntityCachingConfig {
pub enabled: Option<bool>,

#[serde(default)]
pub storage: EntityCachingStorage,

#[serde(default)]
pub redis: Option<EntityCachingRedisConfig>,

/// The ttl to store cache entries with. Defaults to 60s
#[serde(deserialize_with = "duration_str::deserialize_option_duration", default)]
pub ttl: Option<Duration>,
}

#[derive(Debug, Clone, Default, PartialEq, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EntityCachingStorage {
#[default]
Memory,
Redis,
}

#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisConfig {

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding a method to EntityCachingRedisConfig to validate the configuration, ensuring that the url is correctly formatted and the key_prefix is not empty, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_url")]
pub url: url::Url,

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding validation logic in the EntityCachingRedisConfig struct to ensure that the url is a valid Redis URL when deserializing, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_key_prefix")]
pub key_prefix: String,
pub tls: Option<EntityCachingRedisTlsConfig>,
}

impl Default for EntityCachingRedisConfig {
fn default() -> Self {
Self {
url: Self::default_url(),
key_prefix: Self::default_key_prefix(),
tls: None,
}
}
}

impl EntityCachingRedisConfig {
fn default_url() -> url::Url {
url::Url::parse("redis://localhost:6379").expect("must be correct")
}

fn default_key_prefix() -> String {
String::from("grafbase-cache")
}
}

#[derive(Debug, Clone, PartialEq, Default, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
}
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' prepare PR 1 by devslovecoffee · Pull Request #1 · CallstackAI/grafbase · GitHub
Skip to content
Open
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
4 changes: 2 additions & 2 deletions engine/crates/engine-config-builder/src/from_sdl_config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,7 +44,7 @@ pub fn build_with_sdl_config(config: &FederatedGraphConfig, graph: FederatedGrap
rate_limit: context.rate_limit,
timeout: config.timeout,
entity_caching: match config.entity_caching {
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl },
_ => EntityCaching::Disabled,
},
})
Expand DownExpand Up@@ -211,7 +211,7 @@ impl<'a> BuildContext<'a> {
retry,
entity_caching: entity_caching.as_ref().map(|config| match config {
EntityCachingConfig::Disabled => EntityCaching::Disabled,
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl: *ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl: *ttl },

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Ensure that the handling of additional fields in the pattern matching is consistent across all usages to avoid potential bugs.

}),
},
);
Expand Down
1 change: 1 addition & 0 deletions engine/crates/engine/response/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@ mod streaming;
pub struct GraphqlOperationAnalyticsAttributes {
pub name: Option<String>,
pub r#type: common_types::OperationType,
#[serde(default)]
pub used_fields: String,
}

Expand Down
69 changes: 58 additions & 11 deletions engine/crates/parser-sdl/src/federation.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,20 +61,62 @@ pub enum EntityCachingConfig {
Disabled,
Enabled {
ttl: Option<Duration>,
storage: EntityCacheStorage,
},
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum EntityCacheStorage {
#[default]
Memory,
Redis(RedisConfig),
}

impl From<gateway_config::EntityCachingConfig> for EntityCachingConfig {
fn from(config: gateway_config::EntityCachingConfig) -> Self {
match (config.enabled, config.ttl) {
(Some(false), _) => EntityCachingConfig::Disabled,
(Some(true), ttl) => EntityCachingConfig::Enabled { ttl },
(_, Some(ttl)) => EntityCachingConfig::Enabled { ttl: Some(ttl) },
(Some(true), ttl) => EntityCachingConfig::Enabled {
ttl,
storage: entity_cache_storage(config.storage, config.redis),
},
(_, Some(ttl)) => EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: entity_cache_storage(config.storage, config.redis),
},
_ => EntityCachingConfig::Disabled,
}
}
}

fn entity_cache_storage(
storage: gateway_config::EntityCachingStorage,
redis: Option<gateway_config::EntityCachingRedisConfig>,
) -> EntityCacheStorage {
match storage {
gateway_config::EntityCachingStorage::Memory => EntityCacheStorage::Memory,
gateway_config::EntityCachingStorage::Redis => EntityCacheStorage::Redis(redis.unwrap_or_default().into()),
}
}

impl From<gateway_config::EntityCachingRedisConfig> for RedisConfig {
fn from(value: gateway_config::EntityCachingRedisConfig) -> Self {
let gateway_config::EntityCachingRedisConfig { url, key_prefix, tls } = value;
RedisConfig {
url,
key_prefix,
tls: tls.map(Into::into),
}
}
}

impl From<gateway_config::EntityCachingRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::EntityCachingRedisTlsConfig) -> Self {
let gateway_config::EntityCachingRedisTlsConfig { cert, key, ca } = value;
RedisTlsConfig { cert, key, ca }
}
}

impl From<(String, ConnectorHeaderValue)> for SubgraphHeaderRule {
fn from((name, value): (String, ConnectorHeaderValue)) -> Self {
match value {
Expand All@@ -100,7 +142,7 @@ pub struct GraphRateLimit {
pub struct RateLimitConfig {
pub global: Option<GraphRateLimit>,
pub storage: RateLimitStorage,
pub redis: RateLimitRedisConfig,
pub redis: RedisConfig,
}

impl From<gateway_config::RateLimitConfig> for RateLimitConfig {
Expand DownExpand Up@@ -131,7 +173,7 @@ impl From<gateway_config::RateLimitStorage> for RateLimitStorage {
}
}

impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
impl From<gateway_config::RateLimitRedisConfig> for RedisConfig {
fn from(value: gateway_config::RateLimitRedisConfig) -> Self {
Self {
url: value.url,
Expand All@@ -141,7 +183,7 @@ impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
}
}

impl From<gateway_config::RateLimitRedisTlsConfig> for RateLimitRedisTlsConfig {
impl From<gateway_config::RateLimitRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::RateLimitRedisTlsConfig) -> Self {
Self {
cert: value.cert,
Expand All@@ -158,14 +200,14 @@ pub enum RateLimitStorage {
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisConfig {
pub struct RedisConfig {
pub url: url::Url,
pub key_prefix: String,
pub tls: Option<RateLimitRedisTlsConfig>,
pub tls: Option<RedisTlsConfig>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisTlsConfig {
pub struct RedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
Expand DownExpand Up@@ -201,7 +243,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.entity_caching),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default(),
}
)
}
Expand All@@ -218,7 +261,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default()
}
)
}
Expand All@@ -234,7 +278,10 @@ mod tests {

assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled { ttl: None }
EntityCachingConfig::Enabled {
ttl: None,
storage: Default::default()
}
)
}

Expand Down
24 changes: 12 additions & 12 deletions engine/crates/parser-sdl/src/rules/subgraph_directive.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -190,18 +190,18 @@ impl Visitor<'_> for SubgraphDirectiveVisitor {
subgraph.development_url = Some(url.to_string())
}

if let Some(enabled) = directive.entity_caching_enabled {
if enabled {
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: None });
} else {
subgraph.entity_caching = Some(EntityCachingConfig::Disabled);
}
}

if let Some(ttl) = directive.entity_caching_ttl {
// If there's a ttl we always enable
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: Some(ttl) });
}
subgraph.entity_caching = match (directive.entity_caching_enabled, directive.entity_caching_ttl) {
(Some(false), _) => Some(EntityCachingConfig::Disabled),
(Some(true), ttl) => Some(EntityCachingConfig::Enabled {
ttl,
storage: Default::default(),
}),
(_, Some(ttl)) => Some(EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: Default::default(),
}),
_ => None,
};

subgraph.header_rules.extend(
directive
Expand Down
1 change: 1 addition & 0 deletions engine/crates/runtime-local/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ mod kv;
mod log;
mod pg;
pub mod rate_limiting;
#[cfg(feature = "redis")]
pub mod redis;
mod ufd_invoker;

Expand Down
54 changes: 53 additions & 1 deletion gateway/crates/config/src/entity_caching.rs
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,62 @@
use std::time::Duration;
use std::{path::PathBuf, time::Duration};

#[derive(Debug, Default, serde::Deserialize, Clone, PartialEq)]
pub struct EntityCachingConfig {
pub enabled: Option<bool>,

#[serde(default)]
pub storage: EntityCachingStorage,

#[serde(default)]
pub redis: Option<EntityCachingRedisConfig>,

/// The ttl to store cache entries with. Defaults to 60s
#[serde(deserialize_with = "duration_str::deserialize_option_duration", default)]
pub ttl: Option<Duration>,
}

#[derive(Debug, Clone, Default, PartialEq, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EntityCachingStorage {
#[default]
Memory,
Redis,
}

#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisConfig {

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding a method to EntityCachingRedisConfig to validate the configuration, ensuring that the url is correctly formatted and the key_prefix is not empty, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_url")]
pub url: url::Url,

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding validation logic in the EntityCachingRedisConfig struct to ensure that the url is a valid Redis URL when deserializing, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_key_prefix")]
pub key_prefix: String,
pub tls: Option<EntityCachingRedisTlsConfig>,
}

impl Default for EntityCachingRedisConfig {
fn default() -> Self {
Self {
url: Self::default_url(),
key_prefix: Self::default_key_prefix(),
tls: None,
}
}
}

impl EntityCachingRedisConfig {
fn default_url() -> url::Url {
url::Url::parse("redis://localhost:6379").expect("must be correct")
}

fn default_key_prefix() -> String {
String::from("grafbase-cache")
}
}

#[derive(Debug, Clone, PartialEq, Default, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
}
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' prepare PR 1 by devslovecoffee · Pull Request #1 · CallstackAI/grafbase · GitHub
Skip to content
Open
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
4 changes: 2 additions & 2 deletions engine/crates/engine-config-builder/src/from_sdl_config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,7 +44,7 @@ pub fn build_with_sdl_config(config: &FederatedGraphConfig, graph: FederatedGrap
rate_limit: context.rate_limit,
timeout: config.timeout,
entity_caching: match config.entity_caching {
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl },
_ => EntityCaching::Disabled,
},
})
Expand DownExpand Up@@ -211,7 +211,7 @@ impl<'a> BuildContext<'a> {
retry,
entity_caching: entity_caching.as_ref().map(|config| match config {
EntityCachingConfig::Disabled => EntityCaching::Disabled,
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl: *ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl: *ttl },

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Ensure that the handling of additional fields in the pattern matching is consistent across all usages to avoid potential bugs.

}),
},
);
Expand Down
1 change: 1 addition & 0 deletions engine/crates/engine/response/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@ mod streaming;
pub struct GraphqlOperationAnalyticsAttributes {
pub name: Option<String>,
pub r#type: common_types::OperationType,
#[serde(default)]
pub used_fields: String,
}

Expand Down
69 changes: 58 additions & 11 deletions engine/crates/parser-sdl/src/federation.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,20 +61,62 @@ pub enum EntityCachingConfig {
Disabled,
Enabled {
ttl: Option<Duration>,
storage: EntityCacheStorage,
},
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum EntityCacheStorage {
#[default]
Memory,
Redis(RedisConfig),
}

impl From<gateway_config::EntityCachingConfig> for EntityCachingConfig {
fn from(config: gateway_config::EntityCachingConfig) -> Self {
match (config.enabled, config.ttl) {
(Some(false), _) => EntityCachingConfig::Disabled,
(Some(true), ttl) => EntityCachingConfig::Enabled { ttl },
(_, Some(ttl)) => EntityCachingConfig::Enabled { ttl: Some(ttl) },
(Some(true), ttl) => EntityCachingConfig::Enabled {
ttl,
storage: entity_cache_storage(config.storage, config.redis),
},
(_, Some(ttl)) => EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: entity_cache_storage(config.storage, config.redis),
},
_ => EntityCachingConfig::Disabled,
}
}
}

fn entity_cache_storage(
storage: gateway_config::EntityCachingStorage,
redis: Option<gateway_config::EntityCachingRedisConfig>,
) -> EntityCacheStorage {
match storage {
gateway_config::EntityCachingStorage::Memory => EntityCacheStorage::Memory,
gateway_config::EntityCachingStorage::Redis => EntityCacheStorage::Redis(redis.unwrap_or_default().into()),
}
}

impl From<gateway_config::EntityCachingRedisConfig> for RedisConfig {
fn from(value: gateway_config::EntityCachingRedisConfig) -> Self {
let gateway_config::EntityCachingRedisConfig { url, key_prefix, tls } = value;
RedisConfig {
url,
key_prefix,
tls: tls.map(Into::into),
}
}
}

impl From<gateway_config::EntityCachingRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::EntityCachingRedisTlsConfig) -> Self {
let gateway_config::EntityCachingRedisTlsConfig { cert, key, ca } = value;
RedisTlsConfig { cert, key, ca }
}
}

impl From<(String, ConnectorHeaderValue)> for SubgraphHeaderRule {
fn from((name, value): (String, ConnectorHeaderValue)) -> Self {
match value {
Expand All@@ -100,7 +142,7 @@ pub struct GraphRateLimit {
pub struct RateLimitConfig {
pub global: Option<GraphRateLimit>,
pub storage: RateLimitStorage,
pub redis: RateLimitRedisConfig,
pub redis: RedisConfig,
}

impl From<gateway_config::RateLimitConfig> for RateLimitConfig {
Expand DownExpand Up@@ -131,7 +173,7 @@ impl From<gateway_config::RateLimitStorage> for RateLimitStorage {
}
}

impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
impl From<gateway_config::RateLimitRedisConfig> for RedisConfig {
fn from(value: gateway_config::RateLimitRedisConfig) -> Self {
Self {
url: value.url,
Expand All@@ -141,7 +183,7 @@ impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
}
}

impl From<gateway_config::RateLimitRedisTlsConfig> for RateLimitRedisTlsConfig {
impl From<gateway_config::RateLimitRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::RateLimitRedisTlsConfig) -> Self {
Self {
cert: value.cert,
Expand All@@ -158,14 +200,14 @@ pub enum RateLimitStorage {
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisConfig {
pub struct RedisConfig {
pub url: url::Url,
pub key_prefix: String,
pub tls: Option<RateLimitRedisTlsConfig>,
pub tls: Option<RedisTlsConfig>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisTlsConfig {
pub struct RedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
Expand DownExpand Up@@ -201,7 +243,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.entity_caching),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default(),
}
)
}
Expand All@@ -218,7 +261,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default()
}
)
}
Expand All@@ -234,7 +278,10 @@ mod tests {

assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled { ttl: None }
EntityCachingConfig::Enabled {
ttl: None,
storage: Default::default()
}
)
}

Expand Down
24 changes: 12 additions & 12 deletions engine/crates/parser-sdl/src/rules/subgraph_directive.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -190,18 +190,18 @@ impl Visitor<'_> for SubgraphDirectiveVisitor {
subgraph.development_url = Some(url.to_string())
}

if let Some(enabled) = directive.entity_caching_enabled {
if enabled {
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: None });
} else {
subgraph.entity_caching = Some(EntityCachingConfig::Disabled);
}
}

if let Some(ttl) = directive.entity_caching_ttl {
// If there's a ttl we always enable
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: Some(ttl) });
}
subgraph.entity_caching = match (directive.entity_caching_enabled, directive.entity_caching_ttl) {
(Some(false), _) => Some(EntityCachingConfig::Disabled),
(Some(true), ttl) => Some(EntityCachingConfig::Enabled {
ttl,
storage: Default::default(),
}),
(_, Some(ttl)) => Some(EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: Default::default(),
}),
_ => None,
};

subgraph.header_rules.extend(
directive
Expand Down
1 change: 1 addition & 0 deletions engine/crates/runtime-local/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ mod kv;
mod log;
mod pg;
pub mod rate_limiting;
#[cfg(feature = "redis")]
pub mod redis;
mod ufd_invoker;

Expand Down
54 changes: 53 additions & 1 deletion gateway/crates/config/src/entity_caching.rs
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,62 @@
use std::time::Duration;
use std::{path::PathBuf, time::Duration};

#[derive(Debug, Default, serde::Deserialize, Clone, PartialEq)]
pub struct EntityCachingConfig {
pub enabled: Option<bool>,

#[serde(default)]
pub storage: EntityCachingStorage,

#[serde(default)]
pub redis: Option<EntityCachingRedisConfig>,

/// The ttl to store cache entries with. Defaults to 60s
#[serde(deserialize_with = "duration_str::deserialize_option_duration", default)]
pub ttl: Option<Duration>,
}

#[derive(Debug, Clone, Default, PartialEq, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EntityCachingStorage {
#[default]
Memory,
Redis,
}

#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisConfig {

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding a method to EntityCachingRedisConfig to validate the configuration, ensuring that the url is correctly formatted and the key_prefix is not empty, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_url")]
pub url: url::Url,

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding validation logic in the EntityCachingRedisConfig struct to ensure that the url is a valid Redis URL when deserializing, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_key_prefix")]
pub key_prefix: String,
pub tls: Option<EntityCachingRedisTlsConfig>,
}

impl Default for EntityCachingRedisConfig {
fn default() -> Self {
Self {
url: Self::default_url(),
key_prefix: Self::default_key_prefix(),
tls: None,
}
}
}

impl EntityCachingRedisConfig {
fn default_url() -> url::Url {
url::Url::parse("redis://localhost:6379").expect("must be correct")
}

fn default_key_prefix() -> String {
String::from("grafbase-cache")
}
}

#[derive(Debug, Clone, PartialEq, Default, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
}
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); prepare PR 1 by devslovecoffee · Pull Request #1 · CallstackAI/grafbase · GitHub
Skip to content
Open
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
4 changes: 2 additions & 2 deletions engine/crates/engine-config-builder/src/from_sdl_config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,7 +44,7 @@ pub fn build_with_sdl_config(config: &FederatedGraphConfig, graph: FederatedGrap
rate_limit: context.rate_limit,
timeout: config.timeout,
entity_caching: match config.entity_caching {
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl },
_ => EntityCaching::Disabled,
},
})
Expand DownExpand Up@@ -211,7 +211,7 @@ impl<'a> BuildContext<'a> {
retry,
entity_caching: entity_caching.as_ref().map(|config| match config {
EntityCachingConfig::Disabled => EntityCaching::Disabled,
EntityCachingConfig::Enabled { ttl } => EntityCaching::Enabled { ttl: *ttl },
EntityCachingConfig::Enabled { ttl, .. } => EntityCaching::Enabled { ttl: *ttl },

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Ensure that the handling of additional fields in the pattern matching is consistent across all usages to avoid potential bugs.

}),
},
);
Expand Down
1 change: 1 addition & 0 deletions engine/crates/engine/response/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@ mod streaming;
pub struct GraphqlOperationAnalyticsAttributes {
pub name: Option<String>,
pub r#type: common_types::OperationType,
#[serde(default)]
pub used_fields: String,
}

Expand Down
69 changes: 58 additions & 11 deletions engine/crates/parser-sdl/src/federation.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,20 +61,62 @@ pub enum EntityCachingConfig {
Disabled,
Enabled {
ttl: Option<Duration>,
storage: EntityCacheStorage,
},
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum EntityCacheStorage {
#[default]
Memory,
Redis(RedisConfig),
}

impl From<gateway_config::EntityCachingConfig> for EntityCachingConfig {
fn from(config: gateway_config::EntityCachingConfig) -> Self {
match (config.enabled, config.ttl) {
(Some(false), _) => EntityCachingConfig::Disabled,
(Some(true), ttl) => EntityCachingConfig::Enabled { ttl },
(_, Some(ttl)) => EntityCachingConfig::Enabled { ttl: Some(ttl) },
(Some(true), ttl) => EntityCachingConfig::Enabled {
ttl,
storage: entity_cache_storage(config.storage, config.redis),
},
(_, Some(ttl)) => EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: entity_cache_storage(config.storage, config.redis),
},
_ => EntityCachingConfig::Disabled,
}
}
}

fn entity_cache_storage(
storage: gateway_config::EntityCachingStorage,
redis: Option<gateway_config::EntityCachingRedisConfig>,
) -> EntityCacheStorage {
match storage {
gateway_config::EntityCachingStorage::Memory => EntityCacheStorage::Memory,
gateway_config::EntityCachingStorage::Redis => EntityCacheStorage::Redis(redis.unwrap_or_default().into()),
}
}

impl From<gateway_config::EntityCachingRedisConfig> for RedisConfig {
fn from(value: gateway_config::EntityCachingRedisConfig) -> Self {
let gateway_config::EntityCachingRedisConfig { url, key_prefix, tls } = value;
RedisConfig {
url,
key_prefix,
tls: tls.map(Into::into),
}
}
}

impl From<gateway_config::EntityCachingRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::EntityCachingRedisTlsConfig) -> Self {
let gateway_config::EntityCachingRedisTlsConfig { cert, key, ca } = value;
RedisTlsConfig { cert, key, ca }
}
}

impl From<(String, ConnectorHeaderValue)> for SubgraphHeaderRule {
fn from((name, value): (String, ConnectorHeaderValue)) -> Self {
match value {
Expand All@@ -100,7 +142,7 @@ pub struct GraphRateLimit {
pub struct RateLimitConfig {
pub global: Option<GraphRateLimit>,
pub storage: RateLimitStorage,
pub redis: RateLimitRedisConfig,
pub redis: RedisConfig,
}

impl From<gateway_config::RateLimitConfig> for RateLimitConfig {
Expand DownExpand Up@@ -131,7 +173,7 @@ impl From<gateway_config::RateLimitStorage> for RateLimitStorage {
}
}

impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
impl From<gateway_config::RateLimitRedisConfig> for RedisConfig {
fn from(value: gateway_config::RateLimitRedisConfig) -> Self {
Self {
url: value.url,
Expand All@@ -141,7 +183,7 @@ impl From<gateway_config::RateLimitRedisConfig> for RateLimitRedisConfig {
}
}

impl From<gateway_config::RateLimitRedisTlsConfig> for RateLimitRedisTlsConfig {
impl From<gateway_config::RateLimitRedisTlsConfig> for RedisTlsConfig {
fn from(value: gateway_config::RateLimitRedisTlsConfig) -> Self {
Self {
cert: value.cert,
Expand All@@ -158,14 +200,14 @@ pub enum RateLimitStorage {
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisConfig {
pub struct RedisConfig {
pub url: url::Url,
pub key_prefix: String,
pub tls: Option<RateLimitRedisTlsConfig>,
pub tls: Option<RedisTlsConfig>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct RateLimitRedisTlsConfig {
pub struct RedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
Expand DownExpand Up@@ -201,7 +243,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.entity_caching),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default(),
}
)
}
Expand All@@ -218,7 +261,8 @@ mod tests {
assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled {
ttl: Some(Duration::from_secs(60))
ttl: Some(Duration::from_secs(60)),
storage: Default::default()
}
)
}
Expand All@@ -234,7 +278,10 @@ mod tests {

assert_eq!(
EntityCachingConfig::from(config.subgraphs.remove("products").unwrap().entity_caching.unwrap()),
EntityCachingConfig::Enabled { ttl: None }
EntityCachingConfig::Enabled {
ttl: None,
storage: Default::default()
}
)
}

Expand Down
24 changes: 12 additions & 12 deletions engine/crates/parser-sdl/src/rules/subgraph_directive.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -190,18 +190,18 @@ impl Visitor<'_> for SubgraphDirectiveVisitor {
subgraph.development_url = Some(url.to_string())
}

if let Some(enabled) = directive.entity_caching_enabled {
if enabled {
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: None });
} else {
subgraph.entity_caching = Some(EntityCachingConfig::Disabled);
}
}

if let Some(ttl) = directive.entity_caching_ttl {
// If there's a ttl we always enable
subgraph.entity_caching = Some(EntityCachingConfig::Enabled { ttl: Some(ttl) });
}
subgraph.entity_caching = match (directive.entity_caching_enabled, directive.entity_caching_ttl) {
(Some(false), _) => Some(EntityCachingConfig::Disabled),
(Some(true), ttl) => Some(EntityCachingConfig::Enabled {
ttl,
storage: Default::default(),
}),
(_, Some(ttl)) => Some(EntityCachingConfig::Enabled {
ttl: Some(ttl),
storage: Default::default(),
}),
_ => None,
};

subgraph.header_rules.extend(
directive
Expand Down
1 change: 1 addition & 0 deletions engine/crates/runtime-local/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ mod kv;
mod log;
mod pg;
pub mod rate_limiting;
#[cfg(feature = "redis")]
pub mod redis;
mod ufd_invoker;

Expand Down
54 changes: 53 additions & 1 deletion gateway/crates/config/src/entity_caching.rs
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,62 @@
use std::time::Duration;
use std::{path::PathBuf, time::Duration};

#[derive(Debug, Default, serde::Deserialize, Clone, PartialEq)]
pub struct EntityCachingConfig {
pub enabled: Option<bool>,

#[serde(default)]
pub storage: EntityCachingStorage,

#[serde(default)]
pub redis: Option<EntityCachingRedisConfig>,

/// The ttl to store cache entries with. Defaults to 60s
#[serde(deserialize_with = "duration_str::deserialize_option_duration", default)]
pub ttl: Option<Duration>,
}

#[derive(Debug, Clone, Default, PartialEq, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EntityCachingStorage {
#[default]
Memory,
Redis,
}

#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisConfig {

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding a method to EntityCachingRedisConfig to validate the configuration, ensuring that the url is correctly formatted and the key_prefix is not empty, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_url")]
pub url: url::Url,

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion
Consider adding validation logic in the EntityCachingRedisConfig struct to ensure that the url is a valid Redis URL when deserializing, which can prevent runtime errors.

#[serde(default = "EntityCachingRedisConfig::default_key_prefix")]
pub key_prefix: String,
pub tls: Option<EntityCachingRedisTlsConfig>,
}

impl Default for EntityCachingRedisConfig {
fn default() -> Self {
Self {
url: Self::default_url(),
key_prefix: Self::default_key_prefix(),
tls: None,
}
}
}

impl EntityCachingRedisConfig {
fn default_url() -> url::Url {
url::Url::parse("redis://localhost:6379").expect("must be correct")
}

fn default_key_prefix() -> String {
String::from("grafbase-cache")
}
}

#[derive(Debug, Clone, PartialEq, Default, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntityCachingRedisTlsConfig {
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
pub ca: Option<PathBuf>,
}