Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -154,16 +154,16 @@ <h3>Jira connection</h3>
<section class="selection-card" *ngIf="isJiraConnected">
<div class="section-head">
<div>
<h3>Jira projects</h3>
<p>Projects visible to the connected Jira account.</p>
<h3>Jira Cloud Resources</h3>
<p>Accessible Atlassian cloud sites.</p>
</div>
</div>

<div class="repo-list">
<div class="repo-item" *ngFor="let project of jiraProjects">
<div class="repo-item" *ngFor="let resource of jiraResources">
<span>
<strong>{{ project.key }} - {{ project.name }}</strong>
<small>{{ project.siteName || project.siteUrl }}</small>
<strong>{{ resource.name }}</strong>
<small>{{ resource.url }}</small>
</span>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('WorkspaceIntegrationsComponent', () => {

mockIntegrationService.getJiraProjects.and.returnValue(of({
connected: false,
projects: [],
resources: [],
lastSyncAt: undefined
}));
mockIntegrationService.connectJira.and.returnValue(of(void 0));
Expand All @@ -70,8 +70,7 @@ describe('WorkspaceIntegrationsComponent', () => {
provide: ActivatedRoute,
useValue: {
snapshot: {
paramMap: convertToParamMap({ id: 'workspace-1' }),
queryParamMap: convertToParamMap({})
paramMap: convertToParamMap({ id: 'workspace-1' })
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 { JiraProject, JiraSyncStatus } from '../../models/jira-integration.model';
import { AtlassianResource, JiraSyncStatus } from '../../models/jira-integration.model';

@Component({
selector: 'app-workspace-integrations',
Expand Down Expand Up @@ -41,33 +41,15 @@ export class WorkspaceIntegrationsComponent implements OnInit, OnDestroy {
isSyncing = false;

isJiraConnected = false;
jiraProjects: JiraProject[] = [];
jiraResources: AtlassianResource[] = [];
jiraLastSyncAt?: Date;
jiraSyncStatus?: JiraSyncStatus;
jiraFeedbackMessage = '';
jiraErrorMessage = '';
isJiraSyncing = false;

ngOnInit(): void {
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.workspaceId = this.route.snapshot.paramMap.get('id') ?? '';
this.loadAllIntegrations();
window.addEventListener('focus', this.onWindowFocus);
}
Expand Down Expand Up @@ -98,9 +80,6 @@ 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 = '';
Expand All @@ -119,9 +98,6 @@ 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 = '';
Expand All @@ -141,9 +117,6 @@ 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 = '';
Expand Down Expand Up @@ -289,7 +262,7 @@ export class WorkspaceIntegrationsComponent implements OnInit, OnDestroy {
private loadJira(): void {
this.integrationService.getJiraProjects(this.workspaceId).subscribe(response => {
this.isJiraConnected = response.connected;
this.jiraProjects = response.projects;
this.jiraResources = response.resources;
this.jiraLastSyncAt = response.lastSyncAt;
});
}
Expand Down
17 changes: 11 additions & 6 deletions src/app/models/jira-integration.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ export interface JiraProject {
id: string;
key: string;
name: string;
avatarUrl?: string;
siteId: string;
siteName: string;
siteUrl: string;
avatarUrl: string;
}

export interface AtlassianResource {
id: string;
url: string;
name: string;
scopes: string[];
avatarUrl: string;
}

export interface JiraSyncStatus {
Expand All @@ -16,6 +21,6 @@ export interface JiraSyncStatus {

export interface JiraIntegrationResponse {
connected: boolean;
projects: JiraProject[];
lastSyncAt?: Date;
resources: AtlassianResource[];
lastSyncAt?: string;
}
45 changes: 14 additions & 31 deletions src/app/services/integration.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable, inject } from '@angular/core';
import { catchError, map, Observable, of, switchMap, tap, throwError } from 'rxjs';
import { catchError, map, Observable, of, switchMap, 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';

Expand Down Expand Up @@ -38,10 +37,6 @@ interface GitHubSyncResponse {
status: string;
}

interface JiraSyncResponse {
status: string;
}

@Injectable({
providedIn: 'root',
})
Expand All @@ -61,7 +56,6 @@ export class IntegrationService {
return this.getSlackAuthUrl(workspaceId).pipe(
map(({ authUrl }) => {
window.open(authUrl, '_blank');
return;
}),
);
}
Expand Down Expand Up @@ -131,7 +125,6 @@ export class IntegrationService {
return this.getGitHubAuthUrl().pipe(
map(({ authUrl }) => {
window.open(authUrl, '_blank');
return;
}),
);
}
Expand Down Expand Up @@ -189,48 +182,38 @@ export class IntegrationService {
);
}

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);
}
getJiraAuthUrl(workspaceId: string): Observable<{ authUrl: string }> {
const params = new HttpParams().set('workspace_id', workspaceId);
return this.http.get<OAuthResponse>(`${this.apiUrl}/jira/auth`, { params }).pipe(
tap(resp => console.log('Jira Auth URL response:', resp)),
map((response) => ({ authUrl: response.auth_url })),
catchError((error) => {
console.error('Jira Auth URL error:', error);
return throwError(() => toError(error, 'Unable to start Jira connection.'));
}),
catchError((error) => throwError(() => toError(error, 'Unable to start Jira connection.'))),
);
}

connectJira(workspaceId: string): Observable<void> {
const redirectUrl = window.location.origin + window.location.pathname;
return this.getJiraAuthUrl(workspaceId, redirectUrl).pipe(
return this.getJiraAuthUrl(workspaceId).pipe(
map(({ authUrl }) => {
window.location.href = authUrl;
return;
window.open(authUrl, '_blank');
}),
);
}

getJiraProjects(workspaceId: string): Observable<JiraIntegrationResponse> {
getJiraProjects(workspaceId: string): Observable<any> {
return this.getIntegrations(workspaceId).pipe(
switchMap((integrations) => {
const jiraIntegration = integrations.find((integration) => integration.provider === 'jira');
if (!jiraIntegration) {
return of({
connected: false,
projects: [],
resources: [],
});
}

const params = new HttpParams().set('workspace_id', workspaceId);
return this.http.get<JiraProject[]>(`${this.apiUrl}/jira/projects`, { params }).pipe(
map((projects) => ({
return this.http.get<any[]>(`${this.apiUrl}/jira/projects`, { params }).pipe(
map((resources) => ({
connected: true,
projects,
resources: resources,
lastSyncAt: jiraIntegration.updated_at ? new Date(jiraIntegration.updated_at) : undefined,
})),
);
Expand All @@ -246,13 +229,13 @@ export class IntegrationService {
);
}

syncJira(workspaceId: string): Observable<JiraSyncStatus> {
syncJira(workspaceId: string): Observable<any> {
const params = new HttpParams().set('workspace_id', workspaceId);
return this.http.post<JiraSyncResponse>(`${this.apiUrl}/jira/sync`, {}, { params }).pipe(
return this.http.post<any>(`${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.'))),
);
Expand Down
20 changes: 0 additions & 20 deletions src/app/services/signal.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,9 @@ 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;
}
Expand Down Expand Up @@ -98,19 +91,6 @@ 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 ?? {}),
};
Expand Down
Loading