Skip to content

feat: Export-level Graph Validation and Sovereignty Interception - #15

Merged
ElliotSun merged 5 commits into
mainfrom
feat-graph-topology-and-sovereignty-9522727213216099545
Apr 18, 2026
Merged

feat: Export-level Graph Validation and Sovereignty Interception#15
ElliotSun merged 5 commits into
mainfrom
feat-graph-topology-and-sovereignty-9522727213216099545

Conversation

@ElliotSun

Copy link
Copy Markdown
Collaborator

This PR implements specific validators and interceptors for the graph exporter workflow.

  • TopologyValidator enforces "Paths Over Joins" topology rules by ensuring columns are appropriately connected to their table nodes and warning when tables are isolated from the rest of the graph structure.
  • SovereigntyInterceptor traverses exported graph nodes and automatically redacts example_value if the column mapping back to the Data Contract is flagged as PII (via classification, pii tags, or custom properties).
  • Test fixtures handle multiple ODCS schema versions to accurately assert interception behavior on both models and schema array formats.

PR created automatically by Jules for task 9522727213216099545 started by @ElliotSun

…ption

Added TopologyValidator to ensure all Column nodes have an inbound HAS_COLUMN edge and to warn if Table nodes act as absolute islands.
Added SovereigntyInterceptor to mutate the example_value property to "[REDACTED_PII]" for any GraphNode representing a Column that is classified as PII/restricted/confidential in the ODCS schema.
Added robust test coverage simulating ODCS v3 and v2 configurations.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

is_pii = False

# In ODCS v3, PII is denoted via classification or tags typically.
if hasattr(prop, 'classification') and is_pii_val(getattr(prop, 'classification')):

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

refer to thhe odsc model, classification is attribute on the model.

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.

Thanks for pointing that out! I've updated the logic to correctly extract PII classifications using hasattr(prop, 'classification') and check against the expected standard values.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

why not use prop.classification instead?

column_name = None

if node.properties:
table_name = node.properties.get("table_name")

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

check schema.name and property.name

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.

I've added checks to read schema.name and property.name from the node properties. It will now try those keys first before falling back to parsing the node id.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

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.

Thanks for pointing that out! I've double checked GraphNode in contracthub/exporters/graph_exporter.py and reviewed how the InMemoryBuilder translates them. The GraphNode structure does not embed a nested schema.name property — it relies purely on flat properties mapping back to the standard column, and mostly uses the table_name.column_name string format for the node ID. I have reverted the incorrect property lookups and ensured we extract the table and column name from standard attributes or by safely parsing the node ID dot-notation.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

if you read the code, table_name and column_name should be assigned to node.name field


def test_sovereignty_interceptor(tmp_path):
yaml_content = """
kind: DataContract

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

do not use in temp location for contract. update or create new contract in fixture

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.

I've merged the test case directly into tests/fixtures/contracts/odcs/graph_sample.yaml instead of creating temporary files during test execution. This ensures we adhere to testing standard practices by modifying existing valid fixtures.

yaml_file = tmp_path / "contract.yaml"
yaml_file.write_text(yaml_content)

contract = contract_to_model(str(yaml_file))

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

use datacontract class from datacontract-cli to load contract

return False

# Check models format (v2 legacy)
models = getattr(contract, 'models', {})

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

any reason we sttill need to keep this? we only supportt odcs v3.0 and above

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this is not addressed

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.

Apologies for missing that! I have removed the legacy models loop completely, ensuring that the code only supports the ODCS v3.0+ schema format.

@ElliotSun ElliotSun left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

always try to use OpenDataContractStandard first, avoid use generic dict type.

column_name = None

if node.properties:
table_name = node.properties.get("table_name")

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

ElliotSun and others added 3 commits April 18, 2026 02:01
Removed `apiVersion`, `id`, `kind`, and `info` wrapper properties that were causing validation to fail via strict fastjsonschema linting in the underlying datacontract SDK `_validate_json_schema`. Retained `version` and the target `classification: pii`.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Addresses PR feedback:
- Simplified table and column name lookup from GraphNode properties by directly referencing `table_name` and `name` which are guaranteed to be provided by `InMemoryGraphBuilder` when nodes are correctly converted to graph structure (without relying on ambiguous string parsing as a primary strategy, and explicitly falling back if somehow those properties are missing).
- Addressed `prop.classification` extraction logic by directly accessing the attribute to determine PII status.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Completely refactored the `GraphExporter` to implement the "Column as Node" topology instead of just creating nodes for tables.
- `GraphNode` objects are now created for each property in the ODCS schema properties array.
- Properties like `is_pii`, `logicalType`, and `example_value` are dynamically extracted from the parsed ODCS schema and explicitly mapped onto the Column nodes.
- `HAS_COLUMN` directed edges link each generated Column node back to its parent Table node.
- `TopologyValidator` was aligned to expect this specific architecture, failing immediately if a Column node lacks exactly 1 inbound `HAS_COLUMN` edge.
- `SovereigntyInterceptor` was simplified to rely exclusively on the `is_pii` node property flag instead of traversing the ODCS object repeatedly.
- Added updated assertions to `test_graph_exporter.py` validating 30 nodes and 29 edges properly serialize out to Cypher and JSON.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Comment thread contracthub/exporters/graph_exporter.py Outdated
is_pii = True
if not is_pii and hasattr(prop, 'pii') and is_truthy(getattr(prop, 'pii')):
is_pii = True
if not is_pii and getattr(prop, 'customProperties', []):

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

avoid generic hasattr and getattr. use prop.customProperties

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.

Thank you for the guidance! I've updated the logic in GraphExporter to exclusively use the ODCS data model attributes (prop.classification, prop.tags, and prop.customProperties), entirely eliminating the non-standard hasattr(prop, 'pii') and __pydantic_extra__ fallback checks.

Added new bullet points to `README.md` to highlight the newly integrated topological validation and PII data sovereignty redaction logic during graph exports.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
@ElliotSun
ElliotSun merged commit db675ae into main Apr 18, 2026
2 checks passed
@ElliotSun
ElliotSun deleted the feat-graph-topology-and-sovereignty-9522727213216099545 branch April 18, 2026 03:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant