Skip to content

Commit

Permalink
Merge pull request #6984 from ever-co/fix/#6975-github-sync
Browse files Browse the repository at this point in the history
Fix :: #6975 GitHub Sync (Tasks/Labels)
  • Loading branch information
rahul-rocket authored Oct 13, 2023
2 parents 2dbcf98 + 7003460 commit c9bee0d
Show file tree
Hide file tree
Showing 15 changed files with 258 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Subject, of } from 'rxjs';
import { catchError, finalize, map, tap } from 'rxjs/operators';
import { Observable } from 'rxjs/internal/Observable';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { IGithubRepository, IGithubRepositoryResponse, IIntegrationTenant, IOrganization } from '@gauzy/contracts';
import { HttpStatus, IGithubRepository, IGithubRepositoryResponse, IIntegrationTenant, IOrganization } from '@gauzy/contracts';
import { ErrorHandlingService, GithubService, Store } from './../../../../@core/services';

@UntilDestroy({ checkProperties: true })
Expand Down Expand Up @@ -124,6 +124,11 @@ export class RepositorySelectorComponent implements AfterViewInit, OnInit, OnDes
organizationId,
tenantId
}).pipe(
tap((response: IGithubRepositoryResponse) => {
if (response['status'] == HttpStatus.INTERNAL_SERVER_ERROR) {
throw new Error(`${response['message']}`);
}
}),
map(({ repositories }: IGithubRepositoryResponse) => repositories),
// Update component state with fetched repositories
tap((repositories: IGithubRepository[]) => {
Expand Down
10 changes: 5 additions & 5 deletions apps/gauzy/src/app/@shared/pipes/hash-number.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { Pipe, PipeTransform } from '@angular/core';
name: 'hash'
})
export class HashNumberPipe implements PipeTransform {
transform(value: number): string {
if (typeof value === 'number') {
return '#' + value;
} else {
return value; // Return an empty string for non-numeric values
transform(value: number | string): string {
if (value) {
const numericValue = isNaN(Number(value)) ? value : Number(value);
return '#' + numericValue;
}
return ''; // Return an empty string for non-numeric or falsy values
}
}
18 changes: 18 additions & 0 deletions apps/gauzy/src/app/@theme/styles/_overrides.scss
Original file line number Diff line number Diff line change
Expand Up @@ -690,3 +690,21 @@ nb-option-list.fit-content {
width: fit-content !important;
min-width: var(--select-min-width);
}

/* Apply style on spinner */
.sync-container {
.sync {
cursor: pointer;
&.spin {
animation: rotate 1s linear infinite;
}
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,18 @@ <h5>{{ 'INTEGRATIONS.GITHUB_PAGE.NAME' | translate }}</h5>
<nb-card-body>
<ng-template [ngIf]="integration$ | async">
<div class="mb-3">
<button [disabled]="selectedIssues.length == 0" nbButton status="primary" class="mr-2" (click)="syncIssues()">
<nb-icon class="mr-1" icon="edit-outline"></nb-icon>
{{ 'BUTTONS.SYNC' | translate }}
<button
[disabled]="selectedIssues.length == 0"
nbButton
status="primary"
class="mr-2"
debounceClick
(throttledClick)="syncIssues()"
>
<div class="sync-container">
<nb-icon class="sync" icon="sync-outline" [ngClass]="{ 'spin' : syncing }"></nb-icon>
{{ (syncing ? 'BUTTONS.SYNCING' : 'BUTTONS.SYNC') | translate }}
</div>
</button>
</div>
<div class="issues-container">
Expand All @@ -31,6 +40,7 @@ <h5>{{ 'INTEGRATIONS.GITHUB_PAGE.NAME' | translate }}</h5>
[source]="issues$ | async"
(userRowSelect)="selectIssues($event)"
style="cursor: pointer;"
#issuesTable
></ng2-smart-table>
</div>
</ng-template>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AfterViewInit, Component, OnInit } from '@angular/core';
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
import { TitleCasePipe } from '@angular/common';
import { ActivatedRoute, Data } from '@angular/router';
import { debounceTime, firstValueFrom, of } from 'rxjs';
import { debounceTime, finalize, firstValueFrom, of } from 'rxjs';
import { Observable } from 'rxjs/internal/Observable';
import { catchError, filter, map, switchMap, tap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { NbDialogService, NbMenuItem, NbMenuService } from '@nebular/theme';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Ng2SmartTableComponent } from 'ng2-smart-table';
import {
IEntitySettingToSync,
IGithubIssue,
Expand Down Expand Up @@ -45,6 +46,7 @@ import { GithubSettingsDialogComponent } from '../settings-dialog/settings-dialo
export class GithubViewComponent extends TranslationBaseComponent implements AfterViewInit, OnInit {
public parsedInt = parsedInt;

public syncing: boolean = false;
public loading: boolean = false;
public user: IUser = this._store.user;
public organization: IOrganization = this._store.selectedOrganization;
Expand All @@ -59,6 +61,18 @@ export class GithubViewComponent extends TranslationBaseComponent implements Aft
public issues: IGithubIssue[] = [];
public selectedIssues: IGithubIssue[] = [];

/**
* Sets up a property 'issuesTable' to reference an instance of 'Ng2SmartTableComponent'
* when the child component with the template reference variable 'issuesTable' is rendered.
* This allows interaction with the child component from the parent component.
*/
private _issuesTable: Ng2SmartTableComponent;
@ViewChild('issuesTable') set content(content: Ng2SmartTableComponent) {
if (content) {
this._issuesTable = content;
}
}

constructor(
public readonly _translateService: TranslateService,
private readonly _activatedRoute: ActivatedRoute,
Expand Down Expand Up @@ -349,6 +363,12 @@ export class GithubViewComponent extends TranslationBaseComponent implements Aft
if (!this.organization || !this.repository) {
return;
}
// Check if another synchronization is already in progress
if (this.syncing) {
return;
}

this.syncing = true;

const { id: organizationId, tenantId } = this.organization;
const { id: integrationId } = this.integration;
Expand All @@ -372,17 +392,19 @@ export class GithubViewComponent extends TranslationBaseComponent implements Aft
tap(() => {
this._toastrService.success(
this.getTranslation('INTEGRATIONS.GITHUB_PAGE.SYNCED_ISSUES', {
repository: this.repository.name
repository: this.repository.full_name
}),
this.getTranslation('TOASTR.TITLE.SUCCESS')
);
this.resetTableSelectedItems();
}),
catchError((error) => {
// Handle and log errors
console.error('Error while syncing GitHub issues & labels:', error.message);
this._errorHandlingService.handleError(error);
return of(null);
}),
finalize(() => this.syncing = false),
untilDestroyed(this) // Ensure subscription is cleaned up on component destroy
).subscribe();
} catch (error) {
Expand All @@ -393,4 +415,17 @@ export class GithubViewComponent extends TranslationBaseComponent implements Aft
this._errorHandlingService.handleError(error);
}
}

/**
* Clears selected items in the table component and resets the 'selectedIssues' array.
*/
resetTableSelectedItems() {
if (this._issuesTable && this._issuesTable.grid) {
// Deselect all items in the table
this._issuesTable.grid.dataSet.deselectAll();

// Clear the 'selectedIssues' array
this.selectedIssues = [];
}
}
}
2 changes: 2 additions & 0 deletions apps/gauzy/src/app/pages/integrations/github/github.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { GithubInstallationComponent } from './components/installation/installat
import { GithubViewComponent } from './components/view/view.component';
import { GithubSettingsDialogComponent } from './components/settings-dialog/settings-dialog.component';
import { RepositorySelectorModule } from '../../../@shared/integrations/github';
import { DirectivesModule } from '../../../@shared/directives/directives.module';

@NgModule({
declarations: [
Expand All @@ -42,6 +43,7 @@ import { RepositorySelectorModule } from '../../../@shared/integrations/github';
NgSelectModule,
GithubRoutingModule,
TranslateModule,
DirectivesModule,
BackNavigationModule,
RepositorySelectorModule
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,6 @@
.registration-form-group {
display: inline-flex;
flex-direction: column;

& #registrationDate {
width: 175px;
}
}

::ng-deep nb-select.shape-rectangle .select-button {
Expand Down Expand Up @@ -204,4 +200,4 @@
.main-form .actions {
margin-top: 20px;
}
}
}
39 changes: 21 additions & 18 deletions apps/gauzy/src/app/pages/tasks/components/task/task.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from '@gauzy/contracts';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { distinctUntilChange } from '@gauzy/common-angular';
import { HashNumberPipe } from './../../../../@shared/pipes';
import { DeleteConfirmationComponent } from '../../../../@shared/user/forms';
import { MyTaskDialogComponent } from './../my-task-dialog/my-task-dialog.component';
import { TeamTaskDialogComponent } from '../team-task-dialog/team-task-dialog.component';
Expand Down Expand Up @@ -57,10 +58,8 @@ import {
templateUrl: './task.component.html',
styleUrls: ['task.component.scss'],
})
export class TaskComponent
extends PaginationFilterBaseComponent
implements OnInit, OnDestroy
{
export class TaskComponent extends PaginationFilterBaseComponent implements OnInit, OnDestroy {

private _refresh$: Subject<boolean> = new Subject();
private _tasks: ITask[] = [];
settingsSmartTable: object;
Expand Down Expand Up @@ -96,7 +95,8 @@ export class TaskComponent
private readonly _store: Store,
private readonly route: ActivatedRoute,
private readonly httpClient: HttpClient,
private readonly _errorHandlingService: ErrorHandlingService
private readonly _errorHandlingService: ErrorHandlingService,
private readonly _hashNumberPipe: HashNumberPipe
) {
super(translateService);
this.initTasks();
Expand Down Expand Up @@ -182,6 +182,9 @@ export class TaskComponent
filterFunction: (prefix: string) => {
this.setFilter({ field: 'prefix', search: prefix });
},
valuePrepareFunction: (data: number) => {
return this._hashNumberPipe.transform(data);
}
},
description: {
title: this.getTranslation('TASKS_PAGE.TASKS_TITLE'),
Expand Down Expand Up @@ -447,20 +450,20 @@ export class TaskComponent
tenantId,
...(this.selectedProject && this.selectedProject.id
? {
...(this.viewMode === TaskListTypeEnum.SPRINT
? {
organizationSprintId: null,
}
: {}),
projectId: this.selectedProject.id,
}
...(this.viewMode === TaskListTypeEnum.SPRINT
? {
organizationSprintId: null,
}
: {}),
projectId: this.selectedProject.id,
}
: {}),
...(this.selectedEmployeeId
? {
members: {
id: this.selectedEmployeeId,
},
}
members: {
id: this.selectedEmployeeId,
},
}
: {}),
...(this.filters.where ? this.filters.where : {}),
},
Expand All @@ -473,7 +476,7 @@ export class TaskComponent
},
finalize: () => {
this.dataLayoutStyle ===
this.componentLayoutStyleEnum.CARDS_GRID
this.componentLayoutStyleEnum.CARDS_GRID
? this._tasks.push(...this.smartTableSource.getData())
: (this._tasks = this.smartTableSource.getData());
this.storeInstance.loadAllTasks(this._tasks);
Expand Down Expand Up @@ -760,5 +763,5 @@ export class TaskComponent
}
}

