Skip to content

Seanaye/feat/gql#4236

Open
seanaye wants to merge 15 commits into
mainfrom
seanaye/feat/gql
Open

Seanaye/feat/gql#4236
seanaye wants to merge 15 commits into
mainfrom
seanaye/feat/gql

Conversation

@seanaye

@seanaye seanaye commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 10efe23d-e4dd-473d-921f-8511196a33d5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR introduces a new graphql_soup crate that implements a GraphQL inbound adapter for the existing soup domain, exposing a Query.soup resolver with pagination, sorting, filtering, and per-entity field resolution. Two new batch-read APIs are added: get_entity_properties_batch in the properties crate (backed by PropertiesPgRepo and a bulk DB client call) and get_entity_notifications_batch in the notification crate (backed by PgPool with in-process entity-ref matching). These APIs power two async-graphql DataLoaders inside graphql_soup. The document_storage_service is extended with a graphql feature flag that wires a /soup/graphql endpoint handling authentication, inbox-link resolution, team-receipt loading, DataLoader registration, and schema execution.

🚥 Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Title check ⚠️ Warning Title does not follow conventional commits format (missing type prefix like 'feat:', 'fix:', etc.) and uses only a branch name. Update title to follow conventional commits: 'feat: GraphQL Soup implementation with property and notification loaders' or similar descriptive format under 72 characters.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to verify relationship to the changeset. Add a description explaining the purpose, changes, and rationale for this GraphQL Soup feature implementation.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@seanaye seanaye force-pushed the seanaye/feat/gql branch from 5c3b3e4 to a1bbe05 Compare June 22, 2026 16:13
@seanaye seanaye force-pushed the seanaye/feat/soup-threads-4 branch from 8776548 to 722c409 Compare June 22, 2026 16:13
@seanaye seanaye force-pushed the seanaye/feat/gql branch from a1bbe05 to c8f33c3 Compare June 22, 2026 16:15
Base automatically changed from seanaye/feat/soup-threads-4 to main June 22, 2026 16:26
@seanaye seanaye force-pushed the seanaye/feat/gql branch from c8f33c3 to 72b3fe2 Compare June 22, 2026 17:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust/cloud-storage/graphql_soup/src/lib.rs`:
- Around line 1-5: The lib.rs file is missing the required
`#![deny(missing_docs)]` directive that enforces documentation on all public
items. Add the `#![deny(missing_docs)]` attribute at the beginning of the lib.rs
file, immediately after the existing module-level documentation comment (the //!
lines) and before any other code. This ensures all public items in the crate
must have documentation.
- Around line 86-98: The entity type parsing in the map closure is failing
because model_entity EntityType representations don't align with
models_properties::EntityType. When parsing entity types from
self.0.entity_type().to_string() using
models_properties::EntityType::from_str(), three types cause runtime failures:
EmailThread maps to "email_thread" but models_properties expects "thread",
ChannelThread and ForeignEntity have no equivalent in models_properties. Either
extend models_properties::EntityType to include mappings for these missing
entity types (mapping "email_thread" to "thread" and adding support for
"channel_thread" and "foreign_entity"), or filter out these entity types before
they reach the entity_refs mapping logic in the properties method.

In `@rust/cloud-storage/notification/src/outbound/repository.rs`:
- Around line 1186-1198: The loop iterating through entity_refs at the match
operation is processing duplicate entity reference values, causing the same
notification to be added multiple times to the same entity_ref bucket in the
result map. Deduplicate the entity_refs collection before the for loop begins
(before the iteration over &entity_refs) to ensure each unique entity_ref is
only processed once. This prevents duplicate notifications from being inserted
into the result map entry for a given entity_ref key, while maintaining the
correct matching logic in the notification_ref_matches_row function call and the
subsequent push operation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5f142fbe-aa33-4453-b991-85b21a64892c

📥 Commits

Reviewing files that changed from the base of the PR and between 14d4296 and 72b3fe2.

⛔ Files ignored due to path filters (1)
  • rust/cloud-storage/Cargo.lock is excluded by !**/*.lock, !**/Cargo.lock
