mirror of
https://github.com/reconurge/flowsint.git
synced 2026-07-27 10:59:31 -05:00
feat(core): refactor Neo4jDict to GraphDict
This commit is contained in:
+51
-56
@@ -1,4 +1,4 @@
|
||||
@import "tailwindcss";
|
||||
@import 'tailwindcss';
|
||||
@plugin "@tailwindcss/typography";
|
||||
@plugin "tailwindcss-animate";
|
||||
|
||||
@@ -70,8 +70,8 @@ body::-webkit-scrollbar-thumb:hover {
|
||||
body {
|
||||
@apply m-0;
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
|
||||
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell',
|
||||
'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
letter-spacing: var(--tracking-normal);
|
||||
@@ -147,7 +147,6 @@ html {
|
||||
@apply rounded border border-border bg-[var(--muted)] px-1 py-0.5 text-sm;
|
||||
}
|
||||
|
||||
|
||||
.minimal-tiptap-editor .ProseMirror pre {
|
||||
@apply relative overflow-auto rounded font-mono text-sm;
|
||||
@apply border-[var(--mt-pre-border)] bg-[var(--mt-pre-background)] text-[var(--mt-pre-color)];
|
||||
@@ -163,7 +162,6 @@ html {
|
||||
}
|
||||
|
||||
.minimal-tiptap-editor .ProseMirror pre {
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-operator,
|
||||
.hljs-function,
|
||||
@@ -256,7 +254,7 @@ html {
|
||||
list-style: square;
|
||||
}
|
||||
|
||||
.minimal-tiptap-editor .ProseMirror>p.is-editor-empty::before {
|
||||
.minimal-tiptap-editor .ProseMirror > p.is-editor-empty::before {
|
||||
content: attr(data-placeholder);
|
||||
@apply pointer-events-none float-left h-0 text-[var(--mt-secondary)];
|
||||
}
|
||||
@@ -344,10 +342,10 @@ html {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
[data-rmiz-content="found"] img,
|
||||
[data-rmiz-content="found"] svg,
|
||||
[data-rmiz-content="found"] [role="img"],
|
||||
[data-rmiz-content="found"] [data-zoom] {
|
||||
[data-rmiz-content='found'] img,
|
||||
[data-rmiz-content='found'] svg,
|
||||
[data-rmiz-content='found'] [role='img'],
|
||||
[data-rmiz-content='found'] [data-zoom] {
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
@@ -376,11 +374,11 @@ html {
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
[data-rmiz-modal-overlay="hidden"] {
|
||||
[data-rmiz-modal-overlay='hidden'] {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
[data-rmiz-modal-overlay="visible"] {
|
||||
[data-rmiz-modal-overlay='visible'] {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
@@ -399,7 +397,6 @@ html {
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
||||
[data-rmiz-modal-overlay],
|
||||
[data-rmiz-modal-img] {
|
||||
transition-duration: 0.01ms !important;
|
||||
@@ -407,49 +404,49 @@ html {
|
||||
}
|
||||
|
||||
:root {
|
||||
--background: oklch(.99 0 0);
|
||||
--background: oklch(0.99 0 0);
|
||||
--foreground: oklch(0.27 0 0);
|
||||
--card: oklch(1.40 0 0);
|
||||
--card: oklch(1.4 0 0);
|
||||
--card-foreground: oklch(0.27 0 0);
|
||||
--popover: oklch(21.00 0 0);
|
||||
--popover: oklch(21 0 0);
|
||||
--popover-foreground: oklch(0.27 0 0);
|
||||
--primary: oklch(0.6397 0.1720 36.4421);
|
||||
--primary: oklch(0.6397 0.172 36.4421);
|
||||
--primary-foreground: oklch(0 0 0);
|
||||
--secondary: oklch(0.97 0.00 264.54);
|
||||
--secondary-foreground: oklch(0.45 0.03 256.80);
|
||||
--muted: oklch(0.98 0.00 247.84);
|
||||
--secondary: oklch(0.97 0 264.54);
|
||||
--secondary-foreground: oklch(0.45 0.03 256.8);
|
||||
--muted: oklch(0.98 0 247.84);
|
||||
--muted-foreground: oklch(0.55 0.02 264.36);
|
||||
--accent: oklch(0.99 0.02 95.28);
|
||||
--accent-foreground: oklch(0.47 0.12 46.20);
|
||||
--accent-foreground: oklch(0.47 0.12 46.2);
|
||||
--destructive: oklch(0.64 0.21 25.33);
|
||||
--destructive-foreground: oklch(1.00 0 0);
|
||||
--destructive-foreground: oklch(1 0 0);
|
||||
--border: oklch(0.93 0.01 264.53);
|
||||
--input: oklch(0.93 0.01 264.53);
|
||||
--ring: oklch(0.77 0.16 70.08);
|
||||
--chart-1: oklch(0.77 0.16 70.08);
|
||||
--chart-2: oklch(0.67 0.16 58.32);
|
||||
--chart-3: oklch(0.56 0.15 49.00);
|
||||
--chart-4: oklch(0.47 0.12 46.20);
|
||||
--chart-5: oklch(0.41 0.11 45.90);
|
||||
--sidebar: oklch(0.98 0.00 247.84);
|
||||
--chart-3: oklch(0.56 0.15 49);
|
||||
--chart-4: oklch(0.47 0.12 46.2);
|
||||
--chart-5: oklch(0.41 0.11 45.9);
|
||||
--sidebar: oklch(0.98 0 247.84);
|
||||
--sidebar-foreground: oklch(0.27 0 0);
|
||||
--sidebar-primary: oklch(0.77 0.16 70.08);
|
||||
--sidebar-primary-foreground: oklch(1.00 0 0);
|
||||
--sidebar-primary-foreground: oklch(1 0 0);
|
||||
--sidebar-accent: oklch(0.99 0.02 95.28);
|
||||
--sidebar-accent-foreground: oklch(0.47 0.12 46.20);
|
||||
--sidebar-accent-foreground: oklch(0.47 0.12 46.2);
|
||||
--sidebar-border: oklch(0.93 0.01 264.53);
|
||||
--sidebar-ring: oklch(0.77 0.16 70.08);
|
||||
--font-sans: Inter, sans-serif;
|
||||
--font-serif: Source Serif 4, serif;
|
||||
--font-mono: JetBrains Mono, monospace;
|
||||
--radius: 0.580rem;
|
||||
--radius: 0.58rem;
|
||||
--shadow-2xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05);
|
||||
--shadow-xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05);
|
||||
--shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10);
|
||||
--shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10);
|
||||
--shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 2px 4px -2px hsl(0 0% 0% / 0.10);
|
||||
--shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 4px 6px -2px hsl(0 0% 0% / 0.10);
|
||||
--shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 8px 10px -2px hsl(0 0% 0% / 0.10);
|
||||
--shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 1px 2px -2px hsl(0 0% 0% / 0.1);
|
||||
--shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 1px 2px -2px hsl(0 0% 0% / 0.1);
|
||||
--shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 2px 4px -2px hsl(0 0% 0% / 0.1);
|
||||
--shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 4px 6px -2px hsl(0 0% 0% / 0.1);
|
||||
--shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 8px 10px -2px hsl(0 0% 0% / 0.1);
|
||||
--shadow-2xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.25);
|
||||
--success: oklch(0.6 0.12 145);
|
||||
--warning: oklch(0.7 0.12 80);
|
||||
@@ -466,43 +463,43 @@ html {
|
||||
--card-foreground: oklch(0.92 0 0);
|
||||
--popover: oklch(0.27 0 0);
|
||||
--popover-foreground: oklch(0.92 0 0);
|
||||
--primary: oklch(0.6397 0.1720 36.4421);
|
||||
--primary: oklch(0.6397 0.172 36.4421);
|
||||
--primary-foreground: oklch(0 0 0);
|
||||
--secondary: oklch(0.27 0 0);
|
||||
--secondary-foreground: oklch(0.92 0 0);
|
||||
--muted: oklch(0.27 0 0);
|
||||
--muted-foreground: oklch(0.72 0 0);
|
||||
--accent: oklch(0.47 0.12 46.20);
|
||||
--accent: oklch(0.47 0.12 46.2);
|
||||
--accent-foreground: oklch(0.92 0.12 95.75);
|
||||
--destructive: oklch(0.64 0.21 25.33);
|
||||
--destructive-foreground: oklch(1.00 0 0);
|
||||
--destructive-foreground: oklch(1 0 0);
|
||||
--border: oklch(0.29 0 0);
|
||||
--input: oklch(0.37 0 0);
|
||||
--ring: oklch(0.77 0.16 70.08);
|
||||
--chart-1: oklch(0.84 0.16 84.43);
|
||||
--chart-2: oklch(0.67 0.16 58.32);
|
||||
--chart-3: oklch(0.47 0.12 46.20);
|
||||
--chart-4: oklch(0.56 0.15 49.00);
|
||||
--chart-5: oklch(0.47 0.12 46.20);
|
||||
--chart-3: oklch(0.47 0.12 46.2);
|
||||
--chart-4: oklch(0.56 0.15 49);
|
||||
--chart-5: oklch(0.47 0.12 46.2);
|
||||
--sidebar: oklch(0.17 0 0);
|
||||
--sidebar-foreground: oklch(0.92 0 0);
|
||||
--sidebar-primary: oklch(0.77 0.16 70.08);
|
||||
--sidebar-primary-foreground: oklch(1.00 0 0);
|
||||
--sidebar-accent: oklch(0.47 0.12 46.20);
|
||||
--sidebar-primary-foreground: oklch(1 0 0);
|
||||
--sidebar-accent: oklch(0.47 0.12 46.2);
|
||||
--sidebar-accent-foreground: oklch(0.92 0.12 95.75);
|
||||
--sidebar-border: oklch(0.37 0 0);
|
||||
--sidebar-ring: oklch(0.77 0.16 70.08);
|
||||
--font-sans: Inter, sans-serif;
|
||||
--font-serif: Source Serif 4, serif;
|
||||
--font-mono: JetBrains Mono, monospace;
|
||||
--radius: 0.580rem;
|
||||
--radius: 0.58rem;
|
||||
--shadow-2xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05);
|
||||
--shadow-xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05);
|
||||
--shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10);
|
||||
--shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10);
|
||||
--shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 2px 4px -2px hsl(0 0% 0% / 0.10);
|
||||
--shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 4px 6px -2px hsl(0 0% 0% / 0.10);
|
||||
--shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 8px 10px -2px hsl(0 0% 0% / 0.10);
|
||||
--shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 1px 2px -2px hsl(0 0% 0% / 0.1);
|
||||
--shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 1px 2px -2px hsl(0 0% 0% / 0.1);
|
||||
--shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 2px 4px -2px hsl(0 0% 0% / 0.1);
|
||||
--shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 4px 6px -2px hsl(0 0% 0% / 0.1);
|
||||
--shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 8px 10px -2px hsl(0 0% 0% / 0.1);
|
||||
--shadow-2xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.25);
|
||||
}
|
||||
|
||||
@@ -571,7 +568,7 @@ html {
|
||||
@apply z-0 outline-0;
|
||||
}
|
||||
|
||||
.minimal-tiptap-editor .ProseMirror>div.editor {
|
||||
.minimal-tiptap-editor .ProseMirror > div.editor {
|
||||
@apply block flex-1 whitespace-pre-wrap;
|
||||
}
|
||||
|
||||
@@ -604,8 +601,8 @@ html {
|
||||
}
|
||||
|
||||
.minimal-tiptap-editor .ProseMirror li .text-node:has(+ .list-node),
|
||||
.minimal-tiptap-editor .ProseMirror li>.list-node,
|
||||
.minimal-tiptap-editor .ProseMirror li>.text-node,
|
||||
.minimal-tiptap-editor .ProseMirror li > .list-node,
|
||||
.minimal-tiptap-editor .ProseMirror li > .text-node,
|
||||
.minimal-tiptap-editor .ProseMirror li p {
|
||||
@apply mb-0;
|
||||
}
|
||||
@@ -658,7 +655,7 @@ html {
|
||||
}
|
||||
|
||||
/* Basic editor styles */
|
||||
.tiptap> :first-child {
|
||||
.tiptap > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@@ -761,7 +758,7 @@ th,
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
*:hover>.resizer {
|
||||
*:hover > .resizer {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -795,7 +792,6 @@ th,
|
||||
}
|
||||
|
||||
.hide-scrollbar {
|
||||
|
||||
/* Webkit browsers (Chrome, Safari, Edge) */
|
||||
-webkit-scrollbar {
|
||||
display: none;
|
||||
@@ -826,7 +822,6 @@ th,
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
letter-spacing: var(--tracking-normal);
|
||||
}
|
||||
@@ -854,4 +849,4 @@ body {
|
||||
width: 100%;
|
||||
transform-origin: top left;
|
||||
touch-action: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,12 @@ from .connection import Neo4jConnection, neo4j_connection
|
||||
from .repository import Neo4jGraphRepository
|
||||
from .repository_protocol import GraphRepositoryProtocol
|
||||
from .serializer import GraphSerializer
|
||||
from .service import GraphService, create_graph_service, LoggerProtocol
|
||||
from .service import GraphService, LoggerProtocol, create_graph_service
|
||||
from .types import (
|
||||
GraphData,
|
||||
GraphDict,
|
||||
GraphEdge,
|
||||
GraphNode,
|
||||
Neo4jDict,
|
||||
NodeMetadata,
|
||||
)
|
||||
|
||||
@@ -38,6 +38,6 @@ __all__ = [
|
||||
"GraphData",
|
||||
"GraphEdge",
|
||||
"GraphNode",
|
||||
"Neo4jDict",
|
||||
"GraphDict",
|
||||
"NodeMetadata",
|
||||
]
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
Graph database repository for Neo4j operations.
|
||||
|
||||
This module provides a repository pattern implementation for Neo4j,
|
||||
handling raw Neo4jDict object and operations with batching support.
|
||||
handling raw GraphDict object and operations with batching support.
|
||||
"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from .connection import Neo4jConnection
|
||||
from .types import Neo4jDict
|
||||
from .types import GraphDict
|
||||
|
||||
|
||||
class Neo4jGraphRepository:
|
||||
@@ -36,17 +36,17 @@ class Neo4jGraphRepository:
|
||||
|
||||
def create_node(
|
||||
self,
|
||||
node_obj: Neo4jDict,
|
||||
node_obj: GraphDict,
|
||||
sketch_id: str,
|
||||
) -> Optional[str]:
|
||||
"""
|
||||
Create or update a single node in Neo4j.
|
||||
|
||||
Supported signature:
|
||||
1. Neo4jDict object: create_node(obj)
|
||||
1. GraphDict object: create_node(obj)
|
||||
|
||||
Args:
|
||||
node_obj: a Neo4jDict object
|
||||
node_obj: a GraphDict object
|
||||
sketch_id: str
|
||||
|
||||
Returns:
|
||||
@@ -61,7 +61,7 @@ class Neo4jGraphRepository:
|
||||
|
||||
def create_relationship(
|
||||
self,
|
||||
rel_obj: Neo4jDict,
|
||||
rel_obj: GraphDict,
|
||||
sketch_id: str,
|
||||
) -> None:
|
||||
"""
|
||||
@@ -71,7 +71,7 @@ class Neo4jGraphRepository:
|
||||
- Pydantic objects: create_relationship(relation, sketch_id="...")
|
||||
|
||||
Args:
|
||||
rel_obj: the Neo4jDict object
|
||||
rel_obj: the GraphDict object
|
||||
sketch_id: Investigation sketch ID (required)
|
||||
"""
|
||||
if not self._connection:
|
||||
@@ -103,7 +103,7 @@ class Neo4jGraphRepository:
|
||||
self.flush_batch()
|
||||
|
||||
def _build_node_query(
|
||||
self, node_obj: Neo4jDict, sketch_id: str
|
||||
self, node_obj: GraphDict, sketch_id: str
|
||||
) -> Tuple[str, Dict[str, Any]]:
|
||||
node_label = node_obj.get("nodeLabel")
|
||||
node_type = node_obj.get("nodeType")
|
||||
@@ -127,7 +127,7 @@ class Neo4jGraphRepository:
|
||||
|
||||
def _build_relationship_query(
|
||||
self,
|
||||
rel_obj: Neo4jDict,
|
||||
rel_obj: GraphDict,
|
||||
sketch_id: str,
|
||||
) -> Tuple[str, Dict[str, Any]]:
|
||||
from_type = rel_obj["from_type"]
|
||||
@@ -183,14 +183,14 @@ class Neo4jGraphRepository:
|
||||
|
||||
def batch_create_nodes(
|
||||
self,
|
||||
nodes: List[Neo4jDict],
|
||||
nodes: List[GraphDict],
|
||||
sketch_id: str,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Create multiple nodes in a single batch transaction.
|
||||
|
||||
Args:
|
||||
nodes: List of Neo4jDict model instances to insert
|
||||
nodes: List of GraphDict model instances to insert
|
||||
sketch_id: investigation sketch id
|
||||
|
||||
Returns:
|
||||
@@ -256,7 +256,7 @@ class Neo4jGraphRepository:
|
||||
|
||||
Args:
|
||||
edges: List of edge dictionaries, each with:
|
||||
- rel_obj: Source node Neo4jDict model
|
||||
- rel_obj: Source node GraphDict model
|
||||
|
||||
Returns:
|
||||
Dictionary with:
|
||||
@@ -300,9 +300,9 @@ class Neo4jGraphRepository:
|
||||
|
||||
def batch_create_edges_by_element_id(
|
||||
self,
|
||||
edges: List[Neo4jDict],
|
||||
edges: List[GraphDict],
|
||||
sketch_id: str,
|
||||
) -> Neo4jDict:
|
||||
) -> GraphDict:
|
||||
"""
|
||||
Create multiple edges/relationships using element IDs in a single batch transaction.
|
||||
|
||||
@@ -388,7 +388,7 @@ class Neo4jGraphRepository:
|
||||
return {"edges_created": 0, "errors": errors}
|
||||
|
||||
def update_node(
|
||||
self, element_id: str, updates: Neo4jDict, sketch_id: str
|
||||
self, element_id: str, updates: GraphDict, sketch_id: str
|
||||
) -> Optional[str]:
|
||||
if not self._connection:
|
||||
return None
|
||||
@@ -532,7 +532,7 @@ class Neo4jGraphRepository:
|
||||
return {"nodes": nodes_result, "edges": rels_result or []}
|
||||
|
||||
def update_relationship(
|
||||
self, element_id: str, rel_obj: Neo4jDict, sketch_id: str
|
||||
self, element_id: str, rel_obj: GraphDict, sketch_id: str
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
if not self._connection:
|
||||
return None
|
||||
|
||||
@@ -5,9 +5,9 @@ This module defines the interface contract that all graph repository
|
||||
implementations must follow, enabling dependency injection and easier testing.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, List, Optional, Protocol, Tuple
|
||||
from typing import Any, Dict, List, Optional, Protocol
|
||||
|
||||
from .types import Neo4jDict
|
||||
from .types import GraphDict
|
||||
|
||||
|
||||
class GraphRepositoryProtocol(Protocol):
|
||||
@@ -21,12 +21,12 @@ class GraphRepositoryProtocol(Protocol):
|
||||
"""
|
||||
|
||||
# Core node operations
|
||||
def create_node(self, node_obj: Neo4jDict, sketch_id: str) -> Optional[str]:
|
||||
def create_node(self, node_obj: GraphDict, sketch_id: str) -> Optional[str]:
|
||||
"""Create or update a single node. Returns element ID."""
|
||||
...
|
||||
|
||||
def update_node(
|
||||
self, element_id: str, updates: Neo4jDict, sketch_id: str
|
||||
self, element_id: str, updates: GraphDict, sketch_id: str
|
||||
) -> Optional[str]:
|
||||
"""Update a node by its element ID. Returns element ID."""
|
||||
...
|
||||
@@ -52,7 +52,7 @@ class GraphRepositoryProtocol(Protocol):
|
||||
...
|
||||
|
||||
# Core relationship operations
|
||||
def create_relationship(self, rel_obj: Neo4jDict, sketch_id: str) -> None:
|
||||
def create_relationship(self, rel_obj: GraphDict, sketch_id: str) -> None:
|
||||
"""Create a relationship between two nodes."""
|
||||
...
|
||||
|
||||
@@ -67,7 +67,7 @@ class GraphRepositoryProtocol(Protocol):
|
||||
...
|
||||
|
||||
def update_relationship(
|
||||
self, element_id: str, rel_obj: Neo4jDict, sketch_id: str
|
||||
self, element_id: str, rel_obj: GraphDict, sketch_id: str
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Update a relationship by its element ID."""
|
||||
...
|
||||
@@ -100,13 +100,13 @@ class GraphRepositoryProtocol(Protocol):
|
||||
|
||||
# Batch operations
|
||||
def batch_create_nodes(
|
||||
self, nodes: List[Neo4jDict], sketch_id: str
|
||||
self, nodes: List[GraphDict], sketch_id: str
|
||||
) -> Dict[str, Any]:
|
||||
"""Create multiple nodes in a single batch."""
|
||||
...
|
||||
|
||||
def batch_create_edges_by_element_id(
|
||||
self, edges: List[Neo4jDict], sketch_id: str
|
||||
self, edges: List[GraphDict], sketch_id: str
|
||||
) -> Dict[str, Any]:
|
||||
"""Create multiple edges using element IDs in a single batch."""
|
||||
...
|
||||
|
||||
@@ -13,7 +13,7 @@ from pydantic import BaseModel
|
||||
from .repository import Neo4jGraphRepository
|
||||
from .repository_protocol import GraphRepositoryProtocol
|
||||
from .serializer import GraphSerializer
|
||||
from .types import GraphData, GraphNode, Neo4jDict
|
||||
from .types import GraphData, GraphDict, GraphNode
|
||||
|
||||
|
||||
class LoggerProtocol(Protocol):
|
||||
@@ -90,7 +90,7 @@ class GraphService:
|
||||
"create_node method takes a GraphNode as input. If you want to insert a node from a FlowsintType, please use create_node_from_flowsint_type method."
|
||||
)
|
||||
|
||||
neo4j_node_dict: Neo4jDict = GraphSerializer.graph_node_to_neo4j_dict(node_obj)
|
||||
neo4j_node_dict: GraphDict = GraphSerializer.graph_node_to_neo4j_dict(node_obj)
|
||||
|
||||
if self._enable_batching:
|
||||
self._repository.add_to_batch(
|
||||
@@ -120,7 +120,7 @@ class GraphService:
|
||||
"create_node_from_flowsint_type method takes a FlowsintType as input. If you want to insert a node from a GraphNode, please use create_node method."
|
||||
)
|
||||
|
||||
neo4j_node_dict: Neo4jDict = GraphSerializer.flowsint_type_to_neo4j_dict(
|
||||
neo4j_node_dict: GraphDict = GraphSerializer.flowsint_type_to_neo4j_dict(
|
||||
node_obj
|
||||
)
|
||||
|
||||
@@ -169,7 +169,7 @@ class GraphService:
|
||||
**properties: Additional relationship properties
|
||||
"""
|
||||
|
||||
neo4j_rel_dict: Neo4jDict = GraphSerializer.graph_edge_to_neo4j_dict(
|
||||
neo4j_rel_dict: GraphDict = GraphSerializer.graph_edge_to_neo4j_dict(
|
||||
from_obj, to_obj, rel_label
|
||||
)
|
||||
|
||||
@@ -267,7 +267,7 @@ class GraphService:
|
||||
sketch_id=self._sketch_id,
|
||||
)
|
||||
|
||||
def batch_create_nodes(self, nodes: List[Neo4jDict]) -> Dict[str, Any]:
|
||||
def batch_create_nodes(self, nodes: List[GraphDict]) -> Dict[str, Any]:
|
||||
"""Create multiple nodes in a single batch transaction."""
|
||||
return self._repository.batch_create_nodes(
|
||||
nodes=nodes,
|
||||
@@ -275,7 +275,7 @@ class GraphService:
|
||||
)
|
||||
|
||||
def batch_create_edges_by_element_id(
|
||||
self, edges: List[Neo4jDict]
|
||||
self, edges: List[GraphDict]
|
||||
) -> Dict[str, Any]:
|
||||
"""Create multiple edges using element IDs in a single batch transaction."""
|
||||
return self._repository.batch_create_edges_by_element_id(
|
||||
|
||||
@@ -10,7 +10,7 @@ from typing import Any, Dict, List, Optional, Union
|
||||
from flowsint_types import FlowsintType
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
Neo4jDict = Dict[str, Any]
|
||||
GraphDict = Dict[str, Any]
|
||||
|
||||
|
||||
class NodeMetadata(BaseModel):
|
||||
@@ -36,7 +36,7 @@ class GraphNode(BaseModel):
|
||||
nodeFlag: Optional[str] = None
|
||||
|
||||
nodeMetadata: NodeMetadata
|
||||
nodeProperties: FlowsintType
|
||||
nodeProperties: Any
|
||||
|
||||
x: Optional[float] = 100.0
|
||||
y: Optional[float] = 100.0
|
||||
|
||||
@@ -71,9 +71,9 @@ def _get_nodes(nodes: List[Dict]) -> List[EntityPreview]:
|
||||
results = []
|
||||
for node in nodes:
|
||||
node_id = node.get("id")
|
||||
node_type = node.get("type", None)
|
||||
label = node.get("label", None)
|
||||
node_obj = {"type": node_type, "label": label, **node}
|
||||
node_type = node.get("nodeType", node.get("type"))
|
||||
label = node.get("nodeLabel", node.get("label"))
|
||||
node_obj = {"nodeType": node_type, "nodeLabel": label, **node}
|
||||
preview = _parse_node(node_obj)
|
||||
if preview:
|
||||
results.append(
|
||||
@@ -88,10 +88,10 @@ def _get_nodes(nodes: List[Dict]) -> List[EntityPreview]:
|
||||
|
||||
|
||||
def _parse_node(nodeDict: dict) -> EntityPreview | None:
|
||||
type = nodeDict.get("type")
|
||||
type = nodeDict.get("nodeType")
|
||||
first_key = next(iter(nodeDict), None)
|
||||
label = (
|
||||
nodeDict.get("label")
|
||||
nodeDict.get("nodeLabel")
|
||||
or (nodeDict.get(first_key) if first_key else None)
|
||||
or nodeDict.get("id")
|
||||
)
|
||||
|
||||
@@ -4,9 +4,9 @@ import pytest
|
||||
from flowsint_types import Domain, Ip
|
||||
|
||||
from flowsint_core.core.graph import (
|
||||
GraphSerializer,
|
||||
GraphEdge,
|
||||
GraphNode,
|
||||
GraphSerializer,
|
||||
NodeMetadata,
|
||||
)
|
||||
|
||||
@@ -184,7 +184,7 @@ class TestGraphNodeToFlowsintType:
|
||||
assert result.domain == "example.com"
|
||||
|
||||
|
||||
class TestNeo4jDictToGraphEdge:
|
||||
class TestGraphDictToGraphEdge:
|
||||
def test_converts_edge_dict(self):
|
||||
edge_dict = {
|
||||
"id": "edge-123",
|
||||
@@ -207,7 +207,7 @@ class TestNeo4jDictToGraphEdge:
|
||||
assert result.target == "2"
|
||||
|
||||
|
||||
class TestGraphEdgeToNeo4jDict:
|
||||
class TestGraphEdgeToGraphDict:
|
||||
def test_with_flowsint_types(self):
|
||||
from_obj = Domain(domain="source.com")
|
||||
to_obj = Domain(domain="target.com")
|
||||
@@ -313,7 +313,7 @@ class TestDeserializeEdges:
|
||||
assert result == []
|
||||
|
||||
|
||||
class TestNeo4jDictToGraphNodeErrors:
|
||||
class TestGraphDictToGraphNodeErrors:
|
||||
def test_raises_on_missing_data(self):
|
||||
node_dict = {"id": "123"}
|
||||
with pytest.raises(Exception, match="Could not find node data"):
|
||||
|
||||
Reference in New Issue
Block a user