ngOnDestroy(): void {}
ngOnDestroy(): void { }
}
3 changes: 2 additions & 1 deletion apps/gauzy/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"REQUEST": "Request",
"HISTORY": "History",
"SYNC": "Sync",
"SYNCING": "Syncing...",
"UPDATE": "Update",
"AUTO_SYNC": "Auto sync",
"VIEW": "View",
Expand Down Expand Up @@ -1218,7 +1219,7 @@
"NAME": "GitHub",
"SELECT_REPOSITORY": "Select repository",
"SEARCH_REPOSITORY": "Type to search repository",
"SYNCED_ISSUES": "{{repository}} issues & labels synced successfully"
"SYNCED_ISSUES": "'{{repository}}' issues & labels synced successfully"
},
"COMING_SOON": "Coming soon",
"RE_INTEGRATE": "Re-integrate",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CommandHandler, ICommandHandler, CommandBus } from '@nestjs/cqrs';
import { IIntegrationMap, IntegrationEntity } from '@gauzy/contracts';
import { RequestContext } from 'core/context';
import { TaskService } from 'tasks/task.service';
import { TaskCreateCommand, TaskUpdateCommand } from 'tasks/commands';
import { IntegrationMapSyncEntityCommand } from './../integration-map.sync-entity.command';
import { IntegrationMapSyncIssueCommand } from './../integration-map.sync-issue.command';
Expand All @@ -11,7 +12,8 @@ export class IntegrationMapSyncIssueHandler implements ICommandHandler<Integrati