📒 Files selected for processing (27)
  • plan.md
  • rust/cloud-storage/Cargo.toml
  • rust/cloud-storage/document_storage_service/Cargo.toml
  • rust/cloud-storage/document_storage_service/src/api/context.rs
  • rust/cloud-storage/document_storage_service/src/api/graphql_soup.rs
  • rust/cloud-storage/document_storage_service/src/api/mod.rs
  • rust/cloud-storage/document_storage_service/src/main.rs
  • rust/cloud-storage/document_storage_service/src/service/mod.rs
  • rust/cloud-storage/graphql_soup/Cargo.toml
  • rust/cloud-storage/graphql_soup/src/lib.rs
  • rust/cloud-storage/notification/src/domain/models/request.rs
  • rust/cloud-storage/notification/src/domain/ports.rs
  • rust/cloud-storage/notification/src/domain/service/device/test.rs
  • rust/cloud-storage/notification/src/domain/service/ingress.rs
  • rust/cloud-storage/notification/src/domain/service/push_notification_event/test.rs
  • rust/cloud-storage/notification/src/domain/service/test.rs
  • rust/cloud-storage/notification/src/domain/service/voip/test.rs
  • rust/cloud-storage/notification/src/outbound/repository.rs
  • rust/cloud-storage/notification_sandbox/src/adapters/sandbox_repository.rs
  • rust/cloud-storage/properties/Cargo.toml
  • rust/cloud-storage/properties/src/domain/model.rs
  • rust/cloud-storage/properties/src/domain/ports.rs
  • rust/cloud-storage/properties/src/domain/service.rs
  • rust/cloud-storage/properties/src/domain/service_impl/mod.rs
  • rust/cloud-storage/properties/src/lib.rs
  • rust/cloud-storage/properties/src/outbound/properties_pg_repo.rs
  • rust/cloud-storage/soup/src/inbound/axum_router.rs

Comment on lines +1 to +5
//! GraphQL inbound adapter for Soup.
//!
//! This crate is intentionally additive: it maps GraphQL requests onto the
//! existing `soup` domain service without changing the existing REST API.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add #![deny(missing_docs)] directive to enforce documentation on all public items.

Per coding guidelines, new crates must include #![deny(missing_docs)] in lib.rs to enforce documentation on all public items.

📝 Proposed fix
 //! GraphQL inbound adapter for Soup.
 //!
 //! This crate is intentionally additive: it maps GraphQL requests onto the
 //! existing `soup` domain service without changing the existing REST API.
