1+ package io .sentrius .sso .core .integrations .ticketing ;
2+
3+ import java .util .List ;
4+ import java .util .Optional ;
5+
6+ import io .sentrius .sso .core .dto .TicketDTO ;
7+ import io .sentrius .sso .core .model .security .IntegrationSecurityToken ;
8+ import io .sentrius .sso .core .model .users .User ;
9+ import io .sentrius .sso .core .model .verbs .Verb ;
10+ import io .sentrius .sso .core .services .security .IntegrationSecurityTokenService ;
11+ import lombok .extern .slf4j .Slf4j ;
12+ import org .springframework .boot .autoconfigure .condition .ConditionalOnWebApplication ;
13+ import org .springframework .boot .web .client .RestTemplateBuilder ;
14+ import org .springframework .stereotype .Service ;
15+
16+ /**
17+ * Service that exposes JIRA operations as AI-callable verbs.
18+ * This allows AI agents to discover and call JIRA functionality through the capabilities API.
19+ */
20+ @ Slf4j
21+ @ Service
22+ @ ConditionalOnWebApplication (type = ConditionalOnWebApplication .Type .SERVLET )
23+ public class JiraVerbService {
24+
25+ private final TicketService ticketService ;
26+ private final IntegrationSecurityTokenService integrationService ;
27+
28+ public JiraVerbService (TicketService ticketService , IntegrationSecurityTokenService integrationService ) {
29+ this .ticketService = ticketService ;
30+ this .integrationService = integrationService ;
31+ }
32+
33+ /**
34+ * Searches for JIRA tickets based on a query string.
35+ * This method is exposed as a Verb so AI agents can discover and call it.
36+ *
37+ * @param query The search query (can be JQL or simple text)
38+ * @return List of tickets matching the query
39+ */
40+ @ Verb (
41+ name = "searchForTickets" ,
42+ description = "Search for JIRA tickets using a query string. Can use JQL or simple text search." ,
43+ returnType = List .class ,
44+ isAiCallable = true ,
45+ paramDescriptions = {"Search query string (JQL or simple text)" }
46+ )
47+ public List <TicketDTO > searchForTickets (String query ) {
48+ log .info ("Searching for tickets with query: {}" , query );
49+
50+ // Check if JIRA integration is available
51+ if (!isJiraIntegrationAvailable ()) {
52+ log .warn ("JIRA integration not available, returning empty results" );
53+ return List .of ();
54+ }
55+
56+ return ticketService .searchForIncidents (query );
57+ }
58+
59+ /**
60+ * Assigns a JIRA ticket to a user.
61+ * This method is exposed as a Verb so AI agents can discover and call it.
62+ *
63+ * @param ticketKey The JIRA ticket key (e.g., "PROJ-123")
64+ * @param user The user to assign the ticket to
65+ * @return true if assignment was successful, false otherwise
66+ */
67+ @ Verb (
68+ name = "assignTicket" ,
69+ description = "Assign a JIRA ticket to a user" ,
70+ returnType = Boolean .class ,
71+ isAiCallable = true ,
72+ paramDescriptions = {"JIRA ticket key (e.g., PROJ-123)" , "User to assign the ticket to" }
73+ )
74+ public Boolean assignTicket (String ticketKey , User user ) {
75+ log .info ("Assigning ticket {} to user {}" , ticketKey , user .getEmailAddress ());
76+
77+ // Check if JIRA integration is available
78+ if (!isJiraIntegrationAvailable ()) {
79+ log .warn ("JIRA integration not available, cannot assign ticket" );
80+ return false ;
81+ }
82+
83+ return ticketService .assignJira (ticketKey , user );
84+ }
85+
86+ /**
87+ * Updates a JIRA ticket with a comment.
88+ * This method is exposed as a Verb so AI agents can discover and call it.
89+ *
90+ * @param ticketKey The JIRA ticket key (e.g., "PROJ-123")
91+ * @param user The user adding the comment
92+ * @param message The comment message
93+ * @return true if update was successful, false otherwise
94+ */
95+ @ Verb (
96+ name = "updateTicket" ,
97+ description = "Add a comment to a JIRA ticket" ,
98+ returnType = Boolean .class ,
99+ isAiCallable = true ,
100+ paramDescriptions = {"JIRA ticket key (e.g., PROJ-123)" , "User adding the comment" , "Comment message" }
101+ )
102+ public Boolean updateTicket (String ticketKey , User user , String message ) {
103+ log .info ("Updating ticket {} with comment from user {}" , ticketKey , user .getEmailAddress ());
104+
105+ // Check if JIRA integration is available
106+ if (!isJiraIntegrationAvailable ()) {
107+ log .warn ("JIRA integration not available, cannot update ticket" );
108+ return false ;
109+ }
110+
111+ return ticketService .updateJira (ticketKey , user , message );
112+ }
113+
114+ /**
115+ * Checks if at least one JIRA integration is configured and available.
116+ * This method is exposed as a Verb so AI agents can check JIRA availability.
117+ *
118+ * @return true if JIRA integration is available, false otherwise
119+ */
120+ @ Verb (
121+ name = "isJiraAvailable" ,
122+ description = "Check if JIRA integration is configured and available" ,
123+ returnType = Boolean .class ,
124+ isAiCallable = true ,
125+ paramDescriptions = {}
126+ )
127+ public Boolean isJiraAvailable () {
128+ boolean available = isJiraIntegrationAvailable ();
129+ log .info ("JIRA integration availability check: {}" , available );
130+ return available ;
131+ }
132+
133+ /**
134+ * Helper method to check if JIRA integration is available.
135+ *
136+ * @return true if at least one JIRA integration is configured
137+ */
138+ private boolean isJiraIntegrationAvailable () {
139+ try {
140+ List <IntegrationSecurityToken > jiraIntegrations = integrationService .findByConnectionType ("jira" );
141+ return !jiraIntegrations .isEmpty ();
142+ } catch (Exception e ) {
143+ log .error ("Error checking JIRA integration availability" , e );
144+ return false ;
145+ }
146+ }
147+ }
0 commit comments