constructor(
private readonly _commandBus: CommandBus,
private readonly _integrationMapService: IntegrationMapService
private readonly _integrationMapService: IntegrationMapService,
private readonly _taskService: TaskService,
) { }

/**
Expand All @@ -34,25 +36,43 @@ export class IntegrationMapSyncIssueHandler implements ICommandHandler<Integrati
organizationId,
tenantId
});
// Update the corresponding task with the new input data
await this._commandBus.execute(
new TaskUpdateCommand(integrationMap.gauzyId, entity)
);
// Try to find the corresponding task
try {
await this._taskService.findOneByIdString(integrationMap.gauzyId);
// Update the corresponding task with the new input data
await this._commandBus.execute(
new TaskUpdateCommand(integrationMap.gauzyId, entity)
);
} catch (error) {
console.log(`${IntegrationEntity.TASK} Not Found for integration GauzyID %s: `, integrationMap.gauzyId);
// Create a corresponding task with the new input data
await this._commandBus.execute(
new TaskCreateCommand({
id: integrationMap.gauzyId,
...entity
})
);
}
// Return the integration map
return integrationMap;
} catch (error) {
// Create a new task and update the integration map for the issue
// Handle errors and create a new task
// Create a new task with the provided entity data
const task = await this._commandBus.execute(
new TaskCreateCommand(entity)
);
return await this._commandBus.execute(new IntegrationMapSyncEntityCommand({
gauzyId: task.id,
entity: IntegrationEntity.ISSUE,
integrationId,
sourceId,
organizationId,
tenantId
}));

// Create a new integration map for the issue
return await this._commandBus.execute(
new IntegrationMapSyncEntityCommand({
gauzyId: task.id,
entity: IntegrationEntity.ISSUE,
integrationId,
sourceId,
organizationId,
tenantId
})
);
}
}
}
Loading

0 comments on commit c9bee0d

Please sign in to comment.