+#![deny(missing_docs)]

 use async_graphql::{
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//! GraphQL inbound adapter for Soup.
//!
//! This crate is intentionally additive: it maps GraphQL requests onto the
//! existing `soup` domain service without changing the existing REST API.
//! GraphQL inbound adapter for Soup.
//!
//! This crate is intentionally additive: it maps GraphQL requests onto the
//! existing `soup` domain service without changing the existing REST API.
#![deny(missing_docs)]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/cloud-storage/graphql_soup/src/lib.rs` around lines 1 - 5, The lib.rs
file is missing the required `#![deny(missing_docs)]` directive that enforces
documentation on all public items. Add the `#![deny(missing_docs)]` attribute at
the beginning of the lib.rs file, immediately after the existing module-level
documentation comment (the //! lines) and before any other code. This ensures
all public items in the crate must have documentation.

Source: Coding guidelines

Comment on lines +86 to +98
let entity_refs = keys
.iter()
.map(|key| {
let entity_type = models_properties::EntityType::from_str(&key.entity_type)
.map_err(|err| {
rootcause::report!(
"invalid entity type {} for property edge: {err}",
key.entity_type
)
})?;
Ok(EntityReference::new(key.entity_id.clone(), entity_type))
})
.collect::<Result<Vec<_>, rootcause::Report>>()?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify EntityType string conversion compatibility

# Check model_entity::EntityType Display/ToString impl
echo "=== Checking model_entity::EntityType ==="
rg -nP --type=rust -A10 -B2 'impl.*Display.*for.*EntityType' rust/cloud-storage/model-entity/

# Check models_properties::EntityType FromStr impl  
echo "=== Checking models_properties::EntityType FromStr ==="
rg -nP --type=rust -A10 -B2 'impl.*FromStr.*for.*EntityType' rust/cloud-storage/models_properties/

# Check if they are the same type (re-export)
echo "=== Checking if EntityType is re-exported ==="
rg -nP --type=rust 'pub use.*EntityType' rust/cloud-storage/models_properties/ rust/cloud-storage/model-entity/

Repository: macro-inc/macro

Length of output: 1787


🏁 Script executed:

# Find model_entity::EntityType definition
fd --type=f '\.rs$' rust/cloud-storage/model-entity | xargs grep -l 'enum EntityType\|struct EntityType' | head -5

# Check the actual definition
find rust/cloud-storage/model-entity -name '*.rs' -exec grep -l 'EntityType' {} \;

Repository: macro-inc/macro

Length of output: 190


🏁 Script executed:

# Examine graphql_soup at line 577 mentioned in the scratchpad
cat -n rust/cloud-storage/graphql_soup/src/lib.rs | sed -n '570,590p'

Repository: macro-inc/macro

Length of output: 892


🏁 Script executed:

# Check if model_entity is re-exported or if there are multiple EntityType types
rg 'enum EntityType|struct EntityType' rust/cloud-storage/model-entity/ -A5

Repository: macro-inc/macro

Length of output: 487


🏁 Script executed:

# Get the full EntityType enum definition from model-entity
cat -n rust/cloud-storage/model-entity/src/lib.rs | grep -A30 'pub enum EntityType'

Repository: macro-inc/macro

Length of output: 1174


🏁 Script executed:

# Check if model-entity::EntityType has a Display impl or derives Debug
rg 'impl.*Display.*EntityType|impl.*Debug|#\[derive' rust/cloud-storage/model-entity/src/lib.rs -B5 -A15 | head -100

Repository: macro-inc/macro

Length of output: 1069


🏁 Script executed:

# Check the exact Display impl behavior with EnumString and serde attributes
cat -n rust/cloud-storage/model-entity/src/lib.rs | sed -n '20,65p'

Repository: macro-inc/macro

Length of output: 1530


🏁 Script executed:

# Verify which entity types are actually used as Document entities in graphql_soup
rg -B5 -A5 'GraphqlSoupDocument' rust/cloud-storage/graphql_soup/src/lib.rs | head -40

Repository: macro-inc/macro

Length of output: 1256


🏁 Script executed:

# Check what entity types models_properties actually supports
cat -n rust/cloud-storage/models_properties/src/shared/entity_type.rs

Repository: macro-inc/macro

Length of output: 2761


🏁 Script executed:

# Verify what entity types are actually used in SoupPropertyEdgeReader
rg 'EntityPropertiesKey' rust/cloud-storage/graphql_soup/src/lib.rs -B10 -A5

Repository: macro-inc/macro

Length of output: 2931


🏁 Script executed:

# Check what entity types can reach the SoupPropertyEdgeReader batch loader
rg 'SoupPropertyEdgeReader|batch.*SoupProperty' rust/cloud-storage/ -r 'rust/cloud-storage/graphql_soup' -A5 -B5

Repository: macro-inc/macro

Length of output: 4260


Fix incompatible EntityType mappings between model_entity and models_properties.

The properties method on GraphqlSoupDocument (line 577) calls self.0.entity_type().to_string() which returns a model_entity representation, then attempts to parse it with models_properties::EntityType::from_str() at line 89. However, three entity types that appear in GraphqlSoupEntity will fail:

  • EmailThread serializes to "email_thread" but models_properties only has "thread"
  • ChannelThread serializes to "channel_thread" but is not in models_properties at all
  • ForeignEntity serializes to "foreign_entity" but is not in models_properties at all

This causes runtime parse failures with NoConversion errors when loading properties for these entity types. Either map these missing types in models_properties::EntityType or prevent these entity types from reaching the property edge loader.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/cloud-storage/graphql_soup/src/lib.rs` around lines 86 - 98, The entity
type parsing in the map closure is failing because model_entity EntityType
representations don't align with models_properties::EntityType. When parsing
entity types from self.0.entity_type().to_string() using
models_properties::EntityType::from_str(), three types cause runtime failures:
EmailThread maps to "email_thread" but models_properties expects "thread",
ChannelThread and ForeignEntity have no equivalent in models_properties. Either
extend models_properties::EntityType to include mappings for these missing
entity types (mapping "email_thread" to "thread" and adding support for
"channel_thread" and "foreign_entity"), or filter out these entity types before
they reach the entity_refs mapping logic in the properties method.

Comment on lines +1186 to +1198
for entity_ref in &entity_refs {
if notification_ref_matches_row(
entity_ref,
&event_item_id,
&event_item_type,
&notification_event_type,
&notification_metadata,
) {
result
.entry(entity_ref.clone())
.or_default()
.push(notification.clone());
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Deduplicate entity refs before distributing matched notifications

At Line 1186, iterating the original entity_refs can duplicate entries in a bucket when callers pass duplicate refs. The map key is already unique, so values should not be multiplied by duplicate input keys.

Proposed fix
         let mut result = entity_refs
             .iter()
             .cloned()
             .map(|entity_ref| (entity_ref, Vec::new()))
             .collect::<HashMap<_, _>>();
+        let match_entity_refs: Vec<NotificationEntityRef> = result.keys().cloned().collect();

         if entity_refs.is_empty() {
             return Ok(result);
         }
@@
-            for entity_ref in &entity_refs {
+            for entity_ref in &match_entity_refs {
                 if notification_ref_matches_row(
                     entity_ref,
                     &event_item_id,
                     &event_item_type,
                     &notification_event_type,
                     &notification_metadata,
                 ) {
                     result
                         .entry(entity_ref.clone())
                         .or_default()
                         .push(notification.clone());
                 }
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/cloud-storage/notification/src/outbound/repository.rs` around lines 1186
- 1198, The loop iterating through entity_refs at the match operation is
processing duplicate entity reference values, causing the same notification to
be added multiple times to the same entity_ref bucket in the result map.
Deduplicate the entity_refs collection before the for loop begins (before the
iteration over &entity_refs) to ensure each unique entity_ref is only processed
once. This prevents duplicate notifications from being inserted into the result
map entry for a given entity_ref key, while maintaining the correct matching
logic in the notification_ref_matches_row function call and the subsequent push
operation.

@seanaye seanaye force-pushed the seanaye/feat/gql branch from 1ca571d to 1a348e2 Compare June 22, 2026 17:52
@seanaye seanaye force-pushed the seanaye/feat/gql branch from 8ae1bfe to 85b5cc7 Compare June 23, 2026 13:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant