diff --git a/api/src/main/java/io/sentrius/sso/controllers/api/IntegrationApiController.java b/api/src/main/java/io/sentrius/sso/controllers/api/IntegrationApiController.java index 5d6f9b43..f53a0ba2 100644 --- a/api/src/main/java/io/sentrius/sso/controllers/api/IntegrationApiController.java +++ b/api/src/main/java/io/sentrius/sso/controllers/api/IntegrationApiController.java @@ -53,6 +53,25 @@ protected IntegrationApiController( this.cryptoService = cryptoService; } + @PostMapping("/github/add") + public ResponseEntity addGitHubIntegration(HttpServletRequest request, + HttpServletResponse response, + ExternalIntegrationDTO integrationDTO) + throws JsonProcessingException, GeneralSecurityException { + + var json = JsonUtil.MAPPER.writeValueAsString(integrationDTO); + IntegrationSecurityToken token = IntegrationSecurityToken.builder() + .connectionType("github") + .name(integrationDTO.getName()) + .connectionInfo(json) + .build(); + + token = integrationService.save(token); + + // excludes the access token + return ResponseEntity.ok(new ExternalIntegrationDTO(token)); + } + @PostMapping("/jira/add") public ResponseEntity addJiraIntegration(HttpServletRequest request, HttpServletResponse response, ExternalIntegrationDTO integrationDTO) diff --git a/api/src/main/java/io/sentrius/sso/controllers/view/IntegrationController.java b/api/src/main/java/io/sentrius/sso/controllers/view/IntegrationController.java index e7fd958a..b1c4a89f 100644 --- a/api/src/main/java/io/sentrius/sso/controllers/view/IntegrationController.java +++ b/api/src/main/java/io/sentrius/sso/controllers/view/IntegrationController.java @@ -35,29 +35,52 @@ public String getIntegrationDashboard(Model model) { List> integrations = List.of( Map.of( "name", "GitHub", - "description", "Configure GitHub integration settings", - "icon", "fa-brands fa-github", // CSS class for GitHub icon - "href", "/sso/v1/integrations/github" + "description", "Connect your repositories and manage code integration workflows", + "icon", "fa-brands fa-github", + "href", "/sso/v1/integrations/github", + "badge", "Popular", + "badgeType", "popular" ), Map.of( "name", "JIRA", - "description", "Set up JIRA project management integration", - "icon", "fa-brands fa-jira", // CSS class for JIRA icon - "href", "/sso/v1/integrations/jira" + "description", "Streamline project management and issue tracking workflows", + "icon", "fa-brands fa-jira", + "href", "/sso/v1/integrations/jira", + "badge", "Popular", + "badgeType", "popular" ), Map.of( "name", "OpenAI", - "description", "OpenAI connector", - "icon", "fa-solid fa-robot", // CSS class for Slack icon - "href", "/sso/v1/integrations/openai" - ) - /* + "description", "Integrate AI capabilities and natural language processing", + "icon", "fa-solid fa-robot", + "href", "/sso/v1/integrations/openai", + "badge", "AI", + "badgeType", "new" + ), + Map.of( + "name", "Slack", + "description", "Enable team communication and notification workflows", + "icon", "fa-brands fa-slack", + "href", "/sso/v1/integrations/slack", + "badge", "Coming Soon", + "badgeType", "" + ), Map.of( "name", "Database", - "description", "Configure database connections", - "icon", "fa-solid fa-database", // CSS class for database icon - "href", "/sso/v1/integrations/database" - )*/ + "description", "Connect to databases for data integration and analytics", + "icon", "fa-solid fa-database", + "href", "/sso/v1/integrations/database", + "badge", "Coming Soon", + "badgeType", "" + ), + Map.of( + "name", "Microsoft Teams", + "description", "Integrate with Microsoft Teams for collaboration workflows", + "icon", "fa-brands fa-microsoft", + "href", "/sso/v1/integrations/teams", + "badge", "Coming Soon", + "badgeType", "" + ) ); List existingIntegrations = new ArrayList<>(); integrationService.findAll().forEach(token -> { @@ -72,6 +95,13 @@ public String getIntegrationDashboard(Model model) { return "sso/integrations/add_dashboard"; } + @GetMapping("/github") + public String createGitHubIntegration(Model model, @RequestParam(name = "id", required = false) Long id) { + ExternalIntegrationDTO integration = new ExternalIntegrationDTO(); + model.addAttribute("githubIntegration", integration); + return "sso/integrations/add_github"; + } + @GetMapping("/jira") public String createJiraIntegration(Model model, @RequestParam(name = "id", required = false) Long id) { ExternalIntegrationDTO integration = new ExternalIntegrationDTO(); @@ -86,4 +116,19 @@ public String createOpenAIIntegration(Model model, @RequestParam(name = "id", re return "sso/integrations/add_openai"; } + @GetMapping("/slack") + public String createSlackIntegration(Model model) { + return getIntegrationDashboard(model); + } + + @GetMapping("/database") + public String createDatabaseIntegration(Model model) { + return getIntegrationDashboard(model); + } + + @GetMapping("/teams") + public String createTeamsIntegration(Model model) { + return getIntegrationDashboard(model); + } + } diff --git a/api/src/main/resources/templates/sso/integrations/add_dashboard.html b/api/src/main/resources/templates/sso/integrations/add_dashboard.html index 1c7bef09..f0dfafaa 100644 --- a/api/src/main/resources/templates/sso/integrations/add_dashboard.html +++ b/api/src/main/resources/templates/sso/integrations/add_dashboard.html @@ -5,64 +5,190 @@ [[${systemOptions.systemLogoName}]] - Add Integrations @@ -76,66 +202,86 @@
-
-
-

Select Integration to Add

-
- -
-
-

Integrations

- +
+

Active Integrations

+
+
- - + + + - - - - + -
Name TypeConnecting Info StatusConfigurationActions
- GitHub - OpenAI - JIRA + +
+ + +
+ + + + + + Connected + - - - + + + + Protected + + +
-
@@ -165,24 +311,38 @@

Integrations

} } $(document).ready(function () { - // Initialize DataTables - var csrf = "[[${_csrf.token}]]" - - - // Mark the click handler as async - $(".del_bet").click(async function (event) { - event.preventDefault(); // Prevent the default link behavior - var id = $(this).attr('id').replace("del_bet_", ""); // Parse ID from the element's ID + // Initialize variables + var csrf = "[[${_csrf.token}]]"; - var url = '/api/v1/integrations/delete?integrationId=' + id; - - // Call the deleteNotification function - await deleteIntegration(url, null, csrf); + // Handle delete integration button clicks + $(".del_bet").click(async function (event) { + event.preventDefault(); + + // Show confirmation dialog + if (!confirm('Are you sure you want to remove this integration? This action cannot be undone.')) { + return; + } + + var id = $(this).attr('id').replace("del_bet_", ""); + var url = '/api/v1/integrations/delete?integrationId=' + id; + + // Disable button and show loading state + $(this).prop('disabled', true).html(''); - // Optionally reload the page after deletion - window.location.reload(); + try { + await deleteIntegration(url, null, csrf); + // Show success message briefly before reload + $(this).html(''); + setTimeout(() => { + window.location.reload(); + }, 1000); + } catch (error) { + // Reset button on error + $(this).prop('disabled', false).html(''); + alert("An error occurred while removing the integration: " + error.message); + } + }); }); -}); diff --git a/api/src/main/resources/templates/sso/integrations/add_github.html b/api/src/main/resources/templates/sso/integrations/add_github.html new file mode 100644 index 00000000..6a4957a0 --- /dev/null +++ b/api/src/main/resources/templates/sso/integrations/add_github.html @@ -0,0 +1,136 @@ + + + + + [[${systemOptions.systemLogoName}]] - Add GitHub Integration + + + + + +
+
+ +
+
+
+ +
+
+ +

Connect GitHub

+

Integrate your GitHub repositories for seamless code management and workflow automation.

+
+ +
+
+ + +
Choose a name to identify this integration
+
+ +
+ + +
For GitHub Enterprise, use your custom domain
+
+ +
+ + +
+ +
+ + +
+ + Generate a token at GitHub Settings. + Required scopes: repo, read:user +
+
+ +
+ Cancel + +
+ + +
+
+ +
+
+
+
+ + + \ No newline at end of file diff --git a/api/src/main/resources/templates/sso/integrations/add_jira.html b/api/src/main/resources/templates/sso/integrations/add_jira.html index 96793bda..ded33823 100644 --- a/api/src/main/resources/templates/sso/integrations/add_jira.html +++ b/api/src/main/resources/templates/sso/integrations/add_jira.html @@ -31,29 +31,67 @@ color: inherit; } - .card:hover { - background-color: #f9f9f9; + @@ -66,44 +104,63 @@
-
-

Set Up Jira Integration

+ +
+
+ +

Connect JIRA

+

Integrate your JIRA instance for streamlined project management and issue tracking.

+
+
- - + + +
Choose a name to identify this integration
- - + + +
Your JIRA instance URL
- + + th:field="*{username}" placeholder="Enter your JIRA username" required>
- + + th:field="*{apiToken}" placeholder="Enter your JIRA API token" required> +
+ + Generate a token at Atlassian Account Settings +
- + + th:field="*{projectKey}" placeholder="Enter your JIRA project key" required> +
The key of the project you want to integrate with
-
- +
+ Cancel +
+ +
+
diff --git a/api/src/main/resources/templates/sso/integrations/add_openai.html b/api/src/main/resources/templates/sso/integrations/add_openai.html index 4cbfb165..9360d590 100644 --- a/api/src/main/resources/templates/sso/integrations/add_openai.html +++ b/api/src/main/resources/templates/sso/integrations/add_openai.html @@ -2,58 +2,69 @@ - [[${systemOptions.systemLogoName}]] - Add Integrations + [[${systemOptions.systemLogoName}]] - Add OpenAI Integration @@ -65,6 +76,13 @@ const formData = new FormData(form); const jsonData = Object.fromEntries(formData.entries()); var csrf = "[[${_csrf.token}]]" + + // Show loading state + const submitBtn = form.querySelector('button[type="submit"]'); + const originalText = submitBtn.innerHTML; + submitBtn.disabled = true; + submitBtn.innerHTML = 'Connecting...'; + try { const response = await fetch(form.action, { method: "POST", @@ -76,15 +94,19 @@ }); if (response.ok) { - const redirectUrl = "/sso/v1/integrations"; // Modify as needed - window.location.href = redirectUrl; + submitBtn.innerHTML = 'Connected!'; + setTimeout(() => { + const redirectUrl = "/sso/v1/integrations"; + window.location.href = redirectUrl; + }, 1000); } else { - const errorText = await response.text(); - alert("Error: " + errorText); + throw new Error("Failed to create integration"); } } catch (error) { - console.error("Request failed", error); - alert("Failed to save integration."); + submitBtn.disabled = false; + submitBtn.innerHTML = originalText; + console.error("Error:", error); + alert("An error occurred while creating the integration."); } } @@ -97,37 +119,61 @@
-
-

Set Up OpenAI Integration

-
+ +
+
+ +

Connect OpenAI

+

Integrate AI capabilities and natural language processing into your workflows.

+
+
- + + th:field="*{name}" placeholder="My OpenAI Integration" required> +
Choose a name to identify this integration
- + + th:field="*{username}" placeholder="org-xxxxxxxxx (optional)"> +
Your OpenAI organization ID (optional for personal accounts)
- + + th:field="*{apiToken}" placeholder="sk-xxxxxxxxx" required> +
+ + Generate an API key at OpenAI Platform +
-
- +
+ + +
Use default URL unless you're using a custom endpoint
+ +
+ Cancel + +
+ +
+
- + \ No newline at end of file diff --git a/api/src/test/java/io/sentrius/sso/controllers/api/IntegrationApiControllerTest.java b/api/src/test/java/io/sentrius/sso/controllers/api/IntegrationApiControllerTest.java index 5b595743..a5969d56 100644 --- a/api/src/test/java/io/sentrius/sso/controllers/api/IntegrationApiControllerTest.java +++ b/api/src/test/java/io/sentrius/sso/controllers/api/IntegrationApiControllerTest.java @@ -58,6 +58,31 @@ void setUp() { ); } + @Test + void addGitHubIntegrationReturnsSuccessForValidDTO() throws JsonProcessingException, GeneralSecurityException { + ExternalIntegrationDTO dto = new ExternalIntegrationDTO(); + dto.setName("TestGitHub"); + dto.setUsername("testuser"); + dto.setApiToken("ghp_test-token"); + dto.setBaseUrl("https://github.com"); + + IntegrationSecurityToken savedToken = IntegrationSecurityToken.builder() + .id(1L) + .connectionType("github") + .name("TestGitHub") + .connectionInfo("{\"name\":\"TestGitHub\",\"username\":\"testuser\",\"apiToken\":\"ghp_test-token\",\"baseUrl\":\"https://github.com\"}") + .build(); + + when(integrationService.save(any(IntegrationSecurityToken.class))).thenReturn(savedToken); + + ResponseEntity result = controller.addGitHubIntegration(request, response, dto); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + assertNotNull(result.getBody()); + assertEquals("TestGitHub", result.getBody().getName()); + verify(integrationService).save(any(IntegrationSecurityToken.class)); + } + @Test void addOpenaiIntegrationReturnsSuccessForValidDTO() throws JsonProcessingException, GeneralSecurityException { ExternalIntegrationDTO dto = new ExternalIntegrationDTO();