@@ -263,3 +263,105 @@ async def get_issue_comments(issue_ids: List[str], snowflake_token: Optional[str
263
263
logger .error (f"Error fetching comments: { str (e )} " )
264
264
265
265
return comments_data
266
+
267
+
268
+ async def get_issue_links (issue_ids : List [str ], snowflake_token : Optional [str ] = None ) -> Dict [str , List [Dict [str , Any ]]]:
269
+ """Get issue links for given issue IDs from Snowflake"""
270
+ if not issue_ids :
271
+ return {}
272
+
273
+ links_data = {}
274
+
275
+ try :
276
+ # Sanitize and validate issue IDs (should be numeric)
277
+ sanitized_ids = []
278
+ for issue_id in issue_ids :
279
+ # Ensure issue IDs are numeric to prevent injection
280
+ if isinstance (issue_id , (str , int )) and str (issue_id ).isdigit ():
281
+ sanitized_ids .append (str (issue_id ))
282
+
283
+ if not sanitized_ids :
284
+ return {}
285
+
286
+ # Create comma-separated list for IN clause
287
+ ids_str = "'" + "','" .join (sanitized_ids ) + "'"
288
+
289
+ sql = f"""
290
+ SELECT
291
+ il.ID as LINK_ID,
292
+ il.SOURCE,
293
+ il.DESTINATION,
294
+ il.SEQUENCE,
295
+ ilt.LINKNAME,
296
+ ilt.INWARD,
297
+ ilt.OUTWARD,
298
+ si.ISSUE_KEY as SOURCE_KEY,
299
+ di.ISSUE_KEY as DESTINATION_KEY,
300
+ si.SUMMARY as SOURCE_SUMMARY,
301
+ di.SUMMARY as DESTINATION_SUMMARY
302
+ FROM { SNOWFLAKE_DATABASE } .{ SNOWFLAKE_SCHEMA } .JIRA_ISSUELINK_RHAI il
303
+ JOIN { SNOWFLAKE_DATABASE } .{ SNOWFLAKE_SCHEMA } .JIRA_ISSUELINKTYPE_RHAI ilt
304
+ ON il.LINKTYPE = ilt.ID
305
+ LEFT JOIN { SNOWFLAKE_DATABASE } .{ SNOWFLAKE_SCHEMA } .JIRA_ISSUE_NON_PII si
306
+ ON il.SOURCE = si.ID
307
+ LEFT JOIN { SNOWFLAKE_DATABASE } .{ SNOWFLAKE_SCHEMA } .JIRA_ISSUE_NON_PII di
308
+ ON il.DESTINATION = di.ID
309
+ WHERE (il.SOURCE IN ({ ids_str } ) OR il.DESTINATION IN ({ ids_str } ))
310
+ ORDER BY il.SOURCE, il.SEQUENCE
311
+ """
312
+
313
+ rows = await execute_snowflake_query (sql , snowflake_token )
314
+ columns = [
315
+ "LINK_ID" , "SOURCE" , "DESTINATION" , "SEQUENCE" , "LINKNAME" ,
316
+ "INWARD" , "OUTWARD" , "SOURCE_KEY" , "DESTINATION_KEY" ,
317
+ "SOURCE_SUMMARY" , "DESTINATION_SUMMARY"
318
+ ]
319
+
320
+ for row in rows :
321
+ row_dict = format_snowflake_row (row , columns )
322
+ source_id = str (row_dict .get ("SOURCE" ))
323
+ destination_id = str (row_dict .get ("DESTINATION" ))
324
+
325
+ # Create link object
326
+ link = {
327
+ "link_id" : row_dict .get ("LINK_ID" ),
328
+ "source_id" : source_id ,
329
+ "destination_id" : destination_id ,
330
+ "sequence" : row_dict .get ("SEQUENCE" ),
331
+ "link_type" : row_dict .get ("LINKNAME" ),
332
+ "inward_description" : row_dict .get ("INWARD" ),
333
+ "outward_description" : row_dict .get ("OUTWARD" ),
334
+ "source_key" : row_dict .get ("SOURCE_KEY" ),
335
+ "destination_key" : row_dict .get ("DESTINATION_KEY" ),
336
+ "source_summary" : row_dict .get ("SOURCE_SUMMARY" ),
337
+ "destination_summary" : row_dict .get ("DESTINATION_SUMMARY" )
338
+ }
339
+
340
+ # Add to both source and destination issue data
341
+ for issue_id in [source_id , destination_id ]:
342
+ if issue_id in sanitized_ids :
343
+ if issue_id not in links_data :
344
+ links_data [issue_id ] = []
345
+
346
+ # Determine relationship direction for this issue
347
+ if issue_id == source_id :
348
+ link_copy = link .copy ()
349
+ link_copy ["relationship" ] = "outward"
350
+ link_copy ["related_issue_id" ] = destination_id
351
+ link_copy ["related_issue_key" ] = row_dict .get ("DESTINATION_KEY" )
352
+ link_copy ["related_issue_summary" ] = row_dict .get ("DESTINATION_SUMMARY" )
353
+ link_copy ["relationship_description" ] = row_dict .get ("OUTWARD" )
354
+ else :
355
+ link_copy = link .copy ()
356
+ link_copy ["relationship" ] = "inward"
357
+ link_copy ["related_issue_id" ] = source_id
358
+ link_copy ["related_issue_key" ] = row_dict .get ("SOURCE_KEY" )
359
+ link_copy ["related_issue_summary" ] = row_dict .get ("SOURCE_SUMMARY" )
360
+ link_copy ["relationship_description" ] = row_dict .get ("INWARD" )
361
+
362
+ links_data [issue_id ].append (link_copy )
363
+
364
+ except Exception as e :
365
+ logger .error (f"Error fetching issue links: { str (e )} " )
366
+
367
+ return links_data
0 commit comments