diff --git a/src/app/components/workspace-integrations/workspace-integrations.html b/src/app/components/workspace-integrations/workspace-integrations.html index 3ee89a3..9b58331 100644 --- a/src/app/components/workspace-integrations/workspace-integrations.html +++ b/src/app/components/workspace-integrations/workspace-integrations.html @@ -154,16 +154,16 @@

Jira connection

-

Jira Cloud Resources

-

Accessible Atlassian cloud sites.

+

Jira projects

+

Projects visible to the connected Jira account.

-
+
- {{ resource.name }} - {{ resource.url }} + {{ project.key }} - {{ project.name }} + {{ project.siteName || project.siteUrl }}
diff --git a/src/app/components/workspace-integrations/workspace-integrations.spec.ts b/src/app/components/workspace-integrations/workspace-integrations.spec.ts index 440a8b8..ff63e6c 100644 --- a/src/app/components/workspace-integrations/workspace-integrations.spec.ts +++ b/src/app/components/workspace-integrations/workspace-integrations.spec.ts @@ -55,7 +55,7 @@ describe('WorkspaceIntegrationsComponent', () => { mockIntegrationService.getJiraProjects.and.returnValue(of({ connected: false, - resources: [], + projects: [], lastSyncAt: undefined })); mockIntegrationService.connectJira.and.returnValue(of(void 0)); @@ -70,7 +70,8 @@ describe('WorkspaceIntegrationsComponent', () => { provide: ActivatedRoute, useValue: { snapshot: { - paramMap: convertToParamMap({ id: 'workspace-1' }) + paramMap: convertToParamMap({ id: 'workspace-1' }), + queryParamMap: convertToParamMap({}) } } } diff --git a/src/app/components/workspace-integrations/workspace-integrations.ts b/src/app/components/workspace-integrations/workspace-integrations.ts index 7762db3..08564ec 100644 --- a/src/app/components/workspace-integrations/workspace-integrations.ts +++ b/src/app/components/workspace-integrations/workspace-integrations.ts @@ -4,7 +4,7 @@ import { ActivatedRoute, RouterLink } from '@angular/router'; import { IntegrationService } from '../../services/integration.service'; import { GitHubRepo, SyncStatus } from '../../models/github-integration.model'; import { SlackChannel } from '../../models/slack-integration.model'; -import { AtlassianResource, JiraSyncStatus } from '../../models/jira-integration.model'; +import { JiraProject, JiraSyncStatus } from '../../models/jira-integration.model'; @Component({ selector: 'app-workspace-integrations', @@ -41,7 +41,7 @@ export class WorkspaceIntegrationsComponent implements OnInit, OnDestroy { isSyncing = false; isJiraConnected = false; - jiraResources: AtlassianResource[] = []; + jiraProjects: JiraProject[] = []; jiraLastSyncAt?: Date; jiraSyncStatus?: JiraSyncStatus; jiraFeedbackMessage = ''; @@ -49,7 +49,25 @@ export class WorkspaceIntegrationsComponent implements OnInit, OnDestroy { isJiraSyncing = false; ngOnInit(): void { - this.workspaceId = this.route.snapshot.paramMap.get('id') ?? ''; + this.workspaceId = this.route.snapshot.paramMap.get('id') || + this.route.parent?.snapshot.paramMap.get('id') || ''; + + // Check for OAuth redirect results + const queryParams = this.route.snapshot.queryParamMap; + const jiraStatus = queryParams.get('jira'); + if (jiraStatus === 'connected') { + this.jiraFeedbackMessage = 'Jira connected successfully!'; + } else if (jiraStatus === 'failed') { + this.jiraErrorMessage = 'Failed to connect Jira.'; + } + + const githubStatus = queryParams.get('github'); + if (githubStatus === 'connected') { + this.githubFeedbackMessage = 'GitHub connected successfully!'; + } else if (githubStatus === 'failed') { + this.githubErrorMessage = 'Failed to connect GitHub.'; + } + this.loadAllIntegrations(); window.addEventListener('focus', this.onWindowFocus); } @@ -80,6 +98,9 @@ export class WorkspaceIntegrationsComponent implements OnInit, OnDestroy { this.slackErrorMessage = ''; this.slackFeedbackMessage = 'Starting Slack connection...'; this.integrationService.connectSlack(this.workspaceId).subscribe({ + next: () => { + this.slackFeedbackMessage = ''; + }, error: (error: Error) => { this.slackErrorMessage = error.message; this.slackFeedbackMessage = ''; @@ -98,6 +119,9 @@ export class WorkspaceIntegrationsComponent implements OnInit, OnDestroy { this.githubErrorMessage = ''; this.githubFeedbackMessage = 'Starting GitHub connection...'; this.integrationService.connectGitHub().subscribe({ + next: () => { + this.githubFeedbackMessage = ''; + }, error: (error: Error) => { this.githubErrorMessage = error.message; this.githubFeedbackMessage = ''; @@ -117,6 +141,9 @@ export class WorkspaceIntegrationsComponent implements OnInit, OnDestroy { this.jiraErrorMessage = ''; this.jiraFeedbackMessage = 'Starting Jira connection...'; this.integrationService.connectJira(this.workspaceId).subscribe({ + next: () => { + this.jiraFeedbackMessage = ''; + }, error: (error: Error) => { this.jiraErrorMessage = error.message; this.jiraFeedbackMessage = ''; @@ -262,7 +289,7 @@ export class WorkspaceIntegrationsComponent implements OnInit, OnDestroy { private loadJira(): void { this.integrationService.getJiraProjects(this.workspaceId).subscribe(response => { this.isJiraConnected = response.connected; - this.jiraResources = response.resources; + this.jiraProjects = response.projects; this.jiraLastSyncAt = response.lastSyncAt; }); } diff --git a/src/app/models/jira-integration.model.ts b/src/app/models/jira-integration.model.ts index 7f398bf..2172f58 100644 --- a/src/app/models/jira-integration.model.ts +++ b/src/app/models/jira-integration.model.ts @@ -2,15 +2,10 @@ export interface JiraProject { id: string; key: string; name: string; - avatarUrl: string; -} - -export interface AtlassianResource { - id: string; - url: string; - name: string; - scopes: string[]; - avatarUrl: string; + avatarUrl?: string; + siteId: string; + siteName: string; + siteUrl: string; } export interface JiraSyncStatus { @@ -21,6 +16,6 @@ export interface JiraSyncStatus { export interface JiraIntegrationResponse { connected: boolean; - resources: AtlassianResource[]; - lastSyncAt?: string; + projects: JiraProject[]; + lastSyncAt?: Date; } diff --git a/src/app/services/integration.service.ts b/src/app/services/integration.service.ts index f6aa728..9ebaf5b 100644 --- a/src/app/services/integration.service.ts +++ b/src/app/services/integration.service.ts @@ -1,7 +1,8 @@ import { HttpClient, HttpParams } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; -import { catchError, map, Observable, of, switchMap, throwError } from 'rxjs'; +import { catchError, map, Observable, of, switchMap, tap, throwError } from 'rxjs'; import { GitHubConnectionState, GitHubRepo, SyncStatus } from '../models/github-integration.model'; +import { JiraIntegrationResponse, JiraProject, JiraSyncStatus } from '../models/jira-integration.model'; import { SlackConnectionState } from '../models/slack-integration.model'; import { toError } from './http-error'; @@ -37,6 +38,10 @@ interface GitHubSyncResponse { status: string; } +interface JiraSyncResponse { + status: string; +} + @Injectable({ providedIn: 'root', }) @@ -56,6 +61,7 @@ export class IntegrationService { return this.getSlackAuthUrl(workspaceId).pipe( map(({ authUrl }) => { window.open(authUrl, '_blank'); + return; }), ); } @@ -125,6 +131,7 @@ export class IntegrationService { return this.getGitHubAuthUrl().pipe( map(({ authUrl }) => { window.open(authUrl, '_blank'); + return; }), ); } @@ -182,38 +189,48 @@ export class IntegrationService { ); } - getJiraAuthUrl(workspaceId: string): Observable<{ authUrl: string }> { - const params = new HttpParams().set('workspace_id', workspaceId); + getJiraAuthUrl(workspaceId: string, redirectUrl?: string): Observable<{ authUrl: string }> { + console.log('Fetching Jira Auth URL for workspace:', workspaceId); + let params = new HttpParams().set('workspace_id', workspaceId); + if (redirectUrl) { + params = params.set('redirect_url', redirectUrl); + } return this.http.get(`${this.apiUrl}/jira/auth`, { params }).pipe( + tap(resp => console.log('Jira Auth URL response:', resp)), map((response) => ({ authUrl: response.auth_url })), - catchError((error) => throwError(() => toError(error, 'Unable to start Jira connection.'))), + catchError((error) => { + console.error('Jira Auth URL error:', error); + return throwError(() => toError(error, 'Unable to start Jira connection.')); + }), ); } connectJira(workspaceId: string): Observable { - return this.getJiraAuthUrl(workspaceId).pipe( + const redirectUrl = window.location.origin + window.location.pathname; + return this.getJiraAuthUrl(workspaceId, redirectUrl).pipe( map(({ authUrl }) => { - window.open(authUrl, '_blank'); + window.location.href = authUrl; + return; }), ); } - getJiraProjects(workspaceId: string): Observable { + getJiraProjects(workspaceId: string): Observable { return this.getIntegrations(workspaceId).pipe( switchMap((integrations) => { const jiraIntegration = integrations.find((integration) => integration.provider === 'jira'); if (!jiraIntegration) { return of({ connected: false, - resources: [], + projects: [], }); } const params = new HttpParams().set('workspace_id', workspaceId); - return this.http.get(`${this.apiUrl}/jira/projects`, { params }).pipe( - map((resources) => ({ + return this.http.get(`${this.apiUrl}/jira/projects`, { params }).pipe( + map((projects) => ({ connected: true, - resources: resources, + projects, lastSyncAt: jiraIntegration.updated_at ? new Date(jiraIntegration.updated_at) : undefined, })), ); @@ -229,13 +246,13 @@ export class IntegrationService { ); } - syncJira(workspaceId: string): Observable { + syncJira(workspaceId: string): Observable { const params = new HttpParams().set('workspace_id', workspaceId); - return this.http.post(`${this.apiUrl}/jira/sync`, {}, { params }).pipe( + return this.http.post(`${this.apiUrl}/jira/sync`, {}, { params }).pipe( map((response) => { return { status: response.status === 'sync_started' ? 'in_progress' : 'failed', - }; + } as JiraSyncStatus; }), catchError((error) => throwError(() => toError(error, 'Unable to sync Jira.'))), ); diff --git a/src/app/services/signal.service.ts b/src/app/services/signal.service.ts index cf221a7..4d8a94d 100644 --- a/src/app/services/signal.service.ts +++ b/src/app/services/signal.service.ts @@ -22,9 +22,16 @@ interface SignalResponse { state?: 'open' | 'closed'; labels?: string[]; issueType?: string; + issue_type?: string; priority?: string; projectKey?: string; + project_key?: string; + issueKey?: string; + issue_key?: string; + assigneeName?: string; + assignee_name?: string; assignees?: string[]; + status?: string; }; received_at?: string; } @@ -91,6 +98,19 @@ export class SignalService { }; } + if (signal.source_type === 'jira') { + const metadata = signal.source_metadata ?? {}; + return { + ...metadata, + issueType: metadata.issueType ?? metadata.issue_type, + projectKey: metadata.projectKey ?? metadata.project_key, + issueKey: metadata.issueKey ?? metadata.issue_key ?? signal.external_id, + assignees: metadata.assigneeName || metadata.assignee_name + ? [String(metadata.assigneeName ?? metadata.assignee_name)] + : [], + }; + } + return { ...(signal.source_metadata ?? {}), };