1010
1111
1212@unit_of_work (timeout = 3 )
13- async def read_graph (tx , query , requirement_file_id , max_depth ):
13+ async def read_graph_req_file (tx , query , requirement_file_id , max_depth ):
1414 result = await tx .run (
1515 query ,
1616 requirement_file_id = requirement_file_id ,
@@ -19,6 +19,16 @@ async def read_graph(tx, query, requirement_file_id, max_depth):
1919 return await result .single ()
2020
2121
22+ @unit_of_work (timeout = 3 )
23+ async def read_graph_package (tx , query , package_name , max_depth ):
24+ result = await tx .run (
25+ query ,
26+ package_name = package_name ,
27+ max_depth = max_depth
28+ )
29+ return await result .single ()
30+
31+
2232async def create_repository (repository : dict [str , Any ]) -> str :
2333 query = """
2434 MATCH(u:User) WHERE u._id = $user_id
@@ -84,7 +94,7 @@ async def read_repository_by_id(repository_id: str) -> dict[str, str]:
8494 return record [0 ] if record else None
8595
8696
87- async def read_graph_for_info_operation (
97+ async def read_graph_for_req_file_info_operation (
8898 node_type : str ,
8999 requirement_file_id : str ,
90100 max_depth : int
@@ -151,7 +161,7 @@ async def read_graph_for_info_operation(
151161 try :
152162 async with get_graph_db_driver ().session () as session :
153163 record = await session .execute_read (
154- read_graph ,
164+ read_graph_req_file ,
155165 query ,
156166 requirement_file_id ,
157167 max_depth
@@ -167,6 +177,88 @@ async def read_graph_for_info_operation(
167177 raise MemoryOutException () from err
168178
169179
180+ async def read_graph_for_package_info_operation (
181+ node_type : str ,
182+ package_name : str ,
183+ max_depth : int
184+ ) -> dict [str , Any ]:
185+ query = f"""
186+ MATCH (p:{ node_type } {{name:$package_name}})
187+ CALL apoc.path.expandConfig(
188+ p,
189+ {{
190+ relationshipFilter: 'REQUIRE>|HAVE>',
191+ labelFilter: 'Version|{ node_type } ',
192+ maxLevel: $max_depth,
193+ bfs: true,
194+ uniqueness: 'NODE_GLOBAL'
195+ }}
196+ ) YIELD path
197+ WITH
198+ last(nodes(path)) AS pkg,
199+ (length(path) - 1) / 2 AS depth,
200+ last(relationships(path)) AS rel
201+ WHERE '{ node_type } ' IN labels(pkg) AND type(rel) = 'REQUIRE'
202+ OPTIONAL MATCH (pkg:{ node_type } )-[:HAVE]->(v:Version)
203+ WITH
204+ pkg,
205+ depth,
206+ collect(DISTINCT {{
207+ name: v.name,
208+ mean: v.mean,
209+ serial_number: v.serial_number,
210+ weighted_mean: v.weighted_mean,
211+ vulnerability_count: v.vulnerabilities
212+ }}) AS versions,
213+ rel.constraints AS constraints
214+ WITH {{
215+ package_name: pkg.name,
216+ package_vendor: pkg.vendor,
217+ package_constraints: constraints,
218+ versions: versions
219+ }} AS enriched_pkg,
220+ depth
221+ WITH
222+ collect(CASE WHEN depth = 0 THEN enriched_pkg END) AS direct_deps,
223+ collect(CASE WHEN depth > 1 THEN {{node: enriched_pkg, depth: depth}} END) AS indirect_info
224+ WITH
225+ direct_deps,
226+ indirect_info,
227+ reduce(
228+ map = {{}},
229+ entry IN indirect_info |
230+ apoc.map.setKey(
231+ map,
232+ toString(entry.depth),
233+ coalesce(map[toString(entry.depth)], []) + entry.node
234+ )
235+ ) AS indirect_by_depth
236+ RETURN {{
237+ direct_dependencies: direct_deps,
238+ total_direct_dependencies: size(direct_deps),
239+ indirect_dependencies_by_depth: apoc.map.removeKey(indirect_by_depth, null),
240+ total_indirect_dependencies: size(indirect_info)
241+ }}
242+ """
243+ try :
244+ async with get_graph_db_driver ().session () as session :
245+ record = await session .execute_read (
246+ read_graph_package ,
247+ query ,
248+ package_name ,
249+ max_depth
250+ )
251+ return record [0 ] if record else None
252+ except Neo4jError as err :
253+ code = getattr (err , "code" , "" ) or ""
254+ if (
255+ code == "Neo.TransientError.General.MemoryPoolOutOfMemoryError"
256+ or code == "Neo.ClientError.Transaction.TransactionTimedOutClientConfiguration"
257+ or code == "Neo.ClientError.Transaction.TransactionTimedOut"
258+ ):
259+ raise MemoryOutException () from err
260+
261+
170262async def read_data_for_smt_transform (
171263 requirement_file_id : str ,
172264 max_depth : int
@@ -205,7 +297,7 @@ async def read_data_for_smt_transform(
205297 try :
206298 async with get_graph_db_driver ().session () as session :
207299 record = await session .execute_read (
208- read_graph ,
300+ read_graph_req_file ,
209301 query ,
210302 requirement_file_id ,
211303 max_depth
0 commit comments