From 5f0e06589c7d68d4f4f1abce6a0561b6b724c71e Mon Sep 17 00:00:00 2001 From: Badal Khatri Date: Wed, 23 Aug 2023 20:08:45 +0530 Subject: [PATCH 001/104] WIP: GitHub Integration --- packages/core/src/app.module.ts | 40 +++++----- .../core/src/octokit/octokit.controller.ts | 37 +++++++++ packages/core/src/octokit/octokit.module.ts | 15 ++++ packages/core/src/octokit/octokit.service.ts | 76 +++++++++++++++++++ .../commands/handlers/task-create.handler.ts | 28 ++++--- .../commands/handlers/task-update.handler.ts | 35 +++++---- packages/core/src/tasks/task.module.ts | 2 + 7 files changed, 191 insertions(+), 42 deletions(-) create mode 100644 packages/core/src/octokit/octokit.controller.ts create mode 100644 packages/core/src/octokit/octokit.module.ts create mode 100644 packages/core/src/octokit/octokit.service.ts diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index 5e05f1de783..9a63302706d 100644 --- a/packages/core/src/app.module.ts +++ b/packages/core/src/app.module.ts @@ -154,6 +154,7 @@ import { EmailResetModule } from './email-reset/email-reset.module'; import { TaskLinkedIssueModule } from './tasks/linked-issue/task-linked-issue.module'; import { OrganizationTaskSettingModule } from './organization-task-setting/organization-task-setting.module'; import { TaskEstimationModule } from './tasks/estimation/task-estimation.module'; +import { OctokitModule } from 'octokit/octokit.module'; const { unleashConfig } = environment; if (unleashConfig.url) { @@ -235,29 +236,29 @@ if (environment.sentry && environment.sentry.dsn) { }), ...(environment.sentry && environment.sentry.dsn ? [ - SentryModule.forRoot({ - dsn: environment.sentry.dsn, - debug: !environment.production, - environment: environment.production - ? 'production' - : 'development', - // TODO: we should use some internal function which returns version of Gauzy - release: 'gauzy@' + process.env.npm_package_version, - logLevels: ['error'], - integrations: sentryIntegrations, - tracesSampleRate: process.env.SENTRY_TRACES_SAMPLE_RATE - ? parseInt(process.env.SENTRY_TRACES_SAMPLE_RATE) - : 0.01, - }), - ] + SentryModule.forRoot({ + dsn: environment.sentry.dsn, + debug: !environment.production, + environment: environment.production + ? 'production' + : 'development', + // TODO: we should use some internal function which returns version of Gauzy + release: 'gauzy@' + process.env.npm_package_version, + logLevels: ['error'], + integrations: sentryIntegrations, + tracesSampleRate: process.env.SENTRY_TRACES_SAMPLE_RATE + ? parseInt(process.env.SENTRY_TRACES_SAMPLE_RATE) + : 0.01, + }), + ] : []), ThrottlerModule.forRootAsync({ inject: [ConfigService], useFactory: (config: ConfigService): ThrottlerModuleOptions => - ({ - ttl: config.get('THROTTLE_TTL'), - limit: config.get('THROTTLE_LIMIT'), - } as ThrottlerModuleOptions), + ({ + ttl: config.get('THROTTLE_TTL'), + limit: config.get('THROTTLE_LIMIT'), + } as ThrottlerModuleOptions), }), CoreModule, AuthModule, @@ -386,6 +387,7 @@ if (environment.sentry && environment.sentry.dsn) { TaskLinkedIssueModule, OrganizationTaskSettingModule, TaskEstimationModule, + OctokitModule, ], controllers: [AppController], providers: [ diff --git a/packages/core/src/octokit/octokit.controller.ts b/packages/core/src/octokit/octokit.controller.ts new file mode 100644 index 00000000000..f684575e761 --- /dev/null +++ b/packages/core/src/octokit/octokit.controller.ts @@ -0,0 +1,37 @@ +import { Public } from '@gauzy/common'; +import { Body, Controller, Get, Post, Req } from '@nestjs/common'; +import { Request } from 'express'; +import { OctokitService } from './octokit.service'; + +// TODO: +// For now API route is public +// We have to make a guard that validate webhook secret from payload +@Controller() +@Public() +export class OctokitController { + constructor(private readonly _octokitService: OctokitService) {} + + @Get() + async testGet() { + return { hello: 'world' }; + } + + @Post() + async webhook(@Req() request: Request, @Body() body: any) { + // body contains whole payload that webhook send on different event + console.log(body); + + const event = request.headers['x-github-event']; + const action = body.action; + + // Based on event & action we can decide further processing + + if (event === 'issues' && action === 'opened') { + // TODO + } + if (event === 'issues' && action === 'edited') { + // TODO + } + // ... + } +} diff --git a/packages/core/src/octokit/octokit.module.ts b/packages/core/src/octokit/octokit.module.ts new file mode 100644 index 00000000000..cdb4d14aefb --- /dev/null +++ b/packages/core/src/octokit/octokit.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common'; +import { OctokitController } from './octokit.controller'; +import { OctokitService } from './octokit.service'; +import { RouterModule } from 'nest-router'; + +@Module({ + imports: [ + RouterModule.forRoutes([{ path: '/octokit', module: OctokitModule }]), + ], + + controllers: [OctokitController], + providers: [OctokitService], + exports: [OctokitService], +}) +export class OctokitModule {} diff --git a/packages/core/src/octokit/octokit.service.ts b/packages/core/src/octokit/octokit.service.ts new file mode 100644 index 00000000000..fbc4f2da1c4 --- /dev/null +++ b/packages/core/src/octokit/octokit.service.ts @@ -0,0 +1,76 @@ +import { Injectable } from '@nestjs/common'; +import { App } from 'octokit'; + +@Injectable() +export class OctokitService { + constructor() {} + + async createIssue(title: string, body: string) { + // TODO: + // Dynamic ENV variable + const app = new App({ + appId: '', + privateKey: '', + }); + // TODO: + // Need to store user's installationId in DB and make param dynamic + const octokit = await app.getInstallationOctokit(123456); + + octokit + .request('POST /repos/{owner}/{repo}/issues', { + // TODO: + // pass dynamic values as required + // Add all the fields that we have + + owner: 'badal-ever', + repo: 'testing-gauzy-teams-integration', + + title, + body, + // labels: ['bug', 'GauzyAPI'], + + headers: { + 'X-GitHub-Api-Version': '2022-11-28', + }, + }) + .then((data) => { + console.log('data', data); + }) + .catch((error) => { + console.log('error', error); + }); + } + async updateIssue(id: number, title: string, body: string) { + // TODO: + // Dynamic ENV variable + const app = new App({ + appId: '', + privateKey: '', + }); + // TODO: + // Need to store user's installationId in DB and make param dynamic + const octokit = await app.getInstallationOctokit(123456); + + octokit + .request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', { + // TODO: pass dynamic values as required + owner: 'badal-ever', + repo: 'testing-gauzy-teams-integration', + + issue_number: id, + title, + body, + // labels: ['bug', 'GauzyAPI'], + + headers: { + 'X-GitHub-Api-Version': '2022-11-28', + }, + }) + .then((data) => { + console.log('data', data); + }) + .catch((error) => { + console.log('error', error); + }); + } +} diff --git a/packages/core/src/tasks/commands/handlers/task-create.handler.ts b/packages/core/src/tasks/commands/handlers/task-create.handler.ts index 6acf1bb10d4..47657a86750 100644 --- a/packages/core/src/tasks/commands/handlers/task-create.handler.ts +++ b/packages/core/src/tasks/commands/handlers/task-create.handler.ts @@ -5,14 +5,15 @@ import { RequestContext } from './../../../core/context'; import { TaskCreateCommand } from './../task-create.command'; import { OrganizationProjectService } from './../../../organization-project/organization-project.service'; import { TaskService } from '../../task.service'; +import { OctokitService } from 'octokit/octokit.service'; @CommandHandler(TaskCreateCommand) export class TaskCreateHandler implements ICommandHandler { - constructor( private readonly _taskService: TaskService, - private readonly _organizationProjectService: OrganizationProjectService - ) { } + private readonly _organizationProjectService: OrganizationProjectService, + private readonly _octokitService: OctokitService + ) {} public async execute(command: TaskCreateCommand): Promise { try { @@ -24,16 +25,23 @@ export class TaskCreateHandler implements ICommandHandler { /** If project found then use project name as a task prefix */ if (input.projectId) { const { projectId } = input; - project = await this._organizationProjectService.findOneByIdString(projectId); + project = + await this._organizationProjectService.findOneByIdString( + projectId + ); } - const projectId = (project) ? project.id : null; - const taskPrefix = (project) ? project.name.substring(0, 3) : null; + const projectId = project ? project.id : null; + const taskPrefix = project ? project.name.substring(0, 3) : null; - const maxNumber = await this._taskService.getMaxTaskNumberByProject({ - organizationId, - projectId - }); + const maxNumber = await this._taskService.getMaxTaskNumberByProject( + { + organizationId, + projectId, + } + ); + + this._octokitService.createIssue(input.title, input.description); return await this._taskService.create({ ...input, diff --git a/packages/core/src/tasks/commands/handlers/task-update.handler.ts b/packages/core/src/tasks/commands/handlers/task-update.handler.ts index b8089f24569..13b19336bdc 100644 --- a/packages/core/src/tasks/commands/handlers/task-update.handler.ts +++ b/packages/core/src/tasks/commands/handlers/task-update.handler.ts @@ -3,13 +3,14 @@ import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { ITask, ITaskUpdateInput } from '@gauzy/contracts'; import { TaskService } from '../../task.service'; import { TaskUpdateCommand } from '../task-update.command'; +import { OctokitService } from 'octokit/octokit.service'; @CommandHandler(TaskUpdateCommand) export class TaskUpdateHandler implements ICommandHandler { - constructor( - private readonly _taskService: TaskService - ) { } + private readonly _taskService: TaskService, + private readonly _octokitService: OctokitService + ) {} public async execute(command: TaskUpdateCommand): Promise { const { id, input } = command; @@ -23,10 +24,7 @@ export class TaskUpdateHandler implements ICommandHandler { * @param request * @returns */ - public async update( - id: string, - request: ITaskUpdateInput - ): Promise { + public async update(id: string, request: ITaskUpdateInput): Promise { try { const task = await this._taskService.findOneByIdString(id); @@ -37,19 +35,30 @@ export class TaskUpdateHandler implements ICommandHandler { */ if (projectId !== task.projectId) { const { organizationId } = task; - const maxNumber = await this._taskService.getMaxTaskNumberByProject({ - organizationId, - projectId - }); + const maxNumber = + await this._taskService.getMaxTaskNumberByProject({ + organizationId, + projectId, + }); await this._taskService.update(id, { projectId, - number: maxNumber + 1 + number: maxNumber + 1, }); } } + + // TODO: + // We have to store issue_number of github in our task, so that we can use it while sync + // Right now we we have put static 38 value. + this._octokitService.updateIssue( + 38, + request.title, + request.description + ); + return await this._taskService.create({ ...request, - id + id, }); } catch (error) { console.log('Error while updating task', error?.message); diff --git a/packages/core/src/tasks/task.module.ts b/packages/core/src/tasks/task.module.ts index 517851b62f4..9eb29ca7479 100644 --- a/packages/core/src/tasks/task.module.ts +++ b/packages/core/src/tasks/task.module.ts @@ -11,6 +11,7 @@ import { TenantModule } from '../tenant/tenant.module'; import { UserModule } from './../user/user.module'; import { RoleModule } from './../role/role.module'; import { EmployeeModule } from './../employee/employee.module'; +import { OctokitModule } from 'octokit/octokit.module'; @Module({ imports: [ @@ -22,6 +23,7 @@ import { EmployeeModule } from './../employee/employee.module'; EmployeeModule, OrganizationProjectModule, CqrsModule, + OctokitModule, ], controllers: [TaskController], providers: [TaskService, ...CommandHandlers], From 110ccb82a46ed2d86aa929788396ad34883f4f14 Mon Sep 17 00:00:00 2001 From: Badal Khatri Date: Wed, 23 Aug 2023 20:08:45 +0530 Subject: [PATCH 002/104] WIP: GitHub Integration --- packages/core/src/app.module.ts | 40 +++++----- .../core/src/octokit/octokit.controller.ts | 37 +++++++++ packages/core/src/octokit/octokit.module.ts | 15 ++++ packages/core/src/octokit/octokit.service.ts | 76 +++++++++++++++++++ .../commands/handlers/task-create.handler.ts | 28 ++++--- .../commands/handlers/task-update.handler.ts | 35 +++++---- packages/core/src/tasks/task.module.ts | 2 + 7 files changed, 191 insertions(+), 42 deletions(-) create mode 100644 packages/core/src/octokit/octokit.controller.ts create mode 100644 packages/core/src/octokit/octokit.module.ts create mode 100644 packages/core/src/octokit/octokit.service.ts diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index 5e05f1de783..9a63302706d 100644 --- a/packages/core/src/app.module.ts +++ b/packages/core/src/app.module.ts @@ -154,6 +154,7 @@ import { EmailResetModule } from './email-reset/email-reset.module'; import { TaskLinkedIssueModule } from './tasks/linked-issue/task-linked-issue.module'; import { OrganizationTaskSettingModule } from './organization-task-setting/organization-task-setting.module'; import { TaskEstimationModule } from './tasks/estimation/task-estimation.module'; +import { OctokitModule } from 'octokit/octokit.module'; const { unleashConfig } = environment; if (unleashConfig.url) { @@ -235,29 +236,29 @@ if (environment.sentry && environment.sentry.dsn) { }), ...(environment.sentry && environment.sentry.dsn ? [ - SentryModule.forRoot({ - dsn: environment.sentry.dsn, - debug: !environment.production, - environment: environment.production - ? 'production' - : 'development', - // TODO: we should use some internal function which returns version of Gauzy - release: 'gauzy@' + process.env.npm_package_version, - logLevels: ['error'], - integrations: sentryIntegrations, - tracesSampleRate: process.env.SENTRY_TRACES_SAMPLE_RATE - ? parseInt(process.env.SENTRY_TRACES_SAMPLE_RATE) - : 0.01, - }), - ] + SentryModule.forRoot({ + dsn: environment.sentry.dsn, + debug: !environment.production, + environment: environment.production + ? 'production' + : 'development', + // TODO: we should use some internal function which returns version of Gauzy + release: 'gauzy@' + process.env.npm_package_version, + logLevels: ['error'], + integrations: sentryIntegrations, + tracesSampleRate: process.env.SENTRY_TRACES_SAMPLE_RATE + ? parseInt(process.env.SENTRY_TRACES_SAMPLE_RATE) + : 0.01, + }), + ] : []), ThrottlerModule.forRootAsync({ inject: [ConfigService], useFactory: (config: ConfigService): ThrottlerModuleOptions => - ({ - ttl: config.get('THROTTLE_TTL'), - limit: config.get('THROTTLE_LIMIT'), - } as ThrottlerModuleOptions), + ({ + ttl: config.get('THROTTLE_TTL'), + limit: config.get('THROTTLE_LIMIT'), + } as ThrottlerModuleOptions), }), CoreModule, AuthModule, @@ -386,6 +387,7 @@ if (environment.sentry && environment.sentry.dsn) { TaskLinkedIssueModule, OrganizationTaskSettingModule, TaskEstimationModule, + OctokitModule, ], controllers: [AppController], providers: [ diff --git a/packages/core/src/octokit/octokit.controller.ts b/packages/core/src/octokit/octokit.controller.ts new file mode 100644 index 00000000000..f684575e761 --- /dev/null +++ b/packages/core/src/octokit/octokit.controller.ts @@ -0,0 +1,37 @@ +import { Public } from '@gauzy/common'; +import { Body, Controller, Get, Post, Req } from '@nestjs/common'; +import { Request } from 'express'; +import { OctokitService } from './octokit.service'; + +// TODO: +// For now API route is public +// We have to make a guard that validate webhook secret from payload +@Controller() +@Public() +export class OctokitController { + constructor(private readonly _octokitService: OctokitService) {} + + @Get() + async testGet() { + return { hello: 'world' }; + } + + @Post() + async webhook(@Req() request: Request, @Body() body: any) { + // body contains whole payload that webhook send on different event + console.log(body); + + const event = request.headers['x-github-event']; + const action = body.action; + + // Based on event & action we can decide further processing + + if (event === 'issues' && action === 'opened') { + // TODO + } + if (event === 'issues' && action === 'edited') { + // TODO + } + // ... + } +} diff --git a/packages/core/src/octokit/octokit.module.ts b/packages/core/src/octokit/octokit.module.ts new file mode 100644 index 00000000000..cdb4d14aefb --- /dev/null +++ b/packages/core/src/octokit/octokit.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common'; +import { OctokitController } from './octokit.controller'; +import { OctokitService } from './octokit.service'; +import { RouterModule } from 'nest-router'; + +@Module({ + imports: [ + RouterModule.forRoutes([{ path: '/octokit', module: OctokitModule }]), + ], + + controllers: [OctokitController], + providers: [OctokitService], + exports: [OctokitService], +}) +export class OctokitModule {} diff --git a/packages/core/src/octokit/octokit.service.ts b/packages/core/src/octokit/octokit.service.ts new file mode 100644 index 00000000000..fbc4f2da1c4 --- /dev/null +++ b/packages/core/src/octokit/octokit.service.ts @@ -0,0 +1,76 @@ +import { Injectable } from '@nestjs/common'; +import { App } from 'octokit'; + +@Injectable() +export class OctokitService { + constructor() {} + + async createIssue(title: string, body: string) { + // TODO: + // Dynamic ENV variable + const app = new App({ + appId: '', + privateKey: '', + }); + // TODO: + // Need to store user's installationId in DB and make param dynamic + const octokit = await app.getInstallationOctokit(123456); + + octokit + .request('POST /repos/{owner}/{repo}/issues', { + // TODO: + // pass dynamic values as required + // Add all the fields that we have + + owner: 'badal-ever', + repo: 'testing-gauzy-teams-integration', + + title, + body, + // labels: ['bug', 'GauzyAPI'], + + headers: { + 'X-GitHub-Api-Version': '2022-11-28', + }, + }) + .then((data) => { + console.log('data', data); + }) + .catch((error) => { + console.log('error', error); + }); + } + async updateIssue(id: number, title: string, body: string) { + // TODO: + // Dynamic ENV variable + const app = new App({ + appId: '', + privateKey: '', + }); + // TODO: + // Need to store user's installationId in DB and make param dynamic + const octokit = await app.getInstallationOctokit(123456); + + octokit + .request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', { + // TODO: pass dynamic values as required + owner: 'badal-ever', + repo: 'testing-gauzy-teams-integration', + + issue_number: id, + title, + body, + // labels: ['bug', 'GauzyAPI'], + + headers: { + 'X-GitHub-Api-Version': '2022-11-28', + }, + }) + .then((data) => { + console.log('data', data); + }) + .catch((error) => { + console.log('error', error); + }); + } +} diff --git a/packages/core/src/tasks/commands/handlers/task-create.handler.ts b/packages/core/src/tasks/commands/handlers/task-create.handler.ts index 6acf1bb10d4..47657a86750 100644 --- a/packages/core/src/tasks/commands/handlers/task-create.handler.ts +++ b/packages/core/src/tasks/commands/handlers/task-create.handler.ts @@ -5,14 +5,15 @@ import { RequestContext } from './../../../core/context'; import { TaskCreateCommand } from './../task-create.command'; import { OrganizationProjectService } from './../../../organization-project/organization-project.service'; import { TaskService } from '../../task.service'; +import { OctokitService } from 'octokit/octokit.service'; @CommandHandler(TaskCreateCommand) export class TaskCreateHandler implements ICommandHandler { - constructor( private readonly _taskService: TaskService, - private readonly _organizationProjectService: OrganizationProjectService - ) { } + private readonly _organizationProjectService: OrganizationProjectService, + private readonly _octokitService: OctokitService + ) {} public async execute(command: TaskCreateCommand): Promise { try { @@ -24,16 +25,23 @@ export class TaskCreateHandler implements ICommandHandler { /** If project found then use project name as a task prefix */ if (input.projectId) { const { projectId } = input; - project = await this._organizationProjectService.findOneByIdString(projectId); + project = + await this._organizationProjectService.findOneByIdString( + projectId + ); } - const projectId = (project) ? project.id : null; - const taskPrefix = (project) ? project.name.substring(0, 3) : null; + const projectId = project ? project.id : null; + const taskPrefix = project ? project.name.substring(0, 3) : null; - const maxNumber = await this._taskService.getMaxTaskNumberByProject({ - organizationId, - projectId - }); + const maxNumber = await this._taskService.getMaxTaskNumberByProject( + { + organizationId, + projectId, + } + ); + + this._octokitService.createIssue(input.title, input.description); return await this._taskService.create({ ...input, diff --git a/packages/core/src/tasks/commands/handlers/task-update.handler.ts b/packages/core/src/tasks/commands/handlers/task-update.handler.ts index b8089f24569..13b19336bdc 100644 --- a/packages/core/src/tasks/commands/handlers/task-update.handler.ts +++ b/packages/core/src/tasks/commands/handlers/task-update.handler.ts @@ -3,13 +3,14 @@ import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { ITask, ITaskUpdateInput } from '@gauzy/contracts'; import { TaskService } from '../../task.service'; import { TaskUpdateCommand } from '../task-update.command'; +import { OctokitService } from 'octokit/octokit.service'; @CommandHandler(TaskUpdateCommand) export class TaskUpdateHandler implements ICommandHandler { - constructor( - private readonly _taskService: TaskService - ) { } + private readonly _taskService: TaskService, + private readonly _octokitService: OctokitService + ) {} public async execute(command: TaskUpdateCommand): Promise { const { id, input } = command; @@ -23,10 +24,7 @@ export class TaskUpdateHandler implements ICommandHandler { * @param request * @returns */ - public async update( - id: string, - request: ITaskUpdateInput - ): Promise { + public async update(id: string, request: ITaskUpdateInput): Promise { try { const task = await this._taskService.findOneByIdString(id); @@ -37,19 +35,30 @@ export class TaskUpdateHandler implements ICommandHandler { */ if (projectId !== task.projectId) { const { organizationId } = task; - const maxNumber = await this._taskService.getMaxTaskNumberByProject({ - organizationId, - projectId - }); + const maxNumber = + await this._taskService.getMaxTaskNumberByProject({ + organizationId, + projectId, + }); await this._taskService.update(id, { projectId, - number: maxNumber + 1 + number: maxNumber + 1, }); } } + + // TODO: + // We have to store issue_number of github in our task, so that we can use it while sync + // Right now we we have put static 38 value. + this._octokitService.updateIssue( + 38, + request.title, + request.description + ); + return await this._taskService.create({ ...request, - id + id, }); } catch (error) { console.log('Error while updating task', error?.message); diff --git a/packages/core/src/tasks/task.module.ts b/packages/core/src/tasks/task.module.ts index 517851b62f4..9eb29ca7479 100644 --- a/packages/core/src/tasks/task.module.ts +++ b/packages/core/src/tasks/task.module.ts @@ -11,6 +11,7 @@ import { TenantModule } from '../tenant/tenant.module'; import { UserModule } from './../user/user.module'; import { RoleModule } from './../role/role.module'; import { EmployeeModule } from './../employee/employee.module'; +import { OctokitModule } from 'octokit/octokit.module'; @Module({ imports: [ @@ -22,6 +23,7 @@ import { EmployeeModule } from './../employee/employee.module'; EmployeeModule, OrganizationProjectModule, CqrsModule, + OctokitModule, ], controllers: [TaskController], providers: [TaskService, ...CommandHandlers], From 513aed07fcfd276f0078091ee3cb04690f962a9c Mon Sep 17 00:00:00 2001 From: Badal Khatri Date: Thu, 24 Aug 2023 15:43:27 +0530 Subject: [PATCH 003/104] WIP: Github Integration --- package.json | 6 +- packages/core/src/bootstrap/index.ts | 79 +- .../plugins/integration-github/src/handler.ts | 27 + .../plugins/integration-github/src/index.ts | 42 + yarn.lock | 985 +++++++++++++++++- 5 files changed, 1094 insertions(+), 45 deletions(-) create mode 100644 packages/plugins/integration-github/src/handler.ts diff --git a/package.json b/package.json index f7f8c3683e5..d05a02a1b92 100644 --- a/package.json +++ b/package.json @@ -119,10 +119,12 @@ "build:package:contracts": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/contracts build", "build:package:config": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/config build", "build:package:plugin": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugin build", - "build:package:plugins": "yarn run build:package:plugin:integration-ai && yarn run build:package:plugin:integration-hubstaff && yarn run build:package:plugin:integration-upwork && yarn run build:package:plugin:product-reviews", + "build:package:plugins": "yarn run build:package:plugin:integration-ai && yarn run build:package:plugin:integration-hubstaff && yarn run build:package:plugin:integration-upwork && yarn run build:package:plugin:product-reviews && yarn run build:package:plugin:integration-github && yarn run build:package:plugin:integration-jira", "build:package:plugin:integration-ai": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/integration-ai build", "build:package:plugin:integration-hubstaff": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/integration-hubstaff build", "build:package:plugin:integration-upwork": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/integration-upwork build", + "build:package:plugin:integration-github": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/integration-github build", + "build:package:plugin:integration-jira": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/integration-jira build", "build:package:plugin:product-reviews": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/product-reviews build", "build:package:plugin:knowledge-base": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/knowledge-base build", "build:package:plugin:changelog": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/changelog build", @@ -239,6 +241,8 @@ "ffi-napi": "^4.0.3", "iconv": "^3.0.1", "ng2-smart-table": "^1.7.2", + "octokit": "^3.1.0", + "probot": "^12.3.1", "rxjs": "^7.4.0", "unleash": "^2.0.2", "yargs": "^17.5.0" diff --git a/packages/core/src/bootstrap/index.ts b/packages/core/src/bootstrap/index.ts index 8852931c4eb..a68a822dd89 100644 --- a/packages/core/src/bootstrap/index.ts +++ b/packages/core/src/bootstrap/index.ts @@ -20,6 +20,8 @@ import { AppService } from '../app.service'; import { AppModule } from '../app.module'; import { AuthGuard } from './../shared/guards'; import { SharedModule } from './../shared/shared.module'; +import { createNodeMiddleware, createProbot } from 'probot'; +import type { Probot, Context } from 'probot'; export async function bootstrap( pluginConfig?: Partial @@ -27,9 +29,12 @@ export async function bootstrap( const config = await registerPluginConfig(pluginConfig); const { BootstrapModule } = await import('./bootstrap.module'); - const app = await NestFactory.create(BootstrapModule, { - logger: ['log', 'error', 'warn', 'debug', 'verbose'] - }); + const app = await NestFactory.create( + BootstrapModule, + { + logger: ['log', 'error', 'warn', 'debug', 'verbose'], + } + ); // This will lock all routes and make them accessible by authenticated users only. const reflector = app.get(Reflector); @@ -52,7 +57,7 @@ export async function bootstrap( methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS', credentials: true, allowedHeaders: - 'Authorization, Language, Tenant-Id, X-Requested-With, X-Auth-Token, X-HTTP-Method-Override, Content-Type, Content-Language, Accept, Accept-Language, Observe' + 'Authorization, Language, Tenant-Id, X-Requested-With, X-Auth-Token, X-HTTP-Method-Override, Content-Type, Content-Language, Accept, Accept-Language, Observe', }); // TODO: enable csurf @@ -64,7 +69,7 @@ export async function bootstrap( expressSession({ secret: env.EXPRESS_SESSION_SECRET, resave: true, - saveUninitialized: true + saveUninitialized: true, }) ); @@ -95,15 +100,56 @@ export async function bootstrap( console.log(chalk.green(`Configured Host: ${host}`)); console.log(chalk.green(`Configured Port: ${port}`)); - console.log(chalk.green(`Swagger UI available at http://${host}:${port}/swg`)); + console.log( + chalk.green(`Swagger UI available at http://${host}:${port}/swg`) + ); /** * Dependency injection with class-validator */ useContainer(app.select(SharedModule), { fallbackOnErrors: true }); + const GITHUB_INTEGRATION_APP_ID = 123456; + const GITHUB_INTEGRATION_PRIVATE_KEY = ''; + const GITHUB_INTEGRATION_APP_INSTALLATION_ID = 123456; + const GITHUB_INTEGRATION_CLIENT_ID = ''; + const GITHUB_INTEGRATION_CLIENT_SECRET = ''; + const GITHUB_INTEGRATION_WEBHOOK_SECRET = ''; + + const probot = createProbot({ + defaults: { + appId: GITHUB_INTEGRATION_APP_ID, + privateKey: GITHUB_INTEGRATION_PRIVATE_KEY, + secret: GITHUB_INTEGRATION_WEBHOOK_SECRET, + }, + overrides: { + appId: GITHUB_INTEGRATION_APP_ID, + privateKey: GITHUB_INTEGRATION_PRIVATE_KEY, + secret: GITHUB_INTEGRATION_WEBHOOK_SECRET, + }, + }); + + // Import from config and pass here + app.use( + createNodeMiddleware( + (app: Probot) => { + app.on('issues.edited', async (context) => { + // Here we can have logic to sync with Gauzy API + + console.log('payload', context.payload); + }); + }, + { + probot, + webhooksPath: '/api/github/webhooks', + } + ) + ); + await app.listen(port, host, () => { - console.log(chalk.magenta(`Listening at http://${host}:${port}/${globalPrefix}`)); + console.log( + chalk.magenta(`Listening at http://${host}:${port}/${globalPrefix}`) + ); // Execute Seed For Demo Server if (env.demo) { service.executeDemoSeed(); @@ -128,8 +174,8 @@ export async function registerPluginConfig( */ setConfig({ dbConnectionOptions: { - ...getMigrationsSetting() - } + ...getMigrationsSetting(), + }, }); console.log( @@ -145,8 +191,10 @@ export async function registerPluginConfig( setConfig({ dbConnectionOptions: { entities, - subscribers: coreSubscribers as Array>, - } + subscribers: coreSubscribers as Array< + Type + >, + }, }); let registeredConfig = getConfig(); @@ -165,7 +213,7 @@ export async function registerAllEntities( for (const pluginEntity of pluginEntities) { if (allEntities.find((e) => e.name === pluginEntity.name)) { throw new ConflictException({ - message: `error.${pluginEntity.name} conflict by default entities` + message: `error.${pluginEntity.name} conflict by default entities`, }); } else { allEntities.push(pluginEntity); @@ -180,17 +228,16 @@ export async function registerAllEntities( * @returns */ export function getMigrationsSetting() { - console.log(`Reporting __dirname: ${__dirname}`); //TODO: We need to define some dynamic path here return { migrations: [ // join(__dirname, '../../src/database/migrations/*{.ts,.js}'), - join(__dirname, '../database/migrations/*{.ts,.js}') + join(__dirname, '../database/migrations/*{.ts,.js}'), ], cli: { - migrationsDir: join(__dirname, '../../src/database/migrations') + migrationsDir: join(__dirname, '../../src/database/migrations'), }, - } + }; } diff --git a/packages/plugins/integration-github/src/handler.ts b/packages/plugins/integration-github/src/handler.ts new file mode 100644 index 00000000000..bb992ec9b81 --- /dev/null +++ b/packages/plugins/integration-github/src/handler.ts @@ -0,0 +1,27 @@ +import { createNodeMiddleware, createProbot } from 'probot'; +import app from './index'; + +const GITHUB_INTEGRATION_APP_ID = 123456; +const GITHUB_INTEGRATION_PRIVATE_KEY = ''; +const GITHUB_INTEGRATION_APP_INSTALLATION_ID = 123456; +const GITHUB_INTEGRATION_CLIENT_ID = ''; +const GITHUB_INTEGRATION_CLIENT_SECRET = ''; +const GITHUB_INTEGRATION_WEBHOOK_SECRET = ''; + +const probot = createProbot({ + defaults: { + appId: GITHUB_INTEGRATION_APP_ID, + privateKey: GITHUB_INTEGRATION_PRIVATE_KEY, + secret: GITHUB_INTEGRATION_WEBHOOK_SECRET, + }, + overrides: { + appId: GITHUB_INTEGRATION_APP_ID, + privateKey: GITHUB_INTEGRATION_PRIVATE_KEY, + secret: GITHUB_INTEGRATION_WEBHOOK_SECRET, + }, +}); + +export default createNodeMiddleware(app, { + probot, + webhooksPath: '/api/github/webhooks', +}); diff --git a/packages/plugins/integration-github/src/index.ts b/packages/plugins/integration-github/src/index.ts index e69de29bb2d..b9304a3f91a 100644 --- a/packages/plugins/integration-github/src/index.ts +++ b/packages/plugins/integration-github/src/index.ts @@ -0,0 +1,42 @@ +import type { Probot, Context } from 'probot'; + +export default (app: Probot) => { + app.log('Yay! The app was loaded!'); + + app.on('issues.opened', async (context) => { + // Here we can have logic to sync with Gauzy API + + console.log('context.isBot', context.payload); + + return context.octokit.issues.createComment( + context.issue({ + body: `Hi @${context.payload.sender.login}, Thank you for opening this Issue `, + }) + ); + }); + + app.on('issues.closed', async (context: Context) => { + // Here we can have logic to sync with Gauzy API + + return context.octokit.issues.createComment( + context.issue({ body: 'Closed!' }) + ); + }); + + app.on('issue_comment.created', async (context: Context) => { + // Here we can have logic to sync with Gauzy API + + // context.payload is actually webhook payload + console.log(context.payload); + + if (context.isBot) { + // This condition will help us to idenity if request was done by bot + // and using it we can prevent infinity loop + return; + } + + return context.octokit.issues.createComment( + context.issue({ body: 'Again commented!' }) + ); + }); +}; diff --git a/yarn.lock b/yarn.lock index 65bb3cc9d3d..6a8b1e05297 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3067,6 +3067,11 @@ dependencies: "@hapi/hoek" "9.x.x" +"@hapi/bourne@^2.0.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-2.1.0.tgz#66aff77094dc3080bd5df44ec63881f2676eb020" + integrity sha512-i1BpaNDVLJdRBEKeJWkVO6tYX6DMFBuwMhSuWqLsY4ufeTKGVuV5rBsUhxPayXqnnWHgXUAmWK16H/ykO5Wj4Q== + "@hapi/hoek@9.x.x": version "9.3.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" @@ -5833,6 +5838,119 @@ resolved "https://registry.yarnpkg.com/@oclif/screen/-/screen-1.0.4.tgz#b740f68609dfae8aa71c3a6cab15d816407ba493" integrity sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw== +"@octokit/app@^14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@octokit/app/-/app-14.0.0.tgz#cee2edc1fea38af4115261a7b5ffb1e7cb14a0a6" + integrity sha512-g/zDXttroZ9Se08shK0d0d/j0cgSA+h4WV7qGUevNEM0piNBkIlfb4Fm6bSwCNAZhNf72mBgERmYOoxicPkqdw== + dependencies: + "@octokit/auth-app" "^6.0.0" + "@octokit/auth-unauthenticated" "^5.0.0" + "@octokit/core" "^5.0.0" + "@octokit/oauth-app" "^6.0.0" + "@octokit/plugin-paginate-rest" "^8.0.0" + "@octokit/types" "^11.1.0" + "@octokit/webhooks" "^12.0.1" + +"@octokit/auth-app@^4.0.2": + version "4.0.13" + resolved "https://registry.yarnpkg.com/@octokit/auth-app/-/auth-app-4.0.13.tgz#53323bee6bfefbb73ea544dd8e6a0144550e13e3" + integrity sha512-NBQkmR/Zsc+8fWcVIFrwDgNXS7f4XDrkd9LHdi9DPQw1NdGHLviLzRO2ZBwTtepnwHXW5VTrVU9eFGijMUqllg== + dependencies: + "@octokit/auth-oauth-app" "^5.0.0" + "@octokit/auth-oauth-user" "^2.0.0" + "@octokit/request" "^6.0.0" + "@octokit/request-error" "^3.0.0" + "@octokit/types" "^9.0.0" + deprecation "^2.3.1" + lru-cache "^9.0.0" + universal-github-app-jwt "^1.1.1" + universal-user-agent "^6.0.0" + +"@octokit/auth-app@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-app/-/auth-app-6.0.0.tgz#f7d31d40f78973cb801c0661db2cc3719de7328b" + integrity sha512-OKct7Rukf3g9DjpzcpdacQsdmd6oPrJ7fZND22JkjzhDvfhttUOnmh+qPS4kHhaNNyTxqSThnfrUWvkqNLd1nw== + dependencies: + "@octokit/auth-oauth-app" "^7.0.0" + "@octokit/auth-oauth-user" "^4.0.0" + "@octokit/request" "^8.0.2" + "@octokit/request-error" "^5.0.0" + "@octokit/types" "^11.0.0" + deprecation "^2.3.1" + lru-cache "^10.0.0" + universal-github-app-jwt "^1.1.1" + universal-user-agent "^6.0.0" + +"@octokit/auth-oauth-app@^5.0.0": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-app/-/auth-oauth-app-5.0.6.tgz#e5f922623eb261485efc87f5d0d5b509c71caec8" + integrity sha512-SxyfIBfeFcWd9Z/m1xa4LENTQ3l1y6Nrg31k2Dcb1jS5ov7pmwMJZ6OGX8q3K9slRgVpeAjNA1ipOAMHkieqyw== + dependencies: + "@octokit/auth-oauth-device" "^4.0.0" + "@octokit/auth-oauth-user" "^2.0.0" + "@octokit/request" "^6.0.0" + "@octokit/types" "^9.0.0" + "@types/btoa-lite" "^1.0.0" + btoa-lite "^1.0.0" + universal-user-agent "^6.0.0" + +"@octokit/auth-oauth-app@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-app/-/auth-oauth-app-7.0.0.tgz#864f58152b060132098356265eb4fb07ca1fae76" + integrity sha512-8JvJEXGoEqrbzLwt3SwIUvkDd+1wrM8up0KawvDIElB8rbxPbvWppGO0SLKAWSJ0q8ILcVq+mWck6pDcZ3a9KA== + dependencies: + "@octokit/auth-oauth-device" "^6.0.0" + "@octokit/auth-oauth-user" "^4.0.0" + "@octokit/request" "^8.0.2" + "@octokit/types" "^11.0.0" + "@types/btoa-lite" "^1.0.0" + btoa-lite "^1.0.0" + universal-user-agent "^6.0.0" + +"@octokit/auth-oauth-device@^4.0.0": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-device/-/auth-oauth-device-4.0.5.tgz#21e981f51ae63d419ca3db0b75e32c85b33fa0da" + integrity sha512-XyhoWRTzf2ZX0aZ52a6Ew5S5VBAfwwx1QnC2Np6Et3MWQpZjlREIcbcvVZtkNuXp6Z9EeiSLSDUqm3C+aMEHzQ== + dependencies: + "@octokit/oauth-methods" "^2.0.0" + "@octokit/request" "^6.0.0" + "@octokit/types" "^9.0.0" + universal-user-agent "^6.0.0" + +"@octokit/auth-oauth-device@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-device/-/auth-oauth-device-6.0.0.tgz#728143108345e07e06fd5bfec8891e838c3dce96" + integrity sha512-Zgf/LKhwWk54rJaTGYVYtbKgUty+ouil6VQeRd+pCw7Gd0ECoSWaZuHK6uDGC/HtnWHjpSWFhzxPauDoHcNRtg== + dependencies: + "@octokit/oauth-methods" "^4.0.0" + "@octokit/request" "^8.0.0" + "@octokit/types" "^11.0.0" + universal-user-agent "^6.0.0" + +"@octokit/auth-oauth-user@^2.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-user/-/auth-oauth-user-2.1.2.tgz#7091e1b29527e577b16d0f1699d49fe3d39946ff" + integrity sha512-kkRqNmFe7s5GQcojE3nSlF+AzYPpPv7kvP/xYEnE57584pixaFBH8Vovt+w5Y3E4zWUEOxjdLItmBTFAWECPAg== + dependencies: + "@octokit/auth-oauth-device" "^4.0.0" + "@octokit/oauth-methods" "^2.0.0" + "@octokit/request" "^6.0.0" + "@octokit/types" "^9.0.0" + btoa-lite "^1.0.0" + universal-user-agent "^6.0.0" + +"@octokit/auth-oauth-user@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-user/-/auth-oauth-user-4.0.0.tgz#2499f25cf64ce2911ba3f2f12de176bfbc1a3805" + integrity sha512-VOm5aIkVGHaOhIvsF/4YmSjoYDzzrKbbYkdSEO0KqHK7I8SlO3ZndSikQ1fBlNPUEH0ve2BOTxLrVvI1qBf9/Q== + dependencies: + "@octokit/auth-oauth-device" "^6.0.0" + "@octokit/oauth-methods" "^4.0.0" + "@octokit/request" "^8.0.2" + "@octokit/types" "^11.0.0" + btoa-lite "^1.0.0" + universal-user-agent "^6.0.0" + "@octokit/auth-token@^2.4.4": version "2.5.0" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" @@ -5840,7 +5958,33 @@ dependencies: "@octokit/types" "^6.0.3" -"@octokit/core@^3.5.1": +"@octokit/auth-token@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.4.tgz#70e941ba742bdd2b49bdb7393e821dea8520a3db" + integrity sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ== + +"@octokit/auth-token@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-4.0.0.tgz#40d203ea827b9f17f42a29c6afb93b7745ef80c7" + integrity sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA== + +"@octokit/auth-unauthenticated@^3.0.0": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@octokit/auth-unauthenticated/-/auth-unauthenticated-3.0.5.tgz#a562bffd6ca0d0e80541eaf9f9b89b8d53020228" + integrity sha512-yH2GPFcjrTvDWPwJWWCh0tPPtTL5SMgivgKPA+6v/XmYN6hGQkAto8JtZibSKOpf8ipmeYhLNWQ2UgW0GYILCw== + dependencies: + "@octokit/request-error" "^3.0.0" + "@octokit/types" "^9.0.0" + +"@octokit/auth-unauthenticated@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-unauthenticated/-/auth-unauthenticated-5.0.0.tgz#fc0d479cb14f80ba691836c5e8b7744786e4d61e" + integrity sha512-AjOI6FNB2dweJ85p6rf7D4EhE4y6VBcwYfX/7KJkR5Q9fD9ET6NABAjajUTSNFfCxmNIaQgISggZ3pkgwtTqsA== + dependencies: + "@octokit/request-error" "^5.0.0" + "@octokit/types" "^11.0.0" + +"@octokit/core@^3.2.4", "@octokit/core@^3.5.1": version "3.6.0" resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085" integrity sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q== @@ -5853,6 +5997,19 @@ before-after-hook "^2.2.0" universal-user-agent "^6.0.0" +"@octokit/core@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-5.0.0.tgz#0fc2b6eb88437e5c1d69f756a5dcee7472d2b2dd" + integrity sha512-YbAtMWIrbZ9FCXbLwT9wWB8TyLjq9mxpKdgB3dUNxQcIVTf9hJ70gRPwAcqGZdY6WdJPZ0I7jLaaNDCiloGN2A== + dependencies: + "@octokit/auth-token" "^4.0.0" + "@octokit/graphql" "^7.0.0" + "@octokit/request" "^8.0.2" + "@octokit/request-error" "^5.0.0" + "@octokit/types" "^11.0.0" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + "@octokit/endpoint@^6.0.1": version "6.0.12" resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" @@ -5862,6 +6019,24 @@ is-plain-object "^5.0.0" universal-user-agent "^6.0.0" +"@octokit/endpoint@^7.0.0": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.6.tgz#791f65d3937555141fb6c08f91d618a7d645f1e2" + integrity sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg== + dependencies: + "@octokit/types" "^9.0.0" + is-plain-object "^5.0.0" + universal-user-agent "^6.0.0" + +"@octokit/endpoint@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-9.0.0.tgz#c5ce19c74b999b85af9a8a189275c80faa3e90fd" + integrity sha512-szrQhiqJ88gghWY2Htt8MqUDO6++E/EIXqJ2ZEp5ma3uGS46o7LZAzSLt49myB7rT+Hfw5Y6gO3LmOxGzHijAQ== + dependencies: + "@octokit/types" "^11.0.0" + is-plain-object "^5.0.0" + universal-user-agent "^6.0.0" + "@octokit/graphql@^4.5.8": version "4.8.0" resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" @@ -5871,29 +6046,114 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" +"@octokit/graphql@^7.0.0": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-7.0.1.tgz#f2291620e17cdaa8115f8d0cdfc0644789ec2db2" + integrity sha512-T5S3oZ1JOE58gom6MIcrgwZXzTaxRnxBso58xhozxHpOqSTgDS6YNeEUvZ/kRvXgPrRz/KHnZhtb7jUMRi9E6w== + dependencies: + "@octokit/request" "^8.0.1" + "@octokit/types" "^11.0.0" + universal-user-agent "^6.0.0" + +"@octokit/oauth-app@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/oauth-app/-/oauth-app-6.0.0.tgz#a5c3b7794df4280c6aadbadd843119059d70a2c4" + integrity sha512-bNMkS+vJ6oz2hCyraT9ZfTpAQ8dZNqJJQVNaKjPLx4ue5RZiFdU1YWXguOPR8AaSHS+lKe+lR3abn2siGd+zow== + dependencies: + "@octokit/auth-oauth-app" "^7.0.0" + "@octokit/auth-oauth-user" "^4.0.0" + "@octokit/auth-unauthenticated" "^5.0.0" + "@octokit/core" "^5.0.0" + "@octokit/oauth-authorization-url" "^6.0.2" + "@octokit/oauth-methods" "^4.0.0" + "@types/aws-lambda" "^8.10.83" + universal-user-agent "^6.0.0" + +"@octokit/oauth-authorization-url@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@octokit/oauth-authorization-url/-/oauth-authorization-url-5.0.0.tgz#029626ce87f3b31addb98cd0d2355c2381a1c5a1" + integrity sha512-y1WhN+ERDZTh0qZ4SR+zotgsQUE1ysKnvBt1hvDRB2WRzYtVKQjn97HEPzoehh66Fj9LwNdlZh+p6TJatT0zzg== + +"@octokit/oauth-authorization-url@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz#cc82ca29cc5e339c9921672f39f2b3f5c8eb6ef2" + integrity sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA== + +"@octokit/oauth-methods@^2.0.0": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@octokit/oauth-methods/-/oauth-methods-2.0.6.tgz#3a089781e90171cbe8a0efa448a6a60229bdd3fb" + integrity sha512-l9Uml2iGN2aTWLZcm8hV+neBiFXAQ9+3sKiQe/sgumHlL6HDg0AQ8/l16xX/5jJvfxueqTW5CWbzd0MjnlfHZw== + dependencies: + "@octokit/oauth-authorization-url" "^5.0.0" + "@octokit/request" "^6.2.3" + "@octokit/request-error" "^3.0.3" + "@octokit/types" "^9.0.0" + btoa-lite "^1.0.0" + +"@octokit/oauth-methods@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@octokit/oauth-methods/-/oauth-methods-4.0.0.tgz#6e0c190e8ee95afe770a4a9a4321eb159a58c794" + integrity sha512-dqy7BZLfLbi3/8X8xPKUKZclMEK9vN3fK5WF3ortRvtplQTszFvdAGbTo71gGLO+4ZxspNiLjnqdd64Chklf7w== + dependencies: + "@octokit/oauth-authorization-url" "^6.0.2" + "@octokit/request" "^8.0.2" + "@octokit/request-error" "^5.0.0" + "@octokit/types" "^11.0.0" + btoa-lite "^1.0.0" + "@octokit/openapi-types@^12.11.0": version "12.11.0" resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== +"@octokit/openapi-types@^14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-14.0.0.tgz#949c5019028c93f189abbc2fb42f333290f7134a" + integrity sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw== + +"@octokit/openapi-types@^18.0.0": + version "18.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-18.0.0.tgz#f43d765b3c7533fd6fb88f3f25df079c24fccf69" + integrity sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw== + +"@octokit/plugin-enterprise-compatibility@^1.2.8": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-compatibility/-/plugin-enterprise-compatibility-1.3.0.tgz#034f035cc1789b0f0d616e71e41f50f73804e89e" + integrity sha512-h34sMGdEOER/OKrZJ55v26ntdHb9OPfR1fwOx6Q4qYyyhWA104o11h9tFxnS/l41gED6WEI41Vu2G2zHDVC5lQ== + dependencies: + "@octokit/request-error" "^2.1.0" + "@octokit/types" "^6.0.3" + "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== -"@octokit/plugin-paginate-rest@^2.16.8": +"@octokit/plugin-paginate-graphql@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-4.0.0.tgz#b26024fa454039c18b948f13bf754ff86b89e8b9" + integrity sha512-7HcYW5tP7/Z6AETAPU14gp5H5KmCPT3hmJrS/5tO7HIgbwenYmgw4OY9Ma54FDySuxMwD+wsJlxtuGWwuZuItA== + +"@octokit/plugin-paginate-rest@^2.16.8", "@octokit/plugin-paginate-rest@^2.6.2": version "2.21.3" resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e" integrity sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw== dependencies: "@octokit/types" "^6.40.0" +"@octokit/plugin-paginate-rest@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-8.0.0.tgz#417b5367da2ba3c2d255a59b87c1cc608228ec38" + integrity sha512-2xZ+baZWUg+qudVXnnvXz7qfrTmDeYPCzangBVq/1gXxii/OiS//4shJp9dnCCvj1x+JAm9ji1Egwm1BA47lPQ== + dependencies: + "@octokit/types" "^11.0.0" + "@octokit/plugin-request-log@^1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== -"@octokit/plugin-rest-endpoint-methods@^5.12.0": +"@octokit/plugin-rest-endpoint-methods@^5.0.1", "@octokit/plugin-rest-endpoint-methods@^5.12.0": version "5.16.2" resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" integrity sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw== @@ -5901,7 +6161,47 @@ "@octokit/types" "^6.39.0" deprecation "^2.3.1" -"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": +"@octokit/plugin-rest-endpoint-methods@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-9.0.0.tgz#e15d54540893202da107305ded2bfd21ce6f769d" + integrity sha512-KquMF/VB1IkKNiVnzJKspY5mFgGyLd7HzdJfVEGTJFzqu9BRFNWt+nwTCMuUiWc72gLQhRWYubTwOkQj+w/1PA== + dependencies: + "@octokit/types" "^11.0.0" + +"@octokit/plugin-retry@^3.0.6": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-3.0.9.tgz#ae625cca1e42b0253049102acd71c1d5134788fe" + integrity sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ== + dependencies: + "@octokit/types" "^6.0.3" + bottleneck "^2.15.3" + +"@octokit/plugin-retry@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-6.0.0.tgz#4a83ca5d531bbd56e0822a644ab0ba4a3215f87a" + integrity sha512-a1/A4A+PB1QoAHQfLJxGHhLfSAT03bR1jJz3GgQJZvty2ozawFWs93MiBQXO7SL2YbO7CIq0Goj4qLOBj8JeMQ== + dependencies: + "@octokit/request-error" "^5.0.0" + "@octokit/types" "^11.0.0" + bottleneck "^2.15.3" + +"@octokit/plugin-throttling@^3.3.4": + version "3.7.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-throttling/-/plugin-throttling-3.7.0.tgz#a35cd05de22b2ef13fde45390d983ff8365b9a9e" + integrity sha512-qrKT1Yl/KuwGSC6/oHpLBot3ooC9rq0/ryDYBCpkRtoj+R8T47xTMDT6Tk2CxWopFota/8Pi/2SqArqwC0JPow== + dependencies: + "@octokit/types" "^6.0.1" + bottleneck "^2.15.3" + +"@octokit/plugin-throttling@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-throttling/-/plugin-throttling-7.0.0.tgz#89f2580b43cfd5ec17f19e3939d8af549f573b0b" + integrity sha512-KL2k/d0uANc8XqP5S64YcNFCudR3F5AaKO39XWdUtlJIjT9Ni79ekWJ6Kj5xvAw87udkOMEPcVf9xEge2+ahew== + dependencies: + "@octokit/types" "^11.0.0" + bottleneck "^2.15.3" + +"@octokit/request-error@^2.0.2", "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== @@ -5910,6 +6210,24 @@ deprecation "^2.0.0" once "^1.4.0" +"@octokit/request-error@^3.0.0", "@octokit/request-error@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-3.0.3.tgz#ef3dd08b8e964e53e55d471acfe00baa892b9c69" + integrity sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ== + dependencies: + "@octokit/types" "^9.0.0" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request-error@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-5.0.0.tgz#060c5770833f9d563ad9a49fec6650c41584bc40" + integrity sha512-1ue0DH0Lif5iEqT52+Rf/hf0RmGO9NWFjrzmrkArpG9trFfDM/efx00BJHdLGuro4BR/gECxCU2Twf5OKrRFsQ== + dependencies: + "@octokit/types" "^11.0.0" + deprecation "^2.0.0" + once "^1.4.0" + "@octokit/request@^5.6.0", "@octokit/request@^5.6.3": version "5.6.3" resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" @@ -5922,6 +6240,29 @@ node-fetch "^2.6.7" universal-user-agent "^6.0.0" +"@octokit/request@^6.0.0", "@octokit/request@^6.2.3": + version "6.2.8" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-6.2.8.tgz#aaf480b32ab2b210e9dadd8271d187c93171d8eb" + integrity sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw== + dependencies: + "@octokit/endpoint" "^7.0.0" + "@octokit/request-error" "^3.0.0" + "@octokit/types" "^9.0.0" + is-plain-object "^5.0.0" + node-fetch "^2.6.7" + universal-user-agent "^6.0.0" + +"@octokit/request@^8.0.0", "@octokit/request@^8.0.1", "@octokit/request@^8.0.2": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.1.1.tgz#23b4d3f164e973f4c1a0f24f68256f1646c00620" + integrity sha512-8N+tdUz4aCqQmXl8FpHYfKG9GelDFd7XGVzyN8rc6WxVlYcfpHECnuRkgquzz+WzvHTK62co5di8gSXnzASZPQ== + dependencies: + "@octokit/endpoint" "^9.0.0" + "@octokit/request-error" "^5.0.0" + "@octokit/types" "^11.1.0" + is-plain-object "^5.0.0" + universal-user-agent "^6.0.0" + "@octokit/rest@^18.0.0", "@octokit/rest@^18.1.0": version "18.12.0" resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" @@ -5932,13 +6273,74 @@ "@octokit/plugin-request-log" "^1.0.4" "@octokit/plugin-rest-endpoint-methods" "^5.12.0" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": +"@octokit/types@^11.0.0", "@octokit/types@^11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-11.1.0.tgz#9e5db741d582b05718a4d91bac8cc987def235ea" + integrity sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ== + dependencies: + "@octokit/openapi-types" "^18.0.0" + +"@octokit/types@^6.0.1", "@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": version "6.41.0" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== dependencies: "@octokit/openapi-types" "^12.11.0" +"@octokit/types@^8.0.0": + version "8.2.1" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-8.2.1.tgz#a6de091ae68b5541f8d4fcf9a12e32836d4648aa" + integrity sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw== + dependencies: + "@octokit/openapi-types" "^14.0.0" + +"@octokit/types@^9.0.0": + version "9.3.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.3.2.tgz#3f5f89903b69f6a2d196d78ec35f888c0013cac5" + integrity sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA== + dependencies: + "@octokit/openapi-types" "^18.0.0" + +"@octokit/webhooks-methods@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@octokit/webhooks-methods/-/webhooks-methods-2.0.0.tgz#1108b9ea661ca6c81e4a8bfa63a09eb27d5bc2db" + integrity sha512-35cfQ4YWlnZnmZKmIxlGPUPLtbkF8lr/A/1Sk1eC0ddLMwQN06dOuLc+dI3YLQS+T+MoNt3DIQ0NynwgKPilig== + +"@octokit/webhooks-methods@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@octokit/webhooks-methods/-/webhooks-methods-4.0.0.tgz#d1697930ba3d8e6b6d0f8a2c996bb440d2e1df1b" + integrity sha512-M8mwmTXp+VeolOS/kfRvsDdW+IO0qJ8kYodM/sAysk093q6ApgmBXwK1ZlUvAwXVrp/YVHp6aArj4auAxUAOFw== + +"@octokit/webhooks-types@5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@octokit/webhooks-types/-/webhooks-types-5.8.0.tgz#b76d1a3e3ad82cec5680d3c6c3443a620047a6ef" + integrity sha512-8adktjIb76A7viIdayQSFuBEwOzwhDC+9yxZpKNHjfzrlostHCw0/N7JWpWMObfElwvJMk2fY2l1noENCk9wmw== + +"@octokit/webhooks-types@7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@octokit/webhooks-types/-/webhooks-types-7.1.0.tgz#d533dea253416e02dd6c2bfab25e533295bd5d3f" + integrity sha512-y92CpG4kFFtBBjni8LHoV12IegJ+KFxLgKRengrVjKmGE5XMeCuGvlfRe75lTRrgXaG6XIWJlFpIDTlkoJsU8w== + +"@octokit/webhooks@^12.0.1": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@octokit/webhooks/-/webhooks-12.0.3.tgz#91f5df322e83b3b7d8bb9af5e692ffea16d6c8bb" + integrity sha512-8iG+/yza7hwz1RrQ7i7uGpK2/tuItZxZq1aTmeg2TNp2xTUB8F8lZF/FcZvyyAxT8tpDMF74TjFGCDACkf1kAQ== + dependencies: + "@octokit/request-error" "^5.0.0" + "@octokit/webhooks-methods" "^4.0.0" + "@octokit/webhooks-types" "7.1.0" + aggregate-error "^3.1.0" + +"@octokit/webhooks@^9.8.4": + version "9.26.0" + resolved "https://registry.yarnpkg.com/@octokit/webhooks/-/webhooks-9.26.0.tgz#cf453bb313da3b66f1a90c84464d978e1c625cce" + integrity sha512-foZlsgrTDwAmD5j2Czn6ji10lbWjGDVsUxTIydjG9KTkAWKJrFapXJgO5SbGxRwfPd3OJdhK3nA2YPqVhxLXqA== + dependencies: + "@octokit/request-error" "^2.0.2" + "@octokit/webhooks-methods" "^2.0.0" + "@octokit/webhooks-types" "5.8.0" + aggregate-error "^3.1.0" + "@opencensus/core@0.0.9": version "0.0.9" resolved "https://registry.yarnpkg.com/@opencensus/core/-/core-0.0.9.tgz#b16f775435ee309433e4126af194d37313fc93b3" @@ -6056,6 +6458,33 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== +"@probot/get-private-key@^1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@probot/get-private-key/-/get-private-key-1.1.1.tgz#12bf61d00a15760d9b0bd713a794f9c4ba4ad5d3" + integrity sha512-hOmBNSAhSZc6PaNkTvj6CO9R5J67ODJ+w5XQlDW9w/6mtcpHWK4L+PZcW0YwVM7PpetLZjN6rsKQIR9yqIaWlA== + dependencies: + "@types/is-base64" "^1.1.0" + is-base64 "^1.1.0" + +"@probot/octokit-plugin-config@^1.0.0": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@probot/octokit-plugin-config/-/octokit-plugin-config-1.1.6.tgz#c450a746f082c8ec9b6d1a481a71778f7720fa9b" + integrity sha512-L29wmnFvilzSfWn9tUgItxdLv0LJh2ICjma3FmLr80Spu3wZ9nHyRrKMo9R5/K2m7VuWmgoKnkgRt2zPzAQBEQ== + dependencies: + "@types/js-yaml" "^4.0.5" + js-yaml "^4.1.0" + +"@probot/pino@^2.2.0": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@probot/pino/-/pino-2.3.5.tgz#f1d051edfc080c9183592e90ac8ae03c14e3951a" + integrity sha512-IiyiNZonMw1dHC4EAdD55y5owV733d9Gll/IKsrLikB7EJ54+eMCOtL/qo+OmgWN9XV3NTDfziEQF2og/OBKog== + dependencies: + "@sentry/node" "^6.0.0" + pino-pretty "^6.0.0" + pump "^3.0.0" + readable-stream "^3.6.0" + split2 "^4.0.0" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -6400,6 +6829,17 @@ "@sentry/utils" "7.48.0" tslib "^1.9.3" +"@sentry/core@6.19.7": + version "6.19.7" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.19.7.tgz#156aaa56dd7fad8c89c145be6ad7a4f7209f9785" + integrity sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw== + dependencies: + "@sentry/hub" "6.19.7" + "@sentry/minimal" "6.19.7" + "@sentry/types" "6.19.7" + "@sentry/utils" "6.19.7" + tslib "^1.9.3" + "@sentry/core@7.46.0": version "7.46.0" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.46.0.tgz#f377e556d8679f29bde1cce15b1682b6c689d6b7" @@ -6431,6 +6871,15 @@ deepmerge "4.3.0" tslib "^2.5.0" +"@sentry/hub@6.19.7": + version "6.19.7" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.19.7.tgz#58ad7776bbd31e9596a8ec46365b45cd8b9cfd11" + integrity sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA== + dependencies: + "@sentry/types" "6.19.7" + "@sentry/utils" "6.19.7" + tslib "^1.9.3" + "@sentry/hub@^7.48.0": version "7.48.0" resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-7.48.0.tgz#89b0ed9083e9a2a83e511d111f684c041aa531b7" @@ -6441,6 +6890,15 @@ "@sentry/utils" "7.48.0" tslib "^1.9.3" +"@sentry/minimal@6.19.7": + version "6.19.7" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.19.7.tgz#b3ee46d6abef9ef3dd4837ebcb6bdfd01b9aa7b4" + integrity sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ== + dependencies: + "@sentry/hub" "6.19.7" + "@sentry/types" "6.19.7" + tslib "^1.9.3" + "@sentry/node@7.46.0": version "7.46.0" resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.46.0.tgz#f85ee74926372d19d6b6a23f68f19023d7a528a7" @@ -6455,6 +6913,20 @@ lru_map "^0.3.3" tslib "^1.9.3" +"@sentry/node@^6.0.0": + version "6.19.7" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.19.7.tgz#32963b36b48daebbd559e6f13b1deb2415448592" + integrity sha512-gtmRC4dAXKODMpHXKfrkfvyBL3cI8y64vEi3fDD046uqYcrWdgoQsffuBbxMAizc6Ez1ia+f0Flue6p15Qaltg== + dependencies: + "@sentry/core" "6.19.7" + "@sentry/hub" "6.19.7" + "@sentry/types" "6.19.7" + "@sentry/utils" "6.19.7" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + "@sentry/node@^7.48.0": version "7.48.0" resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.48.0.tgz#b2f15502b77796bf7bcaa29f2e9ce1420f7c49d1" @@ -6494,6 +6966,11 @@ dependencies: "@sentry-internal/tracing" "7.48.0" +"@sentry/types@6.19.7": + version "6.19.7" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.19.7.tgz#c6b337912e588083fc2896eb012526cf7cfec7c7" + integrity sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg== + "@sentry/types@7.46.0": version "7.46.0" resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.46.0.tgz#8573ba8676342c594fcfefff4552123278cfec51" @@ -6504,6 +6981,14 @@ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.48.0.tgz#57f3c9cf331a5621e82dda04eefcf8c19ee42bc9" integrity sha512-kkAszZwQ5/v4n7Yyw/DPNRWx7h724mVNRGZIJa9ggUMvTgMe7UKCZZ5wfQmYiKVlGbwd9pxXAcP8Oq15EbByFQ== +"@sentry/utils@6.19.7": + version "6.19.7" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.19.7.tgz#6edd739f8185fd71afe49cbe351c1bbf5e7b7c79" + integrity sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA== + dependencies: + "@sentry/types" "6.19.7" + tslib "^1.9.3" + "@sentry/utils@7.46.0": version "7.46.0" resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.46.0.tgz#7a713724db3d1c8bc0aef6d19a7fe2c76db0bdf2" @@ -6747,6 +7232,11 @@ resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.18.tgz#3d93dde6eab654f7bc23e549d9af5d3fa4a5bdc5" integrity sha512-/IsuXp3B9R//uRLi40VlIYoMp7OzhkunPe2fDu7jGfQXI9y3CDCx6FC4juRLSqrpmLst3vgsiK536AAGJFl4Ww== +"@types/aws-lambda@^8.10.83": + version "8.10.119" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.119.tgz#aaf010a9c892b3e29a290e5c49bfe8bcec82c455" + integrity sha512-Vqm22aZrCvCd6I5g1SvpW151jfqwTzEZ7XJ3yZ6xaZG31nUEOEyzzVImjRcsN8Wi/QyPxId/x8GTtgIbsy8kEw== + "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.7": version "7.20.0" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" @@ -6802,6 +7292,11 @@ dependencies: "@types/node" "*" +"@types/btoa-lite@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/btoa-lite/-/btoa-lite-1.0.0.tgz#e190a5a548e0b348adb0df9ac7fa5f1151c7cca4" + integrity sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg== + "@types/cacheable-request@^6.0.1": version "6.0.3" resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" @@ -6949,7 +7444,7 @@ "@types/qs" "*" "@types/range-parser" "*" -"@types/express@*", "@types/express@^4.17.13": +"@types/express@*", "@types/express@^4.17.13", "@types/express@^4.17.9": version "4.17.17" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== @@ -7064,6 +7559,18 @@ resolved "https://registry.yarnpkg.com/@types/i18n/-/i18n-0.12.0.tgz#ea4e247c2da05a35002d7535d339c912c4513549" integrity sha512-3OlOD518lF2IQmZndRvoO5ZJzgUowLPIRMH4T8LTjTkgbZwZlKMR52/KLml0dJN/Bt6e9R10NxzjOQnDG2DDgg== +"@types/ioredis@^4.27.1": + version "4.28.10" + resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.28.10.tgz#40ceb157a4141088d1394bb87c98ed09a75a06ff" + integrity sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ== + dependencies: + "@types/node" "*" + +"@types/is-base64@^1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/is-base64/-/is-base64-1.1.1.tgz#a17d2b0075f637f80f9ab5f76f0071a65f6965d4" + integrity sha512-JgnGhP+MeSHEQmvxcobcwPEP4Ew56voiq9/0hmP/41lyQ/3gBw/ZCIRy2v+QkEOdeCl58lRcrf6+Y6WMlJGETA== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" @@ -7099,6 +7606,11 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" +"@types/js-yaml@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138" + integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA== + "@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -7116,6 +7628,13 @@ dependencies: "@types/node" "*" +"@types/jsonwebtoken@^9.0.0": + version "9.0.2" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#9eeb56c76dd555039be2a3972218de5bd3b8d83e" + integrity sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q== + dependencies: + "@types/node" "*" + "@types/keyv@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" @@ -7309,6 +7828,37 @@ dependencies: "@types/express" "*" +"@types/pino-http@^5.0.6": + version "5.8.1" + resolved "https://registry.yarnpkg.com/@types/pino-http/-/pino-http-5.8.1.tgz#ebb194750ad2f9245c3028b5d2c4e6d64f685ba9" + integrity sha512-A9MW6VCnx5ii7s+Fs5aFIw+aSZcBCpsZ/atpxamu8tTsvWFacxSf2Hrn1Ohn1jkVRB/LiPGOapRXcFawDBnDnA== + dependencies: + "@types/pino" "6.3" + +"@types/pino-pretty@*": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/pino-pretty/-/pino-pretty-5.0.0.tgz#aa7a61cfd553b051764acfa0a49872f7a09a1722" + integrity sha512-N1uzqSzioqz8R3AkDbSJwcfDWeI3YMPNapSQQhnB2ISU4NYgUIcAh+hYT5ygqBM+klX4htpEhXMmoJv3J7GrdA== + dependencies: + pino-pretty "*" + +"@types/pino-std-serializers@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz#1e28b80b554c8222858e99a4e0fc77fd070e10e8" + integrity sha512-gXfUZx2xIBbFYozGms53fT0nvkacx/+62c8iTxrEqH5PkIGAQvDbXg2774VWOycMPbqn5YJBQ3BMsg4Li3dWbg== + dependencies: + pino-std-serializers "*" + +"@types/pino@6.3", "@types/pino@^6.3.4": + version "6.3.12" + resolved "https://registry.yarnpkg.com/@types/pino/-/pino-6.3.12.tgz#4425db6ced806109c3df957100cba9dfcd73c228" + integrity sha512-dsLRTq8/4UtVSpJgl9aeqHvbh6pzdmjYD3C092SYgLD2TyoCqHpTJk6vp8DvCTGGc7iowZ2MoiYiVUUCcu7muw== + dependencies: + "@types/node" "*" + "@types/pino-pretty" "*" + "@types/pino-std-serializers" "*" + sonic-boom "^2.1.0" + "@types/plist@^3.0.1": version "3.0.2" resolved "https://registry.yarnpkg.com/@types/plist/-/plist-3.0.2.tgz#61b3727bba0f5c462fe333542534a0c3e19ccb01" @@ -7849,6 +8399,13 @@ abbrev@1, abbrev@^1.0.0, abbrev@~1.1.1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + accept-language-parser@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/accept-language-parser/-/accept-language-parser-1.5.0.tgz#8877c54040a8dcb59e0a07d9c1fde42298334791" @@ -8001,7 +8558,7 @@ agentkeepalive@^4.1.0, agentkeepalive@^4.1.3, agentkeepalive@^4.2.1: depd "^2.0.0" humanize-ms "^1.2.1" -aggregate-error@^3.0.0: +aggregate-error@^3.0.0, aggregate-error@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== @@ -8544,6 +9101,16 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +args@^5.0.1: + version "5.0.3" + resolved "https://registry.yarnpkg.com/args/-/args-5.0.3.tgz#943256db85021a85684be2f0882f25d796278702" + integrity sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA== + dependencies: + camelcase "5.0.0" + chalk "2.4.2" + leven "2.1.0" + mri "1.1.4" + argv-formatter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/argv-formatter/-/argv-formatter-1.0.0.tgz#a0ca0cbc29a5b73e836eebe1cbf6c5e0e4eb82f9" @@ -8970,6 +9537,11 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +atomic-sleep@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== + atomically@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/atomically/-/atomically-1.7.0.tgz#c07a0458432ea6dbc9a3506fffa424b48bccaafe" @@ -9676,7 +10248,7 @@ bootstrap@^4.3.1: resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.6.2.tgz#8e0cd61611728a5bf65a3a2b8d6ff6c77d5d7479" integrity sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ== -bottleneck@^2.18.1: +bottleneck@^2.15.3, bottleneck@^2.18.1: version "2.19.5" resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== @@ -10043,6 +10615,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +btoa-lite@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" + integrity sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA== + buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" @@ -10483,6 +11060,11 @@ camelcase-keys@^8.0.2: quick-lru "^6.1.1" type-fest "^2.13.0" +camelcase@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" + integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== + camelcase@^2.0.0, camelcase@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" @@ -10589,6 +11171,15 @@ chalk@1.1.1: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.0, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@3.0.0, chalk@^3.0.0, chalk@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" @@ -10624,15 +11215,6 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.0, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" @@ -11185,6 +11767,11 @@ cls-hooked@^4.2.2: emitter-listener "^1.0.1" semver "^5.4.1" +cluster-key-slot@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" + integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== + cmd-shim@^3.0.0, cmd-shim@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-3.0.3.tgz#2c35238d3df37d98ecdd7d5f6b8dc6b21cadc7cb" @@ -11326,11 +11913,16 @@ colorette@2.0.19, colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.16, color resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== -colorette@^1.1.0: +colorette@^1.1.0, colorette@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== +colorette@^2.0.7: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + colors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" @@ -13412,6 +14004,11 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== +dateformat@^4.5.1, dateformat@^4.6.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" + integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== + dayjs@^1.10.4, dayjs@^1.10.6: version "1.11.7" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" @@ -13727,7 +14324,7 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== -denque@^1.5.0: +denque@^1.1.0, denque@^1.5.0: version "1.5.1" resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== @@ -15741,6 +16338,11 @@ event-stream@4.0.1: stream-combiner "^0.2.2" through "^2.3.8" +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + eventemitter-asyncresource@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz#734ff2e44bf448e627f7748f905d6bdd57bdb65b" @@ -15786,6 +16388,11 @@ events@^3.0.0, events@^3.2.0, events@^3.3.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +eventsource@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508" + integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA== + evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" @@ -15984,6 +16591,15 @@ express-handlebars@^3.0.0: object.assign "^4.1.0" promise "^8.0.2" +express-handlebars@^6.0.3: + version "6.0.7" + resolved "https://registry.yarnpkg.com/express-handlebars/-/express-handlebars-6.0.7.tgz#f779254664eff0e250362ef1c2b30587059c212a" + integrity sha512-iYeMFpc/hMD+E6FNAZA5fgWeXnXr4rslOSPkeEV6TwdmpJ5lEXuWX0u9vFYs31P2MURctQq2batR09oeNj0LIg== + dependencies: + glob "^8.1.0" + graceful-fs "^4.2.10" + handlebars "^4.7.7" + express-session@^1.17.2: version "1.17.3" resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.17.3.tgz#14b997a15ed43e5949cb1d073725675dd2777f36" @@ -16155,6 +16771,11 @@ fancy-log@^2.0.0: dependencies: color-support "^1.1.3" +fast-copy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-3.0.1.tgz#9e89ef498b8c04c1cd76b33b8e14271658a732aa" + integrity sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -16237,7 +16858,12 @@ fast-printf@^1.6.9: dependencies: boolean "^3.1.4" -fast-safe-stringify@2.1.1, fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.1.1: +fast-redact@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.3.0.tgz#7c83ce3a7be4898241a46560d51de10f653f7634" + integrity sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ== + +fast-safe-stringify@2.1.1, fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.0.8, fast-safe-stringify@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== @@ -16673,6 +17299,11 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== +flatstr@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.12.tgz#c2ba6a08173edbb6c9640e3055b95e287ceb5931" + integrity sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw== + flatted@^3.1.0, flatted@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" @@ -17584,7 +18215,7 @@ glob@^5.0.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.1, glob@^8.0.3: +glob@^8.0.0, glob@^8.0.1, glob@^8.0.3, glob@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== @@ -18233,6 +18864,14 @@ help-me@^3.0.0: glob "^7.1.6" readable-stream "^3.6.0" +help-me@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/help-me/-/help-me-4.2.0.tgz#50712bfd799ff1854ae1d312c36eafcea85b0563" + integrity sha512-TAOnTB8Tz5Dw8penUuzHVrKNKlCIbwwbHnXraNJxPwf8LRtE2HlM84RYuezMFcwOJmoYOCWVDyJ8TQGxn9PgxA== + dependencies: + glob "^8.0.0" + readable-stream "^3.6.0" + hexoid@1.0.0, hexoid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18" @@ -19236,6 +19875,23 @@ ionicons@^4.6.3: resolved "https://registry.yarnpkg.com/ionicons/-/ionicons-4.6.3.tgz#e4f3a3e9b66761eb8c0db27798cc1a8ce97777d2" integrity sha512-cgP+VIr2cTJpMfFyVHTerq6n2jeoiGboVoe3GlaAo5zoSBDAEXORwUZhv6m+lCyxlsHCS3nqPUE+MKyZU71t8Q== +ioredis@^4.27.8: + version "4.28.5" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.28.5.tgz#5c149e6a8d76a7f8fa8a504ffc85b7d5b6797f9f" + integrity sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A== + dependencies: + cluster-key-slot "^1.1.0" + debug "^4.3.1" + denque "^1.1.0" + lodash.defaults "^4.2.0" + lodash.flatten "^4.4.0" + lodash.isarguments "^3.1.0" + p-map "^2.1.0" + redis-commands "1.7.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + standard-as-callback "^2.1.0" + ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -19307,6 +19963,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-base64@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-base64/-/is-base64-1.1.0.tgz#8ce1d719895030a457c59a7dcaf39b66d99d56b4" + integrity sha512-Nlhg7Z2dVC4/PTvIFkgVVNvPHSO2eR/Yd0XzhGiXCXEvWnptXlXa/clQ8aePPiMuxEGcWfzWbGw2Fe3d+Y3v1g== + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -21237,6 +21898,11 @@ jmespath@0.16.0: resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== +jmespath@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" + integrity sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w== + joi@^12.0.0: version "12.0.0" resolved "https://registry.yarnpkg.com/joi/-/joi-12.0.0.tgz#46f55e68f4d9628f01bbb695902c8b307ad8d33a" @@ -21246,6 +21912,11 @@ joi@^12.0.0: isemail "3.x.x" topo "2.x.x" +joycon@^3.0.0, joycon@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== + jpeg-js@^0.4.1, jpeg-js@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa" @@ -21301,7 +21972,7 @@ js-stringify@^1.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.14.1, js-yaml@^3.13.1, js-yaml@^3.9.0: +js-yaml@3.14.1, js-yaml@^3.13.1, js-yaml@^3.14.1, js-yaml@^3.9.0: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -22010,7 +22681,7 @@ less@^4.1.0: needle "^3.1.0" source-map "~0.6.0" -leven@^2.1.0: +leven@2.1.0, leven@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" integrity sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA== @@ -22495,6 +23166,17 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" +load-json-file@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-5.3.0.tgz#4d3c1e01fa1c03ea78a60ac7af932c9ce53403f3" + integrity sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw== + dependencies: + graceful-fs "^4.1.15" + parse-json "^4.0.0" + pify "^4.0.1" + strip-bom "^3.0.0" + type-fest "^0.3.0" + load-json-file@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1" @@ -22748,7 +23430,7 @@ lodash.get@^4.0.0, lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== -lodash.isarguments@^3.0.0: +lodash.isarguments@^3.0.0, lodash.isarguments@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== @@ -23065,6 +23747,11 @@ lru-cache@6.0.0, lru-cache@^6.0.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.13.1.tgz#267a81fbd0881327c46a81c5922606a2cfe336c4" integrity sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ== +lru-cache@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" + integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== + lru-cache@^2.6.4: version "2.7.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" @@ -23090,6 +23777,11 @@ lru-cache@^7.5.1, lru-cache@^7.7.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== +lru-cache@^9.0.0: + version "9.1.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.2.tgz#255fdbc14b75589d6d0e73644ca167a8db506835" + integrity sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ== + lru_map@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" @@ -24461,6 +25153,11 @@ mqtt@^4.3.7: ws "^7.5.5" xtend "^4.0.2" +mri@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a" + integrity sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w== + mri@^1.1.5: version "1.2.0" resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" @@ -26123,11 +26820,42 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== +octokit-auth-probot@^1.2.2: + version "1.2.9" + resolved "https://registry.yarnpkg.com/octokit-auth-probot/-/octokit-auth-probot-1.2.9.tgz#835178f2f13209687c23e794af84b373f234b01a" + integrity sha512-mMjw6Y760EwJnW2tSVooJK8BMdsG6D40SoCclnefVf/5yWjaNVquEu8NREBVWb60OwbpnMEz4vREXHB5xdMFYQ== + dependencies: + "@octokit/auth-app" "^4.0.2" + "@octokit/auth-token" "^3.0.0" + "@octokit/auth-unauthenticated" "^3.0.0" + "@octokit/types" "^8.0.0" + +octokit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/octokit/-/octokit-3.1.0.tgz#33f73dfab3cd438818dda4336eddfded13ae7118" + integrity sha512-dmIH5D+edpb4/ASd6ZGo6BiRR1g4ytu8lG4f+6XN/2AW+CSuTsT0nj1d6rv/HKgoflMQ1+rb3KlVWcvrmgQZhw== + dependencies: + "@octokit/app" "^14.0.0" + "@octokit/core" "^5.0.0" + "@octokit/oauth-app" "^6.0.0" + "@octokit/plugin-paginate-graphql" "^4.0.0" + "@octokit/plugin-paginate-rest" "^8.0.0" + "@octokit/plugin-rest-endpoint-methods" "^9.0.0" + "@octokit/plugin-retry" "^6.0.0" + "@octokit/plugin-throttling" "^7.0.0" + "@octokit/request-error" "^5.0.0" + "@octokit/types" "^11.1.0" + omggif@^1.0.10, omggif@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.10.tgz#ddaaf90d4a42f532e9e7cb3a95ecdd47f17c7b19" integrity sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw== +on-exit-leak-free@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz#5c703c968f7e7f851885f6459bf8a8a57edc9cc4" + integrity sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w== + on-finished@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -26450,7 +27178,7 @@ p-map-series@^2.1.0: resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== -p-map@^2.0.0: +p-map@^2.0.0, p-map@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== @@ -27447,6 +28175,89 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== +pino-abstract-transport@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz#cc0d6955fffcadb91b7b49ef220a6cc111d48bb3" + integrity sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA== + dependencies: + readable-stream "^4.0.0" + split2 "^4.0.0" + +pino-http@^5.3.0: + version "5.8.0" + resolved "https://registry.yarnpkg.com/pino-http/-/pino-http-5.8.0.tgz#6e688fd5f965c5b6991f340eb660ea2927be9aa7" + integrity sha512-YwXiyRb9y0WCD1P9PcxuJuh3Dc5qmXde/paJE86UGYRdiFOi828hR9iUGmk5gaw6NBT9gLtKANOHFimvh19U5w== + dependencies: + fast-url-parser "^1.1.3" + pino "^6.13.0" + pino-std-serializers "^4.0.0" + +pino-pretty@*: + version "10.2.0" + resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-10.2.0.tgz#c674a153e15c08d7032a826d0051d786feace1d9" + integrity sha512-tRvpyEmGtc2D+Lr3FulIZ+R1baggQ4S3xD2Ar93KixFEDx6SEAUP3W5aYuEw1C73d6ROrNcB2IXLteW8itlwhA== + dependencies: + colorette "^2.0.7" + dateformat "^4.6.3" + fast-copy "^3.0.0" + fast-safe-stringify "^2.1.1" + help-me "^4.0.1" + joycon "^3.1.1" + minimist "^1.2.6" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^1.0.0" + pump "^3.0.0" + readable-stream "^4.0.0" + secure-json-parse "^2.4.0" + sonic-boom "^3.0.0" + strip-json-comments "^3.1.1" + +pino-pretty@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-6.0.0.tgz#4d7ac8528ad74d90040082816202bb7d48eb1c95" + integrity sha512-jyeR2fXXWc68st1DTTM5NhkHlx8p+1fKZMfm84Jwq+jSw08IwAjNaZBZR6ts69hhPOfOjg/NiE1HYW7vBRPL3A== + dependencies: + "@hapi/bourne" "^2.0.0" + args "^5.0.1" + colorette "^1.3.0" + dateformat "^4.5.1" + fast-safe-stringify "^2.0.7" + jmespath "^0.15.0" + joycon "^3.0.0" + pump "^3.0.0" + readable-stream "^3.6.0" + rfdc "^1.3.0" + split2 "^3.1.1" + strip-json-comments "^3.1.1" + +pino-std-serializers@*: + version "6.2.2" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz#d9a9b5f2b9a402486a5fc4db0a737570a860aab3" + integrity sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA== + +pino-std-serializers@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz#b56487c402d882eb96cd67c257868016b61ad671" + integrity sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg== + +pino-std-serializers@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz#1791ccd2539c091ae49ce9993205e2cd5dbba1e2" + integrity sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q== + +pino@^6.13.0, pino@^6.7.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/pino/-/pino-6.14.0.tgz#b745ea87a99a6c4c9b374e4f29ca7910d4c69f78" + integrity sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg== + dependencies: + fast-redact "^3.0.0" + fast-safe-stringify "^2.0.8" + flatstr "^1.0.12" + pino-std-serializers "^3.1.0" + process-warning "^1.0.0" + quick-format-unescaped "^4.0.3" + sonic-boom "^1.0.2" + pirates@^4.0.1, pirates@^4.0.4, pirates@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" @@ -27478,6 +28289,14 @@ pkg-conf@^2.1.0: find-up "^2.0.0" load-json-file "^4.0.0" +pkg-conf@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-3.1.0.tgz#d9f9c75ea1bae0e77938cde045b276dac7cc69ae" + integrity sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ== + dependencies: + find-up "^3.0.0" + load-json-file "^5.2.0" + "pkg-dir@< 6 >= 5": version "5.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" @@ -28458,6 +29277,45 @@ preview-email@^3.0.5: pug "^3.0.2" uuid "^9.0.0" +probot@^12.3.1: + version "12.3.1" + resolved "https://registry.yarnpkg.com/probot/-/probot-12.3.1.tgz#6a19f3faf941978df04afb2dd3f5f65422450c03" + integrity sha512-ECSgycmAC0ILEK6cOa+x3QPufP5JybsuohOFCYr3glQU5SkbmypZJE/Sfio9mxAFHK5LCXveIDsfZCxf6ck4JA== + dependencies: + "@octokit/core" "^3.2.4" + "@octokit/plugin-enterprise-compatibility" "^1.2.8" + "@octokit/plugin-paginate-rest" "^2.6.2" + "@octokit/plugin-rest-endpoint-methods" "^5.0.1" + "@octokit/plugin-retry" "^3.0.6" + "@octokit/plugin-throttling" "^3.3.4" + "@octokit/types" "^8.0.0" + "@octokit/webhooks" "^9.8.4" + "@probot/get-private-key" "^1.1.0" + "@probot/octokit-plugin-config" "^1.0.0" + "@probot/pino" "^2.2.0" + "@types/express" "^4.17.9" + "@types/ioredis" "^4.27.1" + "@types/pino" "^6.3.4" + "@types/pino-http" "^5.0.6" + commander "^6.2.0" + deepmerge "^4.2.2" + deprecation "^2.3.1" + dotenv "^8.2.0" + eventsource "^2.0.2" + express "^4.17.1" + express-handlebars "^6.0.3" + ioredis "^4.27.8" + js-yaml "^3.14.1" + lru-cache "^6.0.0" + octokit-auth-probot "^1.2.2" + pino "^6.7.0" + pino-http "^5.3.0" + pkg-conf "^3.1.0" + resolve "^1.19.0" + semver "^7.3.4" + update-dotenv "^1.1.1" + uuid "^8.3.2" + proc-log@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-1.0.0.tgz#0d927307401f69ed79341e83a0b2c9a13395eb77" @@ -28961,6 +29819,11 @@ queue@6.0.2: dependencies: inherits "~2.0.3" +quick-format-unescaped@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" + integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== + quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" @@ -29419,6 +30282,17 @@ readable-stream@2.3.7: isarray "0.0.1" string_decoder "~0.10.x" +readable-stream@^4.0.0: + version "4.4.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.4.2.tgz#e6aced27ad3b9d726d8308515b9a1b98dc1b9d13" + integrity sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + readable-web-to-node-stream@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" @@ -29507,7 +30381,7 @@ redeyed@~2.1.0: dependencies: esprima "~4.0.0" -redis-commands@^1.7.0: +redis-commands@1.7.0, redis-commands@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ== @@ -30365,6 +31239,11 @@ secure-compare@3.0.1: resolved "https://registry.yarnpkg.com/secure-compare/-/secure-compare-3.0.1.tgz#f1a0329b308b221fae37b9974f3d578d0ca999e3" integrity sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw== +secure-json-parse@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" + integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== + seed-random@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54" @@ -31090,6 +31969,28 @@ socks@~2.3.2: ip "1.1.5" smart-buffer "^4.1.0" +sonic-boom@^1.0.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-1.4.1.tgz#d35d6a74076624f12e6f917ade7b9d75e918f53e" + integrity sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg== + dependencies: + atomic-sleep "^1.0.0" + flatstr "^1.0.12" + +sonic-boom@^2.1.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.8.0.tgz#c1def62a77425090e6ad7516aad8eb402e047611" + integrity sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg== + dependencies: + atomic-sleep "^1.0.0" + +sonic-boom@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.3.0.tgz#cffab6dafee3b2bcb88d08d589394198bee1838c" + integrity sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g== + dependencies: + atomic-sleep "^1.0.0" + sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" @@ -31314,13 +32215,18 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" -split2@^3.0.0, split2@^3.1.0: +split2@^3.0.0, split2@^3.1.0, split2@^3.1.1: version "3.2.2" resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== dependencies: readable-stream "^3.0.0" +split2@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== + split2@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809" @@ -31500,6 +32406,11 @@ stacktrace-js@^2.0.0: stack-generator "^2.0.5" stacktrace-gps "^3.0.4" +standard-as-callback@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" + integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== + stat-mode@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-1.0.0.tgz#68b55cb61ea639ff57136f36b216a291800d1465" @@ -31927,7 +32838,7 @@ strip-indent@^4.0.0: dependencies: min-indent "^1.0.1" -strip-json-comments@3.1.1: +strip-json-comments@3.1.1, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -33195,6 +34106,11 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" + integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ== + type-fest@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" @@ -33574,6 +34490,14 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" +universal-github-app-jwt@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/universal-github-app-jwt/-/universal-github-app-jwt-1.1.1.tgz#d57cee49020662a95ca750a057e758a1a7190e6e" + integrity sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w== + dependencies: + "@types/jsonwebtoken" "^9.0.0" + jsonwebtoken "^9.0.0" + universal-user-agent@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" @@ -33710,6 +34634,11 @@ update-browserslist-db@^1.0.10: escalade "^3.1.1" picocolors "^1.0.0" +update-dotenv@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-dotenv/-/update-dotenv-1.1.1.tgz#17146f302f216c3c92419d5a327a45be910050ca" + integrity sha512-3cIC18In/t0X/yH793c00qqxcKD8jVCgNOPif/fGQkFpYMGecM9YAc+kaAKXuZsM2dE9I9wFI7KvAuNX22SGMQ== + update-notifier@^2.3.0, update-notifier@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6" From 7c1c8f07668b302df4c0f41f63ceb35342786644 Mon Sep 17 00:00:00 2001 From: Badal Khatri Date: Fri, 25 Aug 2023 11:24:02 +0530 Subject: [PATCH 004/104] Updated Probot Configurationi --- .env.compose | 7 + .env.docker | 7 + .env.local | 7 + .env.sample | 7 + package.json | 1 + .../common/src/interfaces/IGithubConfig.ts | 8 + .../src/environments/environment.prod.ts | 7 + .../config/src/environments/environment.ts | 7 + .../config/src/environments/ienvironment.ts | 2 + packages/core/src/app.module.ts | 30 +++- packages/core/src/bootstrap/index.ts | 39 ----- packages/core/src/github/github.controller.ts | 22 +++ packages/core/src/github/github.module.ts | 17 ++ packages/core/src/github/github.service.ts | 68 ++++++++ .../core/src/octokit/octokit.controller.ts | 37 ----- packages/core/src/octokit/octokit.module.ts | 15 -- packages/core/src/octokit/octokit.service.ts | 76 --------- .../commands/handlers/task-create.handler.ts | 18 ++- .../commands/handlers/task-update.handler.ts | 24 +-- packages/core/src/tasks/task.module.ts | 4 +- packages/plugins/integration-github/README.md | 4 + .../integration-github/src/github.service.ts | 90 +++++++++++ .../plugins/integration-github/src/handler.ts | 27 ---- .../src/hook-metadata.accessor.ts | 12 ++ .../integration-github/src/hook.controller.ts | 20 +++ .../integration-github/src/hook.decorator.ts | 11 ++ .../plugins/integration-github/src/index.ts | 47 +----- .../src/probot.discovery.ts | 151 ++++++++++++++++++ .../integration-github/src/probot.helpers.ts | 54 +++++++ .../integration-github/src/probot.module.ts | 57 +++++++ .../integration-github/src/probot.types.ts | 43 +++++ yarn.lock | 39 ++++- 32 files changed, 702 insertions(+), 256 deletions(-) create mode 100644 packages/core/src/github/github.controller.ts create mode 100644 packages/core/src/github/github.module.ts create mode 100644 packages/core/src/github/github.service.ts delete mode 100644 packages/core/src/octokit/octokit.controller.ts delete mode 100644 packages/core/src/octokit/octokit.module.ts delete mode 100644 packages/core/src/octokit/octokit.service.ts create mode 100644 packages/plugins/integration-github/src/github.service.ts delete mode 100644 packages/plugins/integration-github/src/handler.ts create mode 100644 packages/plugins/integration-github/src/hook-metadata.accessor.ts create mode 100644 packages/plugins/integration-github/src/hook.controller.ts create mode 100644 packages/plugins/integration-github/src/hook.decorator.ts create mode 100644 packages/plugins/integration-github/src/probot.discovery.ts create mode 100644 packages/plugins/integration-github/src/probot.helpers.ts create mode 100644 packages/plugins/integration-github/src/probot.module.ts create mode 100644 packages/plugins/integration-github/src/probot.types.ts diff --git a/.env.compose b/.env.compose index 63e81745b66..ffa7d9efb09 100644 --- a/.env.compose +++ b/.env.compose @@ -265,3 +265,10 @@ FEATURE_ROLES_PERMISSION=true # Email Verification FEATURE_EMAIL_VERIFICATION=false + +# GitHub App Integration +GITHUB_INTEGRATION_APP_ID= +GITHUB_INTEGRATION_CLIENT_ID= +GITHUB_INTEGRATION_CLIENT_SECRET= +GITHUB_INTEGRATION_PRIVATE_KEY= +GITHUB_INTEGRATION_WEBHOOK_SECRET= diff --git a/.env.docker b/.env.docker index 0601038feab..de7ae2a26eb 100644 --- a/.env.docker +++ b/.env.docker @@ -236,3 +236,10 @@ FEATURE_ROLES_PERMISSION=true # Email Verification FEATURE_EMAIL_VERIFICATION=false + +# GitHub App Integration +GITHUB_INTEGRATION_APP_ID= +GITHUB_INTEGRATION_CLIENT_ID= +GITHUB_INTEGRATION_CLIENT_SECRET= +GITHUB_INTEGRATION_PRIVATE_KEY= +GITHUB_INTEGRATION_WEBHOOK_SECRET= diff --git a/.env.local b/.env.local index 1a2cf5e1394..de41c26148a 100644 --- a/.env.local +++ b/.env.local @@ -223,3 +223,10 @@ UNLEASH_INSTANCE_ID= UNLEASH_REFRESH_INTERVAL=15000 UNLEASH_METRICS_INTERVAL=60000 UNLEASH_API_KEY= + +# GitHub App Integration +GITHUB_INTEGRATION_APP_ID= +GITHUB_INTEGRATION_CLIENT_ID= +GITHUB_INTEGRATION_CLIENT_SECRET= +GITHUB_INTEGRATION_PRIVATE_KEY= +GITHUB_INTEGRATION_WEBHOOK_SECRET= diff --git a/.env.sample b/.env.sample index 47d33280f1d..d9377edade0 100644 --- a/.env.sample +++ b/.env.sample @@ -258,3 +258,10 @@ FEATURE_EMAIL_VERIFICATION=false APPLE_ID= APPLE_ID_APP_PASSWORD= CSC_LINK= + +# GitHub App Integration +GITHUB_INTEGRATION_APP_ID= +GITHUB_INTEGRATION_CLIENT_ID= +GITHUB_INTEGRATION_CLIENT_SECRET= +GITHUB_INTEGRATION_PRIVATE_KEY= +GITHUB_INTEGRATION_WEBHOOK_SECRET= diff --git a/package.json b/package.json index d05a02a1b92..8b359b1d259 100644 --- a/package.json +++ b/package.json @@ -244,6 +244,7 @@ "octokit": "^3.1.0", "probot": "^12.3.1", "rxjs": "^7.4.0", + "smee-client": "^1.2.3", "unleash": "^2.0.2", "yargs": "^17.5.0" }, diff --git a/packages/common/src/interfaces/IGithubConfig.ts b/packages/common/src/interfaces/IGithubConfig.ts index 40f541e7398..cd957db7d97 100644 --- a/packages/common/src/interfaces/IGithubConfig.ts +++ b/packages/common/src/interfaces/IGithubConfig.ts @@ -3,3 +3,11 @@ export interface IGithubConfig { readonly clientSecret: string; readonly callbackUrl?: string; } + +export interface IGitHubIntegrationConfig { + readonly clientId: string; + readonly clientSecret: string; + readonly appId: string; + readonly privateKey: string; + readonly webhookSecret: string; +} diff --git a/packages/config/src/environments/environment.prod.ts b/packages/config/src/environments/environment.prod.ts index b1aa21eb8ba..2c0e9c20897 100644 --- a/packages/config/src/environments/environment.prod.ts +++ b/packages/config/src/environments/environment.prod.ts @@ -123,6 +123,13 @@ export const environment: IEnvironment = { process.env.GITHUB_CALLBACK_URL || `http://${process.env.API_HOST}:${process.env.API_PORT}/api/auth/google/callback`, }, + gitHubIntegrationConfig: { + appId: process.env.GITHUB_INTEGRATION_APP_ID, + clientId: process.env.GITHUB_INTEGRATION_CLIENT_ID, + clientSecret: process.env.GITHUB_INTEGRATION_CLIENT_SECRET, + privateKey: process.env.GITHUB_INTEGRATION_PRIVATE_KEY, + webhookSecret: process.env.GITHUB_INTEGRATION_WEBHOOK_SECRET, + }, microsoftConfig: { clientId: process.env.MICROSOFT_CLIENT_ID, diff --git a/packages/config/src/environments/environment.ts b/packages/config/src/environments/environment.ts index b81749fc4d4..313aeaf2370 100644 --- a/packages/config/src/environments/environment.ts +++ b/packages/config/src/environments/environment.ts @@ -127,6 +127,13 @@ export const environment: IEnvironment = { process.env.GITHUB_CALLBACK_URL || `http://${process.env.API_HOST}:${process.env.API_PORT}/api/auth/google/callback`, }, + gitHubIntegrationConfig: { + appId: process.env.GITHUB_INTEGRATION_APP_ID, + clientId: process.env.GITHUB_INTEGRATION_CLIENT_ID, + clientSecret: process.env.GITHUB_INTEGRATION_CLIENT_SECRET, + privateKey: process.env.GITHUB_INTEGRATION_PRIVATE_KEY, + webhookSecret: process.env.GITHUB_INTEGRATION_WEBHOOK_SECRET, + }, microsoftConfig: { clientId: process.env.MICROSOFT_CLIENT_ID, diff --git a/packages/config/src/environments/ienvironment.ts b/packages/config/src/environments/ienvironment.ts index 957ca396f76..95d9f6433a8 100644 --- a/packages/config/src/environments/ienvironment.ts +++ b/packages/config/src/environments/ienvironment.ts @@ -11,6 +11,7 @@ import { IFacebookConfig, IFiverrConfig, IGithubConfig, + IGitHubIntegrationConfig, IGoogleConfig, IKeycloakConfig, ILinkedinConfig, @@ -102,6 +103,7 @@ export interface IEnvironment { facebookConfig: IFacebookConfig; googleConfig: IGoogleConfig; githubConfig: IGithubConfig; + gitHubIntegrationConfig: IGitHubIntegrationConfig; microsoftConfig: IMicrosoftConfig; linkedinConfig: ILinkedinConfig; twitterConfig: ITwitterConfig; diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index 9a63302706d..a5ce1e2f548 100644 --- a/packages/core/src/app.module.ts +++ b/packages/core/src/app.module.ts @@ -23,6 +23,7 @@ import { LanguagesEnum } from '@gauzy/contracts'; import { ConfigService, environment } from '@gauzy/config'; import * as path from 'path'; import * as moment from 'moment'; +import { ProbotModule } from '@gauzy/integration-github'; import { CandidateInterviewersModule } from './candidate-interviewers/candidate-interviewers.module'; import { CandidateSkillModule } from './candidate-skill/candidate-skill.module'; import { InvoiceModule } from './invoice/invoice.module'; @@ -154,7 +155,7 @@ import { EmailResetModule } from './email-reset/email-reset.module'; import { TaskLinkedIssueModule } from './tasks/linked-issue/task-linked-issue.module'; import { OrganizationTaskSettingModule } from './organization-task-setting/organization-task-setting.module'; import { TaskEstimationModule } from './tasks/estimation/task-estimation.module'; -import { OctokitModule } from 'octokit/octokit.module'; +import { GitHubModule } from './github/github.module'; const { unleashConfig } = environment; if (unleashConfig.url) { @@ -252,6 +253,31 @@ if (environment.sentry && environment.sentry.dsn) { }), ] : []), + + // Probot + ...(environment.gitHubIntegrationConfig && + environment.gitHubIntegrationConfig.appId + ? [ + ProbotModule.forRoot({ + path: 'github', // Webhook URL in GitHub will be: https://example.com/api/github + config: { + appId: environment.gitHubIntegrationConfig.appId, + clientId: + environment.gitHubIntegrationConfig.clientId, + clientSecret: + environment.gitHubIntegrationConfig + .clientSecret, + + privateKey: + environment.gitHubIntegrationConfig.privateKey, + webhookSecret: + environment.gitHubIntegrationConfig + .webhookSecret, + }, + }), + ] + : []), + ThrottlerModule.forRootAsync({ inject: [ConfigService], useFactory: (config: ConfigService): ThrottlerModuleOptions => @@ -387,7 +413,7 @@ if (environment.sentry && environment.sentry.dsn) { TaskLinkedIssueModule, OrganizationTaskSettingModule, TaskEstimationModule, - OctokitModule, + GitHubModule, ], controllers: [AppController], providers: [ diff --git a/packages/core/src/bootstrap/index.ts b/packages/core/src/bootstrap/index.ts index a68a822dd89..814216e4bda 100644 --- a/packages/core/src/bootstrap/index.ts +++ b/packages/core/src/bootstrap/index.ts @@ -20,8 +20,6 @@ import { AppService } from '../app.service'; import { AppModule } from '../app.module'; import { AuthGuard } from './../shared/guards'; import { SharedModule } from './../shared/shared.module'; -import { createNodeMiddleware, createProbot } from 'probot'; -import type { Probot, Context } from 'probot'; export async function bootstrap( pluginConfig?: Partial @@ -109,43 +107,6 @@ export async function bootstrap( */ useContainer(app.select(SharedModule), { fallbackOnErrors: true }); - const GITHUB_INTEGRATION_APP_ID = 123456; - const GITHUB_INTEGRATION_PRIVATE_KEY = ''; - const GITHUB_INTEGRATION_APP_INSTALLATION_ID = 123456; - const GITHUB_INTEGRATION_CLIENT_ID = ''; - const GITHUB_INTEGRATION_CLIENT_SECRET = ''; - const GITHUB_INTEGRATION_WEBHOOK_SECRET = ''; - - const probot = createProbot({ - defaults: { - appId: GITHUB_INTEGRATION_APP_ID, - privateKey: GITHUB_INTEGRATION_PRIVATE_KEY, - secret: GITHUB_INTEGRATION_WEBHOOK_SECRET, - }, - overrides: { - appId: GITHUB_INTEGRATION_APP_ID, - privateKey: GITHUB_INTEGRATION_PRIVATE_KEY, - secret: GITHUB_INTEGRATION_WEBHOOK_SECRET, - }, - }); - - // Import from config and pass here - app.use( - createNodeMiddleware( - (app: Probot) => { - app.on('issues.edited', async (context) => { - // Here we can have logic to sync with Gauzy API - - console.log('payload', context.payload); - }); - }, - { - probot, - webhooksPath: '/api/github/webhooks', - } - ) - ); - await app.listen(port, host, () => { console.log( chalk.magenta(`Listening at http://${host}:${port}/${globalPrefix}`) diff --git a/packages/core/src/github/github.controller.ts b/packages/core/src/github/github.controller.ts new file mode 100644 index 00000000000..1c6d1d33fb0 --- /dev/null +++ b/packages/core/src/github/github.controller.ts @@ -0,0 +1,22 @@ +import { Hook } from '@gauzy/integration-github'; +import { Controller } from '@nestjs/common'; +import { Context } from 'probot'; +import { GitHubService } from './github.service'; + +@Controller() +export class GitHubController { + constructor(private readonly gitHubService: GitHubService) {} + + @Hook(['issues.opened']) + async issuesOpened(context: Context) { + await this.gitHubService.issuesOpened(context); + } + + @Hook(['issues.edited']) + async issuesEdited(context: Context) { + await this.gitHubService.issuesEdited(context); + } + + // TODO + // Handle all other required events +} diff --git a/packages/core/src/github/github.module.ts b/packages/core/src/github/github.module.ts new file mode 100644 index 00000000000..7974f54f1e7 --- /dev/null +++ b/packages/core/src/github/github.module.ts @@ -0,0 +1,17 @@ +import { Module } from '@nestjs/common'; +import { GitHubController } from './github.controller'; +import { GitHubService } from './github.service'; +import { RouterModule } from 'nest-router'; +import { ProbotModule } from '@gauzy/integration-github'; + +@Module({ + imports: [ + RouterModule.forRoutes([{ path: '/github', module: GitHubModule }]), + ProbotModule, + ], + + controllers: [GitHubController], + providers: [GitHubService], + exports: [GitHubService], +}) +export class GitHubModule {} diff --git a/packages/core/src/github/github.service.ts b/packages/core/src/github/github.service.ts new file mode 100644 index 00000000000..19c5ca5ccca --- /dev/null +++ b/packages/core/src/github/github.service.ts @@ -0,0 +1,68 @@ +import { Injectable } from '@nestjs/common'; +import { Context } from 'probot'; +import { GitHubService as GitHubIntegrationService } from '@gauzy/integration-github'; + +@Injectable() +export class GitHubService { + constructor( + private readonly gitHubIntegrationService: GitHubIntegrationService + ) {} + + /** + * ----- From GitHub to APIs ----- + */ + + async issuesOpened(context: Context) { + console.log('Issue Created: ', context.payload); + // TODO + // Handle event processing + // Find all the Projects connected to current repo and create new Task + } + async issuesEdited(context: Context) { + console.log('Issue Edited', context.payload); + // TODO + // Handle event processing + // Find all the Projects connected to current repo and edit task + // To edit task we need to save issue_number of GitHub in task table + } + // TODO + // Handle all other required events + + /** + * ----- From APIs to GitHub ----- + */ + async openIssue( + title: string, + body: string, + owner: string, + repo: string, + installationId: number + ) { + await this.gitHubIntegrationService.openIssue( + title, + body, + owner, + repo, + installationId + ); + } + async editIssue( + issueNumber: number, + title: string, + body: string, + owner: string, + repo: string, + installationId: number + ) { + await this.gitHubIntegrationService.editIssue( + issueNumber, + title, + body, + repo, + owner, + installationId + ); + } + // TODO + // Handle all other required events +} diff --git a/packages/core/src/octokit/octokit.controller.ts b/packages/core/src/octokit/octokit.controller.ts deleted file mode 100644 index f684575e761..00000000000 --- a/packages/core/src/octokit/octokit.controller.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Public } from '@gauzy/common'; -import { Body, Controller, Get, Post, Req } from '@nestjs/common'; -import { Request } from 'express'; -import { OctokitService } from './octokit.service'; - -// TODO: -// For now API route is public -// We have to make a guard that validate webhook secret from payload -@Controller() -@Public() -export class OctokitController { - constructor(private readonly _octokitService: OctokitService) {} - - @Get() - async testGet() { - return { hello: 'world' }; - } - - @Post() - async webhook(@Req() request: Request, @Body() body: any) { - // body contains whole payload that webhook send on different event - console.log(body); - - const event = request.headers['x-github-event']; - const action = body.action; - - // Based on event & action we can decide further processing - - if (event === 'issues' && action === 'opened') { - // TODO - } - if (event === 'issues' && action === 'edited') { - // TODO - } - // ... - } -} diff --git a/packages/core/src/octokit/octokit.module.ts b/packages/core/src/octokit/octokit.module.ts deleted file mode 100644 index cdb4d14aefb..00000000000 --- a/packages/core/src/octokit/octokit.module.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Module } from '@nestjs/common'; -import { OctokitController } from './octokit.controller'; -import { OctokitService } from './octokit.service'; -import { RouterModule } from 'nest-router'; - -@Module({ - imports: [ - RouterModule.forRoutes([{ path: '/octokit', module: OctokitModule }]), - ], - - controllers: [OctokitController], - providers: [OctokitService], - exports: [OctokitService], -}) -export class OctokitModule {} diff --git a/packages/core/src/octokit/octokit.service.ts b/packages/core/src/octokit/octokit.service.ts deleted file mode 100644 index fbc4f2da1c4..00000000000 --- a/packages/core/src/octokit/octokit.service.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { App } from 'octokit'; - -@Injectable() -export class OctokitService { - constructor() {} - - async createIssue(title: string, body: string) { - // TODO: - // Dynamic ENV variable - const app = new App({ - appId: '', - privateKey: '', - }); - // TODO: - // Need to store user's installationId in DB and make param dynamic - const octokit = await app.getInstallationOctokit(123456); - - octokit - .request('POST /repos/{owner}/{repo}/issues', { - // TODO: - // pass dynamic values as required - // Add all the fields that we have - - owner: 'badal-ever', - repo: 'testing-gauzy-teams-integration', - - title, - body, - // labels: ['bug', 'GauzyAPI'], - - headers: { - 'X-GitHub-Api-Version': '2022-11-28', - }, - }) - .then((data) => { - console.log('data', data); - }) - .catch((error) => { - console.log('error', error); - }); - } - async updateIssue(id: number, title: string, body: string) { - // TODO: - // Dynamic ENV variable - const app = new App({ - appId: '', - privateKey: '', - }); - // TODO: - // Need to store user's installationId in DB and make param dynamic - const octokit = await app.getInstallationOctokit(123456); - - octokit - .request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', { - // TODO: pass dynamic values as required - owner: 'badal-ever', - repo: 'testing-gauzy-teams-integration', - - issue_number: id, - title, - body, - // labels: ['bug', 'GauzyAPI'], - - headers: { - 'X-GitHub-Api-Version': '2022-11-28', - }, - }) - .then((data) => { - console.log('data', data); - }) - .catch((error) => { - console.log('error', error); - }); - } -} diff --git a/packages/core/src/tasks/commands/handlers/task-create.handler.ts b/packages/core/src/tasks/commands/handlers/task-create.handler.ts index 47657a86750..6791a6e132a 100644 --- a/packages/core/src/tasks/commands/handlers/task-create.handler.ts +++ b/packages/core/src/tasks/commands/handlers/task-create.handler.ts @@ -5,14 +5,16 @@ import { RequestContext } from './../../../core/context'; import { TaskCreateCommand } from './../task-create.command'; import { OrganizationProjectService } from './../../../organization-project/organization-project.service'; import { TaskService } from '../../task.service'; -import { OctokitService } from 'octokit/octokit.service'; +// import { GitHubService } from 'github/github.service'; @CommandHandler(TaskCreateCommand) export class TaskCreateHandler implements ICommandHandler { constructor( + // TODO: + // Uncomment below line for GitHub app integration + // private readonly _gitHubService: GitHubService private readonly _taskService: TaskService, - private readonly _organizationProjectService: OrganizationProjectService, - private readonly _octokitService: OctokitService + private readonly _organizationProjectService: OrganizationProjectService ) {} public async execute(command: TaskCreateCommand): Promise { @@ -41,7 +43,15 @@ export class TaskCreateHandler implements ICommandHandler { } ); - this._octokitService.createIssue(input.title, input.description); + // TODO: + // Make the Repo, Owner and installtion id field dynamic + // this._gitHubService.openIssue( + // input.title, + // input.description, + // '', + // '', + // 12345678 // installtion id + // ); return await this._taskService.create({ ...input, diff --git a/packages/core/src/tasks/commands/handlers/task-update.handler.ts b/packages/core/src/tasks/commands/handlers/task-update.handler.ts index 13b19336bdc..87cc128fb6a 100644 --- a/packages/core/src/tasks/commands/handlers/task-update.handler.ts +++ b/packages/core/src/tasks/commands/handlers/task-update.handler.ts @@ -3,13 +3,15 @@ import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { ITask, ITaskUpdateInput } from '@gauzy/contracts'; import { TaskService } from '../../task.service'; import { TaskUpdateCommand } from '../task-update.command'; -import { OctokitService } from 'octokit/octokit.service'; +// import { GitHubService } from 'github/github.service'; @CommandHandler(TaskUpdateCommand) export class TaskUpdateHandler implements ICommandHandler { constructor( - private readonly _taskService: TaskService, - private readonly _octokitService: OctokitService + // TODO: + // Uncomment below line for GitHub app integration + // private readonly _gitHubService: GitHubService + private readonly _taskService: TaskService ) {} public async execute(command: TaskUpdateCommand): Promise { @@ -48,13 +50,15 @@ export class TaskUpdateHandler implements ICommandHandler { } // TODO: - // We have to store issue_number of github in our task, so that we can use it while sync - // Right now we we have put static 38 value. - this._octokitService.updateIssue( - 38, - request.title, - request.description - ); + // Make the Issue number, Repo, Owner and installtion id field dynamic + // this._gitHubService.editIssue( + // 48, + // task.title, + // task.description, + // '', + // '', + // 12345678 // installtion id + // ); return await this._taskService.create({ ...request, diff --git a/packages/core/src/tasks/task.module.ts b/packages/core/src/tasks/task.module.ts index 9eb29ca7479..d2610a917ac 100644 --- a/packages/core/src/tasks/task.module.ts +++ b/packages/core/src/tasks/task.module.ts @@ -11,7 +11,7 @@ import { TenantModule } from '../tenant/tenant.module'; import { UserModule } from './../user/user.module'; import { RoleModule } from './../role/role.module'; import { EmployeeModule } from './../employee/employee.module'; -import { OctokitModule } from 'octokit/octokit.module'; +import { GitHubModule } from './../github/github.module'; @Module({ imports: [ @@ -23,7 +23,7 @@ import { OctokitModule } from 'octokit/octokit.module'; EmployeeModule, OrganizationProjectModule, CqrsModule, - OctokitModule, + GitHubModule, ], controllers: [TaskController], providers: [TaskService, ...CommandHandlers], diff --git a/packages/plugins/integration-github/README.md b/packages/plugins/integration-github/README.md index 9a6cc7d3360..3242848e9cf 100644 --- a/packages/plugins/integration-github/README.md +++ b/packages/plugins/integration-github/README.md @@ -5,3 +5,7 @@ This library was generated with [Nx](https://nx.dev). ## Running unit tests Run `ng test integration-github` to execute the unit tests via [Jest](https://jestjs.io). + +## License + +In this plugin, we used code from https://github.com/yieldbits/nestjs (MIT license, https://github.com/yieldbits/nestjs/blob/main/LICENSE). diff --git a/packages/plugins/integration-github/src/github.service.ts b/packages/plugins/integration-github/src/github.service.ts new file mode 100644 index 00000000000..23309563b10 --- /dev/null +++ b/packages/plugins/integration-github/src/github.service.ts @@ -0,0 +1,90 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { ModuleProviders, ProbotConfig } from './probot.types'; +import { App } from 'octokit'; + +@Injectable() +export class GitHubService { + private readonly app: App; + + constructor( + @Inject(ModuleProviders.ProbotConfig) + private readonly config: ProbotConfig + ) { + // TODO: BUG + // ENV variable is not working here, hence getting error (" secretOrPrivateKey must be an asymmetric key when using RS256") + this.app = new App({ + appId: this.config.appId, + privateKey: this.config.privateKey, + }); + } + + async openIssue( + title: string, + body: string, + owner: string, + repo: string, + installationId: number + ) { + const octokit = await this.app.getInstallationOctokit(installationId); + + octokit + .request('POST /repos/{owner}/{repo}/issues', { + owner, + repo, + title, + body, + + // TODO: + // pass dynamic values as required + // Add all the fields that we have + // Ex. + // labels: ['bug', 'GauzyAPI'], + + headers: { + 'X-GitHub-Api-Version': '2022-11-28', + }, + }) + .then((data) => { + console.log('data', data); + }) + .catch((error) => { + console.log('error', error); + }); + } + async editIssue( + issueNumber: number, + title: string, + body: string, + repo: string, + owner: string, + installationId: number + ) { + const octokit = await this.app.getInstallationOctokit(installationId); + + octokit + .request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', { + owner, + repo, + title, + body, + + issue_number: issueNumber, + + // TODO: + // pass dynamic values as required + // Add all the fields that we have + // Ex. + // labels: ['bug', 'GauzyAPI'], + + headers: { + 'X-GitHub-Api-Version': '2022-11-28', + }, + }) + .then((data) => { + console.log('data', data); + }) + .catch((error) => { + console.log('error', error); + }); + } +} diff --git a/packages/plugins/integration-github/src/handler.ts b/packages/plugins/integration-github/src/handler.ts deleted file mode 100644 index bb992ec9b81..00000000000 --- a/packages/plugins/integration-github/src/handler.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createNodeMiddleware, createProbot } from 'probot'; -import app from './index'; - -const GITHUB_INTEGRATION_APP_ID = 123456; -const GITHUB_INTEGRATION_PRIVATE_KEY = ''; -const GITHUB_INTEGRATION_APP_INSTALLATION_ID = 123456; -const GITHUB_INTEGRATION_CLIENT_ID = ''; -const GITHUB_INTEGRATION_CLIENT_SECRET = ''; -const GITHUB_INTEGRATION_WEBHOOK_SECRET = ''; - -const probot = createProbot({ - defaults: { - appId: GITHUB_INTEGRATION_APP_ID, - privateKey: GITHUB_INTEGRATION_PRIVATE_KEY, - secret: GITHUB_INTEGRATION_WEBHOOK_SECRET, - }, - overrides: { - appId: GITHUB_INTEGRATION_APP_ID, - privateKey: GITHUB_INTEGRATION_PRIVATE_KEY, - secret: GITHUB_INTEGRATION_WEBHOOK_SECRET, - }, -}); - -export default createNodeMiddleware(app, { - probot, - webhooksPath: '/api/github/webhooks', -}); diff --git a/packages/plugins/integration-github/src/hook-metadata.accessor.ts b/packages/plugins/integration-github/src/hook-metadata.accessor.ts new file mode 100644 index 00000000000..c3935f5e97d --- /dev/null +++ b/packages/plugins/integration-github/src/hook-metadata.accessor.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { EmitterWebhookEventName } from '@octokit/webhooks/dist-types/types'; + +@Injectable() +export class HookMetadataAccessor { + constructor(private readonly reflector: Reflector) {} + + getWebhookEvents(target: () => any): EmitterWebhookEventName[] { + return this.reflector.get('HOOK_EVENTS', target)?.eventOrEvents; + } +} diff --git a/packages/plugins/integration-github/src/hook.controller.ts b/packages/plugins/integration-github/src/hook.controller.ts new file mode 100644 index 00000000000..9e7f8b92142 --- /dev/null +++ b/packages/plugins/integration-github/src/hook.controller.ts @@ -0,0 +1,20 @@ +import { Controller, Post, Req, Type } from '@nestjs/common'; +import { Public } from '@gauzy/common'; +import { Request } from 'express'; +import { ProbotDiscovery } from './probot.discovery'; + +export function getControllerClass({ path }): Type { + @Public() + @Controller() + class HookController { + constructor(private readonly probotDiscovery: ProbotDiscovery) {} + + @Post([path]) + // eslint-disable-next-line @typescript-eslint/no-empty-function,@typescript-eslint/explicit-module-boundary-types + async hooks(@Req() req: Request) { + return await this.probotDiscovery.receiveHook(req); + } + } + + return HookController; +} diff --git a/packages/plugins/integration-github/src/hook.decorator.ts b/packages/plugins/integration-github/src/hook.decorator.ts new file mode 100644 index 00000000000..674017621f6 --- /dev/null +++ b/packages/plugins/integration-github/src/hook.decorator.ts @@ -0,0 +1,11 @@ +import { applyDecorators, SetMetadata } from '@nestjs/common'; +import { EmitterWebhookEventName } from '@octokit/webhooks/dist-types/types'; + +/** + * Sets up hook trigger on functions. + */ +export function Hook( + eventOrEvents: EmitterWebhookEventName[] +): MethodDecorator { + return applyDecorators(SetMetadata('HOOK_EVENTS', { eventOrEvents })); +} diff --git a/packages/plugins/integration-github/src/index.ts b/packages/plugins/integration-github/src/index.ts index b9304a3f91a..76787101aa9 100644 --- a/packages/plugins/integration-github/src/index.ts +++ b/packages/plugins/integration-github/src/index.ts @@ -1,42 +1,7 @@ -import type { Probot, Context } from 'probot'; +export * from './probot.types'; +export * from './probot.module'; +export * from './hook.decorator'; +export * from './probot.helpers'; +export * from './probot.discovery'; -export default (app: Probot) => { - app.log('Yay! The app was loaded!'); - - app.on('issues.opened', async (context) => { - // Here we can have logic to sync with Gauzy API - - console.log('context.isBot', context.payload); - - return context.octokit.issues.createComment( - context.issue({ - body: `Hi @${context.payload.sender.login}, Thank you for opening this Issue `, - }) - ); - }); - - app.on('issues.closed', async (context: Context) => { - // Here we can have logic to sync with Gauzy API - - return context.octokit.issues.createComment( - context.issue({ body: 'Closed!' }) - ); - }); - - app.on('issue_comment.created', async (context: Context) => { - // Here we can have logic to sync with Gauzy API - - // context.payload is actually webhook payload - console.log(context.payload); - - if (context.isBot) { - // This condition will help us to idenity if request was done by bot - // and using it we can prevent infinity loop - return; - } - - return context.octokit.issues.createComment( - context.issue({ body: 'Again commented!' }) - ); - }); -}; +export * from './github.service'; diff --git a/packages/plugins/integration-github/src/probot.discovery.ts b/packages/plugins/integration-github/src/probot.discovery.ts new file mode 100644 index 00000000000..41229fb48fb --- /dev/null +++ b/packages/plugins/integration-github/src/probot.discovery.ts @@ -0,0 +1,151 @@ +import { + Inject, + Injectable, + Logger, + OnApplicationBootstrap, + OnApplicationShutdown, + OnModuleInit, +} from '@nestjs/common'; +import * as _ from 'lodash'; +import { v4 } from 'uuid'; +import { ModuleProviders, ProbotConfig } from './probot.types'; +import { createProbot, createSmee } from './probot.helpers'; +import { Probot } from 'probot'; +import { HookMetadataAccessor } from './hook-metadata.accessor'; +import { DiscoveryService, MetadataScanner } from '@nestjs/core'; +import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper'; + +@Injectable() +export class ProbotDiscovery + implements OnModuleInit, OnApplicationBootstrap, OnApplicationShutdown +{ + private readonly logger = new Logger('ProbotDiscovery'); + + private readonly hooks: Map; + + private smee: any; + + private readonly probot: Probot; + + constructor( + private readonly discoveryService: DiscoveryService, + private readonly metadataAccessor: HookMetadataAccessor, + private readonly metadataScanner: MetadataScanner, + @Inject(ModuleProviders.ProbotConfig) + private readonly config: ProbotConfig + ) { + this.hooks = new Map(); + this.probot = createProbot(this.config); + } + + public async onModuleInit() { + this.explore(); + } + + onApplicationBootstrap(): any { + if (!_.isEmpty(this.config.webhookProxy)) { + this.smee = createSmee(this.config); + this.smee.start(); + } + + this.mountHooks(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onApplicationShutdown(signal?: string): any { + // TODO clear probot event handlers on shutdown + } + + mountHooks() { + this.probot + .load( + (app: { + on: ( + arg0: any, + arg1: (context: any) => Promise + ) => any; + }) => { + this.hooks.forEach((hook) => { + app.on( + hook.eventOrEvents, + this.initContext(hook.target) + ); + }); + } + ) + .then(() => { + this.logger.log('Hook event listeners initialized'); + }) + .catch(this.logger.error); + } + + initContext(fn: (context: any) => any) { + return async (context: any) => { + await fn(context); + }; + } + + explore() { + const instanceWrappers: InstanceWrapper[] = [ + ...this.discoveryService.getControllers(), + ...this.discoveryService.getProviders(), + ]; + + instanceWrappers + .filter((wrapper) => wrapper.isDependencyTreeStatic()) + .forEach((wrapper: InstanceWrapper) => { + const { instance } = wrapper; + + if (!instance || !Object.getPrototypeOf(instance)) { + return; + } + + this.metadataScanner.scanFromPrototype( + instance, + Object.getPrototypeOf(instance), + (key: string) => this.lookupHooks(instance, key) + ); + }); + } + + lookupHooks(instance: Record any>, key: string) { + const methodRef = instance[key]; + const hookMetadata = this.metadataAccessor.getWebhookEvents(methodRef); + const hookFn = this.wrapFunctionInTryCatchBlocks(methodRef, instance); + + // filter functions that do not have a webhook event definition + if (_.isEmpty(hookMetadata)) { + return null; + } + + return this.hooks.set(v4(), { + target: hookFn, + eventOrEvents: hookMetadata, + }); + } + + private wrapFunctionInTryCatchBlocks( + methodRef: () => any, + instance: Record + ) { + return async (...args: unknown[]) => { + try { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await methodRef.call(instance, ...args); + } catch (error) { + this.logger.error(error); + } + }; + } + + receiveHook(request) { + const id = request.headers['x-github-delivery'] as string; + const event = request.headers['x-github-event']; + const body = request.body; + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return this.probot.receive({ id, name: event, payload: body }); + } +} diff --git a/packages/plugins/integration-github/src/probot.helpers.ts b/packages/plugins/integration-github/src/probot.helpers.ts new file mode 100644 index 00000000000..061e6aa9818 --- /dev/null +++ b/packages/plugins/integration-github/src/probot.helpers.ts @@ -0,0 +1,54 @@ +import { getPrivateKey } from '@probot/get-private-key'; +import { Probot } from 'probot'; +import SmeeClient from 'smee-client'; +import { OctokitConfig, ProbotConfig } from './probot.types'; +import { Octokit } from '@octokit/rest'; +import { createAppAuth } from '@octokit/auth-app'; + +export const parseConfig = (config: ProbotConfig): Record => { + return { + appId: config.appId, + privateKey: getPrivateKey({ + env: { PRIVATE_KEY: config.privateKey }, + }) as string, + webhookSecret: config.webhookSecret, + ghUrl: config.ghUrl || 'https://api.github.com', + webhookProxy: config.webhookProxy, + webhookPath: config.webhookPath, + clientId: config.clientId, + clientSecret: config.clientSecret, + }; +}; + +export const createProbot = (config: ProbotConfig): Probot => { + const parsedConfig = parseConfig(config); + return new Probot({ + appId: parsedConfig.appId, + privateKey: parsedConfig.privateKey, + secret: parsedConfig.webhookSecret, + baseUrl: parsedConfig.ghUrl, + }); +}; + +export const createSmee = (config: ProbotConfig) => { + const parsedConfig = parseConfig(config); + return new SmeeClient({ + source: parsedConfig.webhookProxy as string, + target: parsedConfig.webhookPath as string, + logger: console, + }); +}; + +export const createOctokit = (config: OctokitConfig): Octokit => { + return new Octokit({ + authStrategy: createAppAuth, + baseUrl: config.probot.ghUrl, + auth: { + ...config.auth, + appId: config.probot.appId, + privateKey: config.probot.privateKey, + clientId: config.probot.clientId, + clientSecret: config.probot.clientSecret, + }, + }); +}; diff --git a/packages/plugins/integration-github/src/probot.module.ts b/packages/plugins/integration-github/src/probot.module.ts new file mode 100644 index 00000000000..7c4497c49db --- /dev/null +++ b/packages/plugins/integration-github/src/probot.module.ts @@ -0,0 +1,57 @@ +import { DynamicModule, Module } from '@nestjs/common'; +import { + ProbotModuleOptions, + ModuleProviders, + ProbotModuleAsyncOptions, +} from './probot.types'; +import { ProbotDiscovery } from './probot.discovery'; +import { getControllerClass } from './hook.controller'; +import { DiscoveryModule } from '@nestjs/core'; +import { HookMetadataAccessor } from './hook-metadata.accessor'; +import { GitHubService } from './github.service'; + +@Module({ + imports: [DiscoveryModule], +}) +export class ProbotModule { + static forRoot(options: ProbotModuleOptions): DynamicModule { + const { path: hookPath } = options; + const HookController = getControllerClass({ path: hookPath }); + return { + global: options.isGlobal || true, + module: ProbotModule, + controllers: [HookController], + providers: [ + { + provide: ModuleProviders.ProbotConfig, + useFactory: () => options.config, + }, + HookMetadataAccessor, + ProbotDiscovery, + GitHubService, + ], + exports: [GitHubService], + }; + } + + static forRootAsync(options: ProbotModuleAsyncOptions): DynamicModule { + const { path: hookPath } = options; + const HookController = getControllerClass({ path: hookPath }); + return { + module: ProbotModule, + global: options.isGlobal || true, + controllers: [HookController], + providers: [ + { + provide: ModuleProviders.ProbotConfig, + useFactory: options.useFactory, + inject: options.inject || [], + }, + HookMetadataAccessor, + ProbotDiscovery, + GitHubService, + ], + exports: [GitHubService], + }; + } +} diff --git a/packages/plugins/integration-github/src/probot.types.ts b/packages/plugins/integration-github/src/probot.types.ts new file mode 100644 index 00000000000..6bd862571af --- /dev/null +++ b/packages/plugins/integration-github/src/probot.types.ts @@ -0,0 +1,43 @@ +import { ModuleMetadata } from '@nestjs/common'; + +export interface ProbotConfig { + appId: string; + privateKey: string; + + webhookSecret?: string; + webhookPath?: string; + + ghUrl?: string; + + clientId: string; + clientSecret: string; + + webhookProxy?: string; +} + +export interface OctokitConfig { + auth: Record; + probot: ProbotConfig; +} + +export interface ProbotModuleOptions { + isGlobal?: boolean; + path: string; + config: ProbotConfig; +} + +export interface ProbotModuleAsyncOptions + extends Pick { + isGlobal?: boolean; + path: string; + useFactory: (...args: any[]) => Promise | ProbotConfig; + inject?: any[]; +} + +export enum ProbotMetadata { + name = 'probot/metadata/hook', +} + +export enum ModuleProviders { + ProbotConfig = 'probot/provider/config', +} diff --git a/yarn.lock b/yarn.lock index 6a8b1e05297..184c029d0a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12549,7 +12549,7 @@ cookie@0.5.0, cookie@^0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cookiejar@^2.1.4: +cookiejar@^2.1.3, cookiejar@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== @@ -16388,6 +16388,11 @@ events@^3.0.0, events@^3.2.0, events@^3.3.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +eventsource@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.2.tgz#bc75ae1c60209e7cb1541231980460343eaea7c2" + integrity sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA== + eventsource@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508" @@ -17442,7 +17447,7 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" -formidable@^2.1.2: +formidable@^2.0.1, formidable@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.1.2.tgz#fa973a2bec150e4ce7cac15589d7a25fc30ebd89" integrity sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g== @@ -25098,7 +25103,7 @@ moo@^0.5.0, moo@^0.5.1: resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== -morgan@^1.10.0: +morgan@^1.10.0, morgan@^1.9.1: version "1.10.0" resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== @@ -31778,6 +31783,17 @@ smart-buffer@^4.0.2, smart-buffer@^4.1.0, smart-buffer@^4.2.0: resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== +smee-client@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/smee-client/-/smee-client-1.2.3.tgz#a0ef5e86e3640870f19f3953aaf228fa478b082c" + integrity sha512-uDrU8u9/Ln7aRXyzGHgVaNUS8onHZZeSwQjCdkMoSL7U85xI+l+Y2NgjibkMJAyXkW7IAbb8rw9RMHIjS6lAwA== + dependencies: + commander "^2.19.0" + eventsource "^1.1.0" + morgan "^1.9.1" + superagent "^7.1.3" + validator "^13.7.0" + smooth-scrollbar@^8.7.4: version "8.8.1" resolved "https://registry.yarnpkg.com/smooth-scrollbar/-/smooth-scrollbar-8.8.1.tgz#7e0274fbc2a427cbe816c89875c72cac592fbb2f" @@ -33006,6 +33022,23 @@ sumchecker@^3.0.1: dependencies: debug "^4.1.0" +superagent@^7.1.3: + version "7.1.6" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-7.1.6.tgz#64f303ed4e4aba1e9da319f134107a54cacdc9c6" + integrity sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g== + dependencies: + component-emitter "^1.3.0" + cookiejar "^2.1.3" + debug "^4.3.4" + fast-safe-stringify "^2.1.1" + form-data "^4.0.0" + formidable "^2.0.1" + methods "^1.1.2" + mime "2.6.0" + qs "^6.10.3" + readable-stream "^3.6.0" + semver "^7.3.7" + superagent@^8.0.5: version "8.0.9" resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.0.9.tgz#2c6fda6fadb40516515f93e9098c0eb1602e0535" From 482c12643aa78c114b2b0a54aa4f81f627c7013a Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE Date: Wed, 6 Sep 2023 13:11:57 +0530 Subject: [PATCH 005/104] chore(deps): packages for @gauzy/integration-github --- package.json | 3 --- packages/plugins/integration-github/package.json | 4 +++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 1730238e086..e60de4d3567 100644 --- a/package.json +++ b/package.json @@ -241,10 +241,7 @@ "ffi-napi": "^4.0.3", "iconv": "^3.0.1", "ng2-smart-table": "^1.7.2", - "octokit": "^3.1.0", - "probot": "^12.3.1", "rxjs": "^7.4.0", - "smee-client": "^1.2.3", "unleash": "^2.0.2", "yargs": "^17.5.0" }, diff --git a/packages/plugins/integration-github/package.json b/packages/plugins/integration-github/package.json index 0abd24f6142..f797dcc9d77 100644 --- a/packages/plugins/integration-github/package.json +++ b/packages/plugins/integration-github/package.json @@ -28,7 +28,9 @@ }, "keywords": [], "dependencies": { - + "octokit": "^3.1.0", + "probot": "^12.3.1", + "smee-client": "^1.2.3" }, "devDependencies": { "@types/node": "^17.0.33", From 5c1aba4a0e1eb95d5a9f6782523b39cd92343559 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE Date: Wed, 6 Sep 2023 13:15:55 +0530 Subject: [PATCH 006/104] fix(cspell): typo spelling :-) --- .cspell.json | 3 ++- .../core/src/tasks/commands/handlers/task-create.handler.ts | 4 ++-- .../core/src/tasks/commands/handlers/task-update.handler.ts | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.cspell.json b/.cspell.json index 85c8bfd90d4..d03418753a2 100644 --- a/.cspell.json +++ b/.cspell.json @@ -317,7 +317,8 @@ "resave", "signin", "ibase", - "iwindow" + "iwindow", + "Probot" ], "useGitignore": true, "ignorePaths": [ diff --git a/packages/core/src/tasks/commands/handlers/task-create.handler.ts b/packages/core/src/tasks/commands/handlers/task-create.handler.ts index e0ee55ba67b..62d8df07da4 100644 --- a/packages/core/src/tasks/commands/handlers/task-create.handler.ts +++ b/packages/core/src/tasks/commands/handlers/task-create.handler.ts @@ -47,13 +47,13 @@ export class TaskCreateHandler implements ICommandHandler { this._octokitService.createIssue(input.title, input.description); // TODO: - // Make the Repo, Owner and installtion id field dynamic + // Make the Repo, Owner and installation id field dynamic // this._gitHubService.openIssue( // input.title, // input.description, // '', // '', - // 12345678 // installtion id + // 12345678 // installation id // ); return await this._taskService.create({ diff --git a/packages/core/src/tasks/commands/handlers/task-update.handler.ts b/packages/core/src/tasks/commands/handlers/task-update.handler.ts index 04ce5d99be1..b0547563f39 100644 --- a/packages/core/src/tasks/commands/handlers/task-update.handler.ts +++ b/packages/core/src/tasks/commands/handlers/task-update.handler.ts @@ -59,14 +59,14 @@ export class TaskUpdateHandler implements ICommandHandler { request.title, request.description ); - // Make the Issue number, Repo, Owner and installtion id field dynamic + // Make the Issue number, Repo, Owner and installation id field dynamic // this._gitHubService.editIssue( // 48, // task.title, // task.description, // '', // '', - // 12345678 // installtion id + // 12345678 // installation id // ); return await this._taskService.create({ From b69b4b677290a0f0ca6194d0a6ee4c6d48c7d878 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE Date: Wed, 6 Sep 2023 13:18:27 +0530 Subject: [PATCH 007/104] chore(cspell): add my word --- .cspell.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.cspell.json b/.cspell.json index d03418753a2..e85ee114486 100644 --- a/.cspell.json +++ b/.cspell.json @@ -318,7 +318,9 @@ "signin", "ibase", "iwindow", - "Probot" + "Probot", + "Smee", + "badal" ], "useGitignore": true, "ignorePaths": [ From 07d2a5b28249ca6f87dc7189919059c7396e186f Mon Sep 17 00:00:00 2001 From: Ruslan Konviser Date: Fri, 8 Sep 2023 14:16:56 +0200 Subject: [PATCH 008/104] chore: add HubStaff required env vars and also switch to NX Cloud --- .deploy/api/Dockerfile | 6 +++++ .env.compose | 6 ++++- .env.docker | 5 ++++ .env.local | 5 ++++ .env.sample | 6 ++++- nx.json | 7 +++--- package.json | 1 + yarn.lock | 55 +++++++++++++++++++++++++++++++++++++++--- 8 files changed, 83 insertions(+), 8 deletions(-) diff --git a/.deploy/api/Dockerfile b/.deploy/api/Dockerfile index c19f6bfe3df..192a8b9122b 100644 --- a/.deploy/api/Dockerfile +++ b/.deploy/api/Dockerfile @@ -47,6 +47,9 @@ ARG FACEBOOK_GRAPH_VERSION ARG FACEBOOK_CALLBACK_URL ARG INTEGRATED_USER_DEFAULT_PASS ARG UPWORK_CALLBACK_URL +ARG HUBSTAFF_CLIENT_ID +ARG HUBSTAFF_CLIENT_SECRET +ARG HUBSTAFF_PERSONAL_ACCESS_TOKEN ARG FILE_PROVIDER ARG GAUZY_AI_GRAPHQL_ENDPOINT ARG GAUZY_AI_REST_ENDPOINT @@ -263,6 +266,9 @@ ENV FACEBOOK_GRAPH_VERSION=${FACEBOOK_GRAPH_VERSION} ENV FACEBOOK_CALLBACK_URL=${FACEBOOK_CALLBACK_URL} ENV INTEGRATED_USER_DEFAULT_PASS=${INTEGRATED_USER_DEFAULT_PASS} ENV UPWORK_CALLBACK_URL=${UPWORK_CALLBACK_URL} +ENV HUBSTAFF_CLIENT_ID=${HUBSTAFF_CLIENT_ID} +ENV HUBSTAFF_CLIENT_SECRET=${HUBSTAFF_CLIENT_SECRET} +ENV HUBSTAFF_PERSONAL_ACCESS_TOKEN=${HUBSTAFF_PERSONAL_ACCESS_TOKEN} ENV FILE_PROVIDER=${FILE_PROVIDER} ENV GAUZY_AI_GRAPHQL_ENDPOINT=${GAUZY_AI_GRAPHQL_ENDPOINT} ENV GAUZY_AI_REST_ENDPOINT=${GAUZY_AI_REST_ENDPOINT} diff --git a/.env.compose b/.env.compose index 260ffe80550..ffa4d8fa744 100644 --- a/.env.compose +++ b/.env.compose @@ -102,7 +102,6 @@ KEYCLOAK_SECRET=XXXXXXX KEYCLOAK_AUTH_SERVER_URL=XXXXXXX KEYCLOAK_COOKIE_KEY=XXXXXXX -INTEGRATED_HUBSTAFF_USER_PASS=hubstaffPassword UPWORK_CALLBACK_URL=http://localhost:3000/api/integrations/upwork # File System: LOCAL | S3 | WASABI | CLOUDINARY @@ -277,3 +276,8 @@ GITHUB_INTEGRATION_CLIENT_ID= GITHUB_INTEGRATION_CLIENT_SECRET= GITHUB_INTEGRATION_PRIVATE_KEY= GITHUB_INTEGRATION_WEBHOOK_SECRET= + +# HubStaff Integration +HUBSTAFF_CLIENT_ID= +HUBSTAFF_CLIENT_SECRET= +HUBSTAFF_PERSONAL_ACCESS_TOKEN= diff --git a/.env.docker b/.env.docker index 90ff80da92d..38c3e3756c2 100644 --- a/.env.docker +++ b/.env.docker @@ -248,3 +248,8 @@ GITHUB_INTEGRATION_CLIENT_ID= GITHUB_INTEGRATION_CLIENT_SECRET= GITHUB_INTEGRATION_PRIVATE_KEY= GITHUB_INTEGRATION_WEBHOOK_SECRET= + +# HubStaff Integration +HUBSTAFF_CLIENT_ID= +HUBSTAFF_CLIENT_SECRET= +HUBSTAFF_PERSONAL_ACCESS_TOKEN= diff --git a/.env.local b/.env.local index aea76435a29..ab2ec1c6fcd 100644 --- a/.env.local +++ b/.env.local @@ -235,3 +235,8 @@ GITHUB_INTEGRATION_CLIENT_ID= GITHUB_INTEGRATION_CLIENT_SECRET= GITHUB_INTEGRATION_PRIVATE_KEY= GITHUB_INTEGRATION_WEBHOOK_SECRET= + +# HubStaff Integration +HUBSTAFF_CLIENT_ID= +HUBSTAFF_CLIENT_SECRET= +HUBSTAFF_PERSONAL_ACCESS_TOKEN= diff --git a/.env.sample b/.env.sample index 3c1e1805df0..bd594e26fde 100644 --- a/.env.sample +++ b/.env.sample @@ -89,7 +89,6 @@ KEYCLOAK_SECRET=XXXXXXX KEYCLOAK_AUTH_SERVER_URL=XXXXXXX KEYCLOAK_COOKIE_KEY=XXXXXXX -INTEGRATED_HUBSTAFF_USER_PASS=hubstaffPassword UPWORK_CALLBACK_URL=http://localhost:3000/api/integrations/upwork # File System: LOCAL | S3 | WASABI | CLOUDINARY @@ -270,3 +269,8 @@ GITHUB_INTEGRATION_CLIENT_ID= GITHUB_INTEGRATION_CLIENT_SECRET= GITHUB_INTEGRATION_PRIVATE_KEY= GITHUB_INTEGRATION_WEBHOOK_SECRET= + +# HubStaff Integration +HUBSTAFF_CLIENT_ID= +HUBSTAFF_CLIENT_SECRET= +HUBSTAFF_PERSONAL_ACCESS_TOKEN= diff --git a/nx.json b/nx.json index 0a27992f015..19f1aa071e2 100644 --- a/nx.json +++ b/nx.json @@ -16,7 +16,7 @@ }, "tasksRunnerOptions": { "default": { - "runner": "@nrwl/workspace/tasks-runners/default", + "runner": "nx-cloud", "options": { "cacheableOperations": [ "build", @@ -24,7 +24,8 @@ "test", "e2e" ], - "parallel": 1 + "parallel": 1, + "accessToken": "NWJlMDFmMTMtYzUyZS00ZGE4LTk0NzctNWIzZTkxMzNhMTFlfHJlYWQtd3JpdGU=" } } }, @@ -69,4 +70,4 @@ } }, "defaultProject": "gauzy" -} \ No newline at end of file +} diff --git a/package.json b/package.json index e60de4d3567..f66fbea5e27 100644 --- a/package.json +++ b/package.json @@ -269,6 +269,7 @@ "@nrwl/linter": "^13.8.0", "@nrwl/nest": "^13.8.0", "@nrwl/node": "^13.8.0", + "@nrwl/nx-cloud": "^16.3.0", "@nrwl/tao": "^13.8.0", "@nrwl/workspace": "^13.8.0", "@nstudio/angular": "^13.0.1", diff --git a/yarn.lock b/yarn.lock index 98bdfcec2cd..5e23d3dd29d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5480,6 +5480,13 @@ webpack-merge "^5.8.0" webpack-node-externals "^3.0.0" +"@nrwl/nx-cloud@16.3.0", "@nrwl/nx-cloud@^16.3.0": + version "16.3.0" + resolved "https://registry.yarnpkg.com/@nrwl/nx-cloud/-/nx-cloud-16.3.0.tgz#0f1d563200af5bb6ce51a8408d698774d5ccdbbd" + integrity sha512-nJrGsVufhY74KcP7kM7BqFOGAoO5OEF6+wfiM295DgmEG9c1yW+x5QiQaC42K9SWYn/eKQa1X7466ZA5lynXoQ== + dependencies: + nx-cloud "16.3.0" + "@nrwl/storybook@13.10.6": version "13.10.6" resolved "https://registry.yarnpkg.com/@nrwl/storybook/-/storybook-13.10.6.tgz#087cfcac1104463938dbaa5b6ecd1ce78d7b692c" @@ -9616,6 +9623,15 @@ axe-core@3.5.5: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.5.tgz#84315073b53fa3c0c51676c588d59da09a192227" integrity sha512-5P0QZ6J5xGikH780pghEdbEKijCTrruK9KxtPZCFWUpef0f6GipO+xEZ5GKCb020mmqgbiNO6TcA55CriL784Q== +axios@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.3.tgz#8274250dada2edf53814ed7db644b9c2866c1e35" + integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axios@^0.21.0: version "0.21.4" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" @@ -17547,7 +17563,7 @@ fs-extra@9.1.0, fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^11.0.0: +fs-extra@^11.0.0, fs-extra@^11.1.0: version "11.1.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== @@ -25893,6 +25909,11 @@ node-mac@^1.0.1: plist "^3.0.1" yargs "^15.4.0" +node-machine-id@^1.1.12: + version "1.1.12" + resolved "https://registry.yarnpkg.com/node-machine-id/-/node-machine-id-1.1.12.tgz#37904eee1e59b320bb9c5d6c0a59f3b469cb6267" + integrity sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ== + node-notifier@^8.0.0: version "8.0.2" resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" @@ -26640,6 +26661,22 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== +nx-cloud@16.3.0: + version "16.3.0" + resolved "https://registry.yarnpkg.com/nx-cloud/-/nx-cloud-16.3.0.tgz#f916c0be1d7eb5d017d542fea349e09893502ee9" + integrity sha512-hmNgpeLO4v4WDSWa8YhwX+q+9ohIyY8iqxlWyIKixWzQH2XfRgYFjOLH4IDLGOlKa3hg7MB6+4+75cK9CfSmKw== + dependencies: + "@nrwl/nx-cloud" "16.3.0" + axios "1.1.3" + chalk "^4.1.0" + dotenv "~10.0.0" + fs-extra "^11.1.0" + node-machine-id "^1.1.12" + open "~8.4.0" + strip-json-comments "^3.1.1" + tar "6.1.11" + yargs-parser ">=21.1.1" + nx@13.10.6: version "13.10.6" resolved "https://registry.yarnpkg.com/nx/-/nx-13.10.6.tgz#ad9f0afcd3cbc8a6974a477a2c58213823c0b9d2" @@ -26918,7 +26955,7 @@ open@^6.4.0: dependencies: is-wsl "^1.1.0" -open@^8.0.9, open@^8.4.0: +open@^8.0.9, open@^8.4.0, open@~8.4.0: version "8.4.2" resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== @@ -33224,6 +33261,18 @@ tar-stream@^2.1.4, tar-stream@^2.2.0, tar-stream@~2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" +tar@6.1.11: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + tar@^4.4.10, tar@^4.4.12, tar@^4.4.19, tar@^4.4.8: version "4.4.19" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" @@ -36029,7 +36078,7 @@ yargs-parser@21.0.1: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== -yargs-parser@21.1.1, yargs-parser@^21.1.1: +yargs-parser@21.1.1, yargs-parser@>=21.1.1, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== From 734b73157737ecb27e3ba73f21507abff1b301bb Mon Sep 17 00:00:00 2001 From: Ruslan K Date: Sat, 9 Sep 2023 00:01:01 +0200 Subject: [PATCH 009/104] Update README.md --- packages/plugins/integration-github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugins/integration-github/README.md b/packages/plugins/integration-github/README.md index 3242848e9cf..5988c8f5a56 100644 --- a/packages/plugins/integration-github/README.md +++ b/packages/plugins/integration-github/README.md @@ -6,6 +6,6 @@ This library was generated with [Nx](https://nx.dev). Run `ng test integration-github` to execute the unit tests via [Jest](https://jestjs.io). -## License +## Credits In this plugin, we used code from https://github.com/yieldbits/nestjs (MIT license, https://github.com/yieldbits/nestjs/blob/main/LICENSE). From 1141f584527dab90a12eec94f49a72cb23826bab Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:09:03 +0530 Subject: [PATCH 010/104] wip: #6823 alter integration feature structure --- .../src/assets/seed/integrations/github.svg | 23 ++++++ .../api/src/assets/seed/integrations/jira.svg | 19 +++++ .../services/integrations-store.service.ts | 4 +- .../integrations/integrations.component.html | 4 +- .../integrations/integrations.component.scss | 6 +- packages/contracts/src/integration.model.ts | 35 +++++--- .../1691494801748-SeedIntegrationTable.ts | 12 +-- ...582196-AddColumnsToTheIntegrationTables.ts | 81 +++++++++++++++++++ .../integration/integration-type.entity.ts | 16 +++- .../src/integration/integration-type.seed.ts | 16 ++-- .../src/integration/integration.entity.ts | 32 ++++---- .../core/src/integration/integration.seed.ts | 4 +- 12 files changed, 201 insertions(+), 51 deletions(-) create mode 100644 apps/api/src/assets/seed/integrations/github.svg create mode 100644 apps/api/src/assets/seed/integrations/jira.svg create mode 100644 packages/core/src/database/migrations/1694409582196-AddColumnsToTheIntegrationTables.ts diff --git a/apps/api/src/assets/seed/integrations/github.svg b/apps/api/src/assets/seed/integrations/github.svg new file mode 100644 index 00000000000..924c263ac6d --- /dev/null +++ b/apps/api/src/assets/seed/integrations/github.svg @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/apps/api/src/assets/seed/integrations/jira.svg b/apps/api/src/assets/seed/integrations/jira.svg new file mode 100644 index 00000000000..bcd5d465358 --- /dev/null +++ b/apps/api/src/assets/seed/integrations/jira.svg @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/apps/gauzy/src/app/@core/services/integrations-store.service.ts b/apps/gauzy/src/app/@core/services/integrations-store.service.ts index e40b5ede677..bb87c38f6b5 100644 --- a/apps/gauzy/src/app/@core/services/integrations-store.service.ts +++ b/apps/gauzy/src/app/@core/services/integrations-store.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { IIntegrationViewModel, IIntegrationFilter, - IntegrationTypeNameEnum, + IntegrationTypeEnum, IntegrationTypeGroupEnum } from '@gauzy/contracts'; import { BehaviorSubject, Observable, of } from 'rxjs'; @@ -132,7 +132,7 @@ export class IntegrationsStoreService { ({ groupName }) => groupName === IntegrationTypeGroupEnum.FEATURED ); return featuredGroup.integrationTypes.find( - (item) => item.name === IntegrationTypeNameEnum.ALL_INTEGRATIONS + (item) => item.name === IntegrationTypeEnum.ALL_INTEGRATIONS ); } diff --git a/apps/gauzy/src/app/pages/integrations/integrations.component.html b/apps/gauzy/src/app/pages/integrations/integrations.component.html index d11e48e1d62..eeec10ce245 100644 --- a/apps/gauzy/src/app/pages/integrations/integrations.component.html +++ b/apps/gauzy/src/app/pages/integrations/integrations.component.html @@ -98,8 +98,8 @@

{{ 'INTEGRATIONS.AVAILABLE_INTEGRATIONS' | translate }}

[title]="integration.name" /> -
- +
+ {{ 'INTEGRATIONS.COMING_SOON' | translate }}
diff --git a/apps/gauzy/src/app/pages/integrations/integrations.component.scss b/apps/gauzy/src/app/pages/integrations/integrations.component.scss index 6a5023adbee..dd0174d7ed2 100644 --- a/apps/gauzy/src/app/pages/integrations/integrations.component.scss +++ b/apps/gauzy/src/app/pages/integrations/integrations.component.scss @@ -69,7 +69,7 @@ img { width: 100%; } -.comingSoon-wrapper { +.coming-soon-wrapper { position: absolute; right: -5px; top: -5px; @@ -78,7 +78,7 @@ img { overflow: hidden; } -.comingSoon { +.coming-soon { position: absolute; width: 130px; transform: rotate(45deg); @@ -190,4 +190,4 @@ nb-option { button { box-shadow: 0 1px 1px 0 rgb(0 0 0 / 15%); } -} \ No newline at end of file +} diff --git a/packages/contracts/src/integration.model.ts b/packages/contracts/src/integration.model.ts index c569276f331..f0a6a77d753 100644 --- a/packages/contracts/src/integration.model.ts +++ b/packages/contracts/src/integration.model.ts @@ -75,8 +75,11 @@ export interface IIntegration extends IBaseEntityModel { export interface IIntegrationType extends IBaseEntityModel { name: string; + description: string; + icon: string; groupName: string; order: number; + integrations: IIntegration[]; } export interface IIntegrationFilter { @@ -147,7 +150,9 @@ export interface IIntegrationTenantUpdateInput extends Pick[ - IntegrationTypeNameEnum.ALL_INTEGRATIONS + IntegrationTypeEnum.ALL_INTEGRATIONS ], order: 1, - navigationUrl: 'hubstaff' + navigationUrl: 'hubstaff', + slug: 'hubstaff' }, { name: IntegrationEnum.UPWORK, imgSrc: 'upwork.svg', isComingSoon: false, integrationTypesMap: [ - IntegrationTypeNameEnum.ALL_INTEGRATIONS + IntegrationTypeEnum.ALL_INTEGRATIONS ], order: 2, - navigationUrl: 'upwork' + navigationUrl: 'upwork', + slug: 'upwork' }, { name: IntegrationEnum.GAUZY_AI, imgSrc: 'gauzy-ai.svg', isComingSoon: false, integrationTypesMap: [ - IntegrationTypeNameEnum.ALL_INTEGRATIONS + IntegrationTypeEnum.ALL_INTEGRATIONS ], order: 3, - navigationUrl: 'gauzy-ai' + navigationUrl: 'gauzy-ai', + slug: 'gauzy-ai' }, { name: 'Import/Export', imgSrc: 'import-export.svg', isComingSoon: true, integrationTypesMap: [ - IntegrationTypeNameEnum.ALL_INTEGRATIONS, - IntegrationTypeNameEnum.CRM + IntegrationTypeEnum.ALL_INTEGRATIONS, + IntegrationTypeEnum.CRM ], order: 4, - navigationUrl: 'import-export' + navigationUrl: 'import-export', + slug: 'import-export' } ]; diff --git a/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts b/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts index 04066314932..a2611d3d144 100644 --- a/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts +++ b/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts @@ -33,7 +33,7 @@ export class SeedIntegrationTable1691494801748 implements MigrationInterface { public async upsertIntegrationsAndIntegrationTypes(queryRunner: QueryRunner): Promise { const destDir = 'integrations'; - for await (const { name, imgSrc, isComingSoon, order, navigationUrl, integrationTypesMap } of DEFAULT_INTEGRATIONS) { + for await (const { name, imgSrc, isComingSoon, order, navigationUrl, slug, integrationTypesMap } of DEFAULT_INTEGRATIONS) { try { const filepath = `integrations/${imgSrc}`; @@ -56,23 +56,25 @@ export class SeedIntegrationTable1691494801748 implements MigrationInterface { "imgSrc" = $2, "isComingSoon" = $3, "order" = $4, - "navigationUrl" = $5 + "navigationUrl" = $5, + "slug" = $6 RETURNING id; `; } else { upsertQuery = ` INSERT INTO "integration" ( - "name", "imgSrc", "isComingSoon", "order", "navigationUrl" + "name", "imgSrc", "isComingSoon", "order", "navigationUrl", "slug" ) VALUES ( - $1, $2, $3, $4, $5 + $1, $2, $3, $4, $5, $6 ) ON CONFLICT(name) DO UPDATE SET "imgSrc" = $2, "isComingSoon" = $3, "order" = $4, - "navigationUrl" = $5 + "navigationUrl" = $5, + "slug" = $6 RETURNING id; `; } diff --git a/packages/core/src/database/migrations/1694409582196-AddColumnsToTheIntegrationTables.ts b/packages/core/src/database/migrations/1694409582196-AddColumnsToTheIntegrationTables.ts new file mode 100644 index 00000000000..759b917ed16 --- /dev/null +++ b/packages/core/src/database/migrations/1694409582196-AddColumnsToTheIntegrationTables.ts @@ -0,0 +1,81 @@ + +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddColumnsToTheIntegrationTables1694409582196 implements MigrationInterface { + + name = 'AddColumnsToTheIntegrationTables1694409582196'; + + /** + * Up Migration + * + * @param queryRunner + */ + public async up(queryRunner: QueryRunner): Promise { + if (queryRunner.connection.options.type === 'sqlite') { + await this.sqliteUpQueryRunner(queryRunner); + } else { + await this.postgresUpQueryRunner(queryRunner); + } + } + + /** + * Down Migration + * + * @param queryRunner + */ + public async down(queryRunner: QueryRunner): Promise { + if (queryRunner.connection.options.type === 'sqlite') { + await this.sqliteDownQueryRunner(queryRunner); + } else { + await this.postgresDownQueryRunner(queryRunner); + } + } + + /** + * PostgresDB Up Migration + * + * @param queryRunner + */ + public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "integration" DROP COLUMN "navigationUrl"`); + await queryRunner.query(`ALTER TABLE "integration_type" ADD "description" character varying`); + await queryRunner.query(`ALTER TABLE "integration_type" ADD "icon" character varying`); + await queryRunner.query(`ALTER TABLE "integration" ADD "provider" character varying`); + await queryRunner.query(`ALTER TABLE "integration" ADD "slug" character varying`); + await queryRunner.query(`ALTER TABLE "integration" DROP CONSTRAINT "UQ_52d7fa32a7832b377fc2d7f6199"`); + await queryRunner.query(`ALTER TABLE "integration" ADD CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a" UNIQUE ("name", "provider")`); + } + + /** + * PostgresDB Down Migration + * + * @param queryRunner + */ + public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "integration" DROP CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a"`); + await queryRunner.query(`ALTER TABLE "integration" ADD CONSTRAINT "UQ_52d7fa32a7832b377fc2d7f6199" UNIQUE ("name")`); + await queryRunner.query(`ALTER TABLE "integration" DROP COLUMN "slug"`); + await queryRunner.query(`ALTER TABLE "integration" DROP COLUMN "provider"`); + await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "icon"`); + await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "description"`); + await queryRunner.query(`ALTER TABLE "integration" ADD "navigationUrl" character varying`); + } + + /** + * SqliteDB Up Migration + * + * @param queryRunner + */ + public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise { + + } + + /** + * SqliteDB Down Migration + * + * @param queryRunner + */ + public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise { + + } +} diff --git a/packages/core/src/integration/integration-type.entity.ts b/packages/core/src/integration/integration-type.entity.ts index 9abae870f9d..dea36e0f7d8 100644 --- a/packages/core/src/integration/integration-type.entity.ts +++ b/packages/core/src/integration/integration-type.entity.ts @@ -1,6 +1,6 @@ -import { ApiProperty } from '@nestjs/swagger'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Column, Entity, ManyToMany } from 'typeorm'; -import { IsNotEmpty, IsNumber } from 'class-validator'; +import { IsNotEmpty, IsNumber, IsOptional } from 'class-validator'; import { IIntegration, IIntegrationType } from '@gauzy/contracts'; import { BaseEntity, Integration } from '../core/entities/internal'; @@ -12,6 +12,16 @@ export class IntegrationType extends BaseEntity implements IIntegrationType { @Column() name: string; + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @Column({ nullable: true }) + description: string; + + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @Column({ nullable: true }) + icon: string; + @ApiProperty({ type: () => String }) @IsNotEmpty() @Column() @@ -32,5 +42,5 @@ export class IntegrationType extends BaseEntity implements IIntegrationType { onUpdate: 'CASCADE', onDelete: 'CASCADE' }) - integrations?: IIntegration[]; + integrations: IIntegration[]; } diff --git a/packages/core/src/integration/integration-type.seed.ts b/packages/core/src/integration/integration-type.seed.ts index f6563e1e910..35bf0c8490a 100644 --- a/packages/core/src/integration/integration-type.seed.ts +++ b/packages/core/src/integration/integration-type.seed.ts @@ -2,42 +2,42 @@ import { DataSource } from 'typeorm'; import { IntegrationType } from './integration-type.entity'; import { IntegrationTypeGroupEnum, - IntegrationTypeNameEnum + IntegrationTypeEnum } from '@gauzy/contracts'; const DEFAULT_INTEGRATION_TYPES = [ { - name: IntegrationTypeNameEnum.ALL_INTEGRATIONS, + name: IntegrationTypeEnum.ALL_INTEGRATIONS, groupName: IntegrationTypeGroupEnum.FEATURED, order: 1 }, { - name: IntegrationTypeNameEnum.FOR_SALES_TEAMS, + name: IntegrationTypeEnum.FOR_SALES_TEAMS, groupName: IntegrationTypeGroupEnum.FEATURED, order: 1 }, { - name: IntegrationTypeNameEnum.FOR_ACCOUNTANTS, + name: IntegrationTypeEnum.FOR_ACCOUNTANTS, groupName: IntegrationTypeGroupEnum.FEATURED, order: 1 }, { - name: IntegrationTypeNameEnum.FOR_SUPPORT_TEAMS, + name: IntegrationTypeEnum.FOR_SUPPORT_TEAMS, groupName: IntegrationTypeGroupEnum.FEATURED, order: 1 }, { - name: IntegrationTypeNameEnum.CRM, + name: IntegrationTypeEnum.CRM, groupName: IntegrationTypeGroupEnum.CATEGORIES, order: 2 }, { - name: IntegrationTypeNameEnum.SCHEDULING, + name: IntegrationTypeEnum.SCHEDULING, groupName: IntegrationTypeGroupEnum.CATEGORIES, order: 2 }, { - name: IntegrationTypeNameEnum.TOOLS, + name: IntegrationTypeEnum.TOOLS, groupName: IntegrationTypeGroupEnum.CATEGORIES, order: 2 } diff --git a/packages/core/src/integration/integration.entity.ts b/packages/core/src/integration/integration.entity.ts index 790a29c94bb..cbaf05d356d 100644 --- a/packages/core/src/integration/integration.entity.ts +++ b/packages/core/src/integration/integration.entity.ts @@ -7,7 +7,7 @@ import { BaseEntity, Tag } from '../core/entities/internal'; import { IntegrationType } from './integration-type.entity'; @Entity('integration') -@Unique(['name']) +@Unique(['name', 'provider']) export class Integration extends BaseEntity implements IIntegration { @ApiProperty({ type: () => String }) @@ -15,6 +15,16 @@ export class Integration extends BaseEntity implements IIntegration { @Column() // Define a unique constraint on the "name" column name: string; + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @Column({ nullable: true }) // Define a unique constraint on the "provider" column (E.g Github, Jira, Hubstaff) + provider: string; + + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @Column({ nullable: true }) + slug: string; + @ApiPropertyOptional({ type: () => String }) @IsOptional() @Column({ nullable: true }) @@ -24,34 +34,29 @@ export class Integration extends BaseEntity implements IIntegration { @IsOptional() @IsBoolean() @Column({ default: false }) - isComingSoon?: boolean; + isComingSoon: boolean; @ApiPropertyOptional({ type: () => Boolean, default: false }) @IsOptional() @IsBoolean() @Column({ default: false }) - isPaid?: boolean; - - @ApiPropertyOptional({ type: () => String }) - @IsOptional() - @Column({ nullable: true }) - version?: string; + isPaid: boolean; @ApiPropertyOptional({ type: () => String }) @IsOptional() @Column({ nullable: true }) - docUrl?: string; + version: string; @ApiPropertyOptional({ type: () => String }) @IsOptional() @Column({ nullable: true }) - navigationUrl?: string; + docUrl: string; @ApiPropertyOptional({ type: () => Boolean, default: false }) @IsOptional() @IsBoolean() @Column({ default: false }) - isFreeTrial?: boolean; + isFreeTrial: boolean; @ApiPropertyOptional({ type: () => Number }) @IsOptional() @@ -62,16 +67,15 @@ export class Integration extends BaseEntity implements IIntegration { type: 'numeric', transformer: new ColumnNumericTransformerPipe() }) - freeTrialPeriod?: number; + freeTrialPeriod: number; @ApiPropertyOptional({ type: () => Number }) @IsOptional() @IsNumber() @Column({ nullable: true }) - order?: number; + order: number; fullImgUrl?: string; - /* |-------------------------------------------------------------------------- | @ManyToMany diff --git a/packages/core/src/integration/integration.seed.ts b/packages/core/src/integration/integration.seed.ts index ab19d5658b2..2df34d50f17 100644 --- a/packages/core/src/integration/integration.seed.ts +++ b/packages/core/src/integration/integration.seed.ts @@ -22,14 +22,14 @@ export const createDefaultIntegrations = async ( const integrations: IIntegration[] = []; for await (const integration of DEFAULT_INTEGRATIONS) { - const { name, imgSrc, isComingSoon, integrationTypesMap, order, navigationUrl } = integration; + const { name, imgSrc, isComingSoon, integrationTypesMap, order, slug } = integration; const entity = new Integration(); entity.name = name; entity.imgSrc = copyAssets(imgSrc, config, destDir); entity.isComingSoon = isComingSoon; entity.order = order; - entity.navigationUrl = navigationUrl; + entity.slug = slug; entity.integrationTypes = integrationTypes.filter((it) => integrationTypesMap.includes(it.name) ); From 16c0fd8c4edbfed1011a63f7c1baee56fcd9f822 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 11 Sep 2023 17:26:07 +0530 Subject: [PATCH 011/104] chore(deps): yarn.lock --- yarn.lock | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 564ea443a9b..e2c72202519 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6836,6 +6836,17 @@ "@sentry/utils" "7.68.0" tslib "^2.4.1 || ^1.9.3" +"@sentry/core@6.19.7": + version "6.19.7" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.19.7.tgz#156aaa56dd7fad8c89c145be6ad7a4f7209f9785" + integrity sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw== + dependencies: + "@sentry/hub" "6.19.7" + "@sentry/minimal" "6.19.7" + "@sentry/types" "6.19.7" + "@sentry/utils" "6.19.7" + tslib "^1.9.3" + "@sentry/core@7.63.0": version "7.63.0" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.63.0.tgz#8c38da6ef3a1de6e364463a09bc703b196ecbba4" @@ -6868,6 +6879,15 @@ lru_map "^0.3.3" tslib "^2.5.0" +"@sentry/hub@6.19.7": + version "6.19.7" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.19.7.tgz#58ad7776bbd31e9596a8ec46365b45cd8b9cfd11" + integrity sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA== + dependencies: + "@sentry/types" "6.19.7" + "@sentry/utils" "6.19.7" + tslib "^1.9.3" + "@sentry/hub@^7.68.0": version "7.68.0" resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-7.68.0.tgz#f317877a1d2819e9426696bfc0ba0c7cbe608e29" @@ -6878,6 +6898,15 @@ "@sentry/utils" "7.68.0" tslib "^2.4.1 || ^1.9.3" +"@sentry/minimal@6.19.7": + version "6.19.7" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.19.7.tgz#b3ee46d6abef9ef3dd4837ebcb6bdfd01b9aa7b4" + integrity sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ== + dependencies: + "@sentry/hub" "6.19.7" + "@sentry/types" "6.19.7" + tslib "^1.9.3" + "@sentry/node@7.63.0": version "7.63.0" resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.63.0.tgz#38508a440c04c0e98d00f5a1855e5448ee70c8d6" @@ -6892,6 +6921,20 @@ lru_map "^0.3.3" tslib "^2.4.1 || ^1.9.3" +"@sentry/node@^6.0.0": + version "6.19.7" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.19.7.tgz#32963b36b48daebbd559e6f13b1deb2415448592" + integrity sha512-gtmRC4dAXKODMpHXKfrkfvyBL3cI8y64vEi3fDD046uqYcrWdgoQsffuBbxMAizc6Ez1ia+f0Flue6p15Qaltg== + dependencies: + "@sentry/core" "6.19.7" + "@sentry/hub" "6.19.7" + "@sentry/types" "6.19.7" + "@sentry/utils" "6.19.7" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + "@sentry/node@^7.68.0": version "7.68.0" resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.68.0.tgz#da3407ba7455109cf625c9a134a3f74ec5f5ca6b" @@ -6931,6 +6974,11 @@ dependencies: "@sentry-internal/tracing" "7.68.0" +"@sentry/types@6.19.7": + version "6.19.7" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.19.7.tgz#c6b337912e588083fc2896eb012526cf7cfec7c7" + integrity sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg== + "@sentry/types@7.63.0": version "7.63.0" resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.63.0.tgz#8032029fee6f70e04b667646626a674b03e2f79b" @@ -6941,6 +6989,14 @@ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.68.0.tgz#6134511106eed90bf033dc2ce76955f61582b48f" integrity sha512-5J2pH1Pjx/029zTm3CNY9MaE8Aui81nG7JCtlMp7uEfQ//9Ja4d4Sliz/kV4ARbkIKUZerSgaRAm3xCy5XOXLg== +"@sentry/utils@6.19.7": + version "6.19.7" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.19.7.tgz#6edd739f8185fd71afe49cbe351c1bbf5e7b7c79" + integrity sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA== + dependencies: + "@sentry/types" "6.19.7" + tslib "^1.9.3" + "@sentry/utils@7.63.0": version "7.63.0" resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.63.0.tgz#7c598553b4dbb6e3740dc96bc7f112ec32edbe69" @@ -33952,7 +34008,7 @@ tslib@2.5.0, tslib@^2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.2.0, t resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== -tslib@^1.10.0, tslib@^1.13.0, tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.10.0, tslib@^1.13.0, tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== From 611c656d2080e4845eae03f588a1db909737d8ff Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 18 Sep 2023 13:02:07 +0530 Subject: [PATCH 012/104] wip: github & jira integration sync in DB --- .../integrations/integrations.component.ts | 17 +++- packages/contracts/src/integration.model.ts | 66 +-------------- .../1691494801748-SeedIntegrationTable.ts | 21 ++--- .../1692171665427-SeedIntegrationTable.ts | 59 ++++++++++---- ...582196-AddColumnsToTheIntegrationTables.ts | 81 ------------------- .../src/integration/default-integration.ts | 54 +++++++++++++ .../src/integration/integration.entity.ts | 4 +- .../core/src/integration/integration.seed.ts | 8 +- 8 files changed, 131 insertions(+), 179 deletions(-) delete mode 100644 packages/core/src/database/migrations/1694409582196-AddColumnsToTheIntegrationTables.ts create mode 100644 packages/core/src/integration/default-integration.ts diff --git a/apps/gauzy/src/app/pages/integrations/integrations.component.ts b/apps/gauzy/src/app/pages/integrations/integrations.component.ts index f9ab77bc9b2..2378d53de3f 100644 --- a/apps/gauzy/src/app/pages/integrations/integrations.component.ts +++ b/apps/gauzy/src/app/pages/integrations/integrations.component.ts @@ -8,7 +8,7 @@ import { import { Observable } from 'rxjs'; import { IIntegrationViewModel, - DEFAULT_INTEGRATION_PAID_FILTERS + IntegrationFilterEnum } from '@gauzy/contracts'; import { InitialFilter, @@ -29,7 +29,20 @@ export class IntegrationsComponent implements OnInit { isLoading$: Observable = this._integrationsStore.isLoading$; @ViewChild('searchElement', { static: true }) searchElement: ElementRef; - filters = DEFAULT_INTEGRATION_PAID_FILTERS; + public filters = [ + { + label: IntegrationFilterEnum.ALL, + value: 'all' + }, + { + label: IntegrationFilterEnum.FREE, + value: 'false' + }, + { + label: IntegrationFilterEnum.PAID, + value: 'true' + } + ]; constructor( private readonly _integrationsStore: IntegrationsStoreService, diff --git a/packages/contracts/src/integration.model.ts b/packages/contracts/src/integration.model.ts index f0a6a77d753..981870f179d 100644 --- a/packages/contracts/src/integration.model.ts +++ b/packages/contracts/src/integration.model.ts @@ -148,9 +148,10 @@ export interface IIntegrationTenantCreateInput extends IBasePerTenantAndOrganiza export interface IIntegrationTenantUpdateInput extends Pick { } export enum IntegrationEnum { + IMPORT_EXPORT = 'Import-Export', UPWORK = 'Upwork', HUBSTAFF = 'Hubstaff', - GAUZY_AI = 'Gauzy AI', + GAUZY_AI = 'Gauzy_AI', GITHUB = 'Github', JIRA = 'Jira' } @@ -195,69 +196,6 @@ export enum IntegrationFilterEnum { PAID = 'Paid' } -export const DEFAULT_INTEGRATION_PAID_FILTERS = [ - { - label: IntegrationFilterEnum.ALL, - value: 'all' - }, - { - label: IntegrationFilterEnum.FREE, - value: 'false' - }, - { - label: IntegrationFilterEnum.PAID, - value: 'true' - } -]; - -export const DEFAULT_INTEGRATIONS = [ - { - name: IntegrationEnum.HUBSTAFF, - imgSrc: 'hubstaff.svg', - isComingSoon: false, - integrationTypesMap: [ - IntegrationTypeEnum.ALL_INTEGRATIONS - ], - order: 1, - navigationUrl: 'hubstaff', - slug: 'hubstaff' - }, - { - name: IntegrationEnum.UPWORK, - imgSrc: 'upwork.svg', - isComingSoon: false, - integrationTypesMap: [ - IntegrationTypeEnum.ALL_INTEGRATIONS - ], - order: 2, - navigationUrl: 'upwork', - slug: 'upwork' - }, - { - name: IntegrationEnum.GAUZY_AI, - imgSrc: 'gauzy-ai.svg', - isComingSoon: false, - integrationTypesMap: [ - IntegrationTypeEnum.ALL_INTEGRATIONS - ], - order: 3, - navigationUrl: 'gauzy-ai', - slug: 'gauzy-ai' - }, - { - name: 'Import/Export', - imgSrc: 'import-export.svg', - isComingSoon: true, - integrationTypesMap: [ - IntegrationTypeEnum.ALL_INTEGRATIONS, - IntegrationTypeEnum.CRM - ], - order: 4, - navigationUrl: 'import-export', - slug: 'import-export' - } -]; - /** * Hubstaff Integration */ diff --git a/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts b/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts index a2611d3d144..73611908e99 100644 --- a/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts +++ b/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts @@ -1,9 +1,10 @@ import { MigrationInterface, QueryRunner } from "typeorm"; import { v4 as uuidv4 } from 'uuid'; -import { DEFAULT_INTEGRATIONS, IIntegration, IIntegrationType } from "@gauzy/contracts"; +import { IIntegration, IIntegrationType } from "@gauzy/contracts"; import { getConfig } from "@gauzy/config"; import { copyAssets } from "./../../core/seeds/utils"; +import { DEFAULT_INTEGRATIONS } from "./../../integration/default-integration"; export class SeedIntegrationTable1691494801748 implements MigrationInterface { @@ -33,12 +34,12 @@ export class SeedIntegrationTable1691494801748 implements MigrationInterface { public async upsertIntegrationsAndIntegrationTypes(queryRunner: QueryRunner): Promise { const destDir = 'integrations'; - for await (const { name, imgSrc, isComingSoon, order, navigationUrl, slug, integrationTypesMap } of DEFAULT_INTEGRATIONS) { + for await (const { name, imgSrc, isComingSoon, order, provider, redirect_url, integrationTypesMap } of DEFAULT_INTEGRATIONS) { try { const filepath = `integrations/${imgSrc}`; let upsertQuery = ``; - const payload = [name, filepath, isComingSoon, order, navigationUrl]; + const payload = [name, filepath, isComingSoon, order, provider, redirect_url]; if (queryRunner.connection.options.type === 'sqlite') { // For SQLite, manually generate a UUID using uuidv4() @@ -46,24 +47,24 @@ export class SeedIntegrationTable1691494801748 implements MigrationInterface { upsertQuery = ` INSERT INTO integration ( - "name", "imgSrc", "isComingSoon", "order", "navigationUrl", "id" + "name", "imgSrc", "isComingSoon", "order", "provider", "redirect_url", "id" ) VALUES ( - $1, $2, $3, $4, $5, $6 + $1, $2, $3, $4, $5, $6, $7 ) ON CONFLICT(name) DO UPDATE SET "imgSrc" = $2, "isComingSoon" = $3, "order" = $4, - "navigationUrl" = $5, - "slug" = $6 + "provider" = $5, + "redirect_url" = $6 RETURNING id; `; } else { upsertQuery = ` INSERT INTO "integration" ( - "name", "imgSrc", "isComingSoon", "order", "navigationUrl", "slug" + "name", "imgSrc", "isComingSoon", "order", "provider", "redirect_url" ) VALUES ( $1, $2, $3, $4, $5, $6 @@ -73,8 +74,8 @@ export class SeedIntegrationTable1691494801748 implements MigrationInterface { "imgSrc" = $2, "isComingSoon" = $3, "order" = $4, - "navigationUrl" = $5, - "slug" = $6 + "provider" = $5, + "redirect_url" = $6 RETURNING id; `; } diff --git a/packages/core/src/database/migrations/1692171665427-SeedIntegrationTable.ts b/packages/core/src/database/migrations/1692171665427-SeedIntegrationTable.ts index 7b6d3437a98..646fa42625f 100644 --- a/packages/core/src/database/migrations/1692171665427-SeedIntegrationTable.ts +++ b/packages/core/src/database/migrations/1692171665427-SeedIntegrationTable.ts @@ -1,8 +1,9 @@ import { MigrationInterface, QueryRunner } from "typeorm"; -import { DEFAULT_INTEGRATIONS } from "@gauzy/contracts"; +import { v4 as uuidv4 } from 'uuid'; import { getConfig } from "@gauzy/config"; import { copyAssets } from "./../../core/seeds/utils"; +import { DEFAULT_INTEGRATIONS } from "./../../integration/default-integration"; export class SeedIntegrationTable1692171665427 implements MigrationInterface { @@ -31,24 +32,48 @@ export class SeedIntegrationTable1692171665427 implements MigrationInterface { */ public async upsertIntegrations(queryRunner: QueryRunner): Promise { const destDir = 'integrations'; - for await (const { name, imgSrc, navigationUrl } of DEFAULT_INTEGRATIONS) { + for await (const { name, imgSrc, provider, redirect_url } of DEFAULT_INTEGRATIONS) { try { const filepath = `integrations/${imgSrc}`; - const payload = [name, filepath, navigationUrl]; - - const upsertQuery = ` - INSERT INTO integration ( - "name", "imgSrc", "navigationUrl" - ) - VALUES ( - $1, $2, $3 - ) - ON CONFLICT(name) DO UPDATE - SET - "imgSrc" = $2, - "navigationUrl" = $3; - `; - await queryRunner.query(upsertQuery, payload); + const payload = [name, filepath, provider, redirect_url]; + + let upsertQuery = ``; + + if (queryRunner.connection.options.type === 'sqlite') { + // For SQLite, manually generate a UUID using uuidv4() + const generatedId = uuidv4(); payload.push(generatedId); + + upsertQuery = ` + INSERT INTO integration ( + "name", "imgSrc", "provider", "redirect_url", "id" + ) + VALUES ( + $1, $2, $3, $4, $5 + ) + ON CONFLICT(name) DO UPDATE + SET + "imgSrc" = $2, + "provider" = $3, + "redirect_url" = $4; + `; + + } else { + upsertQuery = ` + INSERT INTO integration ( + "name", "imgSrc", "provider", "redirect_url" + ) + VALUES ( + $1, $2, $3, $4 + ) + ON CONFLICT(name) DO UPDATE + SET + "imgSrc" = $2, + "provider" = $3, + "redirect_url" = $4; + `; + } + const [integration] = await queryRunner.query(upsertQuery, payload); + console.log(integration); copyAssets(imgSrc, this.config, destDir); } catch (error) { // since we have errors let's rollback changes we made diff --git a/packages/core/src/database/migrations/1694409582196-AddColumnsToTheIntegrationTables.ts b/packages/core/src/database/migrations/1694409582196-AddColumnsToTheIntegrationTables.ts deleted file mode 100644 index 759b917ed16..00000000000 --- a/packages/core/src/database/migrations/1694409582196-AddColumnsToTheIntegrationTables.ts +++ /dev/null @@ -1,81 +0,0 @@ - -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddColumnsToTheIntegrationTables1694409582196 implements MigrationInterface { - - name = 'AddColumnsToTheIntegrationTables1694409582196'; - - /** - * Up Migration - * - * @param queryRunner - */ - public async up(queryRunner: QueryRunner): Promise { - if (queryRunner.connection.options.type === 'sqlite') { - await this.sqliteUpQueryRunner(queryRunner); - } else { - await this.postgresUpQueryRunner(queryRunner); - } - } - - /** - * Down Migration - * - * @param queryRunner - */ - public async down(queryRunner: QueryRunner): Promise { - if (queryRunner.connection.options.type === 'sqlite') { - await this.sqliteDownQueryRunner(queryRunner); - } else { - await this.postgresDownQueryRunner(queryRunner); - } - } - - /** - * PostgresDB Up Migration - * - * @param queryRunner - */ - public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "integration" DROP COLUMN "navigationUrl"`); - await queryRunner.query(`ALTER TABLE "integration_type" ADD "description" character varying`); - await queryRunner.query(`ALTER TABLE "integration_type" ADD "icon" character varying`); - await queryRunner.query(`ALTER TABLE "integration" ADD "provider" character varying`); - await queryRunner.query(`ALTER TABLE "integration" ADD "slug" character varying`); - await queryRunner.query(`ALTER TABLE "integration" DROP CONSTRAINT "UQ_52d7fa32a7832b377fc2d7f6199"`); - await queryRunner.query(`ALTER TABLE "integration" ADD CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a" UNIQUE ("name", "provider")`); - } - - /** - * PostgresDB Down Migration - * - * @param queryRunner - */ - public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "integration" DROP CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a"`); - await queryRunner.query(`ALTER TABLE "integration" ADD CONSTRAINT "UQ_52d7fa32a7832b377fc2d7f6199" UNIQUE ("name")`); - await queryRunner.query(`ALTER TABLE "integration" DROP COLUMN "slug"`); - await queryRunner.query(`ALTER TABLE "integration" DROP COLUMN "provider"`); - await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "icon"`); - await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "description"`); - await queryRunner.query(`ALTER TABLE "integration" ADD "navigationUrl" character varying`); - } - - /** - * SqliteDB Up Migration - * - * @param queryRunner - */ - public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise { - - } - - /** - * SqliteDB Down Migration - * - * @param queryRunner - */ - public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise { - - } -} diff --git a/packages/core/src/integration/default-integration.ts b/packages/core/src/integration/default-integration.ts new file mode 100644 index 00000000000..c0bc2a968b4 --- /dev/null +++ b/packages/core/src/integration/default-integration.ts @@ -0,0 +1,54 @@ +import { sluggable } from "@gauzy/common"; +import { IntegrationEnum, IntegrationTypeEnum } from "@gauzy/contracts"; + +export const DEFAULT_INTEGRATIONS = [ + { + name: IntegrationEnum.HUBSTAFF, + imgSrc: 'hubstaff.svg', + isComingSoon: false, + integrationTypesMap: [ + IntegrationTypeEnum.ALL_INTEGRATIONS + ], + order: 1, + navigationUrl: 'hubstaff', + redirect_url: sluggable(IntegrationEnum.HUBSTAFF), + provider: IntegrationEnum.HUBSTAFF + }, + { + name: IntegrationEnum.UPWORK, + imgSrc: 'upwork.svg', + isComingSoon: false, + integrationTypesMap: [ + IntegrationTypeEnum.ALL_INTEGRATIONS + ], + order: 2, + navigationUrl: 'upwork', + redirect_url: sluggable(IntegrationEnum.UPWORK), + provider: IntegrationEnum.UPWORK + }, + { + name: IntegrationEnum.GAUZY_AI, + imgSrc: 'gauzy-ai.svg', + isComingSoon: false, + integrationTypesMap: [ + IntegrationTypeEnum.ALL_INTEGRATIONS + ], + order: 3, + navigationUrl: 'gauzy-ai', + redirect_url: sluggable(IntegrationEnum.GAUZY_AI), + provider: IntegrationEnum.GAUZY_AI + }, + { + name: 'Import/Export', + imgSrc: 'import-export.svg', + isComingSoon: true, + integrationTypesMap: [ + IntegrationTypeEnum.ALL_INTEGRATIONS, + IntegrationTypeEnum.CRM + ], + order: 4, + navigationUrl: 'import-export', + redirect_url: sluggable('import-export'), + provider: 'import_export' + } +]; diff --git a/packages/core/src/integration/integration.entity.ts b/packages/core/src/integration/integration.entity.ts index cbaf05d356d..093f2d7da14 100644 --- a/packages/core/src/integration/integration.entity.ts +++ b/packages/core/src/integration/integration.entity.ts @@ -17,13 +17,13 @@ export class Integration extends BaseEntity implements IIntegration { @ApiPropertyOptional({ type: () => String }) @IsOptional() - @Column({ nullable: true }) // Define a unique constraint on the "provider" column (E.g Github, Jira, Hubstaff) + @Column({ nullable: true }) // Define a unique constraint on the "provider" column (E.g github, jira, hubstaff) provider: string; @ApiPropertyOptional({ type: () => String }) @IsOptional() @Column({ nullable: true }) - slug: string; + redirect_url: string; @ApiPropertyOptional({ type: () => String }) @IsOptional() diff --git a/packages/core/src/integration/integration.seed.ts b/packages/core/src/integration/integration.seed.ts index 2df34d50f17..e415abd2bb4 100644 --- a/packages/core/src/integration/integration.seed.ts +++ b/packages/core/src/integration/integration.seed.ts @@ -1,8 +1,9 @@ import { DataSource } from 'typeorm'; import { getConfig } from '@gauzy/config'; -import { IIntegration, DEFAULT_INTEGRATIONS, IIntegrationType } from '@gauzy/contracts'; +import { IIntegration, IIntegrationType } from '@gauzy/contracts'; import { cleanAssets, copyAssets } from './../core/seeds/utils'; import { Integration } from './integration.entity'; +import { DEFAULT_INTEGRATIONS } from './default-integration'; const config = getConfig(); @@ -22,14 +23,15 @@ export const createDefaultIntegrations = async ( const integrations: IIntegration[] = []; for await (const integration of DEFAULT_INTEGRATIONS) { - const { name, imgSrc, isComingSoon, integrationTypesMap, order, slug } = integration; + const { name, imgSrc, isComingSoon, integrationTypesMap, order, provider, redirect_url } = integration; const entity = new Integration(); entity.name = name; entity.imgSrc = copyAssets(imgSrc, config, destDir); entity.isComingSoon = isComingSoon; entity.order = order; - entity.slug = slug; + entity.redirect_url = redirect_url; + entity.provider = provider; entity.integrationTypes = integrationTypes.filter((it) => integrationTypesMap.includes(it.name) ); From 4f9bbff175f4fd7ea909c1fa4749d4589ba21b96 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 18 Sep 2023 15:52:16 +0530 Subject: [PATCH 013/104] feat(migration): integration [table] alter columns --- ...3907817-AddColumnsToTheIntegrationTable.ts | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts diff --git a/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts b/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts new file mode 100644 index 00000000000..778cb8fc3d4 --- /dev/null +++ b/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts @@ -0,0 +1,117 @@ + +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationInterface { + + name = 'AddColumnsToTheIntegrationTable1695023907817'; + + /** + * Up Migration + * + * @param queryRunner + */ + public async up(queryRunner: QueryRunner): Promise { + if (queryRunner.connection.options.type === 'sqlite') { + await this.sqliteUpQueryRunner(queryRunner); + } else { + await this.postgresUpQueryRunner(queryRunner); + } + } + + /** + * Down Migration + * + * @param queryRunner + */ + public async down(queryRunner: QueryRunner): Promise { + if (queryRunner.connection.options.type === 'sqlite') { + await this.sqliteDownQueryRunner(queryRunner); + } else { + await this.postgresDownQueryRunner(queryRunner); + } + } + + /** + * PostgresDB Up Migration + * + * @param queryRunner + */ + public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "integration" RENAME COLUMN "navigationUrl" TO "redirectUrl"`); + await queryRunner.query(`ALTER TABLE "integration_type" ADD "description" character varying`); + await queryRunner.query(`ALTER TABLE "integration_type" ADD "icon" character varying`); + await queryRunner.query(`ALTER TABLE "integration" ADD "provider" character varying`); + await queryRunner.query(`ALTER TABLE "integration" DROP CONSTRAINT "UQ_52d7fa32a7832b377fc2d7f6199"`); + await queryRunner.query(`ALTER TABLE "integration" ADD CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a" UNIQUE ("name", "provider")`); + } + + /** + * PostgresDB Down Migration + * + * @param queryRunner + */ + public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "integration" DROP CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a"`); + await queryRunner.query(`ALTER TABLE "integration" ADD CONSTRAINT "UQ_52d7fa32a7832b377fc2d7f6199" UNIQUE ("name")`); + await queryRunner.query(`ALTER TABLE "integration" DROP COLUMN "provider"`); + await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "icon"`); + await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "description"`); + await queryRunner.query(`ALTER TABLE "integration" RENAME COLUMN "redirectUrl" TO "navigationUrl"`); + } + + /** + * SqliteDB Up Migration + * + * @param queryRunner + */ + public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "integration"`); + await queryRunner.query(`DROP TABLE "integration"`); + await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); + await queryRunner.query(`CREATE TABLE "temporary_integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL, "description" varchar, "icon" varchar)`); + await queryRunner.query(`INSERT INTO "temporary_integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "integration_type"`); + await queryRunner.query(`DROP TABLE "integration_type"`); + await queryRunner.query(`ALTER TABLE "temporary_integration_type" RENAME TO "integration_type"`); + await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "integration"`); + await queryRunner.query(`DROP TABLE "integration"`); + await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); + await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "integration"`); + await queryRunner.query(`DROP TABLE "integration"`); + await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); + await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"), CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a" UNIQUE ("name", "provider"))`); + await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "integration"`); + await queryRunner.query(`DROP TABLE "integration"`); + await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); + } + + /** + * SqliteDB Down Migration + * + * @param queryRunner + */ + public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); + await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "temporary_integration"`); + await queryRunner.query(`DROP TABLE "temporary_integration"`); + await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); + await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "temporary_integration"`); + await queryRunner.query(`DROP TABLE "temporary_integration"`); + await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); + await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "temporary_integration"`); + await queryRunner.query(`DROP TABLE "temporary_integration"`); + await queryRunner.query(`ALTER TABLE "integration_type" RENAME TO "temporary_integration_type"`); + await queryRunner.query(`CREATE TABLE "integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL)`); + await queryRunner.query(`INSERT INTO "integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "temporary_integration_type"`); + await queryRunner.query(`DROP TABLE "temporary_integration_type"`); + await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); + await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "navigationUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "temporary_integration"`); + await queryRunner.query(`DROP TABLE "temporary_integration"`); + } +} From 8796ddcdd33bbb1cf2c55f24754ce54e474568f1 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:20:41 +0530 Subject: [PATCH 014/104] fix: integration columns extended --- packages/contracts/src/integration.model.ts | 5 +++-- packages/core/src/integration/integration.entity.ts | 2 +- packages/core/src/integration/integration.seed.ts | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/contracts/src/integration.model.ts b/packages/contracts/src/integration.model.ts index 981870f179d..9fef1827343 100644 --- a/packages/contracts/src/integration.model.ts +++ b/packages/contracts/src/integration.model.ts @@ -64,7 +64,8 @@ export interface IIntegration extends IBaseEntityModel { isPaid?: boolean; version?: string; docUrl?: string; - navigationUrl?: string; + provider?: string; + redirectUrl?: string; isFreeTrial?: boolean; freeTrialPeriod?: number; order?: number; @@ -148,7 +149,7 @@ export interface IIntegrationTenantCreateInput extends IBasePerTenantAndOrganiza export interface IIntegrationTenantUpdateInput extends Pick { } export enum IntegrationEnum { - IMPORT_EXPORT = 'Import-Export', + IMPORT_EXPORT = 'Import_Export', UPWORK = 'Upwork', HUBSTAFF = 'Hubstaff', GAUZY_AI = 'Gauzy_AI', diff --git a/packages/core/src/integration/integration.entity.ts b/packages/core/src/integration/integration.entity.ts index 093f2d7da14..e595d442842 100644 --- a/packages/core/src/integration/integration.entity.ts +++ b/packages/core/src/integration/integration.entity.ts @@ -23,7 +23,7 @@ export class Integration extends BaseEntity implements IIntegration { @ApiPropertyOptional({ type: () => String }) @IsOptional() @Column({ nullable: true }) - redirect_url: string; + redirectUrl: string; @ApiPropertyOptional({ type: () => String }) @IsOptional() diff --git a/packages/core/src/integration/integration.seed.ts b/packages/core/src/integration/integration.seed.ts index e415abd2bb4..43fa9fff248 100644 --- a/packages/core/src/integration/integration.seed.ts +++ b/packages/core/src/integration/integration.seed.ts @@ -23,14 +23,14 @@ export const createDefaultIntegrations = async ( const integrations: IIntegration[] = []; for await (const integration of DEFAULT_INTEGRATIONS) { - const { name, imgSrc, isComingSoon, integrationTypesMap, order, provider, redirect_url } = integration; + const { name, imgSrc, isComingSoon, integrationTypesMap, order, provider, redirectUrl } = integration; const entity = new Integration(); entity.name = name; entity.imgSrc = copyAssets(imgSrc, config, destDir); entity.isComingSoon = isComingSoon; entity.order = order; - entity.redirect_url = redirect_url; + entity.redirectUrl = redirectUrl; entity.provider = provider; entity.integrationTypes = integrationTypes.filter((it) => integrationTypesMap.includes(it.name) From ccbeb7b2c703f34e6a74abf87ba600b894ee3661 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:21:09 +0530 Subject: [PATCH 015/104] wip: synced new integrations and merged with old once --- .../1691494801748-SeedIntegrationTable.ts | 84 ++--------- .../1692171665427-SeedIntegrationTable.ts | 48 ++++--- ...3907817-AddColumnsToTheIntegrationTable.ts | 117 ---------------- .../src/integration/default-integration.ts | 73 +++++++--- packages/core/src/integration/utils.ts | 132 ++++++++++++++++++ 5 files changed, 233 insertions(+), 221 deletions(-) delete mode 100644 packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts create mode 100644 packages/core/src/integration/utils.ts diff --git a/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts b/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts index 73611908e99..0292c627469 100644 --- a/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts +++ b/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts @@ -1,14 +1,13 @@ import { MigrationInterface, QueryRunner } from "typeorm"; import { v4 as uuidv4 } from 'uuid'; -import { IIntegration, IIntegrationType } from "@gauzy/contracts"; import { getConfig } from "@gauzy/config"; import { copyAssets } from "./../../core/seeds/utils"; -import { DEFAULT_INTEGRATIONS } from "./../../integration/default-integration"; +import { DEFAULT_SYSTEM_INTEGRATIONS } from "./../../integration/default-integration"; +import { IntegrationsUtils } from "./../../integration/utils"; export class SeedIntegrationTable1691494801748 implements MigrationInterface { - config = getConfig(); name = 'SeedIntegrationTable1691494801748'; /** @@ -34,12 +33,12 @@ export class SeedIntegrationTable1691494801748 implements MigrationInterface { public async upsertIntegrationsAndIntegrationTypes(queryRunner: QueryRunner): Promise { const destDir = 'integrations'; - for await (const { name, imgSrc, isComingSoon, order, provider, redirect_url, integrationTypesMap } of DEFAULT_INTEGRATIONS) { + for await (const { name, imgSrc, isComingSoon, order, redirectUrl, integrationTypesMap } of DEFAULT_SYSTEM_INTEGRATIONS) { try { const filepath = `integrations/${imgSrc}`; let upsertQuery = ``; - const payload = [name, filepath, isComingSoon, order, provider, redirect_url]; + const payload = [name, filepath, isComingSoon, order, redirectUrl]; if (queryRunner.connection.options.type === 'sqlite') { // For SQLite, manually generate a UUID using uuidv4() @@ -47,35 +46,33 @@ export class SeedIntegrationTable1691494801748 implements MigrationInterface { upsertQuery = ` INSERT INTO integration ( - "name", "imgSrc", "isComingSoon", "order", "provider", "redirect_url", "id" + "name", "imgSrc", "isComingSoon", "order", "navigationUrl", "id" ) VALUES ( - $1, $2, $3, $4, $5, $6, $7 + $1, $2, $3, $4, $5, $6 ) ON CONFLICT(name) DO UPDATE SET "imgSrc" = $2, "isComingSoon" = $3, "order" = $4, - "provider" = $5, - "redirect_url" = $6 + "navigationUrl" = $5 RETURNING id; `; } else { upsertQuery = ` INSERT INTO "integration" ( - "name", "imgSrc", "isComingSoon", "order", "provider", "redirect_url" + "name", "imgSrc", "isComingSoon", "order", "navigationUrl" ) VALUES ( - $1, $2, $3, $4, $5, $6 + $1, $2, $3, $4, $5 ) ON CONFLICT(name) DO UPDATE SET "imgSrc" = $2, "isComingSoon" = $3, "order" = $4, - "provider" = $5, - "redirect_url" = $6 + "navigationUrl" = $5 RETURNING id; `; } @@ -83,70 +80,17 @@ export class SeedIntegrationTable1691494801748 implements MigrationInterface { const [integration] = await queryRunner.query(upsertQuery, payload); // Step 3: Insert entry in join table to associate Integration with IntegrationType - await this.syncIntegrationType( + await IntegrationsUtils.syncIntegrationType( queryRunner, integration, - await this.getIntegrationTypeByName(queryRunner, integrationTypesMap) - ) + await IntegrationsUtils.getIntegrationTypeByName(queryRunner, integrationTypesMap) + ); - copyAssets(imgSrc, this.config, destDir); + copyAssets(imgSrc, getConfig(), destDir); } catch (error) { // since we have errors let's rollback changes we made console.log(`Error while updating integration: (${name}) in production server`, error); } } } - - /** - * - * @param queryRunner - * @param integrationTypesMap - * @returns - */ - private async getIntegrationTypeByName( - queryRunner: QueryRunner, - integrationTypeNames: any[] - ): Promise { - try { - return await queryRunner.query(`SELECT * FROM "integration_type" WHERE "integration_type"."name" IN ('${integrationTypeNames.join("','")}')`); - } catch (error) { - console.log('Error while querying integration types:', error); - return []; - } - } - - /** - * - * - * @param queryRunner - * @param integration - * @param integrationTypes - */ - private async syncIntegrationType( - queryRunner: QueryRunner, - integration: IIntegration, - integrationTypes: IIntegrationType[] - ) { - if (integration) { - const integrationId = integration.id; - for await (const integrationType of integrationTypes) { - const insertPivotQuery = ` - INSERT INTO "integration_integration_type" ( - "integrationId", - "integrationTypeId" - ) - SELECT - $1, $2 - WHERE NOT EXISTS ( - SELECT 1 - FROM "integration_integration_type" - WHERE - "integrationId" = $1 AND - "integrationTypeId" = $2 - ) - `; - await queryRunner.query(insertPivotQuery, [integrationId, integrationType.id]); - } - } - } } diff --git a/packages/core/src/database/migrations/1692171665427-SeedIntegrationTable.ts b/packages/core/src/database/migrations/1692171665427-SeedIntegrationTable.ts index 646fa42625f..be0f512ddfa 100644 --- a/packages/core/src/database/migrations/1692171665427-SeedIntegrationTable.ts +++ b/packages/core/src/database/migrations/1692171665427-SeedIntegrationTable.ts @@ -3,11 +3,11 @@ import { MigrationInterface, QueryRunner } from "typeorm"; import { v4 as uuidv4 } from 'uuid'; import { getConfig } from "@gauzy/config"; import { copyAssets } from "./../../core/seeds/utils"; -import { DEFAULT_INTEGRATIONS } from "./../../integration/default-integration"; +import { DEFAULT_AI_INTEGRATIONS } from "./../../integration/default-integration"; +import { IntegrationsUtils } from "./../../integration/utils"; export class SeedIntegrationTable1692171665427 implements MigrationInterface { - config = getConfig(); name = 'SeedIntegrationTable1692171665427'; /** @@ -16,7 +16,7 @@ export class SeedIntegrationTable1692171665427 implements MigrationInterface { * @param queryRunner */ public async up(queryRunner: QueryRunner): Promise { - await this.upsertIntegrations(queryRunner); + await this.upsertIntegrationsAndIntegrationTypes(queryRunner); } /** @@ -30,14 +30,15 @@ export class SeedIntegrationTable1692171665427 implements MigrationInterface { * * @param queryRunner */ - public async upsertIntegrations(queryRunner: QueryRunner): Promise { + public async upsertIntegrationsAndIntegrationTypes(queryRunner: QueryRunner): Promise { const destDir = 'integrations'; - for await (const { name, imgSrc, provider, redirect_url } of DEFAULT_INTEGRATIONS) { + + for await (const { name, imgSrc, isComingSoon, order, redirectUrl, integrationTypesMap } of DEFAULT_AI_INTEGRATIONS) { try { const filepath = `integrations/${imgSrc}`; - const payload = [name, filepath, provider, redirect_url]; let upsertQuery = ``; + const payload = [name, filepath, isComingSoon, order, redirectUrl]; if (queryRunner.connection.options.type === 'sqlite') { // For SQLite, manually generate a UUID using uuidv4() @@ -45,36 +46,47 @@ export class SeedIntegrationTable1692171665427 implements MigrationInterface { upsertQuery = ` INSERT INTO integration ( - "name", "imgSrc", "provider", "redirect_url", "id" + "name", "imgSrc", "isComingSoon", "order", "navigationUrl", "id" ) VALUES ( - $1, $2, $3, $4, $5 + $1, $2, $3, $4, $5, $6 ) ON CONFLICT(name) DO UPDATE SET "imgSrc" = $2, - "provider" = $3, - "redirect_url" = $4; + "isComingSoon" = $3, + "order" = $4, + "navigationUrl" = $5 + RETURNING id; `; - } else { upsertQuery = ` - INSERT INTO integration ( - "name", "imgSrc", "provider", "redirect_url" + INSERT INTO "integration" ( + "name", "imgSrc", "isComingSoon", "order", "navigationUrl" ) VALUES ( - $1, $2, $3, $4 + $1, $2, $3, $4, $5 ) ON CONFLICT(name) DO UPDATE SET "imgSrc" = $2, - "provider" = $3, - "redirect_url" = $4; + "isComingSoon" = $3, + "order" = $4, + "navigationUrl" = $5 + RETURNING id; `; } + const [integration] = await queryRunner.query(upsertQuery, payload); - console.log(integration); - copyAssets(imgSrc, this.config, destDir); + + // Step 3: Insert entry in join table to associate Integration with IntegrationType + await IntegrationsUtils.syncIntegrationType( + queryRunner, + integration, + await IntegrationsUtils.getIntegrationTypeByName(queryRunner, integrationTypesMap) + ); + + copyAssets(imgSrc, getConfig(), destDir); } catch (error) { // since we have errors let's rollback changes we made console.log(`Error while updating integration: (${name}) in production server`, error); diff --git a/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts b/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts deleted file mode 100644 index 778cb8fc3d4..00000000000 --- a/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts +++ /dev/null @@ -1,117 +0,0 @@ - -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationInterface { - - name = 'AddColumnsToTheIntegrationTable1695023907817'; - - /** - * Up Migration - * - * @param queryRunner - */ - public async up(queryRunner: QueryRunner): Promise { - if (queryRunner.connection.options.type === 'sqlite') { - await this.sqliteUpQueryRunner(queryRunner); - } else { - await this.postgresUpQueryRunner(queryRunner); - } - } - - /** - * Down Migration - * - * @param queryRunner - */ - public async down(queryRunner: QueryRunner): Promise { - if (queryRunner.connection.options.type === 'sqlite') { - await this.sqliteDownQueryRunner(queryRunner); - } else { - await this.postgresDownQueryRunner(queryRunner); - } - } - - /** - * PostgresDB Up Migration - * - * @param queryRunner - */ - public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "integration" RENAME COLUMN "navigationUrl" TO "redirectUrl"`); - await queryRunner.query(`ALTER TABLE "integration_type" ADD "description" character varying`); - await queryRunner.query(`ALTER TABLE "integration_type" ADD "icon" character varying`); - await queryRunner.query(`ALTER TABLE "integration" ADD "provider" character varying`); - await queryRunner.query(`ALTER TABLE "integration" DROP CONSTRAINT "UQ_52d7fa32a7832b377fc2d7f6199"`); - await queryRunner.query(`ALTER TABLE "integration" ADD CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a" UNIQUE ("name", "provider")`); - } - - /** - * PostgresDB Down Migration - * - * @param queryRunner - */ - public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "integration" DROP CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a"`); - await queryRunner.query(`ALTER TABLE "integration" ADD CONSTRAINT "UQ_52d7fa32a7832b377fc2d7f6199" UNIQUE ("name")`); - await queryRunner.query(`ALTER TABLE "integration" DROP COLUMN "provider"`); - await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "icon"`); - await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "description"`); - await queryRunner.query(`ALTER TABLE "integration" RENAME COLUMN "redirectUrl" TO "navigationUrl"`); - } - - /** - * SqliteDB Up Migration - * - * @param queryRunner - */ - public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "integration"`); - await queryRunner.query(`DROP TABLE "integration"`); - await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); - await queryRunner.query(`CREATE TABLE "temporary_integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL, "description" varchar, "icon" varchar)`); - await queryRunner.query(`INSERT INTO "temporary_integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "integration_type"`); - await queryRunner.query(`DROP TABLE "integration_type"`); - await queryRunner.query(`ALTER TABLE "temporary_integration_type" RENAME TO "integration_type"`); - await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "integration"`); - await queryRunner.query(`DROP TABLE "integration"`); - await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); - await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "integration"`); - await queryRunner.query(`DROP TABLE "integration"`); - await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); - await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"), CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a" UNIQUE ("name", "provider"))`); - await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "integration"`); - await queryRunner.query(`DROP TABLE "integration"`); - await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); - } - - /** - * SqliteDB Down Migration - * - * @param queryRunner - */ - public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); - await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "temporary_integration"`); - await queryRunner.query(`DROP TABLE "temporary_integration"`); - await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); - await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "temporary_integration"`); - await queryRunner.query(`DROP TABLE "temporary_integration"`); - await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); - await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "temporary_integration"`); - await queryRunner.query(`DROP TABLE "temporary_integration"`); - await queryRunner.query(`ALTER TABLE "integration_type" RENAME TO "temporary_integration_type"`); - await queryRunner.query(`CREATE TABLE "integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL)`); - await queryRunner.query(`INSERT INTO "integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "temporary_integration_type"`); - await queryRunner.query(`DROP TABLE "temporary_integration_type"`); - await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); - await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "navigationUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "temporary_integration"`); - await queryRunner.query(`DROP TABLE "temporary_integration"`); - } -} diff --git a/packages/core/src/integration/default-integration.ts b/packages/core/src/integration/default-integration.ts index c0bc2a968b4..be11f229e8f 100644 --- a/packages/core/src/integration/default-integration.ts +++ b/packages/core/src/integration/default-integration.ts @@ -1,7 +1,10 @@ import { sluggable } from "@gauzy/common"; import { IntegrationEnum, IntegrationTypeEnum } from "@gauzy/contracts"; -export const DEFAULT_INTEGRATIONS = [ +/** + * + */ +export const DEFAULT_SYSTEM_INTEGRATIONS = [ { name: IntegrationEnum.HUBSTAFF, imgSrc: 'hubstaff.svg', @@ -10,8 +13,7 @@ export const DEFAULT_INTEGRATIONS = [ IntegrationTypeEnum.ALL_INTEGRATIONS ], order: 1, - navigationUrl: 'hubstaff', - redirect_url: sluggable(IntegrationEnum.HUBSTAFF), + redirectUrl: sluggable(IntegrationEnum.HUBSTAFF), provider: IntegrationEnum.HUBSTAFF }, { @@ -22,10 +24,27 @@ export const DEFAULT_INTEGRATIONS = [ IntegrationTypeEnum.ALL_INTEGRATIONS ], order: 2, - navigationUrl: 'upwork', - redirect_url: sluggable(IntegrationEnum.UPWORK), + redirectUrl: sluggable(IntegrationEnum.UPWORK), provider: IntegrationEnum.UPWORK }, + { + name: 'Import/Export', + imgSrc: 'import-export.svg', + isComingSoon: true, + integrationTypesMap: [ + IntegrationTypeEnum.ALL_INTEGRATIONS, + IntegrationTypeEnum.CRM + ], + order: 4, + redirectUrl: sluggable(IntegrationEnum.IMPORT_EXPORT), + provider: IntegrationEnum.IMPORT_EXPORT + }, +]; + +/** + * + */ +export const DEFAULT_AI_INTEGRATIONS = [ { name: IntegrationEnum.GAUZY_AI, imgSrc: 'gauzy-ai.svg', @@ -34,21 +53,43 @@ export const DEFAULT_INTEGRATIONS = [ IntegrationTypeEnum.ALL_INTEGRATIONS ], order: 3, - navigationUrl: 'gauzy-ai', - redirect_url: sluggable(IntegrationEnum.GAUZY_AI), + redirectUrl: sluggable(IntegrationEnum.GAUZY_AI), provider: IntegrationEnum.GAUZY_AI }, +]; + +/** + * + */ +export const PROJECT_MANAGE_DEFAULT_INTEGRATIONS = [ { - name: 'Import/Export', - imgSrc: 'import-export.svg', - isComingSoon: true, + name: IntegrationEnum.GITHUB, + imgSrc: 'github.svg', + isComingSoon: false, integrationTypesMap: [ IntegrationTypeEnum.ALL_INTEGRATIONS, - IntegrationTypeEnum.CRM + IntegrationTypeEnum.PROJECT_MANAGEMENT ], - order: 4, - navigationUrl: 'import-export', - redirect_url: sluggable('import-export'), - provider: 'import_export' - } + order: 1, + redirectUrl: sluggable(IntegrationEnum.GITHUB), + provider: IntegrationEnum.GITHUB + }, + { + name: IntegrationEnum.JIRA, + imgSrc: 'jira.svg', + isComingSoon: false, + integrationTypesMap: [ + IntegrationTypeEnum.ALL_INTEGRATIONS, + IntegrationTypeEnum.PROJECT_MANAGEMENT + ], + order: 2, + redirectUrl: sluggable(IntegrationEnum.JIRA), + provider: IntegrationEnum.JIRA + }, +]; + +export const DEFAULT_INTEGRATIONS = [ + ...DEFAULT_SYSTEM_INTEGRATIONS, + ...DEFAULT_AI_INTEGRATIONS, + ...PROJECT_MANAGE_DEFAULT_INTEGRATIONS ]; diff --git a/packages/core/src/integration/utils.ts b/packages/core/src/integration/utils.ts new file mode 100644 index 00000000000..847fc55ed17 --- /dev/null +++ b/packages/core/src/integration/utils.ts @@ -0,0 +1,132 @@ + +import { QueryRunner } from "typeorm"; +import { v4 as uuidv4 } from 'uuid'; +import { getConfig } from "@gauzy/config"; +import { IIntegration, IIntegrationType } from "@gauzy/contracts"; +import { copyAssets } from "./../core/seeds/utils"; + +export class IntegrationsUtils { + + /** + * + * @param queryRunner + */ + public static async upsertIntegrationsAndIntegrationTypes(queryRunner: QueryRunner, integrations: any[]): Promise { + const destDir = 'integrations'; + + for await (const { name, imgSrc, isComingSoon, order, provider, redirectUrl, integrationTypesMap } of integrations) { + try { + const filepath = `integrations/${imgSrc}`; + + let upsertQuery = ``; + const payload = [name, filepath, isComingSoon, order, redirectUrl, provider]; + + if (queryRunner.connection.options.type === 'sqlite') { + // For SQLite, manually generate a UUID using uuidv4() + const generatedId = uuidv4(); payload.push(generatedId); + + upsertQuery = ` + INSERT INTO integration ( + "name", "imgSrc", "isComingSoon", "order", "redirectUrl", "provider", "id" + ) + VALUES ( + $1, $2, $3, $4, $5, $6, $7 + ) + ON CONFLICT(name) DO UPDATE + SET + "imgSrc" = $2, + "isComingSoon" = $3, + "order" = $4, + "redirectUrl" = $5, + "provider" = $6 + RETURNING id; + `; + } else { + upsertQuery = ` + INSERT INTO "integration" ( + "name", "imgSrc", "isComingSoon", "order", "redirectUrl", "provider" + ) + VALUES ( + $1, $2, $3, $4, $5, $6 + ) + ON CONFLICT(name) DO UPDATE + SET + "imgSrc" = $2, + "isComingSoon" = $3, + "order" = $4, + "redirectUrl" = $5, + "provider" = $6 + RETURNING id; + `; + } + + const [integration] = await queryRunner.query(upsertQuery, payload); + + // Step 3: Insert entry in join table to associate Integration with IntegrationType + await IntegrationsUtils.syncIntegrationType( + queryRunner, + integration, + await this.getIntegrationTypeByName(queryRunner, integrationTypesMap) + ); + + copyAssets(imgSrc, getConfig(), destDir); + } catch (error) { + // since we have errors let's rollback changes we made + console.log(`Error while updating integration: (${name}) in production server`, error); + } + } + } + + /** + * + * @param queryRunner + * @param integrationTypesMap + * @returns + */ + public static async getIntegrationTypeByName( + queryRunner: QueryRunner, + integrationTypeNames: any[] + ): Promise { + try { + return await queryRunner.query(`SELECT * FROM "integration_type" WHERE "integration_type"."name" IN ('${integrationTypeNames.join("','")}')`); + } catch (error) { + console.log('Error while querying integration types:', error); + return []; + } + } + + /** + * + * + * @param queryRunner + * @param integration + * @param integrationTypes + */ + public static async syncIntegrationType( + queryRunner: QueryRunner, + integration: IIntegration, + integrationTypes: IIntegrationType[] + ) { + if (integration) { + const integrationId = integration.id; + for await (const integrationType of integrationTypes) { + const insertPivotQuery = ` + INSERT INTO "integration_integration_type" ( + "integrationId", + "integrationTypeId" + ) + SELECT + $1, $2 + WHERE NOT EXISTS ( + SELECT 1 + FROM "integration_integration_type" + WHERE + "integrationId" = $1 AND + "integrationTypeId" = $2 + ) + `; + await queryRunner.query(insertPivotQuery, [integrationId, integrationType.id]); + } + } + } +} From 2107a8e1819cf2beb7791eed13a1ccf35a3b7f17 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:32:57 +0530 Subject: [PATCH 016/104] fix: reverted deleted migration by mistake --- ...3907817-AddColumnsToTheIntegrationTable.ts | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts diff --git a/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts b/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts new file mode 100644 index 00000000000..e4d26690b9d --- /dev/null +++ b/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts @@ -0,0 +1,116 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationInterface { + + name = 'AddColumnsToTheIntegrationTable1695023907817'; + + /** + * Up Migration + * + * @param queryRunner + */ + public async up(queryRunner: QueryRunner): Promise { + if (queryRunner.connection.options.type === 'sqlite') { + await this.sqliteUpQueryRunner(queryRunner); + } else { + await this.postgresUpQueryRunner(queryRunner); + } + } + + /** + * Down Migration + * + * @param queryRunner + */ + public async down(queryRunner: QueryRunner): Promise { + if (queryRunner.connection.options.type === 'sqlite') { + await this.sqliteDownQueryRunner(queryRunner); + } else { + await this.postgresDownQueryRunner(queryRunner); + } + } + + /** + * PostgresDB Up Migration + * + * @param queryRunner + */ + public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "integration" RENAME COLUMN "navigationUrl" TO "redirectUrl"`); + await queryRunner.query(`ALTER TABLE "integration_type" ADD "description" character varying`); + await queryRunner.query(`ALTER TABLE "integration_type" ADD "icon" character varying`); + await queryRunner.query(`ALTER TABLE "integration" ADD "provider" character varying`); + await queryRunner.query(`ALTER TABLE "integration" DROP CONSTRAINT "UQ_52d7fa32a7832b377fc2d7f6199"`); + await queryRunner.query(`ALTER TABLE "integration" ADD CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a" UNIQUE ("name", "provider")`); + } + + /** + * PostgresDB Down Migration + * + * @param queryRunner + */ + public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "integration" DROP CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a"`); + await queryRunner.query(`ALTER TABLE "integration" ADD CONSTRAINT "UQ_52d7fa32a7832b377fc2d7f6199" UNIQUE ("name")`); + await queryRunner.query(`ALTER TABLE "integration" DROP COLUMN "provider"`); + await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "icon"`); + await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "description"`); + await queryRunner.query(`ALTER TABLE "integration" RENAME COLUMN "redirectUrl" TO "navigationUrl"`); + } + + /** + * SqliteDB Up Migration + * + * @param queryRunner + */ + public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "integration"`); + await queryRunner.query(`DROP TABLE "integration"`); + await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); + await queryRunner.query(`CREATE TABLE "temporary_integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL, "description" varchar, "icon" varchar)`); + await queryRunner.query(`INSERT INTO "temporary_integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "integration_type"`); + await queryRunner.query(`DROP TABLE "integration_type"`); + await queryRunner.query(`ALTER TABLE "temporary_integration_type" RENAME TO "integration_type"`); + await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "integration"`); + await queryRunner.query(`DROP TABLE "integration"`); + await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); + await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "integration"`); + await queryRunner.query(`DROP TABLE "integration"`); + await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); + await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"), CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a" UNIQUE ("name", "provider"))`); + await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "integration"`); + await queryRunner.query(`DROP TABLE "integration"`); + await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); + } + + /** + * SqliteDB Down Migration + * + * @param queryRunner + */ + public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); + await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "temporary_integration"`); + await queryRunner.query(`DROP TABLE "temporary_integration"`); + await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); + await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "temporary_integration"`); + await queryRunner.query(`DROP TABLE "temporary_integration"`); + await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); + await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "temporary_integration"`); + await queryRunner.query(`DROP TABLE "temporary_integration"`); + await queryRunner.query(`ALTER TABLE "integration_type" RENAME TO "temporary_integration_type"`); + await queryRunner.query(`CREATE TABLE "integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL)`); + await queryRunner.query(`INSERT INTO "integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "temporary_integration_type"`); + await queryRunner.query(`DROP TABLE "temporary_integration_type"`); + await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); + await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "navigationUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "temporary_integration"`); + await queryRunner.query(`DROP TABLE "temporary_integration"`); + } +} From 647c8e83bd19faf1a7c4e052e88b179ab5fb0326 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 18 Sep 2023 17:04:14 +0530 Subject: [PATCH 017/104] fix: #6823 added new integration type --- packages/contracts/src/integration.model.ts | 2 +- ...3907817-AddColumnsToTheIntegrationTable.ts | 3 +- .../integration/default-integration-type.ts | 53 +++++++++++++++ .../src/integration/integration-type.seed.ts | 66 ++++--------------- 4 files changed, 69 insertions(+), 55 deletions(-) create mode 100644 packages/core/src/integration/default-integration-type.ts diff --git a/packages/contracts/src/integration.model.ts b/packages/contracts/src/integration.model.ts index 9fef1827343..db3e622b989 100644 --- a/packages/contracts/src/integration.model.ts +++ b/packages/contracts/src/integration.model.ts @@ -188,7 +188,7 @@ export enum IntegrationTypeEnum { SCHEDULING = 'Scheduling', TOOLS = 'Tools', PROJECT_MANAGEMENT = ' Project Management', - COMMUNICATION = 'Communication' + // COMMUNICATION = 'Communication' } export enum IntegrationFilterEnum { diff --git a/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts b/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts index e4d26690b9d..f92110acee9 100644 --- a/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts +++ b/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts @@ -1,5 +1,5 @@ import { MigrationInterface, QueryRunner } from "typeorm"; - +import * as chalk from 'chalk'; export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationInterface { name = 'AddColumnsToTheIntegrationTable1695023907817'; @@ -10,6 +10,7 @@ export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationIn * @param queryRunner */ public async up(queryRunner: QueryRunner): Promise { + console.log(chalk.magenta(`AddColumnsToTheIntegrationTable1695023907817 start running!`)); if (queryRunner.connection.options.type === 'sqlite') { await this.sqliteUpQueryRunner(queryRunner); } else { diff --git a/packages/core/src/integration/default-integration-type.ts b/packages/core/src/integration/default-integration-type.ts new file mode 100644 index 00000000000..6b6e2373c80 --- /dev/null +++ b/packages/core/src/integration/default-integration-type.ts @@ -0,0 +1,53 @@ +import { IntegrationTypeEnum, IntegrationTypeGroupEnum } from "@gauzy/contracts"; + +export const DEFAULT_INTEGRATION_TYPES = [ + { + name: IntegrationTypeEnum.ALL_INTEGRATIONS, + groupName: IntegrationTypeGroupEnum.FEATURED, + order: 1, + description: null, + icon: null + }, + { + name: IntegrationTypeEnum.FOR_SALES_TEAMS, + groupName: IntegrationTypeGroupEnum.FEATURED, + order: 1, + description: null, + icon: null + }, + { + name: IntegrationTypeEnum.FOR_ACCOUNTANTS, + groupName: IntegrationTypeGroupEnum.FEATURED, + order: 1, + description: null, + icon: null + }, + { + name: IntegrationTypeEnum.FOR_SUPPORT_TEAMS, + groupName: IntegrationTypeGroupEnum.FEATURED, + order: 1, + description: null, + icon: null + }, + { + name: IntegrationTypeEnum.CRM, + groupName: IntegrationTypeGroupEnum.CATEGORIES, + order: 2, + description: null, + icon: null + }, + { + name: IntegrationTypeEnum.SCHEDULING, + groupName: IntegrationTypeGroupEnum.CATEGORIES, + order: 2, + description: null, + icon: null + }, + { + name: IntegrationTypeEnum.PROJECT_MANAGEMENT, + groupName: IntegrationTypeGroupEnum.CATEGORIES, + order: 2, + description: null, + icon: null + } +]; diff --git a/packages/core/src/integration/integration-type.seed.ts b/packages/core/src/integration/integration-type.seed.ts index 35bf0c8490a..77192e2230e 100644 --- a/packages/core/src/integration/integration-type.seed.ts +++ b/packages/core/src/integration/integration-type.seed.ts @@ -1,65 +1,25 @@ import { DataSource } from 'typeorm'; import { IntegrationType } from './integration-type.entity'; -import { - IntegrationTypeGroupEnum, - IntegrationTypeEnum -} from '@gauzy/contracts'; - -const DEFAULT_INTEGRATION_TYPES = [ - { - name: IntegrationTypeEnum.ALL_INTEGRATIONS, - groupName: IntegrationTypeGroupEnum.FEATURED, - order: 1 - }, - { - name: IntegrationTypeEnum.FOR_SALES_TEAMS, - groupName: IntegrationTypeGroupEnum.FEATURED, - order: 1 - }, - { - name: IntegrationTypeEnum.FOR_ACCOUNTANTS, - groupName: IntegrationTypeGroupEnum.FEATURED, - order: 1 - }, - { - name: IntegrationTypeEnum.FOR_SUPPORT_TEAMS, - groupName: IntegrationTypeGroupEnum.FEATURED, - order: 1 - }, - { - name: IntegrationTypeEnum.CRM, - groupName: IntegrationTypeGroupEnum.CATEGORIES, - order: 2 - }, - { - name: IntegrationTypeEnum.SCHEDULING, - groupName: IntegrationTypeGroupEnum.CATEGORIES, - order: 2 - }, - { - name: IntegrationTypeEnum.TOOLS, - groupName: IntegrationTypeGroupEnum.CATEGORIES, - order: 2 - } -]; +import { DEFAULT_INTEGRATION_TYPES } from './default-integration-type'; export const createDefaultIntegrationTypes = async ( dataSource: DataSource ): Promise => { - const integrationTypes = DEFAULT_INTEGRATION_TYPES.map( - ({ name, groupName, order }) => { - const entity = new IntegrationType(); - entity.name = name; - entity.groupName = groupName; - entity.order = order; - return entity; - } - ); + const integrationTypes = DEFAULT_INTEGRATION_TYPES.map(({ name, groupName, order, icon, description }) => { + const entity = new IntegrationType(); + entity.name = name; + entity.groupName = groupName; + entity.order = order; + entity.icon = icon; + entity.description = description; + return entity; + }); return await insertIntegrationTypes(dataSource, integrationTypes); }; const insertIntegrationTypes = async ( dataSource: DataSource, integrationTypes: IntegrationType[] -): Promise => - await dataSource.manager.save(integrationTypes); +): Promise => { + return await dataSource.manager.save(integrationTypes); +} From 62e2c97e44cccbf0f2cc3d4e647df67364174b14 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 18 Sep 2023 17:47:58 +0530 Subject: [PATCH 018/104] fix: integration order & redirect url --- .../app/pages/integrations/integrations.component.html | 4 ++-- packages/core/src/integration/default-integration.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/gauzy/src/app/pages/integrations/integrations.component.html b/apps/gauzy/src/app/pages/integrations/integrations.component.html index eeec10ce245..70a7762c69b 100644 --- a/apps/gauzy/src/app/pages/integrations/integrations.component.html +++ b/apps/gauzy/src/app/pages/integrations/integrations.component.html @@ -88,14 +88,14 @@

{{ 'INTEGRATIONS.AVAILABLE_INTEGRATIONS' | translate }}

image not found
diff --git a/packages/core/src/integration/default-integration.ts b/packages/core/src/integration/default-integration.ts index be11f229e8f..47a7567940a 100644 --- a/packages/core/src/integration/default-integration.ts +++ b/packages/core/src/integration/default-integration.ts @@ -35,7 +35,7 @@ export const DEFAULT_SYSTEM_INTEGRATIONS = [ IntegrationTypeEnum.ALL_INTEGRATIONS, IntegrationTypeEnum.CRM ], - order: 4, + order: 6, redirectUrl: sluggable(IntegrationEnum.IMPORT_EXPORT), provider: IntegrationEnum.IMPORT_EXPORT }, @@ -46,7 +46,7 @@ export const DEFAULT_SYSTEM_INTEGRATIONS = [ */ export const DEFAULT_AI_INTEGRATIONS = [ { - name: IntegrationEnum.GAUZY_AI, + name: 'Gauzy AI', imgSrc: 'gauzy-ai.svg', isComingSoon: false, integrationTypesMap: [ @@ -70,19 +70,19 @@ export const PROJECT_MANAGE_DEFAULT_INTEGRATIONS = [ IntegrationTypeEnum.ALL_INTEGRATIONS, IntegrationTypeEnum.PROJECT_MANAGEMENT ], - order: 1, + order: 4, redirectUrl: sluggable(IntegrationEnum.GITHUB), provider: IntegrationEnum.GITHUB }, { name: IntegrationEnum.JIRA, imgSrc: 'jira.svg', - isComingSoon: false, + isComingSoon: true, integrationTypesMap: [ IntegrationTypeEnum.ALL_INTEGRATIONS, IntegrationTypeEnum.PROJECT_MANAGEMENT ], - order: 2, + order: 5, redirectUrl: sluggable(IntegrationEnum.JIRA), provider: IntegrationEnum.JIRA }, From 0f50dff4bde5e3cf7a7e65497b6ef067e7156261 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 18 Sep 2023 19:39:44 +0530 Subject: [PATCH 019/104] fix: updated migration for create new project management integrations --- .../integrations/integrations.component.html | 2 +- ...3907817-AddColumnsToTheIntegrationTable.ts | 56 ++----------------- ...20330-SeedProjectManagementIntegrations.ts | 29 ++++++++++ packages/core/src/integration/utils.ts | 5 +- 4 files changed, 35 insertions(+), 57 deletions(-) create mode 100644 packages/core/src/database/migrations/1695043020330-SeedProjectManagementIntegrations.ts diff --git a/apps/gauzy/src/app/pages/integrations/integrations.component.html b/apps/gauzy/src/app/pages/integrations/integrations.component.html index 70a7762c69b..f69a53bf57d 100644 --- a/apps/gauzy/src/app/pages/integrations/integrations.component.html +++ b/apps/gauzy/src/app/pages/integrations/integrations.component.html @@ -95,7 +95,7 @@

{{ 'INTEGRATIONS.AVAILABLE_INTEGRATIONS' | translate }}

width="100%" height="100%" alt="image not found" - [title]="integration.name | replace: '_':' ' | titlecase" + [title]="integration.name | replace: '_':' '" />
diff --git a/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts b/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts index f92110acee9..a65d860af9d 100644 --- a/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts +++ b/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts @@ -37,12 +37,7 @@ export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationIn * @param queryRunner */ public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "integration" RENAME COLUMN "navigationUrl" TO "redirectUrl"`); - await queryRunner.query(`ALTER TABLE "integration_type" ADD "description" character varying`); - await queryRunner.query(`ALTER TABLE "integration_type" ADD "icon" character varying`); - await queryRunner.query(`ALTER TABLE "integration" ADD "provider" character varying`); - await queryRunner.query(`ALTER TABLE "integration" DROP CONSTRAINT "UQ_52d7fa32a7832b377fc2d7f6199"`); - await queryRunner.query(`ALTER TABLE "integration" ADD CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a" UNIQUE ("name", "provider")`); + } /** @@ -51,12 +46,7 @@ export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationIn * @param queryRunner */ public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "integration" DROP CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a"`); - await queryRunner.query(`ALTER TABLE "integration" ADD CONSTRAINT "UQ_52d7fa32a7832b377fc2d7f6199" UNIQUE ("name")`); - await queryRunner.query(`ALTER TABLE "integration" DROP COLUMN "provider"`); - await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "icon"`); - await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "description"`); - await queryRunner.query(`ALTER TABLE "integration" RENAME COLUMN "redirectUrl" TO "navigationUrl"`); + } /** @@ -65,26 +55,7 @@ export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationIn * @param queryRunner */ public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "integration"`); - await queryRunner.query(`DROP TABLE "integration"`); - await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); - await queryRunner.query(`CREATE TABLE "temporary_integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL, "description" varchar, "icon" varchar)`); - await queryRunner.query(`INSERT INTO "temporary_integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "integration_type"`); - await queryRunner.query(`DROP TABLE "integration_type"`); - await queryRunner.query(`ALTER TABLE "temporary_integration_type" RENAME TO "integration_type"`); - await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "integration"`); - await queryRunner.query(`DROP TABLE "integration"`); - await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); - await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "integration"`); - await queryRunner.query(`DROP TABLE "integration"`); - await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); - await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"), CONSTRAINT "UQ_3bdf30194e3e21c3985f538c91a" UNIQUE ("name", "provider"))`); - await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "integration"`); - await queryRunner.query(`DROP TABLE "integration"`); - await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); + } /** @@ -93,25 +64,6 @@ export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationIn * @param queryRunner */ public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); - await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "temporary_integration"`); - await queryRunner.query(`DROP TABLE "temporary_integration"`); - await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); - await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order", "provider", "redirectUrl" FROM "temporary_integration"`); - await queryRunner.query(`DROP TABLE "temporary_integration"`); - await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); - await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "temporary_integration"`); - await queryRunner.query(`DROP TABLE "temporary_integration"`); - await queryRunner.query(`ALTER TABLE "integration_type" RENAME TO "temporary_integration_type"`); - await queryRunner.query(`CREATE TABLE "integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL)`); - await queryRunner.query(`INSERT INTO "integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "temporary_integration_type"`); - await queryRunner.query(`DROP TABLE "temporary_integration_type"`); - await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); - await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "navigationUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); - await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "temporary_integration"`); - await queryRunner.query(`DROP TABLE "temporary_integration"`); + } } diff --git a/packages/core/src/database/migrations/1695043020330-SeedProjectManagementIntegrations.ts b/packages/core/src/database/migrations/1695043020330-SeedProjectManagementIntegrations.ts new file mode 100644 index 00000000000..c5dc7e054bb --- /dev/null +++ b/packages/core/src/database/migrations/1695043020330-SeedProjectManagementIntegrations.ts @@ -0,0 +1,29 @@ + +import { MigrationInterface, QueryRunner } from "typeorm"; +import { IntegrationsUtils } from "./../../integration/utils"; +import { DEFAULT_INTEGRATIONS, PROJECT_MANAGE_DEFAULT_INTEGRATIONS } from "./../../integration/default-integration"; + +/** + * + */ +export class SeedProjectManagementIntegrations1695043020330 implements MigrationInterface { + + name = 'SeedProjectManagementIntegrations1695043020330'; + + /** + * Up Migration + * + * @param queryRunner + */ + public async up(queryRunner: QueryRunner): Promise { + await IntegrationsUtils.upsertIntegrationsAndIntegrationTypes(queryRunner, PROJECT_MANAGE_DEFAULT_INTEGRATIONS); + await IntegrationsUtils.upsertIntegrationsAndIntegrationTypes(queryRunner, DEFAULT_INTEGRATIONS); + } + + /** + * Down Migration + * + * @param queryRunner + */ + public async down(queryRunner: QueryRunner): Promise { } +} diff --git a/packages/core/src/integration/utils.ts b/packages/core/src/integration/utils.ts index 847fc55ed17..8d7a236e328 100644 --- a/packages/core/src/integration/utils.ts +++ b/packages/core/src/integration/utils.ts @@ -13,8 +13,7 @@ export class IntegrationsUtils { */ public static async upsertIntegrationsAndIntegrationTypes(queryRunner: QueryRunner, integrations: any[]): Promise { const destDir = 'integrations'; - - for await (const { name, imgSrc, isComingSoon, order, provider, redirectUrl, integrationTypesMap } of integrations) { + for await (const { name, imgSrc, isComingSoon, order, redirectUrl, provider, integrationTypesMap } of integrations) { try { const filepath = `integrations/${imgSrc}`; @@ -24,7 +23,6 @@ export class IntegrationsUtils { if (queryRunner.connection.options.type === 'sqlite') { // For SQLite, manually generate a UUID using uuidv4() const generatedId = uuidv4(); payload.push(generatedId); - upsertQuery = ` INSERT INTO integration ( "name", "imgSrc", "isComingSoon", "order", "redirectUrl", "provider", "id" @@ -59,7 +57,6 @@ export class IntegrationsUtils { RETURNING id; `; } - const [integration] = await queryRunner.query(upsertQuery, payload); // Step 3: Insert entry in join table to associate Integration with IntegrationType From 444508e4b0edd75b4e0649c87c5647a060affdf9 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 18 Sep 2023 22:08:11 +0530 Subject: [PATCH 020/104] fix: regenerate migration unique constraint --- ...3907817-AddColumnsToTheIntegrationTable.ts | 36 ++++++++++++++++--- .../src/integration/integration.entity.ts | 2 +- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts b/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts index a65d860af9d..76a6b80c563 100644 --- a/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts +++ b/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts @@ -37,7 +37,10 @@ export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationIn * @param queryRunner */ public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise { - + await queryRunner.query(`ALTER TABLE "integration" RENAME COLUMN "navigationUrl" TO "redirectUrl"`); + await queryRunner.query(`ALTER TABLE "integration_type" ADD "description" character varying`); + await queryRunner.query(`ALTER TABLE "integration_type" ADD "icon" character varying`); + await queryRunner.query(`ALTER TABLE "integration" ADD "provider" character varying`); } /** @@ -46,7 +49,10 @@ export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationIn * @param queryRunner */ public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise { - + await queryRunner.query(`ALTER TABLE "integration" DROP COLUMN "provider"`); + await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "icon"`); + await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "description"`); + await queryRunner.query(`ALTER TABLE "integration" RENAME COLUMN "redirectUrl" TO "navigationUrl"`); } /** @@ -55,7 +61,18 @@ export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationIn * @param queryRunner */ public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise { - + await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "integration"`); + await queryRunner.query(`DROP TABLE "integration"`); + await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); + await queryRunner.query(`CREATE TABLE "temporary_integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL, "description" varchar, "icon" varchar)`); + await queryRunner.query(`INSERT INTO "temporary_integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "integration_type"`); + await queryRunner.query(`DROP TABLE "integration_type"`); + await queryRunner.query(`ALTER TABLE "temporary_integration_type" RENAME TO "integration_type"`); + await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "integration"`); + await queryRunner.query(`DROP TABLE "integration"`); + await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); } /** @@ -64,6 +81,17 @@ export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationIn * @param queryRunner */ public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise { - + await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); + await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "temporary_integration"`); + await queryRunner.query(`DROP TABLE "temporary_integration"`); + await queryRunner.query(`ALTER TABLE "integration_type" RENAME TO "temporary_integration_type"`); + await queryRunner.query(`CREATE TABLE "integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL)`); + await queryRunner.query(`INSERT INTO "integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "temporary_integration_type"`); + await queryRunner.query(`DROP TABLE "temporary_integration_type"`); + await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); + await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "navigationUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "temporary_integration"`); + await queryRunner.query(`DROP TABLE "temporary_integration"`); } } diff --git a/packages/core/src/integration/integration.entity.ts b/packages/core/src/integration/integration.entity.ts index e595d442842..fa0a9197ae5 100644 --- a/packages/core/src/integration/integration.entity.ts +++ b/packages/core/src/integration/integration.entity.ts @@ -7,7 +7,7 @@ import { BaseEntity, Tag } from '../core/entities/internal'; import { IntegrationType } from './integration-type.entity'; @Entity('integration') -@Unique(['name', 'provider']) +@Unique(['name']) export class Integration extends BaseEntity implements IIntegration { @ApiProperty({ type: () => String }) From c025ac7a251f8b570c60561b5d672e0ba14ddf02 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 19 Sep 2023 13:27:45 +0530 Subject: [PATCH 021/104] fix: insert & update new integration type --- packages/contracts/src/integration.model.ts | 2 +- .../1691494801748-SeedIntegrationTable.ts | 16 +++-- .../1692171665427-SeedIntegrationTable.ts | 18 +++--- .../integration/default-integration-type.ts | 7 +++ .../integration/integration-type.entity.ts | 3 +- packages/core/src/integration/utils.ts | 59 ++++++++++++++++++- 6 files changed, 82 insertions(+), 23 deletions(-) diff --git a/packages/contracts/src/integration.model.ts b/packages/contracts/src/integration.model.ts index db3e622b989..9fef1827343 100644 --- a/packages/contracts/src/integration.model.ts +++ b/packages/contracts/src/integration.model.ts @@ -188,7 +188,7 @@ export enum IntegrationTypeEnum { SCHEDULING = 'Scheduling', TOOLS = 'Tools', PROJECT_MANAGEMENT = ' Project Management', - // COMMUNICATION = 'Communication' + COMMUNICATION = 'Communication' } export enum IntegrationFilterEnum { diff --git a/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts b/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts index 0292c627469..9b5c83da148 100644 --- a/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts +++ b/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts @@ -33,12 +33,12 @@ export class SeedIntegrationTable1691494801748 implements MigrationInterface { public async upsertIntegrationsAndIntegrationTypes(queryRunner: QueryRunner): Promise { const destDir = 'integrations'; - for await (const { name, imgSrc, isComingSoon, order, redirectUrl, integrationTypesMap } of DEFAULT_SYSTEM_INTEGRATIONS) { + for await (const { name, imgSrc, isComingSoon, order, integrationTypesMap } of DEFAULT_SYSTEM_INTEGRATIONS) { try { const filepath = `integrations/${imgSrc}`; let upsertQuery = ``; - const payload = [name, filepath, isComingSoon, order, redirectUrl]; + const payload = [name, filepath, isComingSoon, order]; if (queryRunner.connection.options.type === 'sqlite') { // For SQLite, manually generate a UUID using uuidv4() @@ -46,23 +46,22 @@ export class SeedIntegrationTable1691494801748 implements MigrationInterface { upsertQuery = ` INSERT INTO integration ( - "name", "imgSrc", "isComingSoon", "order", "navigationUrl", "id" + "name", "imgSrc", "isComingSoon", "order", "id" ) VALUES ( - $1, $2, $3, $4, $5, $6 + $1, $2, $3, $4, $5 ) ON CONFLICT(name) DO UPDATE SET "imgSrc" = $2, "isComingSoon" = $3, - "order" = $4, - "navigationUrl" = $5 + "order" = $4 RETURNING id; `; } else { upsertQuery = ` INSERT INTO "integration" ( - "name", "imgSrc", "isComingSoon", "order", "navigationUrl" + "name", "imgSrc", "isComingSoon", "order" ) VALUES ( $1, $2, $3, $4, $5 @@ -71,8 +70,7 @@ export class SeedIntegrationTable1691494801748 implements MigrationInterface { SET "imgSrc" = $2, "isComingSoon" = $3, - "order" = $4, - "navigationUrl" = $5 + "order" = $4 RETURNING id; `; } diff --git a/packages/core/src/database/migrations/1692171665427-SeedIntegrationTable.ts b/packages/core/src/database/migrations/1692171665427-SeedIntegrationTable.ts index be0f512ddfa..32ab6d83c55 100644 --- a/packages/core/src/database/migrations/1692171665427-SeedIntegrationTable.ts +++ b/packages/core/src/database/migrations/1692171665427-SeedIntegrationTable.ts @@ -33,12 +33,12 @@ export class SeedIntegrationTable1692171665427 implements MigrationInterface { public async upsertIntegrationsAndIntegrationTypes(queryRunner: QueryRunner): Promise { const destDir = 'integrations'; - for await (const { name, imgSrc, isComingSoon, order, redirectUrl, integrationTypesMap } of DEFAULT_AI_INTEGRATIONS) { + for await (const { name, imgSrc, isComingSoon, order, integrationTypesMap } of DEFAULT_AI_INTEGRATIONS) { try { const filepath = `integrations/${imgSrc}`; let upsertQuery = ``; - const payload = [name, filepath, isComingSoon, order, redirectUrl]; + const payload = [name, filepath, isComingSoon, order]; if (queryRunner.connection.options.type === 'sqlite') { // For SQLite, manually generate a UUID using uuidv4() @@ -46,33 +46,31 @@ export class SeedIntegrationTable1692171665427 implements MigrationInterface { upsertQuery = ` INSERT INTO integration ( - "name", "imgSrc", "isComingSoon", "order", "navigationUrl", "id" + "name", "imgSrc", "isComingSoon", "order", "id" ) VALUES ( - $1, $2, $3, $4, $5, $6 + $1, $2, $3, $4, $5 ) ON CONFLICT(name) DO UPDATE SET "imgSrc" = $2, "isComingSoon" = $3, - "order" = $4, - "navigationUrl" = $5 + "order" = $4 RETURNING id; `; } else { upsertQuery = ` INSERT INTO "integration" ( - "name", "imgSrc", "isComingSoon", "order", "navigationUrl" + "name", "imgSrc", "isComingSoon", "order" ) VALUES ( - $1, $2, $3, $4, $5 + $1, $2, $3, $4 ) ON CONFLICT(name) DO UPDATE SET "imgSrc" = $2, "isComingSoon" = $3, - "order" = $4, - "navigationUrl" = $5 + "order" = $4 RETURNING id; `; } diff --git a/packages/core/src/integration/default-integration-type.ts b/packages/core/src/integration/default-integration-type.ts index 6b6e2373c80..5598bc46e17 100644 --- a/packages/core/src/integration/default-integration-type.ts +++ b/packages/core/src/integration/default-integration-type.ts @@ -49,5 +49,12 @@ export const DEFAULT_INTEGRATION_TYPES = [ order: 2, description: null, icon: null + }, + { + name: IntegrationTypeEnum.COMMUNICATION, + groupName: IntegrationTypeGroupEnum.CATEGORIES, + order: 2, + description: null, + icon: null } ]; diff --git a/packages/core/src/integration/integration-type.entity.ts b/packages/core/src/integration/integration-type.entity.ts index dea36e0f7d8..9866c79617d 100644 --- a/packages/core/src/integration/integration-type.entity.ts +++ b/packages/core/src/integration/integration-type.entity.ts @@ -1,10 +1,11 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { Column, Entity, ManyToMany } from 'typeorm'; +import { Column, Entity, ManyToMany, Unique } from 'typeorm'; import { IsNotEmpty, IsNumber, IsOptional } from 'class-validator'; import { IIntegration, IIntegrationType } from '@gauzy/contracts'; import { BaseEntity, Integration } from '../core/entities/internal'; @Entity('integration_type') +@Unique(['name']) export class IntegrationType extends BaseEntity implements IIntegrationType { @ApiProperty({ type: () => String }) diff --git a/packages/core/src/integration/utils.ts b/packages/core/src/integration/utils.ts index 8d7a236e328..87058f0e1b2 100644 --- a/packages/core/src/integration/utils.ts +++ b/packages/core/src/integration/utils.ts @@ -2,11 +2,11 @@ import { QueryRunner } from "typeorm"; import { v4 as uuidv4 } from 'uuid'; import { getConfig } from "@gauzy/config"; -import { IIntegration, IIntegrationType } from "@gauzy/contracts"; +import { IIntegration, IIntegrationType, IntegrationTypeEnum } from "@gauzy/contracts"; import { copyAssets } from "./../core/seeds/utils"; +import { DEFAULT_INTEGRATION_TYPES } from "./default-integration-type"; export class IntegrationsUtils { - /** * * @param queryRunner @@ -85,6 +85,7 @@ export class IntegrationsUtils { integrationTypeNames: any[] ): Promise { try { + await this.upsertIntegrationTypes(queryRunner, integrationTypeNames); return await queryRunner.query(`SELECT * FROM "integration_type" WHERE "integration_type"."name" IN ('${integrationTypeNames.join("','")}')`); } catch (error) { console.log('Error while querying integration types:', error); @@ -92,6 +93,60 @@ export class IntegrationsUtils { } } + /** + * + * @param queryRunner + * @param integrationTypeName + */ + public static async upsertIntegrationTypes( + queryRunner: QueryRunner, + integrationTypeNames: IntegrationTypeEnum[] + ) { + for await (const integrationTypeName of integrationTypeNames) { + const { name, description, icon, groupName, order } = DEFAULT_INTEGRATION_TYPES.find( + (type) => type.name === integrationTypeName + ); + const payload = [name, description, icon, groupName, order]; + + let upsertQuery = ``; + if (queryRunner.connection.options.type === 'sqlite') { + // For SQLite, manually generate a UUID using uuidv4() + upsertQuery = ` + INSERT INTO "integration_type" ( + "name", "description", "icon", "groupName", "order", "id" + ) + VALUES ( + $1, $2, $3, $4, $5, $6 + ) + ON CONFLICT(name) DO UPDATE + SET + "description" = $2, + "icon" = $3, + "groupName" = $4, + "order" = $5 + RETURNING id; + `; + } else { + upsertQuery = ` + INSERT INTO "integration_type" ( + "name", "description", "icon", "groupName", "order" + ) + VALUES ( + $1, $2, $3, $4, $5 + ) + ON CONFLICT(name) DO UPDATE + SET + "description" = $2, + "icon" = $3, + "groupName" = $4, + "order" = $5 + RETURNING id; + `; + } + await queryRunner.query(upsertQuery, payload); + } + } + /** * * From 9419ca848b6dbd53bf36c29606f2fc6bd0c6f839 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 19 Sep 2023 15:40:54 +0530 Subject: [PATCH 022/104] fix: #6823 github integrations DB seeds --- packages/contracts/src/integration.model.ts | 2 +- ...3907817-AddColumnsToTheIntegrationTable.ts | 12 --- ...783-AddColumnsToTheIntegrationTypeTable.ts | 87 +++++++++++++++++++ ...40-SeedIntegrationsAndIntegrationTypes.ts} | 10 +-- 4 files changed, 91 insertions(+), 20 deletions(-) create mode 100644 packages/core/src/database/migrations/1695111838783-AddColumnsToTheIntegrationTypeTable.ts rename packages/core/src/database/migrations/{1695043020330-SeedProjectManagementIntegrations.ts => 1695112275840-SeedIntegrationsAndIntegrationTypes.ts} (81%) diff --git a/packages/contracts/src/integration.model.ts b/packages/contracts/src/integration.model.ts index 9fef1827343..5b184265666 100644 --- a/packages/contracts/src/integration.model.ts +++ b/packages/contracts/src/integration.model.ts @@ -187,7 +187,7 @@ export enum IntegrationTypeEnum { CRM = 'CRM', SCHEDULING = 'Scheduling', TOOLS = 'Tools', - PROJECT_MANAGEMENT = ' Project Management', + PROJECT_MANAGEMENT = 'Project Management', COMMUNICATION = 'Communication' } diff --git a/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts b/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts index 76a6b80c563..bac23b5d526 100644 --- a/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts +++ b/packages/core/src/database/migrations/1695023907817-AddColumnsToTheIntegrationTable.ts @@ -38,8 +38,6 @@ export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationIn */ public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise { await queryRunner.query(`ALTER TABLE "integration" RENAME COLUMN "navigationUrl" TO "redirectUrl"`); - await queryRunner.query(`ALTER TABLE "integration_type" ADD "description" character varying`); - await queryRunner.query(`ALTER TABLE "integration_type" ADD "icon" character varying`); await queryRunner.query(`ALTER TABLE "integration" ADD "provider" character varying`); } @@ -50,8 +48,6 @@ export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationIn */ public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise { await queryRunner.query(`ALTER TABLE "integration" DROP COLUMN "provider"`); - await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "icon"`); - await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "description"`); await queryRunner.query(`ALTER TABLE "integration" RENAME COLUMN "redirectUrl" TO "navigationUrl"`); } @@ -65,10 +61,6 @@ export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationIn await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "integration"`); await queryRunner.query(`DROP TABLE "integration"`); await queryRunner.query(`ALTER TABLE "temporary_integration" RENAME TO "integration"`); - await queryRunner.query(`CREATE TABLE "temporary_integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL, "description" varchar, "icon" varchar)`); - await queryRunner.query(`INSERT INTO "temporary_integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "integration_type"`); - await queryRunner.query(`DROP TABLE "integration_type"`); - await queryRunner.query(`ALTER TABLE "temporary_integration_type" RENAME TO "integration_type"`); await queryRunner.query(`CREATE TABLE "temporary_integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "provider" varchar, "redirectUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); await queryRunner.query(`INSERT INTO "temporary_integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "integration"`); await queryRunner.query(`DROP TABLE "integration"`); @@ -85,10 +77,6 @@ export class AddColumnsToTheIntegrationTable1695023907817 implements MigrationIn await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "temporary_integration"`); await queryRunner.query(`DROP TABLE "temporary_integration"`); - await queryRunner.query(`ALTER TABLE "integration_type" RENAME TO "temporary_integration_type"`); - await queryRunner.query(`CREATE TABLE "integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL)`); - await queryRunner.query(`INSERT INTO "integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "temporary_integration_type"`); - await queryRunner.query(`DROP TABLE "temporary_integration_type"`); await queryRunner.query(`ALTER TABLE "integration" RENAME TO "temporary_integration"`); await queryRunner.query(`CREATE TABLE "integration" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "imgSrc" varchar, "isComingSoon" boolean NOT NULL DEFAULT (0), "isPaid" boolean NOT NULL DEFAULT (0), "version" varchar, "docUrl" varchar, "isFreeTrial" boolean NOT NULL DEFAULT (0), "freeTrialPeriod" numeric DEFAULT (0), "order" integer, "navigationUrl" varchar, CONSTRAINT "UQ_938c19d92ad3f290ff5fc163531" UNIQUE ("name"))`); await queryRunner.query(`INSERT INTO "integration"("id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order") SELECT "id", "createdAt", "updatedAt", "name", "imgSrc", "isComingSoon", "isPaid", "version", "docUrl", "isFreeTrial", "freeTrialPeriod", "order" FROM "temporary_integration"`); diff --git a/packages/core/src/database/migrations/1695111838783-AddColumnsToTheIntegrationTypeTable.ts b/packages/core/src/database/migrations/1695111838783-AddColumnsToTheIntegrationTypeTable.ts new file mode 100644 index 00000000000..93539e3b896 --- /dev/null +++ b/packages/core/src/database/migrations/1695111838783-AddColumnsToTheIntegrationTypeTable.ts @@ -0,0 +1,87 @@ + +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddColumnsToTheIntegrationTypeTable1695111838783 implements MigrationInterface { + + name = 'AddColumnsToTheIntegrationTypeTable1695111838783'; + + /** + * Up Migration + * + * @param queryRunner + */ + public async up(queryRunner: QueryRunner): Promise { + if (queryRunner.connection.options.type === 'sqlite') { + await this.sqliteUpQueryRunner(queryRunner); + } else { + await this.postgresUpQueryRunner(queryRunner); + } + } + + /** + * Down Migration + * + * @param queryRunner + */ + public async down(queryRunner: QueryRunner): Promise { + if (queryRunner.connection.options.type === 'sqlite') { + await this.sqliteDownQueryRunner(queryRunner); + } else { + await this.postgresDownQueryRunner(queryRunner); + } + } + + /** + * PostgresDB Up Migration + * + * @param queryRunner + */ + public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "integration_type" ADD "description" character varying`); + await queryRunner.query(`ALTER TABLE "integration_type" ADD "icon" character varying`); + await queryRunner.query(`ALTER TABLE "integration_type" ADD CONSTRAINT "UQ_83443d669822bbbf2bd0ebdacd7" UNIQUE ("name")`); + } + + /** + * PostgresDB Down Migration + * + * @param queryRunner + */ + public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "integration_type" DROP CONSTRAINT "UQ_83443d669822bbbf2bd0ebdacd7"`); + await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "icon"`); + await queryRunner.query(`ALTER TABLE "integration_type" DROP COLUMN "description"`); + } + + /** + * SqliteDB Up Migration + * + * @param queryRunner + */ + public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "temporary_integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL, "description" varchar, "icon" varchar)`); + await queryRunner.query(`INSERT INTO "temporary_integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "integration_type"`); + await queryRunner.query(`DROP TABLE "integration_type"`); + await queryRunner.query(`ALTER TABLE "temporary_integration_type" RENAME TO "integration_type"`); + await queryRunner.query(`CREATE TABLE "temporary_integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL, "description" varchar, "icon" varchar, CONSTRAINT "UQ_be0d67f4df84f036fab2ed89c47" UNIQUE ("name"))`); + await queryRunner.query(`INSERT INTO "temporary_integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order", "description", "icon") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order", "description", "icon" FROM "integration_type"`); + await queryRunner.query(`DROP TABLE "integration_type"`); + await queryRunner.query(`ALTER TABLE "temporary_integration_type" RENAME TO "integration_type"`); + } + + /** + * SqliteDB Down Migration + * + * @param queryRunner + */ + public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "integration_type" RENAME TO "temporary_integration_type"`); + await queryRunner.query(`CREATE TABLE "integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL, "description" varchar, "icon" varchar)`); + await queryRunner.query(`INSERT INTO "integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order", "description", "icon") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order", "description", "icon" FROM "temporary_integration_type"`); + await queryRunner.query(`DROP TABLE "temporary_integration_type"`); + await queryRunner.query(`ALTER TABLE "integration_type" RENAME TO "temporary_integration_type"`); + await queryRunner.query(`CREATE TABLE "integration_type" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar NOT NULL, "groupName" varchar NOT NULL, "order" integer NOT NULL)`); + await queryRunner.query(`INSERT INTO "integration_type"("id", "createdAt", "updatedAt", "name", "groupName", "order") SELECT "id", "createdAt", "updatedAt", "name", "groupName", "order" FROM "temporary_integration_type"`); + await queryRunner.query(`DROP TABLE "temporary_integration_type"`); + } +} diff --git a/packages/core/src/database/migrations/1695043020330-SeedProjectManagementIntegrations.ts b/packages/core/src/database/migrations/1695112275840-SeedIntegrationsAndIntegrationTypes.ts similarity index 81% rename from packages/core/src/database/migrations/1695043020330-SeedProjectManagementIntegrations.ts rename to packages/core/src/database/migrations/1695112275840-SeedIntegrationsAndIntegrationTypes.ts index c5dc7e054bb..6d1e4157ff0 100644 --- a/packages/core/src/database/migrations/1695043020330-SeedProjectManagementIntegrations.ts +++ b/packages/core/src/database/migrations/1695112275840-SeedIntegrationsAndIntegrationTypes.ts @@ -1,14 +1,11 @@ import { MigrationInterface, QueryRunner } from "typeorm"; -import { IntegrationsUtils } from "./../../integration/utils"; import { DEFAULT_INTEGRATIONS, PROJECT_MANAGE_DEFAULT_INTEGRATIONS } from "./../../integration/default-integration"; +import { IntegrationsUtils } from "./../../integration/utils"; -/** - * - */ -export class SeedProjectManagementIntegrations1695043020330 implements MigrationInterface { +export class SeedIntegrationsAndIntegrationTypes1695112275840 implements MigrationInterface { - name = 'SeedProjectManagementIntegrations1695043020330'; + name = 'SeedIntegrationsAndIntegrationTypes1695112275840'; /** * Up Migration @@ -19,7 +16,6 @@ export class SeedProjectManagementIntegrations1695043020330 implements Migration await IntegrationsUtils.upsertIntegrationsAndIntegrationTypes(queryRunner, PROJECT_MANAGE_DEFAULT_INTEGRATIONS); await IntegrationsUtils.upsertIntegrationsAndIntegrationTypes(queryRunner, DEFAULT_INTEGRATIONS); } - /** * Down Migration * From 450c387fb7381f21859ae813e64c715a4c6ad6b5 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 19 Sep 2023 16:05:25 +0530 Subject: [PATCH 023/104] fix: #6823 github & jira integration icons --- .../src/assets/seed/integrations/github.svg | 24 +------------- .../api/src/assets/seed/integrations/jira.svg | 33 +++++++++---------- 2 files changed, 17 insertions(+), 40 deletions(-) diff --git a/apps/api/src/assets/seed/integrations/github.svg b/apps/api/src/assets/seed/integrations/github.svg index 924c263ac6d..dae0903d050 100644 --- a/apps/api/src/assets/seed/integrations/github.svg +++ b/apps/api/src/assets/seed/integrations/github.svg @@ -1,23 +1 @@ - - - - - - - - + \ No newline at end of file diff --git a/apps/api/src/assets/seed/integrations/jira.svg b/apps/api/src/assets/seed/integrations/jira.svg index bcd5d465358..20eb4f1b918 100644 --- a/apps/api/src/assets/seed/integrations/jira.svg +++ b/apps/api/src/assets/seed/integrations/jira.svg @@ -1,19 +1,18 @@ - - - - - - - - + + + + + + + + + + + + + + + + From efebcbf0c9e2ae10c997eb49ae90cac66989b92a Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 19 Sep 2023 17:09:05 +0530 Subject: [PATCH 024/104] fix: build issue --- packages/core/src/github/github.module.ts | 10 +++--- packages/core/src/github/github.service.ts | 36 +++++++++---------- .../integration-github/src/probot.module.ts | 10 +++--- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/packages/core/src/github/github.module.ts b/packages/core/src/github/github.module.ts index 7974f54f1e7..8c5eb732b73 100644 --- a/packages/core/src/github/github.module.ts +++ b/packages/core/src/github/github.module.ts @@ -1,12 +1,14 @@ import { Module } from '@nestjs/common'; -import { GitHubController } from './github.controller'; -import { GitHubService } from './github.service'; import { RouterModule } from 'nest-router'; import { ProbotModule } from '@gauzy/integration-github'; +import { GitHubController } from './github.controller'; +import { GitHubService } from './github.service'; @Module({ imports: [ - RouterModule.forRoutes([{ path: '/github', module: GitHubModule }]), + RouterModule.forRoutes([ + { path: '/github', module: GitHubModule } + ]), ProbotModule, ], @@ -14,4 +16,4 @@ import { ProbotModule } from '@gauzy/integration-github'; providers: [GitHubService], exports: [GitHubService], }) -export class GitHubModule {} +export class GitHubModule { } diff --git a/packages/core/src/github/github.service.ts b/packages/core/src/github/github.service.ts index 19c5ca5ccca..35750435dfe 100644 --- a/packages/core/src/github/github.service.ts +++ b/packages/core/src/github/github.service.ts @@ -1,12 +1,12 @@ import { Injectable } from '@nestjs/common'; import { Context } from 'probot'; -import { GitHubService as GitHubIntegrationService } from '@gauzy/integration-github'; +// import { GitHubService as GitHubIntegrationService } from '@gauzy/integration-github'; @Injectable() export class GitHubService { constructor( - private readonly gitHubIntegrationService: GitHubIntegrationService - ) {} + // private readonly gitHubIntegrationService: GitHubIntegrationService + ) { } /** * ----- From GitHub to APIs ----- @@ -38,13 +38,13 @@ export class GitHubService { repo: string, installationId: number ) { - await this.gitHubIntegrationService.openIssue( - title, - body, - owner, - repo, - installationId - ); + // await this.gitHubIntegrationService.openIssue( + // title, + // body, + // owner, + // repo, + // installationId + // ); } async editIssue( issueNumber: number, @@ -54,14 +54,14 @@ export class GitHubService { repo: string, installationId: number ) { - await this.gitHubIntegrationService.editIssue( - issueNumber, - title, - body, - repo, - owner, - installationId - ); + // await this.gitHubIntegrationService.editIssue( + // issueNumber, + // title, + // body, + // repo, + // owner, + // installationId + // ); } // TODO // Handle all other required events diff --git a/packages/plugins/integration-github/src/probot.module.ts b/packages/plugins/integration-github/src/probot.module.ts index 7c4497c49db..a4b83ba9636 100644 --- a/packages/plugins/integration-github/src/probot.module.ts +++ b/packages/plugins/integration-github/src/probot.module.ts @@ -15,8 +15,7 @@ import { GitHubService } from './github.service'; }) export class ProbotModule { static forRoot(options: ProbotModuleOptions): DynamicModule { - const { path: hookPath } = options; - const HookController = getControllerClass({ path: hookPath }); + const HookController = getControllerClass({ path: options.path }); return { global: options.isGlobal || true, module: ProbotModule, @@ -35,8 +34,7 @@ export class ProbotModule { } static forRootAsync(options: ProbotModuleAsyncOptions): DynamicModule { - const { path: hookPath } = options; - const HookController = getControllerClass({ path: hookPath }); + const HookController = getControllerClass({ path: options.path }); return { module: ProbotModule, global: options.isGlobal || true, @@ -51,7 +49,9 @@ export class ProbotModule { ProbotDiscovery, GitHubService, ], - exports: [GitHubService], + exports: [ + GitHubService + ], }; } } From 31b800f51eaf98e6770bff30eba8a3d3cd160d11 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 19 Sep 2023 18:02:30 +0530 Subject: [PATCH 025/104] chore(deps): added deps package to avoid build --- packages/plugins/integration-github/package.json | 1 + yarn.lock | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/plugins/integration-github/package.json b/packages/plugins/integration-github/package.json index f797dcc9d77..08515f35265 100644 --- a/packages/plugins/integration-github/package.json +++ b/packages/plugins/integration-github/package.json @@ -29,6 +29,7 @@ "keywords": [], "dependencies": { "octokit": "^3.1.0", + "pino-std-serializers": "^6.2.2", "probot": "^12.3.1", "smee-client": "^1.2.3" }, diff --git a/yarn.lock b/yarn.lock index 12779360523..e8733d117e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28269,7 +28269,7 @@ pino-pretty@^6.0.0: split2 "^3.1.1" strip-json-comments "^3.1.1" -pino-std-serializers@*: +pino-std-serializers@*, pino-std-serializers@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz#d9a9b5f2b9a402486a5fc4db0a737570a860aab3" integrity sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA== From eb62f5f2c2bb8765e60c01289f1dd999396db4ce Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 19 Sep 2023 18:04:35 +0530 Subject: [PATCH 026/104] fix: cspell spelling ;-) --- .cspell.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.cspell.json b/.cspell.json index 992190eb9fd..08613228f55 100644 --- a/.cspell.json +++ b/.cspell.json @@ -372,7 +372,8 @@ "USERTRX", "MAINUNEXCEPTION", "Harbir", - "Chahal" + "Chahal", + "pino" ], "useGitignore": true, "ignorePaths": [ From d51b7170c42e73bda391a52837df646936b043a5 Mon Sep 17 00:00:00 2001 From: Ruslan K Date: Tue, 19 Sep 2023 16:12:03 +0200 Subject: [PATCH 027/104] Update desktop-ipc.ts --- packages/desktop-libs/src/lib/desktop-ipc.ts | 25 +++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/desktop-libs/src/lib/desktop-ipc.ts b/packages/desktop-libs/src/lib/desktop-ipc.ts index 38aa076dd17..42751cdbd79 100644 --- a/packages/desktop-libs/src/lib/desktop-ipc.ts +++ b/packages/desktop-libs/src/lib/desktop-ipc.ts @@ -183,25 +183,34 @@ export function ipcMainHandler( } else { await logout(); } + try { timeTrackerWindow.webContents.send( 'preferred_language_change', TranslateService.preferredLanguage ); + const lastTime = await TimerData.getLastCaptureTimeSlot( knex, LocalStore.beforeRequestParams() ); + console.log('Last Capture Time (Desktop IPC):', lastTime); + await offlineMode.connectivity(); - console.log('Network state',offlineMode.enabled ? 'Offline' : 'Online') + + console.log('Network state:', offlineMode.enabled ? 'Offline' : 'Online'); + event.sender.send('timer_tracker_show', { ...LocalStore.beforeRequestParams(), timeSlotId: lastTime ? lastTime.timeslotId : null, isOffline: offlineMode.enabled, }); - await countIntervalQueue(timeTrackerWindow, false); + + await countIntervalQueue(timeTrackerWindow, false); + await sequentialSyncQueue(timeTrackerWindow); + await latestScreenshots(timeTrackerWindow); } catch (error) { throw new UIError('500', error, 'IPCINIT'); @@ -404,15 +413,21 @@ export function ipcTimer( ipcMain.handle('START_TIMER', async (event, arg) => { try { powerManager = new DesktopPowerManager(timeTrackerWindow); + powerManagerPreventSleep = new PowerManagerPreventDisplaySleep( powerManager ); + powerManagerDetectInactivity = new PowerManagerDetectInactivity( powerManager ); + new DesktopOsInactivityHandler(powerManagerDetectInactivity); + const setting = LocalStore.getStore('appSetting'); + log.info(`Timer Start: ${moment().format()}`); + store.set({ project: { projectId: arg.projectId, @@ -422,13 +437,17 @@ export function ipcTimer( organizationContactId: arg.organizationContactId } }); - // Check api connection before to start + + // Check API connection before starting await offlineMode.connectivity(); + // Start Timer const timerResponse = await timerHandler.startTimer(setupWindow, knex, timeTrackerWindow, arg.timeLog); + settingWindow.webContents.send('app_setting_update', { setting: LocalStore.getStore('appSetting') }); + if (setting && setting.preventDisplaySleep) { powerManagerPreventSleep.start(); } From 4238ee2853981926fba80ea3ff02f918cef6c5bf Mon Sep 17 00:00:00 2001 From: Ruslan Konviser Date: Tue, 19 Sep 2023 16:47:37 +0200 Subject: [PATCH 028/104] fix: build (related to new plugins) --- .deploy/api/Dockerfile | 4 ++++ .deploy/webapp/Dockerfile | 2 ++ apps/desktop/src/package.json | 4 +++- apps/gauzy-e2e/package.json | 8 +++++--- apps/server/src/package.json | 4 +++- packages/core/package.json | 2 ++ 6 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.deploy/api/Dockerfile b/.deploy/api/Dockerfile index a78a66b5a7b..bbba4273051 100644 --- a/.deploy/api/Dockerfile +++ b/.deploy/api/Dockerfile @@ -107,6 +107,8 @@ COPY --chown=node:node packages/plugin/package.json ./packages/plugin/ COPY --chown=node:node packages/plugins/integration-ai/package.json ./packages/plugins/integration-ai/ COPY --chown=node:node packages/plugins/integration-hubstaff/package.json ./packages/plugins/integration-hubstaff/ COPY --chown=node:node packages/plugins/integration-upwork/package.json ./packages/plugins/integration-upwork/ +COPY --chown=node:node packages/plugins/integration-github/package.json ./packages/plugins/integration-github/ +COPY --chown=node:node packages/plugins/integration-jira/package.json ./packages/plugins/integration-jira/ COPY --chown=node:node packages/plugins/product-reviews/package.json ./packages/plugins/product-reviews/ COPY --chown=node:node packages/plugins/knowledge-base/package.json ./packages/plugins/knowledge-base/ COPY --chown=node:node packages/plugins/changelog/package.json ./packages/plugins/changelog/ @@ -150,6 +152,8 @@ COPY --chown=node:node packages/plugin/package.json ./packages/plugin/ COPY --chown=node:node packages/plugins/integration-ai/package.json ./packages/plugins/integration-ai/ COPY --chown=node:node packages/plugins/integration-hubstaff/package.json ./packages/plugins/integration-hubstaff/ COPY --chown=node:node packages/plugins/integration-upwork/package.json ./packages/plugins/integration-upwork/ +COPY --chown=node:node packages/plugins/integration-github/package.json ./packages/plugins/integration-github/ +COPY --chown=node:node packages/plugins/integration-jira/package.json ./packages/plugins/integration-jira/ COPY --chown=node:node packages/plugins/product-reviews/package.json ./packages/plugins/product-reviews/ COPY --chown=node:node packages/plugins/knowledge-base/package.json ./packages/plugins/knowledge-base/ COPY --chown=node:node packages/plugins/changelog/package.json ./packages/plugins/changelog/ diff --git a/.deploy/webapp/Dockerfile b/.deploy/webapp/Dockerfile index 637a6f5718d..740215ed754 100644 --- a/.deploy/webapp/Dockerfile +++ b/.deploy/webapp/Dockerfile @@ -55,6 +55,8 @@ COPY --chown=node:node packages/plugin/package.json ./packages/plugin/ COPY --chown=node:node packages/plugins/integration-ai/package.json ./packages/plugins/integration-ai/ COPY --chown=node:node packages/plugins/integration-hubstaff/package.json ./packages/plugins/integration-hubstaff/ COPY --chown=node:node packages/plugins/integration-upwork/package.json ./packages/plugins/integration-upwork/ +COPY --chown=node:node packages/plugins/integration-github/package.json ./packages/plugins/integration-github/ +COPY --chown=node:node packages/plugins/integration-jira/package.json ./packages/plugins/integration-jira/ COPY --chown=node:node packages/plugins/product-reviews/package.json ./packages/plugins/product-reviews/ COPY --chown=node:node packages/plugins/knowledge-base/package.json ./packages/plugins/knowledge-base/ COPY --chown=node:node packages/plugins/changelog/package.json ./packages/plugins/changelog/ diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index a5a41a298da..2db04e9ee4d 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -28,11 +28,13 @@ "../../../packages/plugins/knowledge-base", "../../../packages/plugins/changelog", "../../../packages/common", - "../../../packages/plugins/integration-ai", "../../../packages/config", "../../../packages/contracts", + "../../../packages/plugins/integration-ai", "../../../packages/plugins/integration-hubstaff", "../../../packages/plugins/integration-upwork", + "../../../packages/plugins/integration-github", + "../../../packages/plugins/integration-jira", "../../../packages/plugin" ] }, diff --git a/apps/gauzy-e2e/package.json b/apps/gauzy-e2e/package.json index 2bddf0d5397..55fb9017f2b 100644 --- a/apps/gauzy-e2e/package.json +++ b/apps/gauzy-e2e/package.json @@ -38,15 +38,17 @@ "build:package:plugin:knowledge-base": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/knowledge-base build", "build:package:core": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/core build", "build:package:auth": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/auth build", - "build:package:plugins": "yarn run build:package:plugin:integration-ai && yarn run build:package:plugin:integration-hubstaff && yarn run build:package:plugin:integration-upwork && yarn run build:package:plugin:product-reviews", + "build:package:plugins": "yarn run build:package:plugin:integration-ai && yarn run build:package:plugin:integration-hubstaff && yarn run build:package:plugin:integration-upwork && yarn run build:package:plugin:integration-github && yarn run build:package:plugin:integration-jira && yarn run build:package:plugin:product-reviews", "build": "yarn build:package:all && concurrently --raw \"yarn build:api\" \"yarn build:gauzy\"", "build:api": "yarn ng build api", "build:gauzy": "yarn run postinstall.web && yarn run config:dev && yarn ng build gauzy", "build:package:plugin:integration-ai": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/integration-ai build", "build:package:plugin:integration-hubstaff": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/integration-hubstaff build", - "build:package:desktop-window": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/desktop-window build", - "build:package:plugin:product-reviews": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/product-reviews build", "build:package:plugin:integration-upwork": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/integration-upwork build", + "build:package:plugin:integration-github": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/integration-github build", + "build:package:plugin:integration-jira": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/integration-jira build", + "build:package:plugin:product-reviews": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/plugins/product-reviews build", + "build:package:desktop-window": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/desktop-window build", "postinstall.web": "yarn node ./decorate-angular-cli.js && yarn npx ngcc --properties es2016 browser module main --first-only --create-ivy-entry-points && yarn node tools/web/postinstall" }, "resolutions": { diff --git a/apps/server/src/package.json b/apps/server/src/package.json index 9f4e5fe4f44..78a1abe80e4 100755 --- a/apps/server/src/package.json +++ b/apps/server/src/package.json @@ -29,11 +29,13 @@ "../../../packages/plugins/knowledge-base", "../../../packages/plugins/changelog", "../../../packages/common", - "../../../packages/plugins/integration-ai", "../../../packages/config", "../../../packages/contracts", + "../../../packages/plugins/integration-ai", "../../../packages/plugins/integration-hubstaff", "../../../packages/plugins/integration-upwork", + "../../../packages/plugins/integration-github", + "../../../packages/plugins/integration-jira", "../../../packages/plugin" ] }, diff --git a/packages/core/package.json b/packages/core/package.json index 1a821dd0a13..e30e7c85a9f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -48,6 +48,8 @@ "@gauzy/integration-ai": "^0.1.0", "@gauzy/integration-hubstaff": "^0.1.0", "@gauzy/integration-upwork": "^0.1.0", + "@gauzy/integration-github": "^0.1.0", + "@gauzy/integration-jira": "^0.1.0", "@gauzy/plugin": "^0.1.0", "@godaddy/terminus": "^4.5.0", "@grpc/grpc-js": "^1.6.7", From 26a0c0832ca818398206600e21a8369b58f64a7d Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:44:34 +0530 Subject: [PATCH 029/104] chore(deps): chore(deps): downgrade octokit from ^3.1.0 to 2.1.0 --- .../plugins/integration-github/package.json | 3 +- yarn.lock | 281 ++++++++---------- 2 files changed, 132 insertions(+), 152 deletions(-) diff --git a/packages/plugins/integration-github/package.json b/packages/plugins/integration-github/package.json index 08515f35265..3c10c0deefa 100644 --- a/packages/plugins/integration-github/package.json +++ b/packages/plugins/integration-github/package.json @@ -28,7 +28,8 @@ }, "keywords": [], "dependencies": { - "octokit": "^3.1.0", + "@octokit/rest": "^20.0.1", + "octokit": "2.1.0", "pino-std-serializers": "^6.2.2", "probot": "^12.3.1", "smee-client": "^1.2.3" diff --git a/yarn.lock b/yarn.lock index e8733d117e5..5a457bdfd0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5845,20 +5845,20 @@ resolved "https://registry.yarnpkg.com/@oclif/screen/-/screen-1.0.4.tgz#b740f68609dfae8aa71c3a6cab15d816407ba493" integrity sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw== -"@octokit/app@^14.0.0": - version "14.0.0" - resolved "https://registry.yarnpkg.com/@octokit/app/-/app-14.0.0.tgz#cee2edc1fea38af4115261a7b5ffb1e7cb14a0a6" - integrity sha512-g/zDXttroZ9Se08shK0d0d/j0cgSA+h4WV7qGUevNEM0piNBkIlfb4Fm6bSwCNAZhNf72mBgERmYOoxicPkqdw== +"@octokit/app@^13.1.5": + version "13.1.8" + resolved "https://registry.yarnpkg.com/@octokit/app/-/app-13.1.8.tgz#9e43e7e1ffc8f028130cabdf587cbacccf0c0257" + integrity sha512-bCncePMguVyFpdBbnceFKfmPOuUD94T189GuQ0l00ZcQ+mX4hyPqnaWJlsXE2HSdA71eV7p8GPDZ+ErplTkzow== dependencies: - "@octokit/auth-app" "^6.0.0" - "@octokit/auth-unauthenticated" "^5.0.0" - "@octokit/core" "^5.0.0" - "@octokit/oauth-app" "^6.0.0" - "@octokit/plugin-paginate-rest" "^8.0.0" - "@octokit/types" "^11.1.0" - "@octokit/webhooks" "^12.0.1" + "@octokit/auth-app" "^4.0.13" + "@octokit/auth-unauthenticated" "^3.0.0" + "@octokit/core" "^4.0.0" + "@octokit/oauth-app" "^4.0.7" + "@octokit/plugin-paginate-rest" "^6.0.0" + "@octokit/types" "^9.0.0" + "@octokit/webhooks" "^10.0.0" -"@octokit/auth-app@^4.0.2": +"@octokit/auth-app@^4.0.13", "@octokit/auth-app@^4.0.2": version "4.0.13" resolved "https://registry.yarnpkg.com/@octokit/auth-app/-/auth-app-4.0.13.tgz#53323bee6bfefbb73ea544dd8e6a0144550e13e3" integrity sha512-NBQkmR/Zsc+8fWcVIFrwDgNXS7f4XDrkd9LHdi9DPQw1NdGHLviLzRO2ZBwTtepnwHXW5VTrVU9eFGijMUqllg== @@ -5873,21 +5873,6 @@ universal-github-app-jwt "^1.1.1" universal-user-agent "^6.0.0" -"@octokit/auth-app@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-app/-/auth-app-6.0.0.tgz#f7d31d40f78973cb801c0661db2cc3719de7328b" - integrity sha512-OKct7Rukf3g9DjpzcpdacQsdmd6oPrJ7fZND22JkjzhDvfhttUOnmh+qPS4kHhaNNyTxqSThnfrUWvkqNLd1nw== - dependencies: - "@octokit/auth-oauth-app" "^7.0.0" - "@octokit/auth-oauth-user" "^4.0.0" - "@octokit/request" "^8.0.2" - "@octokit/request-error" "^5.0.0" - "@octokit/types" "^11.0.0" - deprecation "^2.3.1" - lru-cache "^10.0.0" - universal-github-app-jwt "^1.1.1" - universal-user-agent "^6.0.0" - "@octokit/auth-oauth-app@^5.0.0": version "5.0.6" resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-app/-/auth-oauth-app-5.0.6.tgz#e5f922623eb261485efc87f5d0d5b509c71caec8" @@ -5901,19 +5886,6 @@ btoa-lite "^1.0.0" universal-user-agent "^6.0.0" -"@octokit/auth-oauth-app@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-app/-/auth-oauth-app-7.0.0.tgz#864f58152b060132098356265eb4fb07ca1fae76" - integrity sha512-8JvJEXGoEqrbzLwt3SwIUvkDd+1wrM8up0KawvDIElB8rbxPbvWppGO0SLKAWSJ0q8ILcVq+mWck6pDcZ3a9KA== - dependencies: - "@octokit/auth-oauth-device" "^6.0.0" - "@octokit/auth-oauth-user" "^4.0.0" - "@octokit/request" "^8.0.2" - "@octokit/types" "^11.0.0" - "@types/btoa-lite" "^1.0.0" - btoa-lite "^1.0.0" - universal-user-agent "^6.0.0" - "@octokit/auth-oauth-device@^4.0.0": version "4.0.5" resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-device/-/auth-oauth-device-4.0.5.tgz#21e981f51ae63d419ca3db0b75e32c85b33fa0da" @@ -5924,16 +5896,6 @@ "@octokit/types" "^9.0.0" universal-user-agent "^6.0.0" -"@octokit/auth-oauth-device@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-device/-/auth-oauth-device-6.0.0.tgz#728143108345e07e06fd5bfec8891e838c3dce96" - integrity sha512-Zgf/LKhwWk54rJaTGYVYtbKgUty+ouil6VQeRd+pCw7Gd0ECoSWaZuHK6uDGC/HtnWHjpSWFhzxPauDoHcNRtg== - dependencies: - "@octokit/oauth-methods" "^4.0.0" - "@octokit/request" "^8.0.0" - "@octokit/types" "^11.0.0" - universal-user-agent "^6.0.0" - "@octokit/auth-oauth-user@^2.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-user/-/auth-oauth-user-2.1.2.tgz#7091e1b29527e577b16d0f1699d49fe3d39946ff" @@ -5946,18 +5908,6 @@ btoa-lite "^1.0.0" universal-user-agent "^6.0.0" -"@octokit/auth-oauth-user@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-user/-/auth-oauth-user-4.0.0.tgz#2499f25cf64ce2911ba3f2f12de176bfbc1a3805" - integrity sha512-VOm5aIkVGHaOhIvsF/4YmSjoYDzzrKbbYkdSEO0KqHK7I8SlO3ZndSikQ1fBlNPUEH0ve2BOTxLrVvI1qBf9/Q== - dependencies: - "@octokit/auth-oauth-device" "^6.0.0" - "@octokit/oauth-methods" "^4.0.0" - "@octokit/request" "^8.0.2" - "@octokit/types" "^11.0.0" - btoa-lite "^1.0.0" - universal-user-agent "^6.0.0" - "@octokit/auth-token@^2.4.4": version "2.5.0" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" @@ -5983,14 +5933,6 @@ "@octokit/request-error" "^3.0.0" "@octokit/types" "^9.0.0" -"@octokit/auth-unauthenticated@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-unauthenticated/-/auth-unauthenticated-5.0.0.tgz#fc0d479cb14f80ba691836c5e8b7744786e4d61e" - integrity sha512-AjOI6FNB2dweJ85p6rf7D4EhE4y6VBcwYfX/7KJkR5Q9fD9ET6NABAjajUTSNFfCxmNIaQgISggZ3pkgwtTqsA== - dependencies: - "@octokit/request-error" "^5.0.0" - "@octokit/types" "^11.0.0" - "@octokit/core@^3.2.4", "@octokit/core@^3.5.1": version "3.6.0" resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085" @@ -6004,6 +5946,19 @@ before-after-hook "^2.2.0" universal-user-agent "^6.0.0" +"@octokit/core@^4.0.0", "@octokit/core@^4.2.1": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.2.4.tgz#d8769ec2b43ff37cc3ea89ec4681a20ba58ef907" + integrity sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ== + dependencies: + "@octokit/auth-token" "^3.0.0" + "@octokit/graphql" "^5.0.0" + "@octokit/request" "^6.0.0" + "@octokit/request-error" "^3.0.0" + "@octokit/types" "^9.0.0" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + "@octokit/core@^5.0.0": version "5.0.0" resolved "https://registry.yarnpkg.com/@octokit/core/-/core-5.0.0.tgz#0fc2b6eb88437e5c1d69f756a5dcee7472d2b2dd" @@ -6053,6 +6008,15 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" +"@octokit/graphql@^5.0.0": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.6.tgz#9eac411ac4353ccc5d3fca7d76736e6888c5d248" + integrity sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw== + dependencies: + "@octokit/request" "^6.0.0" + "@octokit/types" "^9.0.0" + universal-user-agent "^6.0.0" + "@octokit/graphql@^7.0.0": version "7.0.1" resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-7.0.1.tgz#f2291620e17cdaa8115f8d0cdfc0644789ec2db2" @@ -6062,18 +6026,19 @@ "@octokit/types" "^11.0.0" universal-user-agent "^6.0.0" -"@octokit/oauth-app@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@octokit/oauth-app/-/oauth-app-6.0.0.tgz#a5c3b7794df4280c6aadbadd843119059d70a2c4" - integrity sha512-bNMkS+vJ6oz2hCyraT9ZfTpAQ8dZNqJJQVNaKjPLx4ue5RZiFdU1YWXguOPR8AaSHS+lKe+lR3abn2siGd+zow== +"@octokit/oauth-app@^4.0.7", "@octokit/oauth-app@^4.2.1": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@octokit/oauth-app/-/oauth-app-4.2.4.tgz#d385ffebe116c684940bf255a2189665c61ee5a0" + integrity sha512-iuOVFrmm5ZKNavRtYu5bZTtmlKLc5uVgpqTfMEqYYf2OkieV6VdxKZAb5qLVdEPL8LU2lMWcGpavPBV835cgoA== dependencies: - "@octokit/auth-oauth-app" "^7.0.0" - "@octokit/auth-oauth-user" "^4.0.0" - "@octokit/auth-unauthenticated" "^5.0.0" - "@octokit/core" "^5.0.0" - "@octokit/oauth-authorization-url" "^6.0.2" - "@octokit/oauth-methods" "^4.0.0" + "@octokit/auth-oauth-app" "^5.0.0" + "@octokit/auth-oauth-user" "^2.0.0" + "@octokit/auth-unauthenticated" "^3.0.0" + "@octokit/core" "^4.0.0" + "@octokit/oauth-authorization-url" "^5.0.0" + "@octokit/oauth-methods" "^2.0.0" "@types/aws-lambda" "^8.10.83" + fromentries "^1.3.1" universal-user-agent "^6.0.0" "@octokit/oauth-authorization-url@^5.0.0": @@ -6081,11 +6046,6 @@ resolved "https://registry.yarnpkg.com/@octokit/oauth-authorization-url/-/oauth-authorization-url-5.0.0.tgz#029626ce87f3b31addb98cd0d2355c2381a1c5a1" integrity sha512-y1WhN+ERDZTh0qZ4SR+zotgsQUE1ysKnvBt1hvDRB2WRzYtVKQjn97HEPzoehh66Fj9LwNdlZh+p6TJatT0zzg== -"@octokit/oauth-authorization-url@^6.0.2": - version "6.0.2" - resolved "https://registry.yarnpkg.com/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz#cc82ca29cc5e339c9921672f39f2b3f5c8eb6ef2" - integrity sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA== - "@octokit/oauth-methods@^2.0.0": version "2.0.6" resolved "https://registry.yarnpkg.com/@octokit/oauth-methods/-/oauth-methods-2.0.6.tgz#3a089781e90171cbe8a0efa448a6a60229bdd3fb" @@ -6097,17 +6057,6 @@ "@octokit/types" "^9.0.0" btoa-lite "^1.0.0" -"@octokit/oauth-methods@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@octokit/oauth-methods/-/oauth-methods-4.0.0.tgz#6e0c190e8ee95afe770a4a9a4321eb159a58c794" - integrity sha512-dqy7BZLfLbi3/8X8xPKUKZclMEK9vN3fK5WF3ortRvtplQTszFvdAGbTo71gGLO+4ZxspNiLjnqdd64Chklf7w== - dependencies: - "@octokit/oauth-authorization-url" "^6.0.2" - "@octokit/request" "^8.0.2" - "@octokit/request-error" "^5.0.0" - "@octokit/types" "^11.0.0" - btoa-lite "^1.0.0" - "@octokit/openapi-types@^12.11.0": version "12.11.0" resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" @@ -6136,11 +6085,6 @@ resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== -"@octokit/plugin-paginate-graphql@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-4.0.0.tgz#b26024fa454039c18b948f13bf754ff86b89e8b9" - integrity sha512-7HcYW5tP7/Z6AETAPU14gp5H5KmCPT3hmJrS/5tO7HIgbwenYmgw4OY9Ma54FDySuxMwD+wsJlxtuGWwuZuItA== - "@octokit/plugin-paginate-rest@^2.16.8", "@octokit/plugin-paginate-rest@^2.6.2": version "2.21.3" resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e" @@ -6148,6 +6092,14 @@ dependencies: "@octokit/types" "^6.40.0" +"@octokit/plugin-paginate-rest@^6.0.0", "@octokit/plugin-paginate-rest@^6.1.0": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz#f86456a7a1fe9e58fec6385a85cf1b34072341f8" + integrity sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ== + dependencies: + "@octokit/tsconfig" "^1.0.2" + "@octokit/types" "^9.2.3" + "@octokit/plugin-paginate-rest@^8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-8.0.0.tgz#417b5367da2ba3c2d255a59b87c1cc608228ec38" @@ -6160,6 +6112,11 @@ resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== +"@octokit/plugin-request-log@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-4.0.0.tgz#260fa6970aa97bbcbd91f99f3cd812e2b285c9f1" + integrity sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA== + "@octokit/plugin-rest-endpoint-methods@^5.0.1", "@octokit/plugin-rest-endpoint-methods@^5.12.0": version "5.16.2" resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" @@ -6168,6 +6125,13 @@ "@octokit/types" "^6.39.0" deprecation "^2.3.1" +"@octokit/plugin-rest-endpoint-methods@^7.1.1": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz#37a84b171a6cb6658816c82c4082ac3512021797" + integrity sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA== + dependencies: + "@octokit/types" "^10.0.0" + "@octokit/plugin-rest-endpoint-methods@^9.0.0": version "9.0.0" resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-9.0.0.tgz#e15d54540893202da107305ded2bfd21ce6f769d" @@ -6183,13 +6147,12 @@ "@octokit/types" "^6.0.3" bottleneck "^2.15.3" -"@octokit/plugin-retry@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-6.0.0.tgz#4a83ca5d531bbd56e0822a644ab0ba4a3215f87a" - integrity sha512-a1/A4A+PB1QoAHQfLJxGHhLfSAT03bR1jJz3GgQJZvty2ozawFWs93MiBQXO7SL2YbO7CIq0Goj4qLOBj8JeMQ== +"@octokit/plugin-retry@^4.1.3": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-4.1.6.tgz#e33b1e520f0bd24d515c9901676b55df64dfc795" + integrity sha512-obkYzIgEC75r8+9Pnfiiqy3y/x1bc3QLE5B7qvv9wi9Kj0R5tGQFC6QMBg1154WQ9lAVypuQDGyp3hNpp15gQQ== dependencies: - "@octokit/request-error" "^5.0.0" - "@octokit/types" "^11.0.0" + "@octokit/types" "^9.0.0" bottleneck "^2.15.3" "@octokit/plugin-throttling@^3.3.4": @@ -6200,12 +6163,12 @@ "@octokit/types" "^6.0.1" bottleneck "^2.15.3" -"@octokit/plugin-throttling@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-throttling/-/plugin-throttling-7.0.0.tgz#89f2580b43cfd5ec17f19e3939d8af549f573b0b" - integrity sha512-KL2k/d0uANc8XqP5S64YcNFCudR3F5AaKO39XWdUtlJIjT9Ni79ekWJ6Kj5xvAw87udkOMEPcVf9xEge2+ahew== +"@octokit/plugin-throttling@^5.2.2": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-throttling/-/plugin-throttling-5.2.3.tgz#9f552a14dcee5c7326dd9dee64a71ea76b108814" + integrity sha512-C9CFg9mrf6cugneKiaI841iG8DOv6P5XXkjmiNNut+swePxQ7RWEdAZRp5rJoE1hjsIqiYcKa/ZkOQ+ujPI39Q== dependencies: - "@octokit/types" "^11.0.0" + "@octokit/types" "^9.0.0" bottleneck "^2.15.3" "@octokit/request-error@^2.0.2", "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": @@ -6217,7 +6180,7 @@ deprecation "^2.0.0" once "^1.4.0" -"@octokit/request-error@^3.0.0", "@octokit/request-error@^3.0.3": +"@octokit/request-error@^3.0.0", "@octokit/request-error@^3.0.3", "@octokit/request-error@^v3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-3.0.3.tgz#ef3dd08b8e964e53e55d471acfe00baa892b9c69" integrity sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ== @@ -6259,7 +6222,7 @@ node-fetch "^2.6.7" universal-user-agent "^6.0.0" -"@octokit/request@^8.0.0", "@octokit/request@^8.0.1", "@octokit/request@^8.0.2": +"@octokit/request@^8.0.1", "@octokit/request@^8.0.2": version "8.1.1" resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.1.1.tgz#23b4d3f164e973f4c1a0f24f68256f1646c00620" integrity sha512-8N+tdUz4aCqQmXl8FpHYfKG9GelDFd7XGVzyN8rc6WxVlYcfpHECnuRkgquzz+WzvHTK62co5di8gSXnzASZPQ== @@ -6280,6 +6243,28 @@ "@octokit/plugin-request-log" "^1.0.4" "@octokit/plugin-rest-endpoint-methods" "^5.12.0" +"@octokit/rest@^20.0.1": + version "20.0.1" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-20.0.1.tgz#b8ee194b7cf89772d1e3fea3209f741c76a5efd3" + integrity sha512-wROV21RwHQIMNb2Dgd4+pY+dVy1Dwmp85pBrgr6YRRDYRBu9Gb+D73f4Bl2EukZSj5hInq2Tui9o7gAQpc2k2Q== + dependencies: + "@octokit/core" "^5.0.0" + "@octokit/plugin-paginate-rest" "^8.0.0" + "@octokit/plugin-request-log" "^4.0.0" + "@octokit/plugin-rest-endpoint-methods" "^9.0.0" + +"@octokit/tsconfig@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@octokit/tsconfig/-/tsconfig-1.0.2.tgz#59b024d6f3c0ed82f00d08ead5b3750469125af7" + integrity sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA== + +"@octokit/types@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-10.0.0.tgz#7ee19c464ea4ada306c43f1a45d444000f419a4a" + integrity sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg== + dependencies: + "@octokit/openapi-types" "^18.0.0" + "@octokit/types@^11.0.0", "@octokit/types@^11.1.0": version "11.1.0" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-11.1.0.tgz#9e5db741d582b05718a4d91bac8cc987def235ea" @@ -6301,7 +6286,7 @@ dependencies: "@octokit/openapi-types" "^14.0.0" -"@octokit/types@^9.0.0": +"@octokit/types@^9.0.0", "@octokit/types@^9.2.2", "@octokit/types@^9.2.3": version "9.3.2" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.3.2.tgz#3f5f89903b69f6a2d196d78ec35f888c0013cac5" integrity sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA== @@ -6313,29 +6298,29 @@ resolved "https://registry.yarnpkg.com/@octokit/webhooks-methods/-/webhooks-methods-2.0.0.tgz#1108b9ea661ca6c81e4a8bfa63a09eb27d5bc2db" integrity sha512-35cfQ4YWlnZnmZKmIxlGPUPLtbkF8lr/A/1Sk1eC0ddLMwQN06dOuLc+dI3YLQS+T+MoNt3DIQ0NynwgKPilig== -"@octokit/webhooks-methods@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@octokit/webhooks-methods/-/webhooks-methods-4.0.0.tgz#d1697930ba3d8e6b6d0f8a2c996bb440d2e1df1b" - integrity sha512-M8mwmTXp+VeolOS/kfRvsDdW+IO0qJ8kYodM/sAysk093q6ApgmBXwK1ZlUvAwXVrp/YVHp6aArj4auAxUAOFw== +"@octokit/webhooks-methods@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@octokit/webhooks-methods/-/webhooks-methods-3.0.3.tgz#2648668d34fe44e437eca90c9031d0f3cb759c77" + integrity sha512-2vM+DCNTJ5vL62O5LagMru6XnYhV4fJslK+5YUkTa6rWlW2S+Tqs1lF9Wr9OGqHfVwpBj3TeztWfVON/eUoW1Q== "@octokit/webhooks-types@5.8.0": version "5.8.0" resolved "https://registry.yarnpkg.com/@octokit/webhooks-types/-/webhooks-types-5.8.0.tgz#b76d1a3e3ad82cec5680d3c6c3443a620047a6ef" integrity sha512-8adktjIb76A7viIdayQSFuBEwOzwhDC+9yxZpKNHjfzrlostHCw0/N7JWpWMObfElwvJMk2fY2l1noENCk9wmw== -"@octokit/webhooks-types@7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@octokit/webhooks-types/-/webhooks-types-7.1.0.tgz#d533dea253416e02dd6c2bfab25e533295bd5d3f" - integrity sha512-y92CpG4kFFtBBjni8LHoV12IegJ+KFxLgKRengrVjKmGE5XMeCuGvlfRe75lTRrgXaG6XIWJlFpIDTlkoJsU8w== +"@octokit/webhooks-types@6.11.0": + version "6.11.0" + resolved "https://registry.yarnpkg.com/@octokit/webhooks-types/-/webhooks-types-6.11.0.tgz#1fb903bff3f2883490d6ba88d8cb8f8a55f68176" + integrity sha512-AanzbulOHljrku1NGfafxdpTCfw2ENaWzH01N2vqQM+cUFbk868Cgh0xylz0JIM9BoKbfI++bdD6EYX0Q/UTEw== -"@octokit/webhooks@^12.0.1": - version "12.0.3" - resolved "https://registry.yarnpkg.com/@octokit/webhooks/-/webhooks-12.0.3.tgz#91f5df322e83b3b7d8bb9af5e692ffea16d6c8bb" - integrity sha512-8iG+/yza7hwz1RrQ7i7uGpK2/tuItZxZq1aTmeg2TNp2xTUB8F8lZF/FcZvyyAxT8tpDMF74TjFGCDACkf1kAQ== +"@octokit/webhooks@^10.0.0": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@octokit/webhooks/-/webhooks-10.9.1.tgz#4674a6924567419d7d0187a8b6c88ec468a97a86" + integrity sha512-5NXU4VfsNOo2VSU/SrLrpPH2Z1ZVDOWFcET4EpnEBX1uh/v8Uz65UVuHIRx5TZiXhnWyRE9AO1PXHa+M/iWwZA== dependencies: - "@octokit/request-error" "^5.0.0" - "@octokit/webhooks-methods" "^4.0.0" - "@octokit/webhooks-types" "7.1.0" + "@octokit/request-error" "^3.0.0" + "@octokit/webhooks-methods" "^3.0.0" + "@octokit/webhooks-types" "6.11.0" aggregate-error "^3.1.0" "@octokit/webhooks@^9.8.4": @@ -17522,7 +17507,7 @@ from@^0.1.7, from@~0: resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== -fromentries@^1.3.2: +fromentries@^1.3.1, fromentries@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg== @@ -23765,11 +23750,6 @@ lru-cache@6.0.0, lru-cache@^6.0.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.13.1.tgz#267a81fbd0881327c46a81c5922606a2cfe336c4" integrity sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ== -lru-cache@^10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" - integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== - lru-cache@^2.6.4: version "2.7.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" @@ -26869,21 +26849,20 @@ octokit-auth-probot@^1.2.2: "@octokit/auth-unauthenticated" "^3.0.0" "@octokit/types" "^8.0.0" -octokit@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/octokit/-/octokit-3.1.0.tgz#33f73dfab3cd438818dda4336eddfded13ae7118" - integrity sha512-dmIH5D+edpb4/ASd6ZGo6BiRR1g4ytu8lG4f+6XN/2AW+CSuTsT0nj1d6rv/HKgoflMQ1+rb3KlVWcvrmgQZhw== - dependencies: - "@octokit/app" "^14.0.0" - "@octokit/core" "^5.0.0" - "@octokit/oauth-app" "^6.0.0" - "@octokit/plugin-paginate-graphql" "^4.0.0" - "@octokit/plugin-paginate-rest" "^8.0.0" - "@octokit/plugin-rest-endpoint-methods" "^9.0.0" - "@octokit/plugin-retry" "^6.0.0" - "@octokit/plugin-throttling" "^7.0.0" - "@octokit/request-error" "^5.0.0" - "@octokit/types" "^11.1.0" +octokit@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/octokit/-/octokit-2.1.0.tgz#93863ce6630d358327d3959ca5d08a97fd3606b2" + integrity sha512-Pxi6uKTjBRZWgAwsw1NgHdRlL+QASCN35OYS7X79o7PtBME0CLXEroZmPtEwlWZbPTP+iDbEy2wCbSOgm0uGIQ== + dependencies: + "@octokit/app" "^13.1.5" + "@octokit/core" "^4.2.1" + "@octokit/oauth-app" "^4.2.1" + "@octokit/plugin-paginate-rest" "^6.1.0" + "@octokit/plugin-rest-endpoint-methods" "^7.1.1" + "@octokit/plugin-retry" "^4.1.3" + "@octokit/plugin-throttling" "^5.2.2" + "@octokit/request-error" "^v3.0.3" + "@octokit/types" "^9.2.2" omggif@^1.0.10, omggif@^1.0.9: version "1.0.10" From 9fa835c004d9dd68854b275808d25e2fd294966d Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:49:46 +0530 Subject: [PATCH 030/104] removed: octokit module removed from core package --- packages/core/src/app.module.ts | 1 - .../core/src/octokit/octokit.controller.ts | 37 --------- packages/core/src/octokit/octokit.module.ts | 15 ---- packages/core/src/octokit/octokit.service.ts | 76 ------------------- 4 files changed, 129 deletions(-) delete mode 100644 packages/core/src/octokit/octokit.controller.ts delete mode 100644 packages/core/src/octokit/octokit.module.ts delete mode 100644 packages/core/src/octokit/octokit.service.ts diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index ed0f7cfd675..643f212bd97 100644 --- a/packages/core/src/app.module.ts +++ b/packages/core/src/app.module.ts @@ -154,7 +154,6 @@ import { EmailResetModule } from './email-reset/email-reset.module'; import { TaskLinkedIssueModule } from './tasks/linked-issue/task-linked-issue.module'; import { OrganizationTaskSettingModule } from './organization-task-setting/organization-task-setting.module'; import { TaskEstimationModule } from './tasks/estimation/task-estimation.module'; -import { OctokitModule } from 'octokit/octokit.module'; import { GitHubModule } from './github/github.module'; const { unleashConfig } = environment; diff --git a/packages/core/src/octokit/octokit.controller.ts b/packages/core/src/octokit/octokit.controller.ts deleted file mode 100644 index f684575e761..00000000000 --- a/packages/core/src/octokit/octokit.controller.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Public } from '@gauzy/common'; -import { Body, Controller, Get, Post, Req } from '@nestjs/common'; -import { Request } from 'express'; -import { OctokitService } from './octokit.service'; - -// TODO: -// For now API route is public -// We have to make a guard that validate webhook secret from payload -@Controller() -@Public() -export class OctokitController { - constructor(private readonly _octokitService: OctokitService) {} - - @Get() - async testGet() { - return { hello: 'world' }; - } - - @Post() - async webhook(@Req() request: Request, @Body() body: any) { - // body contains whole payload that webhook send on different event - console.log(body); - - const event = request.headers['x-github-event']; - const action = body.action; - - // Based on event & action we can decide further processing - - if (event === 'issues' && action === 'opened') { - // TODO - } - if (event === 'issues' && action === 'edited') { - // TODO - } - // ... - } -} diff --git a/packages/core/src/octokit/octokit.module.ts b/packages/core/src/octokit/octokit.module.ts deleted file mode 100644 index cdb4d14aefb..00000000000 --- a/packages/core/src/octokit/octokit.module.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Module } from '@nestjs/common'; -import { OctokitController } from './octokit.controller'; -import { OctokitService } from './octokit.service'; -import { RouterModule } from 'nest-router'; - -@Module({ - imports: [ - RouterModule.forRoutes([{ path: '/octokit', module: OctokitModule }]), - ], - - controllers: [OctokitController], - providers: [OctokitService], - exports: [OctokitService], -}) -export class OctokitModule {} diff --git a/packages/core/src/octokit/octokit.service.ts b/packages/core/src/octokit/octokit.service.ts deleted file mode 100644 index fbc4f2da1c4..00000000000 --- a/packages/core/src/octokit/octokit.service.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { App } from 'octokit'; - -@Injectable() -export class OctokitService { - constructor() {} - - async createIssue(title: string, body: string) { - // TODO: - // Dynamic ENV variable - const app = new App({ - appId: '', - privateKey: '', - }); - // TODO: - // Need to store user's installationId in DB and make param dynamic - const octokit = await app.getInstallationOctokit(123456); - - octokit - .request('POST /repos/{owner}/{repo}/issues', { - // TODO: - // pass dynamic values as required - // Add all the fields that we have - - owner: 'badal-ever', - repo: 'testing-gauzy-teams-integration', - - title, - body, - // labels: ['bug', 'GauzyAPI'], - - headers: { - 'X-GitHub-Api-Version': '2022-11-28', - }, - }) - .then((data) => { - console.log('data', data); - }) - .catch((error) => { - console.log('error', error); - }); - } - async updateIssue(id: number, title: string, body: string) { - // TODO: - // Dynamic ENV variable - const app = new App({ - appId: '', - privateKey: '', - }); - // TODO: - // Need to store user's installationId in DB and make param dynamic - const octokit = await app.getInstallationOctokit(123456); - - octokit - .request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', { - // TODO: pass dynamic values as required - owner: 'badal-ever', - repo: 'testing-gauzy-teams-integration', - - issue_number: id, - title, - body, - // labels: ['bug', 'GauzyAPI'], - - headers: { - 'X-GitHub-Api-Version': '2022-11-28', - }, - }) - .then((data) => { - console.log('data', data); - }) - .catch((error) => { - console.log('error', error); - }); - } -} From 81c1e449a2b6d86e348badd95ae079f2bf04c91c Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:53:01 +0530 Subject: [PATCH 031/104] fix: removed octokit feature module completely --- packages/core/src/app.module.ts | 1 - .../src/tasks/commands/handlers/task-create.handler.ts | 7 ++----- .../src/tasks/commands/handlers/task-update.handler.ts | 9 +-------- packages/core/src/tasks/task.module.ts | 2 -- 4 files changed, 3 insertions(+), 16 deletions(-) diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index 643f212bd97..22457000237 100644 --- a/packages/core/src/app.module.ts +++ b/packages/core/src/app.module.ts @@ -411,7 +411,6 @@ if (environment.sentry && environment.sentry.dsn) { TaskLinkedIssueModule, OrganizationTaskSettingModule, TaskEstimationModule, - OctokitModule, GitHubModule ], controllers: [AppController], diff --git a/packages/core/src/tasks/commands/handlers/task-create.handler.ts b/packages/core/src/tasks/commands/handlers/task-create.handler.ts index 11445ed296c..3f2d3458900 100644 --- a/packages/core/src/tasks/commands/handlers/task-create.handler.ts +++ b/packages/core/src/tasks/commands/handlers/task-create.handler.ts @@ -5,8 +5,7 @@ import { RequestContext } from './../../../core/context'; import { TaskCreateCommand } from './../task-create.command'; import { OrganizationProjectService } from './../../../organization-project/organization-project.service'; import { TaskService } from '../../task.service'; -import { OctokitService } from 'octokit/octokit.service'; -import { GitHubService } from 'github/github.service'; +import { GitHubService } from './../../../github/github.service'; @CommandHandler(TaskCreateCommand) export class TaskCreateHandler implements ICommandHandler { @@ -15,8 +14,7 @@ export class TaskCreateHandler implements ICommandHandler { // Uncomment below line for GitHub app integration private readonly _gitHubService: GitHubService, private readonly _taskService: TaskService, - private readonly _organizationProjectService: OrganizationProjectService, - private readonly _octokitService: OctokitService, + private readonly _organizationProjectService: OrganizationProjectService ) { } public async execute(command: TaskCreateCommand): Promise { @@ -45,7 +43,6 @@ export class TaskCreateHandler implements ICommandHandler { } ); - this._octokitService.createIssue(input.title, input.description); // TODO: // Make the Repo, Owner and installation id field dynamic // this._gitHubService.openIssue( diff --git a/packages/core/src/tasks/commands/handlers/task-update.handler.ts b/packages/core/src/tasks/commands/handlers/task-update.handler.ts index 433498f147c..6d7aee187a6 100644 --- a/packages/core/src/tasks/commands/handlers/task-update.handler.ts +++ b/packages/core/src/tasks/commands/handlers/task-update.handler.ts @@ -3,14 +3,12 @@ import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { ITask, ITaskUpdateInput } from '@gauzy/contracts'; import { TaskService } from '../../task.service'; import { TaskUpdateCommand } from '../task-update.command'; -import { OctokitService } from 'octokit/octokit.service'; -import { GitHubService } from 'github/github.service'; +import { GitHubService } from './../../../github/github.service'; @CommandHandler(TaskUpdateCommand) export class TaskUpdateHandler implements ICommandHandler { constructor( private readonly _taskService: TaskService, - private readonly _octokitService: OctokitService, // TODO: // Uncomment below line for GitHub app integration private readonly _gitHubService: GitHubService @@ -54,11 +52,6 @@ export class TaskUpdateHandler implements ICommandHandler { // TODO: // We have to store issue_number of github in our task, so that we can use it while sync // Right now we we have put static 38 value. - this._octokitService.updateIssue( - 38, - request.title, - request.description - ); // Make the Issue number, Repo, Owner and installation id field dynamic // this._gitHubService.editIssue( // 48, diff --git a/packages/core/src/tasks/task.module.ts b/packages/core/src/tasks/task.module.ts index 3e2d555b7aa..d3a4f1e6a86 100644 --- a/packages/core/src/tasks/task.module.ts +++ b/packages/core/src/tasks/task.module.ts @@ -11,7 +11,6 @@ import { TenantModule } from '../tenant/tenant.module'; import { UserModule } from './../user/user.module'; import { RoleModule } from './../role/role.module'; import { EmployeeModule } from './../employee/employee.module'; -import { OctokitModule } from 'octokit/octokit.module'; import { GitHubModule } from './../github/github.module'; @Module({ @@ -24,7 +23,6 @@ import { GitHubModule } from './../github/github.module'; EmployeeModule, OrganizationProjectModule, CqrsModule, - OctokitModule, GitHubModule ], controllers: [TaskController], From 6543b5320e2e4b03a0c046def9fb65ea2f1d2173 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 19 Sep 2023 22:05:46 +0530 Subject: [PATCH 032/104] chore(deps): downgrade @octokit/rest from ^20.0.1 to ^18.0.0 --- .../plugins/integration-github/package.json | 2 +- yarn.lock | 92 ------------------- 2 files changed, 1 insertion(+), 93 deletions(-) diff --git a/packages/plugins/integration-github/package.json b/packages/plugins/integration-github/package.json index 3c10c0deefa..846e7a6cafd 100644 --- a/packages/plugins/integration-github/package.json +++ b/packages/plugins/integration-github/package.json @@ -28,7 +28,7 @@ }, "keywords": [], "dependencies": { - "@octokit/rest": "^20.0.1", + "@octokit/rest": "^18.0.0", "octokit": "2.1.0", "pino-std-serializers": "^6.2.2", "probot": "^12.3.1", diff --git a/yarn.lock b/yarn.lock index 5a457bdfd0a..bae9c5e64f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5920,11 +5920,6 @@ resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.4.tgz#70e941ba742bdd2b49bdb7393e821dea8520a3db" integrity sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ== -"@octokit/auth-token@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-4.0.0.tgz#40d203ea827b9f17f42a29c6afb93b7745ef80c7" - integrity sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA== - "@octokit/auth-unauthenticated@^3.0.0": version "3.0.5" resolved "https://registry.yarnpkg.com/@octokit/auth-unauthenticated/-/auth-unauthenticated-3.0.5.tgz#a562bffd6ca0d0e80541eaf9f9b89b8d53020228" @@ -5959,19 +5954,6 @@ before-after-hook "^2.2.0" universal-user-agent "^6.0.0" -"@octokit/core@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-5.0.0.tgz#0fc2b6eb88437e5c1d69f756a5dcee7472d2b2dd" - integrity sha512-YbAtMWIrbZ9FCXbLwT9wWB8TyLjq9mxpKdgB3dUNxQcIVTf9hJ70gRPwAcqGZdY6WdJPZ0I7jLaaNDCiloGN2A== - dependencies: - "@octokit/auth-token" "^4.0.0" - "@octokit/graphql" "^7.0.0" - "@octokit/request" "^8.0.2" - "@octokit/request-error" "^5.0.0" - "@octokit/types" "^11.0.0" - before-after-hook "^2.2.0" - universal-user-agent "^6.0.0" - "@octokit/endpoint@^6.0.1": version "6.0.12" resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" @@ -5990,15 +5972,6 @@ is-plain-object "^5.0.0" universal-user-agent "^6.0.0" -"@octokit/endpoint@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-9.0.0.tgz#c5ce19c74b999b85af9a8a189275c80faa3e90fd" - integrity sha512-szrQhiqJ88gghWY2Htt8MqUDO6++E/EIXqJ2ZEp5ma3uGS46o7LZAzSLt49myB7rT+Hfw5Y6gO3LmOxGzHijAQ== - dependencies: - "@octokit/types" "^11.0.0" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - "@octokit/graphql@^4.5.8": version "4.8.0" resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" @@ -6017,15 +5990,6 @@ "@octokit/types" "^9.0.0" universal-user-agent "^6.0.0" -"@octokit/graphql@^7.0.0": - version "7.0.1" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-7.0.1.tgz#f2291620e17cdaa8115f8d0cdfc0644789ec2db2" - integrity sha512-T5S3oZ1JOE58gom6MIcrgwZXzTaxRnxBso58xhozxHpOqSTgDS6YNeEUvZ/kRvXgPrRz/KHnZhtb7jUMRi9E6w== - dependencies: - "@octokit/request" "^8.0.1" - "@octokit/types" "^11.0.0" - universal-user-agent "^6.0.0" - "@octokit/oauth-app@^4.0.7", "@octokit/oauth-app@^4.2.1": version "4.2.4" resolved "https://registry.yarnpkg.com/@octokit/oauth-app/-/oauth-app-4.2.4.tgz#d385ffebe116c684940bf255a2189665c61ee5a0" @@ -6100,23 +6064,11 @@ "@octokit/tsconfig" "^1.0.2" "@octokit/types" "^9.2.3" -"@octokit/plugin-paginate-rest@^8.0.0": - version "8.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-8.0.0.tgz#417b5367da2ba3c2d255a59b87c1cc608228ec38" - integrity sha512-2xZ+baZWUg+qudVXnnvXz7qfrTmDeYPCzangBVq/1gXxii/OiS//4shJp9dnCCvj1x+JAm9ji1Egwm1BA47lPQ== - dependencies: - "@octokit/types" "^11.0.0" - "@octokit/plugin-request-log@^1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== -"@octokit/plugin-request-log@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-4.0.0.tgz#260fa6970aa97bbcbd91f99f3cd812e2b285c9f1" - integrity sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA== - "@octokit/plugin-rest-endpoint-methods@^5.0.1", "@octokit/plugin-rest-endpoint-methods@^5.12.0": version "5.16.2" resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" @@ -6132,13 +6084,6 @@ dependencies: "@octokit/types" "^10.0.0" -"@octokit/plugin-rest-endpoint-methods@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-9.0.0.tgz#e15d54540893202da107305ded2bfd21ce6f769d" - integrity sha512-KquMF/VB1IkKNiVnzJKspY5mFgGyLd7HzdJfVEGTJFzqu9BRFNWt+nwTCMuUiWc72gLQhRWYubTwOkQj+w/1PA== - dependencies: - "@octokit/types" "^11.0.0" - "@octokit/plugin-retry@^3.0.6": version "3.0.9" resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-3.0.9.tgz#ae625cca1e42b0253049102acd71c1d5134788fe" @@ -6189,15 +6134,6 @@ deprecation "^2.0.0" once "^1.4.0" -"@octokit/request-error@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-5.0.0.tgz#060c5770833f9d563ad9a49fec6650c41584bc40" - integrity sha512-1ue0DH0Lif5iEqT52+Rf/hf0RmGO9NWFjrzmrkArpG9trFfDM/efx00BJHdLGuro4BR/gECxCU2Twf5OKrRFsQ== - dependencies: - "@octokit/types" "^11.0.0" - deprecation "^2.0.0" - once "^1.4.0" - "@octokit/request@^5.6.0", "@octokit/request@^5.6.3": version "5.6.3" resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" @@ -6222,17 +6158,6 @@ node-fetch "^2.6.7" universal-user-agent "^6.0.0" -"@octokit/request@^8.0.1", "@octokit/request@^8.0.2": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.1.1.tgz#23b4d3f164e973f4c1a0f24f68256f1646c00620" - integrity sha512-8N+tdUz4aCqQmXl8FpHYfKG9GelDFd7XGVzyN8rc6WxVlYcfpHECnuRkgquzz+WzvHTK62co5di8gSXnzASZPQ== - dependencies: - "@octokit/endpoint" "^9.0.0" - "@octokit/request-error" "^5.0.0" - "@octokit/types" "^11.1.0" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - "@octokit/rest@^18.0.0", "@octokit/rest@^18.1.0": version "18.12.0" resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" @@ -6243,16 +6168,6 @@ "@octokit/plugin-request-log" "^1.0.4" "@octokit/plugin-rest-endpoint-methods" "^5.12.0" -"@octokit/rest@^20.0.1": - version "20.0.1" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-20.0.1.tgz#b8ee194b7cf89772d1e3fea3209f741c76a5efd3" - integrity sha512-wROV21RwHQIMNb2Dgd4+pY+dVy1Dwmp85pBrgr6YRRDYRBu9Gb+D73f4Bl2EukZSj5hInq2Tui9o7gAQpc2k2Q== - dependencies: - "@octokit/core" "^5.0.0" - "@octokit/plugin-paginate-rest" "^8.0.0" - "@octokit/plugin-request-log" "^4.0.0" - "@octokit/plugin-rest-endpoint-methods" "^9.0.0" - "@octokit/tsconfig@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@octokit/tsconfig/-/tsconfig-1.0.2.tgz#59b024d6f3c0ed82f00d08ead5b3750469125af7" @@ -6265,13 +6180,6 @@ dependencies: "@octokit/openapi-types" "^18.0.0" -"@octokit/types@^11.0.0", "@octokit/types@^11.1.0": - version "11.1.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-11.1.0.tgz#9e5db741d582b05718a4d91bac8cc987def235ea" - integrity sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ== - dependencies: - "@octokit/openapi-types" "^18.0.0" - "@octokit/types@^6.0.1", "@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": version "6.41.0" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" From ae890e01f3393351c9af7ef7dd5002c004dfd64b Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 19 Sep 2023 22:19:37 +0530 Subject: [PATCH 033/104] fix: get get the currently running version of Node.js --- packages/config/src/database.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/config/src/database.ts b/packages/config/src/database.ts index cd125ddbe66..fe70da8a208 100644 --- a/packages/config/src/database.ts +++ b/packages/config/src/database.ts @@ -1,10 +1,11 @@ import * as path from 'path'; +import * as chalk from 'chalk'; import { TlsOptions } from 'tls'; import { TypeOrmModuleOptions } from '@nestjs/typeorm'; import { DataSourceOptions } from 'typeorm'; import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions'; -let dbType:string; +let dbType: string; if (process.env.DB_TYPE) dbType = process.env.DB_TYPE; @@ -13,6 +14,9 @@ else console.log(`Selected DB Type (DB_TYPE env var): ${dbType}`); +// `process` is a built-in global in Node.js, no need to `require()` +console.log(chalk.magenta(`Currently running Node.js version %s`), process.version); + let connectionConfig: TypeOrmModuleOptions; switch (dbType) { @@ -51,8 +55,8 @@ switch (dbType) { logger: 'file', // Removes console logging, instead logs all queries in a file ormlogs.log synchronize: process.env.DB_SYNCHRONIZE === 'true' ? true : false, // We are using migrations, synchronize should be set to false. uuidExtension: 'pgcrypto', - migrations: ["src/modules/not-exists/*.migration{.ts,.js}"], - entities: ["src/modules/not-exists/*.entity{.ts,.js}"], + migrations: ["src/modules/not-exists/*.migration{.ts,.js}"], + entities: ["src/modules/not-exists/*.entity{.ts,.js}"], }; connectionConfig = postgresConnectionOptions; From 28bf00d61c2a58f7e5ebb816f5c7eb42e66d079f Mon Sep 17 00:00:00 2001 From: Ruslan Konviser Date: Wed, 20 Sep 2023 11:36:44 +0200 Subject: [PATCH 034/104] chore: disable for now NX cloud cache --- .github/workflows/desktop-app-prod.yml | 3 +++ .github/workflows/desktop-app-stage.yml | 3 +++ .github/workflows/desktop-timer-app-prod.yml | 3 +++ .github/workflows/desktop-timer-app-stage.yml | 3 +++ .github/workflows/server-prod.yml | 3 +++ .github/workflows/server-stage.yml | 3 +++ nx.json | 2 +- package.json | 4 ++-- 8 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.github/workflows/desktop-app-prod.yml b/.github/workflows/desktop-app-prod.yml index 1c8856ef0f9..45f68f15a0a 100644 --- a/.github/workflows/desktop-app-prod.yml +++ b/.github/workflows/desktop-app-prod.yml @@ -66,6 +66,7 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true release-mac: runs-on: ${{ matrix.os }} @@ -115,6 +116,7 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true release-windows: runs-on: ${{ matrix.os }} @@ -164,3 +166,4 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true diff --git a/.github/workflows/desktop-app-stage.yml b/.github/workflows/desktop-app-stage.yml index c4e91bca88c..db2f33480eb 100644 --- a/.github/workflows/desktop-app-stage.yml +++ b/.github/workflows/desktop-app-stage.yml @@ -66,6 +66,7 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true release-mac: runs-on: ${{ matrix.os }} @@ -115,6 +116,7 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true release-windows: runs-on: ${{ matrix.os }} @@ -164,3 +166,4 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true diff --git a/.github/workflows/desktop-timer-app-prod.yml b/.github/workflows/desktop-timer-app-prod.yml index 8a1dfe7caee..79783176c1e 100644 --- a/.github/workflows/desktop-timer-app-prod.yml +++ b/.github/workflows/desktop-timer-app-prod.yml @@ -66,6 +66,7 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true release-mac: runs-on: ${{ matrix.os }} @@ -115,6 +116,7 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true release-windows: runs-on: ${{ matrix.os }} @@ -164,3 +166,4 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true diff --git a/.github/workflows/desktop-timer-app-stage.yml b/.github/workflows/desktop-timer-app-stage.yml index fa18e0a5d38..66f4d4c0ead 100644 --- a/.github/workflows/desktop-timer-app-stage.yml +++ b/.github/workflows/desktop-timer-app-stage.yml @@ -66,6 +66,7 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true release-mac: runs-on: ${{ matrix.os }} @@ -115,6 +116,7 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true release-windows: runs-on: ${{ matrix.os }} @@ -164,3 +166,4 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true diff --git a/.github/workflows/server-prod.yml b/.github/workflows/server-prod.yml index f1b72b827b7..24d5aa043ee 100644 --- a/.github/workflows/server-prod.yml +++ b/.github/workflows/server-prod.yml @@ -66,6 +66,7 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true release-mac: runs-on: ${{ matrix.os }} @@ -115,6 +116,7 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true release-windows: runs-on: ${{ matrix.os }} @@ -164,3 +166,4 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true diff --git a/.github/workflows/server-stage.yml b/.github/workflows/server-stage.yml index 33b09705672..ce461c9edc7 100644 --- a/.github/workflows/server-stage.yml +++ b/.github/workflows/server-stage.yml @@ -66,6 +66,7 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true release-mac: runs-on: ${{ matrix.os }} @@ -115,6 +116,7 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true release-windows: runs-on: ${{ matrix.os }} @@ -164,3 +166,4 @@ jobs: SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' DO_KEY_ID: ${{ secrets.DO_KEY_ID }} DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true diff --git a/nx.json b/nx.json index 19f1aa071e2..5234e6fe88f 100644 --- a/nx.json +++ b/nx.json @@ -70,4 +70,4 @@ } }, "defaultProject": "gauzy" -} +} \ No newline at end of file diff --git a/package.json b/package.json index ceda0c825b9..38678208723 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "doc:build-serve": "compodoc -p tsconfig.json -d docs -s", "postinstall.electron": "yarn electron-builder install-app-deps && yarn node tools/electron/postinstall", "postinstall.web": "yarn node ./decorate-angular-cli.js && yarn npx ngcc --properties es2016 browser module main --first-only --create-ivy-entry-points && yarn node tools/web/postinstall", - "build:desktop": "cross-env NODE_ENV=production yarn run copy-files-i18n-desktop && yarn run postinstall.electron && yarn build:package:all && yarn run config:prod && yarn run config:desktop:prod && yarn ng:prod build desktop --prod --base-href ./ && yarn run prepare:desktop && yarn ng:prod build desktop-api --prod && yarn ng:prod build api-desktop --prod && yarn ng:prod build desktop-ui --prod --base-href ./ && yarn run copy-files-desktop && yarn run copy-assets-gauzy", + "build:desktop": "cross-env NODE_ENV=production yarn run copy-files-i18n-desktop && yarn run postinstall.electron && yarn build:package:all && yarn run config:prod && yarn run config:desktop:prod && yarn ng:prod build desktop --prod --base-href ./ && yarn run prepare:desktop && yarn ng:prod build desktop-api --prod && yarn ng:prod build api-desktop --prod --output-path=dist/apps/desktop/desktop-api && yarn ng:prod build desktop-ui --prod --base-href ./ && yarn run copy-files-desktop && yarn run copy-assets-gauzy", "build:desktop:local": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=7000 yarn run config:prod && yarn run build:desktop && electron dist/apps/desktop", "build:desktop:linux": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=7000 yarn run config:prod && yarn run build:desktop && npm config set cache .cache && yarn electron-builder -c.electronVersion=20.2.0 build --linux --project dist/apps/desktop", "build:desktop:windows": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=7000 yarn run config:prod && yarn run build:desktop && npm config set cache .cache && yarn electron-builder -c.electronVersion=20.2.0 -c.extraMetadata.author.name=Ever build --windows --project dist/apps/desktop", @@ -141,7 +141,7 @@ "build:package:gauzy": "yarn run build:package:contracts && yarn run build:package:common && yarn run build:package:common-angular && yarn run build:package:config && yarn run build:package:plugin && yarn run build:package:plugins && yarn run build:package:auth && yarn run build:package:core && yarn run build:package:plugin:knowledge-base && yarn run build:package:plugin:changelog", "build:package:desktop-ui-lib": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=7000 yarn --cwd ./packages/desktop-ui-lib build", "copy-files-desktop": "copyfiles -f packages/core/src/**/*.gql dist/apps/desktop/data/", - "build:desktop-timer": "cross-env NODE_ENV=production yarn copy-files-i18n-desktop-timer && yarn run postinstall.electron && yarn build:package:all && yarn run config:prod && yarn run config:desktop-timer:prod && yarn ng build desktop-timer --prod --base-href ./ && yarn run prepare:desktop-timer && yarn ng build api-desktop --prod --output-path=dist/apps/desktop-timer/desktop-api && yarn run copy-assets-gauzy-timer", + "build:desktop-timer": "cross-env NODE_ENV=production yarn copy-files-i18n-desktop-timer && yarn run postinstall.electron && yarn build:package:all && yarn run config:prod && yarn run config:desktop-timer:prod && yarn ng:prod build desktop-timer --prod --base-href ./ && yarn run prepare:desktop-timer && yarn ng:prod build api-desktop --prod --output-path=dist/apps/desktop-timer/desktop-api && yarn run copy-assets-gauzy-timer", "prepare:desktop-timer": "yarn run postinstall.electron && tsc -p apps/desktop-timer/tsconfig.json", "build:desktop-timer:linux": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=7000 yarn run config:prod && yarn run build:desktop-timer && npm config set cache .cache && yarn electron-builder -c.electronVersion=20.2.0 build --linux --project dist/apps/desktop-timer", "build:desktop-timer:windows": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=7000 yarn run config:prod && yarn run build:desktop-timer && npm config set cache .cache && yarn electron-builder -c.electronVersion=20.2.0 -c.extraMetadata.author.name=Ever build --windows --project dist/apps/desktop-timer", From da04a46c4cc2c0488a3a244e6c01a4f81e1a51f0 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Wed, 20 Sep 2023 22:22:14 +0530 Subject: [PATCH 035/104] feat: environment for GitHub integration --- .scripts/configure.ts | 14 ++++++++++++-- .scripts/env.ts | 10 ++++++++++ apps/gauzy/src/environments/model.ts | 6 ++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/.scripts/configure.ts b/.scripts/configure.ts index 09a1833909c..15ccbb7abc5 100644 --- a/.scripts/configure.ts +++ b/.scripts/configure.ts @@ -142,6 +142,11 @@ if (!env.IS_DOCKER) { GAUZY_CLOUD_APP: '${env.GAUZY_CLOUD_APP}', FILE_PROVIDER: '${env.FILE_PROVIDER}', + + GITHUB_APP_NAME: '${env.GITHUB_APP_NAME}', + GITHUB_APP_ID: '${env.GITHUB_APP_ID}', + GITHUB_CLIENT_ID: '${env.GITHUB_CLIENT_ID}', + GITHUB_REDIRECT_URL: '${env.GITHUB_REDIRECT_URL}', }; `; } else { @@ -240,6 +245,11 @@ if (!env.IS_DOCKER) { GAUZY_CLOUD_APP: 'DOCKER_GAUZY_CLOUD_APP', FILE_PROVIDER: '${env.FILE_PROVIDER}', + + GITHUB_APP_NAME: '${env.GITHUB_APP_NAME}', + GITHUB_APP_ID: '${env.GITHUB_APP_ID}', + GITHUB_CLIENT_ID: '${env.GITHUB_CLIENT_ID}', + GITHUB_REDIRECT_URL: '${env.GITHUB_REDIRECT_URL}', }; `; } @@ -266,10 +276,10 @@ if (!isProd) { // we always want first to remove old generated files (one of them is not needed for current build) try { unlinkSync(`./apps/gauzy/src/environments/environment.ts`); -} catch {} +} catch { } try { unlinkSync(`./apps/gauzy/src/environments/environment.prod.ts`); -} catch {} +} catch { } const envFileDest: string = isProd ? 'environment.prod.ts' : 'environment.ts'; const envFileDestOther: string = !isProd diff --git a/.scripts/env.ts b/.scripts/env.ts index 6d4a1e190dd..46485b6db8f 100644 --- a/.scripts/env.ts +++ b/.scripts/env.ts @@ -65,6 +65,11 @@ export type Env = Readonly<{ GAUZY_CLOUD_APP: string; FILE_PROVIDER: string; + + GITHUB_APP_NAME: string; + GITHUB_APP_ID: string; + GITHUB_CLIENT_ID: string; + GITHUB_REDIRECT_URL: string; }>; export const env: Env = cleanEnv( @@ -118,6 +123,11 @@ export const env: Env = cleanEnv( GAUZY_CLOUD_APP: str({ default: 'https://app.gauzy.co/#' }), FILE_PROVIDER: str({ default: 'LOCAL' }), + + GITHUB_APP_NAME: str({ default: '' }), + GITHUB_APP_ID: str({ default: '' }), + GITHUB_CLIENT_ID: str({ default: '' }), + GITHUB_REDIRECT_URL: str({ default: '' }), }, { strict: true, dotEnvPath: __dirname + '/../.env' } ); diff --git a/apps/gauzy/src/environments/model.ts b/apps/gauzy/src/environments/model.ts index e452b073c42..742b70ab14f 100644 --- a/apps/gauzy/src/environments/model.ts +++ b/apps/gauzy/src/environments/model.ts @@ -69,4 +69,10 @@ export interface Environment { GAUZY_CLOUD_APP: string; FILE_PROVIDER: string; + + /** Github Integration */ + GITHUB_APP_NAME: string; + GITHUB_APP_ID: string; + GITHUB_CLIENT_ID: string; + GITHUB_REDIRECT_URL: string; } From ae53124d31e4644bae931df6f0c711762c4b23d1 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Wed, 20 Sep 2023 22:23:22 +0530 Subject: [PATCH 036/104] feat: #6734 GitHub authorization pages --- .../@core/services/github/github.service.ts | 12 +++ apps/gauzy/src/app/@core/services/index.ts | 1 + .../authorization.component.html | 0 .../authorization.component.scss | 6 ++ .../authorization.component.spec.ts | 24 ++++++ .../authorization/authorization.component.ts | 79 +++++++++++++++++++ .../installations.component.html | 8 ++ .../installations/installations.component.ts | 60 ++++++++++++++ .../wizard/github/github-routing.module.ts | 31 ++++++++ .../wizard/github/github.config.ts | 1 + .../wizard/github/github.module.ts | 21 +++++ packages/contracts/src/github.model.ts | 12 +++ packages/contracts/src/index.ts | 1 + 13 files changed, 256 insertions(+) create mode 100644 apps/gauzy/src/app/@core/services/github/github.service.ts create mode 100644 apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.html create mode 100644 apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.scss create mode 100644 apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.spec.ts create mode 100644 apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts create mode 100644 apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.html create mode 100644 apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts create mode 100644 apps/gauzy/src/app/pages/integrations/wizard/github/github-routing.module.ts create mode 100644 apps/gauzy/src/app/pages/integrations/wizard/github/github.config.ts create mode 100644 apps/gauzy/src/app/pages/integrations/wizard/github/github.module.ts create mode 100644 packages/contracts/src/github.model.ts diff --git a/apps/gauzy/src/app/@core/services/github/github.service.ts b/apps/gauzy/src/app/@core/services/github/github.service.ts new file mode 100644 index 00000000000..27f4d05552f --- /dev/null +++ b/apps/gauzy/src/app/@core/services/github/github.service.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; + +@Injectable({ + providedIn: 'root', +}) +export class GithubService { + + constructor( + private readonly _http: HttpClient + ) { } +} diff --git a/apps/gauzy/src/app/@core/services/index.ts b/apps/gauzy/src/app/@core/services/index.ts index bb4c70f1d6b..44349ff90cb 100644 --- a/apps/gauzy/src/app/@core/services/index.ts +++ b/apps/gauzy/src/app/@core/services/index.ts @@ -128,3 +128,4 @@ export * from './users.service'; export * from './warehouse.service'; export * from './organization-task-setting.service'; export * from './gauzy-ai/gauzy-ai.service'; +export * from './github/github.service'; diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.html b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.scss b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.scss new file mode 100644 index 00000000000..0b27efbff7d --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.scss @@ -0,0 +1,6 @@ +:host { + nb-card, + nb-card-body { + background-color: var(--gauzy-card-2); + } +} diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.spec.ts b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.spec.ts new file mode 100644 index 00000000000..b13ce5dfe93 --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.spec.ts @@ -0,0 +1,24 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { GithubAuthorizationComponent } from './authorization.component'; + +describe('GithubAuthorizationComponent', () => { + let component: GithubAuthorizationComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GithubAuthorizationComponent], + teardown: { destroyAfterEach: false } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(GithubAuthorizationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts new file mode 100644 index 00000000000..d5ab17003a3 --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts @@ -0,0 +1,79 @@ +import { Component, OnInit } from '@angular/core'; +import { debounceTime, filter, tap } from 'rxjs/operators'; +import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; +import { environment } from '@env/environment'; +import { IOrganization, IntegrationEntity, IntegrationEnum } from '@gauzy/contracts'; +import { distinctUntilChange, toParams } from '@gauzy/common-angular'; +import { Store } from '../../../../../../@core/services'; +import { GITHUB_AUTHORIZATION_URL } from '../../github.config'; +import { Router } from '@angular/router'; + +@UntilDestroy() +@Component({ + template: '' +}) +export class GithubAuthorizationComponent implements OnInit { + + public organization: IOrganization; + + constructor( + private readonly router: Router, + private readonly store: Store, + ) { } + + ngOnInit() { + this.store.selectedOrganization$ + .pipe( + debounceTime(100), + distinctUntilChange(), + filter((organization: IOrganization) => !!organization), + tap((organization: IOrganization) => this.organization = organization), + tap(() => this.gitHubAppAuthorization()), + // tap(() => this.oAuthAppAuthorization()), + untilDestroyed(this) + ) + .subscribe(); + } + + /** + * Authorize a client for Github integration. + * Redirect the user to GitHub for authorization + */ + private oAuthAppAuthorization() { + const redirect_uri = environment.GITHUB_REDIRECT_URL; + const client_id = environment.GITHUB_CLIENT_ID; + + // Define your query parameters + const queryParams = toParams({ + 'redirect_uri': `${redirect_uri}`, + 'client_id': `${client_id}`, + 'scope': 'user' + }); + + // Construct the external URL with the query parameters + const externalUrl = `${GITHUB_AUTHORIZATION_URL}?${queryParams.toString()}`; + + /** Navigate to the external URL with query parameters */ + window.location.replace(externalUrl); + } + + /** + * + */ + private gitHubAppAuthorization() { + const state = environment.GITHUB_CLIENT_ID; + + const width = 600, height = 600; + const left = window.innerWidth / 2 - width / 2; + const top = window.innerHeight / 2 - height / 2; + + /** Navigate to the external URL with query parameters */ + window.open(`https://github.com/apps/${environment.GITHUB_APP_NAME}/installations/new?state=${state.toString()}`, "", `width=${width}, height=${height}, top=${top}, left=${left}`); + + this.router.navigate(['/pages/integrations/new'], { + queryParams: { + provider: IntegrationEnum.GITHUB + } + }); + } +} diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.html b/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.html new file mode 100644 index 00000000000..72e52909053 --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.html @@ -0,0 +1,8 @@ + + + +
+

Installing. Please wait...

+
+ + diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts b/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts new file mode 100644 index 00000000000..6aea4867d62 --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts @@ -0,0 +1,60 @@ +import { AfterViewInit, Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { debounceTime, filter, tap } from 'rxjs/operators'; +import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; +import { IGithubAppInstallInput, IOrganization } from '@gauzy/contracts'; +import { distinctUntilChange } from '@gauzy/common-angular'; +import { GithubService, Store } from './../../../../../../@core/services'; + +interface IGithuIntegrationAuthorizationResponse { + state?: string; + provider?: string; + code?: string; +} + +@UntilDestroy() +@Component({ + selector: 'github-integration-installations', + templateUrl: './installations.component.html' +}) +export class GithubInstallationsComponent implements AfterViewInit, OnInit { + + public organization: IOrganization; + + constructor( + private readonly _route: ActivatedRoute, + private readonly _store: Store, + private readonly _githubService: GithubService, + ) { } + + ngAfterViewInit() { } + + ngOnInit() { + this._store.selectedOrganization$ + .pipe( + debounceTime(100), + distinctUntilChange(), + filter((organization: IOrganization) => !!organization), + tap((organization: IOrganization) => this.organization = organization), + tap(() => this.verifyGitHubAppAuthorization()), + untilDestroyed(this) + ) + .subscribe(); + } + + /** + * + * @returns + */ + verifyGitHubAppAuthorization() { + if (!this.organization) { + return; + } + const { id: organizationId, tenantId } = this.organization; + // const client_id = environment.GITHUB_CLIENT_ID; + + const { installation_id, setup_action } = this._route.snapshot.queryParams as IGithubAppInstallInput; + console.log(installation_id, setup_action, organizationId, tenantId); + console.log(this._githubService); + } +} diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/github-routing.module.ts b/apps/gauzy/src/app/pages/integrations/wizard/github/github-routing.module.ts new file mode 100644 index 00000000000..e1db88dd56f --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/github-routing.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { GithubAuthorizationComponent } from './components/authorization/authorization.component'; +import { GithubInstallationsComponent } from './components/installations/installations.component'; + +const routes: Routes = [ + { + path: '', + children: [ + { + path: '', + redirectTo: 'authorization', + pathMatch: 'full' + }, + { + path: 'authorization', + component: GithubAuthorizationComponent, + } + ] + }, + { + path: 'installations', + component: GithubInstallationsComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class GithubRoutingModule { } diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/github.config.ts b/apps/gauzy/src/app/pages/integrations/wizard/github/github.config.ts new file mode 100644 index 00000000000..c0918ed44d0 --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/github.config.ts @@ -0,0 +1 @@ +export const GITHUB_AUTHORIZATION_URL = 'https://github.com/login/oauth/authorize'; diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/github.module.ts b/apps/gauzy/src/app/pages/integrations/wizard/github/github.module.ts new file mode 100644 index 00000000000..2a9b7afa06a --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/github.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { NbCardModule } from '@nebular/theme'; +import { TranslateModule } from './../../../../@shared/translate/translate.module'; +import { GithubRoutingModule } from './github-routing.module'; +import { GithubAuthorizationComponent } from './components/authorization/authorization.component'; +import { GithubInstallationsComponent } from './components/installations/installations.component'; + +@NgModule({ + declarations: [ + GithubAuthorizationComponent, + GithubInstallationsComponent + ], + imports: [ + CommonModule, + NbCardModule, + GithubRoutingModule, + TranslateModule + ] +}) +export class GithubModule { } diff --git a/packages/contracts/src/github.model.ts b/packages/contracts/src/github.model.ts new file mode 100644 index 00000000000..98bf87b2943 --- /dev/null +++ b/packages/contracts/src/github.model.ts @@ -0,0 +1,12 @@ +import { IBasePerTenantAndOrganizationEntityModel } from "./base-entity.model"; + +export interface IGithubAppInstallInput extends IOAuthAppInstallInput { + installation_id?: string; + setup_action?: string; + state?: string; +} + +export interface IOAuthAppInstallInput extends IBasePerTenantAndOrganizationEntityModel { + provider?: string; + code?: string; +} diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts index c18d59575af..d3bc38a07c4 100644 --- a/packages/contracts/src/index.ts +++ b/packages/contracts/src/index.ts @@ -119,6 +119,7 @@ export * from './user.model'; export * from './wakatime.model'; export * from './organization-task-setting.model'; export * from './task-estimation.model'; +export * from './github.model'; export { IBaseEntityModel as BaseEntityModel } from './base-entity.model'; export { From 7087024ec6e166f5936c49f2d461449b12def13b Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Wed, 20 Sep 2023 22:29:22 +0530 Subject: [PATCH 037/104] feat: #6734 github API module --- packages/core/src/github/github.controller.ts | 22 --- packages/core/src/github/github.module.ts | 19 --- packages/core/src/github/github.service.ts | 68 --------- .../src/integration/github/github.config.ts | 1 + .../integration/github/github.controller.ts | 24 ++++ .../github/github.events.controller.ts | 31 +++++ .../src/integration/github/github.module.ts | 26 ++++ .../src/integration/github/github.service.ts | 130 ++++++++++++++++++ .../src/integration/integration.module.ts | 3 + 9 files changed, 215 insertions(+), 109 deletions(-) delete mode 100644 packages/core/src/github/github.controller.ts delete mode 100644 packages/core/src/github/github.module.ts delete mode 100644 packages/core/src/github/github.service.ts create mode 100644 packages/core/src/integration/github/github.config.ts create mode 100644 packages/core/src/integration/github/github.controller.ts create mode 100644 packages/core/src/integration/github/github.events.controller.ts create mode 100644 packages/core/src/integration/github/github.module.ts create mode 100644 packages/core/src/integration/github/github.service.ts diff --git a/packages/core/src/github/github.controller.ts b/packages/core/src/github/github.controller.ts deleted file mode 100644 index 1c6d1d33fb0..00000000000 --- a/packages/core/src/github/github.controller.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Hook } from '@gauzy/integration-github'; -import { Controller } from '@nestjs/common'; -import { Context } from 'probot'; -import { GitHubService } from './github.service'; - -@Controller() -export class GitHubController { - constructor(private readonly gitHubService: GitHubService) {} - - @Hook(['issues.opened']) - async issuesOpened(context: Context) { - await this.gitHubService.issuesOpened(context); - } - - @Hook(['issues.edited']) - async issuesEdited(context: Context) { - await this.gitHubService.issuesEdited(context); - } - - // TODO - // Handle all other required events -} diff --git a/packages/core/src/github/github.module.ts b/packages/core/src/github/github.module.ts deleted file mode 100644 index 8c5eb732b73..00000000000 --- a/packages/core/src/github/github.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Module } from '@nestjs/common'; -import { RouterModule } from 'nest-router'; -import { ProbotModule } from '@gauzy/integration-github'; -import { GitHubController } from './github.controller'; -import { GitHubService } from './github.service'; - -@Module({ - imports: [ - RouterModule.forRoutes([ - { path: '/github', module: GitHubModule } - ]), - ProbotModule, - ], - - controllers: [GitHubController], - providers: [GitHubService], - exports: [GitHubService], -}) -export class GitHubModule { } diff --git a/packages/core/src/github/github.service.ts b/packages/core/src/github/github.service.ts deleted file mode 100644 index 35750435dfe..00000000000 --- a/packages/core/src/github/github.service.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Context } from 'probot'; -// import { GitHubService as GitHubIntegrationService } from '@gauzy/integration-github'; - -@Injectable() -export class GitHubService { - constructor( - // private readonly gitHubIntegrationService: GitHubIntegrationService - ) { } - - /** - * ----- From GitHub to APIs ----- - */ - - async issuesOpened(context: Context) { - console.log('Issue Created: ', context.payload); - // TODO - // Handle event processing - // Find all the Projects connected to current repo and create new Task - } - async issuesEdited(context: Context) { - console.log('Issue Edited', context.payload); - // TODO - // Handle event processing - // Find all the Projects connected to current repo and edit task - // To edit task we need to save issue_number of GitHub in task table - } - // TODO - // Handle all other required events - - /** - * ----- From APIs to GitHub ----- - */ - async openIssue( - title: string, - body: string, - owner: string, - repo: string, - installationId: number - ) { - // await this.gitHubIntegrationService.openIssue( - // title, - // body, - // owner, - // repo, - // installationId - // ); - } - async editIssue( - issueNumber: number, - title: string, - body: string, - owner: string, - repo: string, - installationId: number - ) { - // await this.gitHubIntegrationService.editIssue( - // issueNumber, - // title, - // body, - // repo, - // owner, - // installationId - // ); - } - // TODO - // Handle all other required events -} diff --git a/packages/core/src/integration/github/github.config.ts b/packages/core/src/integration/github/github.config.ts new file mode 100644 index 00000000000..657b558e652 --- /dev/null +++ b/packages/core/src/integration/github/github.config.ts @@ -0,0 +1 @@ +export const GITHUB_ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token'; diff --git a/packages/core/src/integration/github/github.controller.ts b/packages/core/src/integration/github/github.controller.ts new file mode 100644 index 00000000000..654bdfbe95a --- /dev/null +++ b/packages/core/src/integration/github/github.controller.ts @@ -0,0 +1,24 @@ +import { Controller, Post, Body, UseGuards } from '@nestjs/common'; +import { IGithubAppInstallInput, PermissionsEnum } from '@gauzy/contracts'; +import { PermissionGuard, TenantPermissionGuard } from 'shared/guards'; +import { Permissions } from 'shared/decorators'; +import { GithubService } from './github.service'; + +@UseGuards(TenantPermissionGuard, PermissionGuard) +@Permissions(PermissionsEnum.INTEGRATION_VIEW) +@Controller() +export class GitHubController { + constructor( + private readonly _githubService: GithubService + ) { } + + /** + * + * @param body + * @returns + */ + @Post('oauth/access_token') + async create(@Body() input: IGithubAppInstallInput) { + return await this._githubService.oAuthEndpointAuthorization(input); + } +} diff --git a/packages/core/src/integration/github/github.events.controller.ts b/packages/core/src/integration/github/github.events.controller.ts new file mode 100644 index 00000000000..70c07d49046 --- /dev/null +++ b/packages/core/src/integration/github/github.events.controller.ts @@ -0,0 +1,31 @@ +import { Controller } from '@nestjs/common'; +import { Context } from 'probot'; +import { Public } from '@gauzy/common'; +import { Hook } from '@gauzy/integration-github'; +import { GithubService } from './github.service'; + +@Public() +@Controller('events') +export class GitHubEventsController { + constructor( + private readonly _githubService: GithubService + ) { } + + /** + * + * @param context + */ + @Hook(['issues.opened']) + async issuesOpened(context: Context) { + await this._githubService.issuesOpened(context); + } + + /** + * + * @param context + */ + @Hook(['issues.edited']) + async issuesEdited(context: Context) { + await this._githubService.issuesEdited(context); + } +} diff --git a/packages/core/src/integration/github/github.module.ts b/packages/core/src/integration/github/github.module.ts new file mode 100644 index 00000000000..36bde14915a --- /dev/null +++ b/packages/core/src/integration/github/github.module.ts @@ -0,0 +1,26 @@ +import { Module } from '@nestjs/common'; +import { HttpModule } from '@nestjs/axios'; +import { CqrsModule } from '@nestjs/cqrs'; +import { ProbotModule } from '@gauzy/integration-github'; +import { TenantModule } from './../../tenant/tenant.module'; +import { UserModule } from './../../user/user.module'; +import { GithubService } from './github.service'; +import { GitHubController } from './github.controller'; +import { GitHubEventsController } from './github.events.controller'; + +@Module({ + imports: [ + HttpModule, + TenantModule, + UserModule, + ProbotModule, + CqrsModule + ], + controllers: [ + GitHubController, + GitHubEventsController + ], + providers: [GithubService], + exports: [GithubService], +}) +export class GithubModule { } diff --git a/packages/core/src/integration/github/github.service.ts b/packages/core/src/integration/github/github.service.ts new file mode 100644 index 00000000000..2ce1a03c058 --- /dev/null +++ b/packages/core/src/integration/github/github.service.ts @@ -0,0 +1,130 @@ +import { IGithubAppInstallInput, IIntegrationTenant, IntegrationEnum } from '@gauzy/contracts'; +import { HttpService } from '@nestjs/axios'; +import { BadRequestException, Injectable, UnauthorizedException } from '@nestjs/common'; +import { CommandBus } from '@nestjs/cqrs'; +import { Context } from 'probot'; +import { catchError, lastValueFrom, switchMap } from 'rxjs'; +import { filter, tap } from 'rxjs/operators'; +import { IntegrationTenantCreateCommand } from 'integration-tenant/commands'; +import { RequestContext } from './../../core/context'; +import { GITHUB_ACCESS_TOKEN_URL } from './github.config'; + +@Injectable() +export class GithubService { + constructor( + private readonly _httpService: HttpService, + private readonly _commandBus: CommandBus + ) { } + + /** + * ----- From GitHub to APIs ----- + */ + + async issuesOpened(context: Context) { + console.log('Issue Created: ', context.payload); + // TODO + // Handle event processing + // Find all the Projects connected to current repo and create new Task + } + async issuesEdited(context: Context) { + console.log('Issue Edited', context.payload); + // TODO + // Handle event processing + // Find all the Projects connected to current repo and edit task + // To edit task we need to save issue_number of GitHub in task table + } + // TODO + // Handle all other required events + + /** + * ----- From APIs to GitHub ----- + */ + async openIssue( + title: string, + body: string, + owner: string, + repo: string, + installationId: number + ) { + // await this.gitHubIntegrationService.openIssue( + // title, + // body, + // owner, + // repo, + // installationId + // ); + } + async editIssue( + issueNumber: number, + title: string, + body: string, + owner: string, + repo: string, + installationId: number + ) { + // await this.gitHubIntegrationService.editIssue( + // issueNumber, + // title, + // body, + // repo, + // owner, + // installationId + // ); + } + // TODO + // Handle all other required events + + + /** + * + * @param input + * @returns + */ + async oAuthEndpointAuthorization(input: IGithubAppInstallInput): Promise { + const tenantId = RequestContext.currentTenantId() || input.tenantId; + const { code, organizationId } = input; + + if (!code) { + throw new UnauthorizedException(); + } + + const urlParams = new URLSearchParams(); + urlParams.append('client_id', process.env.GITHUB_CLIENT_ID); + urlParams.append('client_secret', process.env.GITHUB_CLIENT_SECRET); + urlParams.append('code', code); + + const tokens$ = this._httpService.post(GITHUB_ACCESS_TOKEN_URL, urlParams, { + headers: { + 'accept': 'application/json' + } + }).pipe( + filter(({ data }) => !!data.error), + switchMap(({ data }) => this._commandBus.execute( + new IntegrationTenantCreateCommand({ + tenantId, + organizationId, + name: IntegrationEnum.GITHUB, + entitySettings: [], + settings: [ + { + settingsName: 'token_type', + settingsValue: data.token_type + }, + { + settingsName: 'access_token', + settingsValue: data.access_token + }, + { + settingsName: 'scope', + settingsValue: data.scope + } + ] + }) + )), + catchError((err) => { + throw new BadRequestException(err); + }) + ); + return await lastValueFrom(tokens$); + } +} diff --git a/packages/core/src/integration/integration.module.ts b/packages/core/src/integration/integration.module.ts index b276b1a04ee..bb06f181e68 100644 --- a/packages/core/src/integration/integration.module.ts +++ b/packages/core/src/integration/integration.module.ts @@ -11,6 +11,7 @@ import { IntegrationService } from './integration.service'; import { IntegrationController } from './integration.controller'; import { CommandHandlers } from './commands/handlers'; import { IntegrationTenantModule } from '../integration-tenant/integration-tenant.module'; +import { GithubModule } from './github/github.module'; @Module({ imports: [ @@ -19,6 +20,7 @@ import { IntegrationTenantModule } from '../integration-tenant/integration-tenan path: '/integration', module: IntegrationModule, children: [ { path: '/hubstaff', module: HubstaffModule }, + { path: '/github', module: GithubModule }, { path: '/', module: IntegrationModule } ] }, @@ -32,6 +34,7 @@ import { IntegrationTenantModule } from '../integration-tenant/integration-tenan TenantModule, UserModule, HubstaffModule, + GithubModule, CqrsModule ], controllers: [ From 76582128b0473e7004a9ee33ae0dfa4bbd3cdaec Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Wed, 20 Sep 2023 22:30:00 +0530 Subject: [PATCH 038/104] fix: #6734 integrations moved on wizard folder --- .../gauzy-logo/gauzy-logo.component.ts | 3 +- apps/gauzy/src/app/app-routing.module.ts | 4 +- .../integrations-routing.module.ts | 46 ++++++++++++------- .../integrations/integrations.component.html | 2 +- .../gauzy-ai-authorize.component.html | 0 .../gauzy-ai-authorize.component.scss | 0 .../gauzy-ai-authorize.component.spec.ts | 0 .../gauzy-ai-authorize.component.ts | 2 +- .../gauzy-ai/gauzy-ai-routing.module.ts | 0 .../{ => wizard}/gauzy-ai/gauzy-ai.module.ts | 4 +- packages/core/src/app.module.ts | 21 +++------ .../commands/handlers/task-create.handler.ts | 31 +++---------- .../commands/handlers/task-update.handler.ts | 30 +++--------- packages/core/src/tasks/task.module.ts | 4 +- 14 files changed, 56 insertions(+), 91 deletions(-) rename apps/gauzy/src/app/pages/integrations/{ => wizard}/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.html (100%) rename apps/gauzy/src/app/pages/integrations/{ => wizard}/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.scss (100%) rename apps/gauzy/src/app/pages/integrations/{ => wizard}/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.spec.ts (100%) rename apps/gauzy/src/app/pages/integrations/{ => wizard}/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts (99%) rename apps/gauzy/src/app/pages/integrations/{ => wizard}/gauzy-ai/gauzy-ai-routing.module.ts (100%) rename apps/gauzy/src/app/pages/integrations/{ => wizard}/gauzy-ai/gauzy-ai.module.ts (82%) diff --git a/apps/gauzy/src/app/@theme/components/gauzy-logo/gauzy-logo.component.ts b/apps/gauzy/src/app/@theme/components/gauzy-logo/gauzy-logo.component.ts index a40fca560e6..70f8ef305b6 100644 --- a/apps/gauzy/src/app/@theme/components/gauzy-logo/gauzy-logo.component.ts +++ b/apps/gauzy/src/app/@theme/components/gauzy-logo/gauzy-logo.component.ts @@ -90,7 +90,7 @@ export class GauzyLogoComponent implements AfterViewInit, OnInit, OnDestroy { { title: 'Integrations', icon: 'fas fa-swatchbook', - link: '/pages/integrations', + link: '/pages/integrations/new', pathMatch: 'prefix', data: { translationKey: 'MENU.INTEGRATIONS', @@ -99,7 +99,6 @@ export class GauzyLogoComponent implements AfterViewInit, OnInit, OnDestroy { } } ]; - settings = { title: 'Settings', icon: 'fas fa-cog', diff --git a/apps/gauzy/src/app/app-routing.module.ts b/apps/gauzy/src/app/app-routing.module.ts index be849873700..f7e5decc424 100644 --- a/apps/gauzy/src/app/app-routing.module.ts +++ b/apps/gauzy/src/app/app-routing.module.ts @@ -42,7 +42,7 @@ const routes: Routes = [ ]; const config: ExtraOptions = { - useHash: true + useHash: false }; @NgModule({ @@ -54,4 +54,4 @@ const config: ExtraOptions = { ], exports: [RouterModule] }) -export class AppRoutingModule {} +export class AppRoutingModule { } diff --git a/apps/gauzy/src/app/pages/integrations/integrations-routing.module.ts b/apps/gauzy/src/app/pages/integrations/integrations-routing.module.ts index 41ae814ebb5..7a6d3253800 100644 --- a/apps/gauzy/src/app/pages/integrations/integrations-routing.module.ts +++ b/apps/gauzy/src/app/pages/integrations/integrations-routing.module.ts @@ -4,26 +4,38 @@ import { IntegrationsComponent } from './integrations.component'; const routes: Routes = [ { - path: '', + path: 'new', component: IntegrationsComponent }, + /** Integrations Wizard List */ { - path: 'upwork', - loadChildren: () => import('../upwork/upwork.module').then( - (m) => m.UpworkModule - ) - }, - { - path: 'hubstaff', - loadChildren: () => import('../hubstaff/hubstaff.module').then( - (m) => m.HubstaffModule - ) - }, - { - path: 'gauzy-ai', - loadChildren: () => import('./gauzy-ai/gauzy-ai.module').then( - (m) => m.GauzyAIModule - ) + path: 'wizard', + children: [ + { + path: 'upwork', + loadChildren: () => import('../upwork/upwork.module').then( + (m) => m.UpworkModule + ) + }, + { + path: 'hubstaff', + loadChildren: () => import('../hubstaff/hubstaff.module').then( + (m) => m.HubstaffModule + ) + }, + { + path: 'gauzy-ai', + loadChildren: () => import('./wizard/gauzy-ai/gauzy-ai.module').then( + (m) => m.GauzyAIModule + ) + }, + { + path: 'github', + loadChildren: () => import('./wizard/github/github.module').then( + (m) => m.GithubModule + ) + } + ] } ]; diff --git a/apps/gauzy/src/app/pages/integrations/integrations.component.html b/apps/gauzy/src/app/pages/integrations/integrations.component.html index f69a53bf57d..8867d33485b 100644 --- a/apps/gauzy/src/app/pages/integrations/integrations.component.html +++ b/apps/gauzy/src/app/pages/integrations/integrations.component.html @@ -88,7 +88,7 @@

{{ 'INTEGRATIONS.AVAILABLE_INTEGRATIONS' | translate }}

{ constructor( - // TODO: - // Uncomment below line for GitHub app integration - private readonly _gitHubService: GitHubService, private readonly _taskService: TaskService, private readonly _organizationProjectService: OrganizationProjectService ) { } @@ -27,31 +23,18 @@ export class TaskCreateHandler implements ICommandHandler { /** If project found then use project name as a task prefix */ if (input.projectId) { const { projectId } = input; - project = - await this._organizationProjectService.findOneByIdString( - projectId - ); + project = await this._organizationProjectService.findOneByIdString( + projectId + ); } const projectId = project ? project.id : null; const taskPrefix = project ? project.name.substring(0, 3) : null; - const maxNumber = await this._taskService.getMaxTaskNumberByProject( - { - organizationId, - projectId, - } - ); - - // TODO: - // Make the Repo, Owner and installation id field dynamic - // this._gitHubService.openIssue( - // input.title, - // input.description, - // '', - // '', - // 12345678 // installation id - // ); + const maxNumber = await this._taskService.getMaxTaskNumberByProject({ + organizationId, + projectId, + }); return await this._taskService.create({ ...input, diff --git a/packages/core/src/tasks/commands/handlers/task-update.handler.ts b/packages/core/src/tasks/commands/handlers/task-update.handler.ts index 6d7aee187a6..1b54b6e58ae 100644 --- a/packages/core/src/tasks/commands/handlers/task-update.handler.ts +++ b/packages/core/src/tasks/commands/handlers/task-update.handler.ts @@ -3,15 +3,11 @@ import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { ITask, ITaskUpdateInput } from '@gauzy/contracts'; import { TaskService } from '../../task.service'; import { TaskUpdateCommand } from '../task-update.command'; -import { GitHubService } from './../../../github/github.service'; @CommandHandler(TaskUpdateCommand) export class TaskUpdateHandler implements ICommandHandler { constructor( - private readonly _taskService: TaskService, - // TODO: - // Uncomment below line for GitHub app integration - private readonly _gitHubService: GitHubService + private readonly _taskService: TaskService ) { } public async execute(command: TaskUpdateCommand): Promise { @@ -37,11 +33,10 @@ export class TaskUpdateHandler implements ICommandHandler { */ if (projectId !== task.projectId) { const { organizationId } = task; - const maxNumber = - await this._taskService.getMaxTaskNumberByProject({ - organizationId, - projectId, - }); + const maxNumber = await this._taskService.getMaxTaskNumberByProject({ + organizationId, + projectId, + }); await this._taskService.update(id, { projectId, number: maxNumber + 1, @@ -49,22 +44,9 @@ export class TaskUpdateHandler implements ICommandHandler { } } - // TODO: - // We have to store issue_number of github in our task, so that we can use it while sync - // Right now we we have put static 38 value. - // Make the Issue number, Repo, Owner and installation id field dynamic - // this._gitHubService.editIssue( - // 48, - // task.title, - // task.description, - // '', - // '', - // 12345678 // installation id - // ); - return await this._taskService.create({ ...request, - id, + id }); } catch (error) { console.log('Error while updating task %s', error?.message); diff --git a/packages/core/src/tasks/task.module.ts b/packages/core/src/tasks/task.module.ts index d3a4f1e6a86..752ad952e19 100644 --- a/packages/core/src/tasks/task.module.ts +++ b/packages/core/src/tasks/task.module.ts @@ -11,7 +11,6 @@ import { TenantModule } from '../tenant/tenant.module'; import { UserModule } from './../user/user.module'; import { RoleModule } from './../role/role.module'; import { EmployeeModule } from './../employee/employee.module'; -import { GitHubModule } from './../github/github.module'; @Module({ imports: [ @@ -22,8 +21,7 @@ import { GitHubModule } from './../github/github.module'; RoleModule, EmployeeModule, OrganizationProjectModule, - CqrsModule, - GitHubModule + CqrsModule ], controllers: [TaskController], providers: [TaskService, ...CommandHandlers], From 0ce94737015899b0f3c40a4492366d1175297f7b Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Thu, 21 Sep 2023 12:13:32 +0530 Subject: [PATCH 039/104] fix: #6734 improved probot helper --- .../plugins/integration-github/package.json | 3 +- .../src/probot.discovery.ts | 8 +-- .../integration-github/src/probot.helpers.ts | 56 ++++++++++++------- .../integration-github/src/probot.types.ts | 3 +- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/packages/plugins/integration-github/package.json b/packages/plugins/integration-github/package.json index 846e7a6cafd..2e2d6aea63b 100644 --- a/packages/plugins/integration-github/package.json +++ b/packages/plugins/integration-github/package.json @@ -32,7 +32,8 @@ "octokit": "2.1.0", "pino-std-serializers": "^6.2.2", "probot": "^12.3.1", - "smee-client": "^1.2.3" + "smee-client": "^1.2.3", + "underscore": "^1.13.3" }, "devDependencies": { "@types/node": "^17.0.33", diff --git a/packages/plugins/integration-github/src/probot.discovery.ts b/packages/plugins/integration-github/src/probot.discovery.ts index 41229fb48fb..ad65392b575 100644 --- a/packages/plugins/integration-github/src/probot.discovery.ts +++ b/packages/plugins/integration-github/src/probot.discovery.ts @@ -6,7 +6,7 @@ import { OnApplicationShutdown, OnModuleInit, } from '@nestjs/common'; -import * as _ from 'lodash'; +import * as _ from 'underscore'; import { v4 } from 'uuid'; import { ModuleProviders, ProbotConfig } from './probot.types'; import { createProbot, createSmee } from './probot.helpers'; @@ -17,8 +17,7 @@ import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper'; @Injectable() export class ProbotDiscovery - implements OnModuleInit, OnApplicationBootstrap, OnApplicationShutdown -{ + implements OnModuleInit, OnApplicationBootstrap, OnApplicationShutdown { private readonly logger = new Logger('ProbotDiscovery'); private readonly hooks: Map; @@ -140,12 +139,11 @@ export class ProbotDiscovery } receiveHook(request) { + console.log({ request }); const id = request.headers['x-github-delivery'] as string; const event = request.headers['x-github-event']; const body = request.body; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore return this.probot.receive({ id, name: event, payload: body }); } } diff --git a/packages/plugins/integration-github/src/probot.helpers.ts b/packages/plugins/integration-github/src/probot.helpers.ts index 061e6aa9818..4da669cd5d0 100644 --- a/packages/plugins/integration-github/src/probot.helpers.ts +++ b/packages/plugins/integration-github/src/probot.helpers.ts @@ -5,33 +5,44 @@ import { OctokitConfig, ProbotConfig } from './probot.types'; import { Octokit } from '@octokit/rest'; import { createAppAuth } from '@octokit/auth-app'; -export const parseConfig = (config: ProbotConfig): Record => { - return { - appId: config.appId, - privateKey: getPrivateKey({ - env: { PRIVATE_KEY: config.privateKey }, - }) as string, - webhookSecret: config.webhookSecret, - ghUrl: config.ghUrl || 'https://api.github.com', - webhookProxy: config.webhookProxy, - webhookPath: config.webhookPath, - clientId: config.clientId, - clientSecret: config.clientSecret, - }; -}; +/** + * Parse and restructure Probot configuration into a more organized format. + * @param config - Probot configuration. + * @returns Parsed configuration object. + */ +export const parseConfig = (config: ProbotConfig): Record => ({ + appId: config.appId, + privateKey: getPrivateKey({ env: { PRIVATE_KEY: config.privateKey } }) as string, + webhookSecret: config.webhookSecret, + ghUrl: config.ghUrl || 'https://api.github.com', + webhookProxy: config.webhookProxy, + webhookPath: config.webhookPath, + clientId: config.clientId, + clientSecret: config.clientSecret, +}); + +/** + * Create and configure a Probot instance. + * @param config - Probot configuration. + * @returns A configured Probot instance. + */ export const createProbot = (config: ProbotConfig): Probot => { const parsedConfig = parseConfig(config); + return new Probot({ - appId: parsedConfig.appId, - privateKey: parsedConfig.privateKey, - secret: parsedConfig.webhookSecret, - baseUrl: parsedConfig.ghUrl, + ...parsedConfig, // Spread the parsed configuration properties }); }; -export const createSmee = (config: ProbotConfig) => { +/** + * Create and configure a SmeeClient instance. + * @param config - Probot configuration. + * @returns A configured SmeeClient instance. + */ +export const createSmee = (config: ProbotConfig): SmeeClient => { const parsedConfig = parseConfig(config); + return new SmeeClient({ source: parsedConfig.webhookProxy as string, target: parsedConfig.webhookPath as string, @@ -39,16 +50,21 @@ export const createSmee = (config: ProbotConfig) => { }); }; +/** + * Create and configure an Octokit instance for GitHub API requests. + * @param config - Configuration options for Octokit. + * @returns An Octokit instance. + */ export const createOctokit = (config: OctokitConfig): Octokit => { return new Octokit({ authStrategy: createAppAuth, baseUrl: config.probot.ghUrl, auth: { - ...config.auth, appId: config.probot.appId, privateKey: config.probot.privateKey, clientId: config.probot.clientId, clientSecret: config.probot.clientSecret, + ...config.auth, // Include other auth options if needed }, }); }; diff --git a/packages/plugins/integration-github/src/probot.types.ts b/packages/plugins/integration-github/src/probot.types.ts index 6bd862571af..7147d5422c6 100644 --- a/packages/plugins/integration-github/src/probot.types.ts +++ b/packages/plugins/integration-github/src/probot.types.ts @@ -26,8 +26,7 @@ export interface ProbotModuleOptions { config: ProbotConfig; } -export interface ProbotModuleAsyncOptions - extends Pick { +export interface ProbotModuleAsyncOptions extends Pick { isGlobal?: boolean; path: string; useFactory: (...args: any[]) => Promise | ProbotConfig; From 0acae483c455c3d21add770aebc350dd1e11bbc9 Mon Sep 17 00:00:00 2001 From: Govindram Date: Thu, 21 Sep 2023 13:01:46 +0530 Subject: [PATCH 040/104] Translation Added --- apps/gauzy/src/assets/i18n/bg.json | 1 + apps/gauzy/src/assets/i18n/en.json | 1 + apps/gauzy/src/assets/i18n/es.json | 1 + apps/gauzy/src/assets/i18n/fr.json | 1 + apps/gauzy/src/assets/i18n/he.json | 1 + apps/gauzy/src/assets/i18n/ru.json | 1 + 6 files changed, 6 insertions(+) diff --git a/apps/gauzy/src/assets/i18n/bg.json b/apps/gauzy/src/assets/i18n/bg.json index aee4c0fe900..0ae47a9f95b 100644 --- a/apps/gauzy/src/assets/i18n/bg.json +++ b/apps/gauzy/src/assets/i18n/bg.json @@ -521,6 +521,7 @@ "REMOVE_IMAGE": "Премахни снимка", "UPLOADER_PLACEHOLDER": "Снимка", "UPLOADER_DOCUMENT_PLACEHOLDER": "URL", + "ADD_REMOVE_PROJECTS": "Добавяне или премахване на проекти", "ADD_REMOVE_EMPLOYEES": "Добави или премахни служители", "ADD_REMOVE_CANDIDATE": "Add Candidate", "ADD_REMOVE_EMPLOYEE": "Add Interviewer", diff --git a/apps/gauzy/src/assets/i18n/en.json b/apps/gauzy/src/assets/i18n/en.json index 424f79f6488..0e8ba8cdec8 100644 --- a/apps/gauzy/src/assets/i18n/en.json +++ b/apps/gauzy/src/assets/i18n/en.json @@ -525,6 +525,7 @@ "UPLOADER_PLACEHOLDER": "Image", "UPLOADER_DOCUMENT_PLACEHOLDER": "URL", "ADD_REMOVE_EMPLOYEES": "Add or Remove Employees", + "ADD_REMOVE_PROJECTS": "Add or Remove Projects", "ADD_REMOVE_CANDIDATE": "Add Candidate", "ADD_REMOVE_EMPLOYEE": "Add Interviewer", "ADD_REMOVE_INTERVIEWER": "Select interviewer", diff --git a/apps/gauzy/src/assets/i18n/es.json b/apps/gauzy/src/assets/i18n/es.json index 4be361a0698..7809fe30b8e 100644 --- a/apps/gauzy/src/assets/i18n/es.json +++ b/apps/gauzy/src/assets/i18n/es.json @@ -525,6 +525,7 @@ "UPLOADER_PLACEHOLDER": "Imagen", "UPLOADER_DOCUMENT_PLACEHOLDER": "URL (Uniform Resource Locator)", "ADD_REMOVE_EMPLOYEES": "Agregar o eliminar empleados.", + "ADD_REMOVE_PROJECTS": "Agregar o quitar proyecto", "ADD_REMOVE_CANDIDATE": "Agregar candidato", "ADD_REMOVE_EMPLOYEE": "Añadir entrevistador", "ADD_REMOVE_INTERVIEWER": "Seleccionar entrevistador", diff --git a/apps/gauzy/src/assets/i18n/fr.json b/apps/gauzy/src/assets/i18n/fr.json index 117a9bb05e7..f71755d32bf 100644 --- a/apps/gauzy/src/assets/i18n/fr.json +++ b/apps/gauzy/src/assets/i18n/fr.json @@ -525,6 +525,7 @@ "UPLOADER_PLACEHOLDER": "Image", "UPLOADER_DOCUMENT_PLACEHOLDER": "URL (Uniform Resource Locator)", "ADD_REMOVE_EMPLOYEES": "Ajouter ou Supprimer des Employés", + "ADD_REMOVE_PROJECTS": "Ajouter ou supprimer un projet", "ADD_REMOVE_CANDIDATE": "Ajouter un candidat", "ADD_REMOVE_EMPLOYEE": "Ajouter un intervieweur", "ADD_REMOVE_INTERVIEWER": "Sélectionnez l'intervieweur", diff --git a/apps/gauzy/src/assets/i18n/he.json b/apps/gauzy/src/assets/i18n/he.json index e564be9804d..6b2147432e6 100644 --- a/apps/gauzy/src/assets/i18n/he.json +++ b/apps/gauzy/src/assets/i18n/he.json @@ -522,6 +522,7 @@ "UPLOADER_PLACEHOLDER": "תמונה", "UPLOADER_DOCUMENT_PLACEHOLDER": "URL", "ADD_REMOVE_EMPLOYEES": "Add or Remove Employees", + "ADD_REMOVE_PROJECTS": "הוסף או הסר פרויקט", "ADD_REMOVE_CANDIDATE": "Add Candidate", "ADD_REMOVE_EMPLOYEE": "Add Interviewer", "ADD_REMOVE_INTERVIEWER": "Select interviewer", diff --git a/apps/gauzy/src/assets/i18n/ru.json b/apps/gauzy/src/assets/i18n/ru.json index ed6695d0876..db282aa352a 100644 --- a/apps/gauzy/src/assets/i18n/ru.json +++ b/apps/gauzy/src/assets/i18n/ru.json @@ -522,6 +522,7 @@ "UPLOADER_PLACEHOLDER": "Изображение", "UPLOADER_DOCUMENT_PLACEHOLDER": "URL", "ADD_REMOVE_EMPLOYEES": "Добавление или удаление сотрудников", + "ADD_REMOVE_PROJECTS": "Добавить или удалить проект", "ADD_REMOVE_CANDIDATE": "Добавить Кандидата", "ADD_REMOVE_EMPLOYEE": "Добавить интервьюера", "ADD_REMOVE_INTERVIEWER": "Выбрать интервьюера", From 91763f1815b4e15c794f6a74371551a355824f1f Mon Sep 17 00:00:00 2001 From: Badal Khatri Date: Thu, 21 Sep 2023 13:28:22 +0530 Subject: [PATCH 041/104] Added isActive and isOnline field for Public Team --- .../public-share/team/public-team.service.ts | 100 ++++++++++-------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/packages/core/src/public-share/team/public-team.service.ts b/packages/core/src/public-share/team/public-team.service.ts index 818bf1da1e7..6a455477e0e 100644 --- a/packages/core/src/public-share/team/public-team.service.ts +++ b/packages/core/src/public-share/team/public-team.service.ts @@ -2,7 +2,7 @@ import { IDateRangePicker, IOrganizationTeam, IOrganizationTeamEmployee, - IOrganizationTeamStatisticInput + IOrganizationTeamStatisticInput, } from '@gauzy/contracts'; import { Injectable, NotFoundException } from '@nestjs/common'; import { parseToBoolean } from '@gauzy/common'; @@ -14,14 +14,13 @@ import { TimerService } from './../../time-tracking/timer/timer.service'; @Injectable() export class PublicTeamService { - constructor( @InjectRepository(OrganizationTeam) private readonly repository: Repository, private readonly _statisticService: StatisticService, private readonly _timerService: TimerService - ) { } + ) {} /** * GET organization team by profile link @@ -40,7 +39,7 @@ export class PublicTeamService { organization: { id: true, name: true, - brandColor: true + brandColor: true, }, members: { id: true, @@ -53,24 +52,29 @@ export class PublicTeamService { id: true, firstName: true, lastName: true, - imageUrl: true - } - } - } + imageUrl: true, + }, + isActive: true, + isOnline: true, + }, + }, }, where: { public: true, - ...params + ...params, }, - ...( - (options.relations) ? { - relations: options.relations - } : {} - ), + ...(options.relations + ? { + relations: options.relations, + } + : {}), }); if ('members' in team) { const { members, organizationId, tenantId } = team; - team['members'] = await this.syncMembers({ organizationId, tenantId, members }, options); + team['members'] = await this.syncMembers( + { organizationId, tenantId, members }, + options + ); } return team; } catch (error) { @@ -85,55 +89,57 @@ export class PublicTeamService { * @returns */ async syncMembers( - { - organizationId, - tenantId, - members - }, + { organizationId, tenantId, members }, options: IDateRangePicker & IOrganizationTeamStatisticInput ): Promise { try { const { startDate, endDate, withLaskWorkedTask, source } = options; return await Promise.all( - await members.map( - async (member: IOrganizationTeamEmployee) => { - const { employeeId, organizationTeamId } = member; - const timerWorkedStatus = await this._timerService.getTimerWorkedStatus({ + await members.map(async (member: IOrganizationTeamEmployee) => { + const { employeeId, organizationTeamId } = member; + const timerWorkedStatus = + await this._timerService.getTimerWorkedStatus({ source, employeeId, organizationTeamId, organizationId, tenantId, - ...( - (parseToBoolean(withLaskWorkedTask)) ? { - relations: ['task'] - } : {} - ), + ...(parseToBoolean(withLaskWorkedTask) + ? { + relations: ['task'], + } + : {}), }); - return { - ...member, - lastWorkedTask: parseToBoolean(withLaskWorkedTask) ? timerWorkedStatus.lastLog?.task : null, - timerStatus: timerWorkedStatus?.timerStatus, - totalWorkedTasks: await this._statisticService.getTasks({ - organizationId, - tenantId, - organizationTeamId, - employeeIds: [employeeId] - }), - totalTodayTasks: await this._statisticService.getTasks({ + return { + ...member, + lastWorkedTask: parseToBoolean(withLaskWorkedTask) + ? timerWorkedStatus.lastLog?.task + : null, + timerStatus: timerWorkedStatus?.timerStatus, + totalWorkedTasks: await this._statisticService.getTasks( + { organizationId, tenantId, organizationTeamId, employeeIds: [employeeId], - startDate, - endDate - }), - } - } - ) + } + ), + totalTodayTasks: await this._statisticService.getTasks({ + organizationId, + tenantId, + organizationTeamId, + employeeIds: [employeeId], + startDate, + endDate, + }), + }; + }) ); } catch (error) { - console.log('Error while retrieving team members worked tasks', error); + console.log( + 'Error while retrieving team members worked tasks', + error + ); } } } From 842798bbd98742d72bfd7f547b5d402de7f4bb4f Mon Sep 17 00:00:00 2001 From: Govindram Date: Thu, 21 Sep 2023 13:32:10 +0530 Subject: [PATCH 042/104] Project Adding feature added while team create --- .../project/project.component.html | 1 + .../project/project.component.ts | 1 + .../teams-mutation.component.html | 5 +++ .../teams-mutation.component.ts | 21 +++++++-- .../src/app/pages/teams/teams.component.html | 1 + .../src/app/pages/teams/teams.component.ts | 44 +++++++++++++++++++ .../gauzy/src/app/pages/teams/teams.module.ts | 2 + .../contracts/src/organization-team.model.ts | 7 +-- 8 files changed, 76 insertions(+), 6 deletions(-) diff --git a/apps/gauzy/src/app/@shared/project-select/project/project.component.html b/apps/gauzy/src/app/@shared/project-select/project/project.component.html index d44fa021a2e..43209ef665b 100644 --- a/apps/gauzy/src/app/@shared/project-select/project/project.component.html +++ b/apps/gauzy/src/app/@shared/project-select/project/project.component.html @@ -1,3 +1,4 @@ +
+ +
+
diff --git a/apps/gauzy/src/app/pages/teams/teams.component.ts b/apps/gauzy/src/app/pages/teams/teams.component.ts index d1e155316bb..8f298279a54 100644 --- a/apps/gauzy/src/app/pages/teams/teams.component.ts +++ b/apps/gauzy/src/app/pages/teams/teams.component.ts @@ -16,6 +16,8 @@ import { RolesEnum, ComponentLayoutStyleEnum, ISelectedEmployee, + IOrganizationProject, + PermissionsEnum, } from '@gauzy/contracts'; import { NbDialogRef, NbDialogService } from '@nebular/theme'; import { TranslateService } from '@ngx-translate/core'; @@ -39,6 +41,7 @@ import { } from '../../@shared/table-components'; import { EmployeesService, + OrganizationProjectsService, OrganizationTeamsService, Store, ToastrService @@ -65,6 +68,7 @@ export class TeamsComponent extends PaginationFilterBaseComponent teams: IOrganizationTeam[] = []; employees: IEmployee[] = []; tags: ITag[] = []; + projects: IOrganizationProject[] = []; viewComponentName: ComponentEnum; dataLayoutStyle = ComponentLayoutStyleEnum.TABLE; @@ -77,6 +81,8 @@ export class TeamsComponent extends PaginationFilterBaseComponent public organization: IOrganization; teams$: Subject = this.subject$; employees$: Subject = new Subject(); + projects$: Subject = new Subject(); + selected = { team: null, state: false @@ -94,6 +100,7 @@ export class TeamsComponent extends PaginationFilterBaseComponent constructor( private readonly organizationTeamsService: OrganizationTeamsService, private readonly employeesService: EmployeesService, + private readonly projectService: OrganizationProjectsService, private readonly toastrService: ToastrService, private readonly dialogService: NbDialogService, private readonly store: Store, @@ -134,6 +141,15 @@ export class TeamsComponent extends PaginationFilterBaseComponent ) .subscribe(); + this.projects$ + .pipe( + debounceTime(300), + tap(() => this._refresh$.next(true)), + tap(() => this.loadOrganizationProjects()), + untilDestroyed(this) + ) + .subscribe(); + const storeOrganization$ = this.store.selectedOrganization$; const storeEmployee$ = this.store.selectedEmployee$; combineLatest([storeOrganization$, storeEmployee$]) @@ -147,6 +163,7 @@ export class TeamsComponent extends PaginationFilterBaseComponent tap(() => this._refresh$.next(true)), tap(() => this.teams$.next(true)), tap(() => this.employees$.next(true)), + tap(() => this.projects$.next(true)), untilDestroyed(this) ) .subscribe(); @@ -158,6 +175,7 @@ export class TeamsComponent extends PaginationFilterBaseComponent ), tap(() => this.teams$.next(true)), tap(() => this.employees$.next(true)), + tap(() => this.projects$.next(true)), debounceTime(1000), tap(() => this.openDialog(this.addEditTemplate, false)), untilDestroyed(this) @@ -315,6 +333,31 @@ export class TeamsComponent extends PaginationFilterBaseComponent this.employees = items; } + /** + * load organization projects + * + * @returns + */ + private async loadOrganizationProjects(): Promise { + if (!this.organization || !this.store.hasAnyPermission( + PermissionsEnum.ALL_ORG_VIEW, + PermissionsEnum.ORG_PROJECT_VIEW + )) { + return; + } + + const { tenantId } = this.store.user; + const { id: organizationId } = this.organization; + + this.projects = (await this.projectService.getAll( + [], + { + organizationId, + tenantId + } + )).items; + } + public getTagsByEmployeeId(id: string) { const employee = this.employees.find((empl) => empl.id === id); return employee ? employee.tags : []; @@ -340,6 +383,7 @@ export class TeamsComponent extends PaginationFilterBaseComponent 'members.role', 'members.employee.user', 'tags', + 'projects' ], where: { organizationId, diff --git a/apps/gauzy/src/app/pages/teams/teams.module.ts b/apps/gauzy/src/app/pages/teams/teams.module.ts index 60f03a25a20..bb64e4fb3f4 100644 --- a/apps/gauzy/src/app/pages/teams/teams.module.ts +++ b/apps/gauzy/src/app/pages/teams/teams.module.ts @@ -29,6 +29,7 @@ import { EmployeeMultiSelectModule } from '../../@shared/employee/employee-multi import { GauzyButtonActionModule } from '../../@shared/gauzy-button-action/gauzy-button-action.module'; import { PaginationModule } from '../../@shared/pagination/pagination.module'; import { ImageUploaderModule } from '../../@shared/image-uploader/image-uploader.module'; +import { ProjectSelectModule } from '../../@shared/project-select/project-select.module'; @NgModule({ imports: [ @@ -54,6 +55,7 @@ import { ImageUploaderModule } from '../../@shared/image-uploader/image-uploader TranslateModule, HeaderTitleModule, EmployeeMultiSelectModule, + ProjectSelectModule, PaginationModule, GauzyButtonActionModule, CommonModule, diff --git a/packages/contracts/src/organization-team.model.ts b/packages/contracts/src/organization-team.model.ts index 28038a5c4b9..079cf8c710b 100644 --- a/packages/contracts/src/organization-team.model.ts +++ b/packages/contracts/src/organization-team.model.ts @@ -10,7 +10,7 @@ import { IOrganizationProject } from './organization-projects.model'; export interface IOrganizationTeam extends IBasePerTenantAndOrganizationEntityModel, - IRelationalImageAsset { + IRelationalImageAsset { name: string; color?: string; emoji?: string; @@ -21,13 +21,14 @@ export interface IOrganizationTeam profile_link?: string; members?: IOrganizationTeamEmployee[]; managers?: IOrganizationTeamEmployee[]; + projects?: IOrganizationProject[]; tags?: ITag[]; tasks?: ITask[]; } export interface IOrganizationTeamFindInput extends IBasePerTenantAndOrganizationEntityModel, - IRelationalEmployee { + IRelationalEmployee { name?: string; prefix?: string; public?: boolean; @@ -36,7 +37,7 @@ export interface IOrganizationTeamFindInput export interface IOrganizationTeamCreateInput extends IBasePerTenantAndOrganizationEntityModel, - IRelationalImageAsset { + IRelationalImageAsset { name: string; emoji?: string; teamSize?: string; From 3fe04451d205ded9676462ea738d92562cf380ae Mon Sep 17 00:00:00 2001 From: Govindram Date: Thu, 21 Sep 2023 15:07:53 +0530 Subject: [PATCH 043/104] label optional --- .../src/app/@shared/project-select/project/project.component.ts | 2 +- .../pages/teams/teams-mutation/teams-mutation.component.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/gauzy/src/app/@shared/project-select/project/project.component.ts b/apps/gauzy/src/app/@shared/project-select/project/project.component.ts index 6f1cfe92f28..db7390c9ac5 100644 --- a/apps/gauzy/src/app/@shared/project-select/project/project.component.ts +++ b/apps/gauzy/src/app/@shared/project-select/project/project.component.ts @@ -60,7 +60,7 @@ export class ProjectSelectorComponent @Input() shortened = false; @Input() disabled = false; @Input() multiple = false; - @Input() label = 'FORM.PLACEHOLDERS.ADD_REMOVE_PROJECTS'; + @Input() label = null; private _projectId: string | string[]; get projectId(): string | string[] { diff --git a/apps/gauzy/src/app/pages/teams/teams-mutation/teams-mutation.component.html b/apps/gauzy/src/app/pages/teams/teams-mutation/teams-mutation.component.html index bb92713a9bf..fc00c118dc9 100644 --- a/apps/gauzy/src/app/pages/teams/teams-mutation/teams-mutation.component.html +++ b/apps/gauzy/src/app/pages/teams/teams-mutation/teams-mutation.component.html @@ -62,7 +62,7 @@
From 891370cbf81b549de0577039d9b2ba6f4354bf40 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Thu, 21 Sep 2023 15:09:07 +0530 Subject: [PATCH 044/104] fix: #6734 GitHub app installation API integration --- .scripts/configure.ts | 4 +- .scripts/env.ts | 4 +- .../@core/services/github/github.service.ts | 14 +++++ .../authorization/authorization.component.ts | 18 ++++-- .../installations/installations.component.ts | 61 +++++++++---------- apps/gauzy/src/environments/model.ts | 2 +- .../integration/github/github.controller.ts | 12 +++- .../src/integration/github/github.service.ts | 38 ++++++++++-- 8 files changed, 104 insertions(+), 49 deletions(-) diff --git a/.scripts/configure.ts b/.scripts/configure.ts index 15ccbb7abc5..588e17ed45f 100644 --- a/.scripts/configure.ts +++ b/.scripts/configure.ts @@ -146,7 +146,7 @@ if (!env.IS_DOCKER) { GITHUB_APP_NAME: '${env.GITHUB_APP_NAME}', GITHUB_APP_ID: '${env.GITHUB_APP_ID}', GITHUB_CLIENT_ID: '${env.GITHUB_CLIENT_ID}', - GITHUB_REDIRECT_URL: '${env.GITHUB_REDIRECT_URL}', + GITHUB_POST_INSTALLATION_URL: '${env.GITHUB_POST_INSTALLATION_URL}', }; `; } else { @@ -249,7 +249,7 @@ if (!env.IS_DOCKER) { GITHUB_APP_NAME: '${env.GITHUB_APP_NAME}', GITHUB_APP_ID: '${env.GITHUB_APP_ID}', GITHUB_CLIENT_ID: '${env.GITHUB_CLIENT_ID}', - GITHUB_REDIRECT_URL: '${env.GITHUB_REDIRECT_URL}', + GITHUB_POST_INSTALLATION_URL: '${env.GITHUB_POST_INSTALLATION_URL}', }; `; } diff --git a/.scripts/env.ts b/.scripts/env.ts index 46485b6db8f..32b4f3bcc73 100644 --- a/.scripts/env.ts +++ b/.scripts/env.ts @@ -69,7 +69,7 @@ export type Env = Readonly<{ GITHUB_APP_NAME: string; GITHUB_APP_ID: string; GITHUB_CLIENT_ID: string; - GITHUB_REDIRECT_URL: string; + GITHUB_POST_INSTALLATION_URL: string; }>; export const env: Env = cleanEnv( @@ -127,7 +127,7 @@ export const env: Env = cleanEnv( GITHUB_APP_NAME: str({ default: '' }), GITHUB_APP_ID: str({ default: '' }), GITHUB_CLIENT_ID: str({ default: '' }), - GITHUB_REDIRECT_URL: str({ default: '' }), + GITHUB_POST_INSTALLATION_URL: str({ default: '' }), }, { strict: true, dotEnvPath: __dirname + '/../.env' } ); diff --git a/apps/gauzy/src/app/@core/services/github/github.service.ts b/apps/gauzy/src/app/@core/services/github/github.service.ts index 27f4d05552f..dd03ca1f82d 100644 --- a/apps/gauzy/src/app/@core/services/github/github.service.ts +++ b/apps/gauzy/src/app/@core/services/github/github.service.ts @@ -1,5 +1,8 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; +import { IGithubAppInstallInput, IIntegrationTenant } from '@gauzy/contracts'; +import { firstValueFrom } from 'rxjs'; +import { API_PREFIX } from '../../constants'; @Injectable({ providedIn: 'root', @@ -9,4 +12,15 @@ export class GithubService { constructor( private readonly _http: HttpClient ) { } + + /** + * + * @param input + * @returns + */ + async addInstallationApp(input: IGithubAppInstallInput): Promise { + return firstValueFrom( + this._http.post(`${API_PREFIX}/integration/github/install`, input) + ); + } } diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts index d5ab17003a3..3d77b455b4f 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { debounceTime, filter, tap } from 'rxjs/operators'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { environment } from '@env/environment'; -import { IOrganization, IntegrationEntity, IntegrationEnum } from '@gauzy/contracts'; +import { IOrganization, IntegrationEnum } from '@gauzy/contracts'; import { distinctUntilChange, toParams } from '@gauzy/common-angular'; import { Store } from '../../../../../../@core/services'; import { GITHUB_AUTHORIZATION_URL } from '../../github.config'; @@ -28,8 +28,7 @@ export class GithubAuthorizationComponent implements OnInit { distinctUntilChange(), filter((organization: IOrganization) => !!organization), tap((organization: IOrganization) => this.organization = organization), - tap(() => this.gitHubAppAuthorization()), - // tap(() => this.oAuthAppAuthorization()), + tap(() => this.githubAppInstallation()), untilDestroyed(this) ) .subscribe(); @@ -40,7 +39,7 @@ export class GithubAuthorizationComponent implements OnInit { * Redirect the user to GitHub for authorization */ private oAuthAppAuthorization() { - const redirect_uri = environment.GITHUB_REDIRECT_URL; + const redirect_uri = environment.GITHUB_POST_INSTALLATION_URL; const client_id = environment.GITHUB_CLIENT_ID; // Define your query parameters @@ -60,8 +59,12 @@ export class GithubAuthorizationComponent implements OnInit { /** * */ - private gitHubAppAuthorization() { - const state = environment.GITHUB_CLIENT_ID; + private githubAppInstallation() { + if (!this.organization) { + return; + } + const { id: organizationId, tenantId } = this.organization; + const state = organizationId + '|' + tenantId; const width = 600, height = 600; const left = window.innerWidth / 2 - width / 2; @@ -70,6 +73,9 @@ export class GithubAuthorizationComponent implements OnInit { /** Navigate to the external URL with query parameters */ window.open(`https://github.com/apps/${environment.GITHUB_APP_NAME}/installations/new?state=${state.toString()}`, "", `width=${width}, height=${height}, top=${top}, left=${left}`); + /** + * + */ this.router.navigate(['/pages/integrations/new'], { queryParams: { provider: IntegrationEnum.GITHUB diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts b/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts index 6aea4867d62..49f40281f2c 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts @@ -1,60 +1,57 @@ -import { AfterViewInit, Component, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { debounceTime, filter, tap } from 'rxjs/operators'; +import { tap } from 'rxjs/operators'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { IGithubAppInstallInput, IOrganization } from '@gauzy/contracts'; -import { distinctUntilChange } from '@gauzy/common-angular'; -import { GithubService, Store } from './../../../../../../@core/services'; +import { GithubService } from './../../../../../../@core/services'; -interface IGithuIntegrationAuthorizationResponse { - state?: string; - provider?: string; - code?: string; -} - -@UntilDestroy() +@UntilDestroy({ checkProperties: true }) @Component({ selector: 'github-integration-installations', templateUrl: './installations.component.html' }) -export class GithubInstallationsComponent implements AfterViewInit, OnInit { +export class GithubInstallationsComponent implements OnInit { public organization: IOrganization; constructor( private readonly _route: ActivatedRoute, - private readonly _store: Store, private readonly _githubService: GithubService, ) { } - ngAfterViewInit() { } - ngOnInit() { - this._store.selectedOrganization$ + this._route.queryParams .pipe( - debounceTime(100), - distinctUntilChange(), - filter((organization: IOrganization) => !!organization), - tap((organization: IOrganization) => this.organization = organization), - tap(() => this.verifyGitHubAppAuthorization()), + tap(({ installation_id, setup_action, state }: IGithubAppInstallInput) => { + this.verifyGitHubAppAuthorization({ + installation_id, + setup_action, + state + }) + }), untilDestroyed(this) ) - .subscribe(); + .subscribe() } /** * - * @returns + * + * @param input */ - verifyGitHubAppAuthorization() { - if (!this.organization) { - return; + async verifyGitHubAppAuthorization(input: IGithubAppInstallInput) { + const { installation_id, setup_action, state } = input; + const [organizationId, tenantId] = state.split('|'); + + try { + await this._githubService.addInstallationApp({ + installation_id, + setup_action, + organizationId, + tenantId + }); + } catch (error) { + console.log('Error while install github app: %s', installation_id); } - const { id: organizationId, tenantId } = this.organization; - // const client_id = environment.GITHUB_CLIENT_ID; - - const { installation_id, setup_action } = this._route.snapshot.queryParams as IGithubAppInstallInput; - console.log(installation_id, setup_action, organizationId, tenantId); - console.log(this._githubService); } } diff --git a/apps/gauzy/src/environments/model.ts b/apps/gauzy/src/environments/model.ts index 742b70ab14f..02c25be5a32 100644 --- a/apps/gauzy/src/environments/model.ts +++ b/apps/gauzy/src/environments/model.ts @@ -74,5 +74,5 @@ export interface Environment { GITHUB_APP_NAME: string; GITHUB_APP_ID: string; GITHUB_CLIENT_ID: string; - GITHUB_REDIRECT_URL: string; + GITHUB_POST_INSTALLATION_URL: string; } diff --git a/packages/core/src/integration/github/github.controller.ts b/packages/core/src/integration/github/github.controller.ts index 654bdfbe95a..169bc239426 100644 --- a/packages/core/src/integration/github/github.controller.ts +++ b/packages/core/src/integration/github/github.controller.ts @@ -12,13 +12,23 @@ export class GitHubController { private readonly _githubService: GithubService ) { } + /** + * + * @param body + * @returns + */ + @Post('install') + async addInstallationApp(@Body() input: IGithubAppInstallInput) { + return await this._githubService.addInstallationApp(input); + } + /** * * @param body * @returns */ @Post('oauth/access_token') - async create(@Body() input: IGithubAppInstallInput) { + async oAuthEndpointAuthorization(@Body() input: IGithubAppInstallInput) { return await this._githubService.oAuthEndpointAuthorization(input); } } diff --git a/packages/core/src/integration/github/github.service.ts b/packages/core/src/integration/github/github.service.ts index 2ce1a03c058..c1706db9c2a 100644 --- a/packages/core/src/integration/github/github.service.ts +++ b/packages/core/src/integration/github/github.service.ts @@ -4,7 +4,7 @@ import { BadRequestException, Injectable, UnauthorizedException } from '@nestjs/ import { CommandBus } from '@nestjs/cqrs'; import { Context } from 'probot'; import { catchError, lastValueFrom, switchMap } from 'rxjs'; -import { filter, tap } from 'rxjs/operators'; +import { filter } from 'rxjs/operators'; import { IntegrationTenantCreateCommand } from 'integration-tenant/commands'; import { RequestContext } from './../../core/context'; import { GITHUB_ACCESS_TOKEN_URL } from './github.config'; @@ -33,8 +33,6 @@ export class GithubService { // Find all the Projects connected to current repo and edit task // To edit task we need to save issue_number of GitHub in task table } - // TODO - // Handle all other required events /** * ----- From APIs to GitHub ----- @@ -71,9 +69,39 @@ export class GithubService { // installationId // ); } - // TODO - // Handle all other required events + /** + * + * @param input + * @returns + */ + async addInstallationApp(input: IGithubAppInstallInput): Promise { + const tenantId = RequestContext.currentTenantId() || input.tenantId; + const { installation_id, setup_action, organizationId } = input; + + return await this._commandBus.execute( + new IntegrationTenantCreateCommand({ + name: IntegrationEnum.GITHUB, + tenantId, + organizationId, + entitySettings: [], + settings: [ + { + settingsName: 'installation_id', + settingsValue: installation_id, + tenantId, + organizationId + }, + { + settingsName: 'setup_action', + settingsValue: setup_action, + tenantId, + organizationId + } + ] + }) + ); + } /** * From 983aa2ad51e98273d18b5e88f896851eadbcd355 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Thu, 21 Sep 2023 17:22:37 +0530 Subject: [PATCH 045/104] refactor: #6734 github integration config --- packages/auth/src/github/github.strategy.ts | 16 ++++++------ .../common/src/interfaces/IGithubConfig.ts | 24 ++++++++++-------- .../src/environments/environment.prod.ts | 25 +++++++++---------- .../config/src/environments/environment.ts | 17 +++---------- .../config/src/environments/ienvironment.ts | 5 +--- 5 files changed, 40 insertions(+), 47 deletions(-) diff --git a/packages/auth/src/github/github.strategy.ts b/packages/auth/src/github/github.strategy.ts index d361086eb18..533e0c8b037 100644 --- a/packages/auth/src/github/github.strategy.ts +++ b/packages/auth/src/github/github.strategy.ts @@ -30,17 +30,19 @@ export class GithubStrategy extends PassportStrategy(Strategy, 'github') { } } +/** + * + * @param configService + * @returns + */ export const config = (configService: ConfigService) => { - const GITHUB_CONFIG = configService.get( - 'githubConfig' - ) as IEnvironment['githubConfig']; + const github = configService.get('github') as IEnvironment['github']; const { baseUrl } = configService.apiConfigOptions as IApiServerOptions; return { - clientID: GITHUB_CONFIG.clientId || 'disabled', - clientSecret: GITHUB_CONFIG.clientSecret || 'disabled', - callbackURL: - GITHUB_CONFIG.callbackUrl || `${baseUrl}/api/auth/github/callback`, + clientID: github.CLIENT_ID || 'disabled', + clientSecret: github.CLIENT_SECRET || 'disabled', + callbackURL: github.CALLBACK_URL || `${baseUrl}/api/auth/github/callback`, passReqToCallback: true, scope: ['user:email'] }; diff --git a/packages/common/src/interfaces/IGithubConfig.ts b/packages/common/src/interfaces/IGithubConfig.ts index cd957db7d97..7700f258430 100644 --- a/packages/common/src/interfaces/IGithubConfig.ts +++ b/packages/common/src/interfaces/IGithubConfig.ts @@ -1,13 +1,17 @@ -export interface IGithubConfig { - readonly clientId: string; - readonly clientSecret: string; - readonly callbackUrl?: string; +export interface IGithubConfig extends Partial { + /** */ + readonly CLIENT_ID: string; + readonly CLIENT_SECRET: string; + readonly CALLBACK_URL: string; } -export interface IGitHubIntegrationConfig { - readonly clientId: string; - readonly clientSecret: string; - readonly appId: string; - readonly privateKey: string; - readonly webhookSecret: string; +export interface IGithubIntegrationConfig { + /** */ + readonly APP_ID: string; + readonly APP_NAME: string; + readonly APP_PRIVATE_KEY: string; + + /** */ + readonly WEBHOOK_URL: string; + readonly WEBHOOK_SECRET: string; } diff --git a/packages/config/src/environments/environment.prod.ts b/packages/config/src/environments/environment.prod.ts index 598158b2fa7..8da5a024ce9 100644 --- a/packages/config/src/environments/environment.prod.ts +++ b/packages/config/src/environments/environment.prod.ts @@ -111,21 +111,20 @@ export const environment: IEnvironment = { `${process.env.API_HOST}:${process.env.API_PORT}/api/auth/google/callback`, }, - githubConfig: { - clientId: process.env.GITHUB_CLIENT_ID, - clientSecret: process.env.GITHUB_CLIENT_SECRET, - callbackUrl: - process.env.GITHUB_CALLBACK_URL || - `http://${process.env.API_HOST}:${process.env.API_PORT}/api/auth/google/callback`, - }, - gitHubIntegrationConfig: { - appId: process.env.GITHUB_INTEGRATION_APP_ID, - clientId: process.env.GITHUB_INTEGRATION_CLIENT_ID, - clientSecret: process.env.GITHUB_INTEGRATION_CLIENT_SECRET, - privateKey: process.env.GITHUB_INTEGRATION_PRIVATE_KEY, - webhookSecret: process.env.GITHUB_INTEGRATION_WEBHOOK_SECRET, + github: { + CLIENT_ID: process.env.GITHUB_CLIENT_ID, + CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET, + CALLBACK_URL: process.env.GITHUB_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/github/callback` }, + // gitHubIntegrationConfig: { + // appId: process.env.GITHUB_INTEGRATION_APP_ID, + // clientId: process.env.GITHUB_INTEGRATION_CLIENT_ID, + // clientSecret: process.env.GITHUB_INTEGRATION_CLIENT_SECRET, + // privateKey: process.env.GITHUB_INTEGRATION_PRIVATE_KEY, + // webhookSecret: process.env.GITHUB_INTEGRATION_WEBHOOK_SECRET, + // }, + microsoftConfig: { clientId: process.env.MICROSOFT_CLIENT_ID, clientSecret: process.env.MICROSOFT_CLIENT_SECRET, diff --git a/packages/config/src/environments/environment.ts b/packages/config/src/environments/environment.ts index 54fabba39bb..8fad00cd61c 100644 --- a/packages/config/src/environments/environment.ts +++ b/packages/config/src/environments/environment.ts @@ -115,19 +115,10 @@ export const environment: IEnvironment = { `http://${process.env.API_HOST}:${process.env.API_PORT}/api/auth/google/callback`, }, - githubConfig: { - clientId: process.env.GITHUB_CLIENT_ID, - clientSecret: process.env.GITHUB_CLIENT_SECRET, - callbackUrl: - process.env.GITHUB_CALLBACK_URL || - `http://${process.env.API_HOST}:${process.env.API_PORT}/api/auth/google/callback`, - }, - gitHubIntegrationConfig: { - appId: process.env.GITHUB_INTEGRATION_APP_ID, - clientId: process.env.GITHUB_INTEGRATION_CLIENT_ID, - clientSecret: process.env.GITHUB_INTEGRATION_CLIENT_SECRET, - privateKey: process.env.GITHUB_INTEGRATION_PRIVATE_KEY, - webhookSecret: process.env.GITHUB_INTEGRATION_WEBHOOK_SECRET, + github: { + CLIENT_ID: process.env.GITHUB_CLIENT_ID, + CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET, + CALLBACK_URL: process.env.GITHUB_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/github/callback` }, microsoftConfig: { diff --git a/packages/config/src/environments/ienvironment.ts b/packages/config/src/environments/ienvironment.ts index f33b3461ab2..bcfd0bcb3da 100644 --- a/packages/config/src/environments/ienvironment.ts +++ b/packages/config/src/environments/ienvironment.ts @@ -11,7 +11,6 @@ import { IFacebookConfig, IFiverrConfig, IGithubConfig, - IGitHubIntegrationConfig, IGoogleConfig, IKeycloakConfig, ILinkedinConfig, @@ -100,11 +99,9 @@ export interface IEnvironment { awsConfig?: IAwsConfig; wasabiConfig?: IWasabiConfig; cloudinaryConfig?: ICloudinaryConfig; - facebookConfig: IFacebookConfig; googleConfig: IGoogleConfig; - githubConfig: IGithubConfig; - gitHubIntegrationConfig: IGitHubIntegrationConfig; + github: IGithubConfig; /** Github Configuration */ microsoftConfig: IMicrosoftConfig; linkedinConfig: ILinkedinConfig; twitterConfig: ITwitterConfig; From 349cbb9413d435fe08d8b872eb7d0f07491f783f Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Thu, 21 Sep 2023 18:21:13 +0530 Subject: [PATCH 046/104] fix: #6734 github configuration envs --- .../src/environments/environment.prod.ts | 18 +++++++++--------- .../config/src/environments/environment.ts | 10 +++++++++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/config/src/environments/environment.prod.ts b/packages/config/src/environments/environment.prod.ts index 8da5a024ce9..c1cad8d6646 100644 --- a/packages/config/src/environments/environment.prod.ts +++ b/packages/config/src/environments/environment.prod.ts @@ -112,19 +112,19 @@ export const environment: IEnvironment = { }, github: { + /**Github OAuth Configuration */ CLIENT_ID: process.env.GITHUB_CLIENT_ID, CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET, - CALLBACK_URL: process.env.GITHUB_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/github/callback` + CALLBACK_URL: process.env.GITHUB_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/github/callback`, + + /** Github App Install Configuration */ + APP_ID: process.env.GITHUB_APP_ID, + APP_NAME: process.env.GITHUB_APP_NAME, + APP_PRIVATE_KEY: process.env.GITHUB_APP_PRIVATE_KEY, + WEBHOOK_SECRET: process.env.GITHUB_WEBHOOK_SECRET, + WEBHOOK_URL: process.env.GITHUB_WEBHOOK_URL }, - // gitHubIntegrationConfig: { - // appId: process.env.GITHUB_INTEGRATION_APP_ID, - // clientId: process.env.GITHUB_INTEGRATION_CLIENT_ID, - // clientSecret: process.env.GITHUB_INTEGRATION_CLIENT_SECRET, - // privateKey: process.env.GITHUB_INTEGRATION_PRIVATE_KEY, - // webhookSecret: process.env.GITHUB_INTEGRATION_WEBHOOK_SECRET, - // }, - microsoftConfig: { clientId: process.env.MICROSOFT_CLIENT_ID, clientSecret: process.env.MICROSOFT_CLIENT_SECRET, diff --git a/packages/config/src/environments/environment.ts b/packages/config/src/environments/environment.ts index 8fad00cd61c..783512305d3 100644 --- a/packages/config/src/environments/environment.ts +++ b/packages/config/src/environments/environment.ts @@ -116,9 +116,17 @@ export const environment: IEnvironment = { }, github: { + /**Github OAuth Configuration */ CLIENT_ID: process.env.GITHUB_CLIENT_ID, CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET, - CALLBACK_URL: process.env.GITHUB_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/github/callback` + CALLBACK_URL: process.env.GITHUB_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/github/callback`, + + /** Github App Install Configuration */ + APP_ID: process.env.GITHUB_APP_ID, + APP_NAME: process.env.GITHUB_APP_NAME, + APP_PRIVATE_KEY: process.env.GITHUB_APP_PRIVATE_KEY, + WEBHOOK_SECRET: process.env.GITHUB_WEBHOOK_SECRET, + WEBHOOK_URL: process.env.GITHUB_WEBHOOK_URL }, microsoftConfig: { From f964edfa282d1891c51394345b5d1e58380d146a Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Thu, 21 Sep 2023 19:28:48 +0530 Subject: [PATCH 047/104] fix: #6734 probot github events subscription --- packages/core/src/app.module.ts | 18 +++++----- .../integration-github/src/hook.controller.ts | 3 +- .../src/probot.discovery.ts | 33 ++++++++----------- .../integration-github/src/probot.helpers.ts | 7 ++-- .../integration-github/src/probot.module.ts | 16 ++++++--- yarn.lock | 6 ++-- 6 files changed, 41 insertions(+), 42 deletions(-) diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index 3087f0c20d2..57dfda6b3bb 100644 --- a/packages/core/src/app.module.ts +++ b/packages/core/src/app.module.ts @@ -154,7 +154,7 @@ import { EmailResetModule } from './email-reset/email-reset.module'; import { TaskLinkedIssueModule } from './tasks/linked-issue/task-linked-issue.module'; import { OrganizationTaskSettingModule } from './organization-task-setting/organization-task-setting.module'; import { TaskEstimationModule } from './tasks/estimation/task-estimation.module'; -const { unleashConfig } = environment; +const { unleashConfig, github } = environment; if (unleashConfig.url) { const unleashInstanceConfig: UnleashConfig = { @@ -253,17 +253,17 @@ if (environment.sentry && environment.sentry.dsn) { : []), // Probot - ...(environment.gitHubIntegrationConfig && - environment.gitHubIntegrationConfig.appId + ...(github && environment.github.APP_ID ? [ ProbotModule.forRoot({ - path: 'api/integration/github/events', // Webhook URL in GitHub will be: https://example.com/api/integration/github + path: 'integration/github/events', // Webhook URL in GitHub will be: https://api.gauzy.co/api/integration/github/events config: { - appId: environment.gitHubIntegrationConfig.appId, - clientId: environment.gitHubIntegrationConfig.clientId, - clientSecret: environment.gitHubIntegrationConfig.clientSecret, - privateKey: environment.gitHubIntegrationConfig.privateKey, - webhookSecret: environment.gitHubIntegrationConfig.webhookSecret, + /** Client Configuration */ + clientId: environment.github.CLIENT_ID, + clientSecret: environment.github.CLIENT_SECRET, + appId: environment.github.APP_ID, + privateKey: environment.github.APP_PRIVATE_KEY, + webhookSecret: environment.github.WEBHOOK_SECRET }, }), ] diff --git a/packages/plugins/integration-github/src/hook.controller.ts b/packages/plugins/integration-github/src/hook.controller.ts index 9e7f8b92142..76498746789 100644 --- a/packages/plugins/integration-github/src/hook.controller.ts +++ b/packages/plugins/integration-github/src/hook.controller.ts @@ -7,10 +7,9 @@ export function getControllerClass({ path }): Type { @Public() @Controller() class HookController { - constructor(private readonly probotDiscovery: ProbotDiscovery) {} + constructor(private readonly probotDiscovery: ProbotDiscovery) { } @Post([path]) - // eslint-disable-next-line @typescript-eslint/no-empty-function,@typescript-eslint/explicit-module-boundary-types async hooks(@Req() req: Request) { return await this.probotDiscovery.receiveHook(req); } diff --git a/packages/plugins/integration-github/src/probot.discovery.ts b/packages/plugins/integration-github/src/probot.discovery.ts index ad65392b575..83ad94792fb 100644 --- a/packages/plugins/integration-github/src/probot.discovery.ts +++ b/packages/plugins/integration-github/src/probot.discovery.ts @@ -1,3 +1,5 @@ +import { DiscoveryService, MetadataScanner } from '@nestjs/core'; +import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper'; import { Inject, Injectable, @@ -6,24 +8,20 @@ import { OnApplicationShutdown, OnModuleInit, } from '@nestjs/common'; +import { Probot } from 'probot'; +import SmeeClient from 'smee-client'; import * as _ from 'underscore'; import { v4 } from 'uuid'; import { ModuleProviders, ProbotConfig } from './probot.types'; import { createProbot, createSmee } from './probot.helpers'; -import { Probot } from 'probot'; import { HookMetadataAccessor } from './hook-metadata.accessor'; -import { DiscoveryService, MetadataScanner } from '@nestjs/core'; -import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper'; @Injectable() -export class ProbotDiscovery - implements OnModuleInit, OnApplicationBootstrap, OnApplicationShutdown { - private readonly logger = new Logger('ProbotDiscovery'); +export class ProbotDiscovery implements OnModuleInit, OnApplicationBootstrap, OnApplicationShutdown { + private readonly logger = new Logger('ProbotDiscovery'); private readonly hooks: Map; - - private smee: any; - + private smee: SmeeClient; private readonly probot: Probot; constructor( @@ -50,7 +48,6 @@ export class ProbotDiscovery this.mountHooks(); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars onApplicationShutdown(signal?: string): any { // TODO clear probot event handlers on shutdown } @@ -91,19 +88,17 @@ export class ProbotDiscovery ]; instanceWrappers - .filter((wrapper) => wrapper.isDependencyTreeStatic()) + .filter((wrapper: InstanceWrapper) => wrapper.isDependencyTreeStatic()) .forEach((wrapper: InstanceWrapper) => { const { instance } = wrapper; - if (!instance || !Object.getPrototypeOf(instance)) { return; } - this.metadataScanner.scanFromPrototype( - instance, - Object.getPrototypeOf(instance), - (key: string) => this.lookupHooks(instance, key) - ); + const prototype = Object.getPrototypeOf(instance); + const methodNames = this.metadataScanner.getAllMethodNames(prototype); + + methodNames.forEach((key: string) => this.lookupHooks(instance, key)); }); } @@ -138,12 +133,12 @@ export class ProbotDiscovery }; } - receiveHook(request) { - console.log({ request }); + receiveHook(request: any) { const id = request.headers['x-github-delivery'] as string; const event = request.headers['x-github-event']; const body = request.body; + console.log({ id, event, body }); return this.probot.receive({ id, name: event, payload: body }); } } diff --git a/packages/plugins/integration-github/src/probot.helpers.ts b/packages/plugins/integration-github/src/probot.helpers.ts index 4da669cd5d0..39820342fba 100644 --- a/packages/plugins/integration-github/src/probot.helpers.ts +++ b/packages/plugins/integration-github/src/probot.helpers.ts @@ -1,10 +1,9 @@ import { getPrivateKey } from '@probot/get-private-key'; import { Probot } from 'probot'; import SmeeClient from 'smee-client'; -import { OctokitConfig, ProbotConfig } from './probot.types'; import { Octokit } from '@octokit/rest'; import { createAppAuth } from '@octokit/auth-app'; - +import { OctokitConfig, ProbotConfig } from './probot.types'; /** * Parse and restructure Probot configuration into a more organized format. @@ -19,7 +18,7 @@ export const parseConfig = (config: ProbotConfig): Record => ({ webhookProxy: config.webhookProxy, webhookPath: config.webhookPath, clientId: config.clientId, - clientSecret: config.clientSecret, + clientSecret: config.clientSecret }); /** @@ -29,7 +28,6 @@ export const parseConfig = (config: ProbotConfig): Record => ({ */ export const createProbot = (config: ProbotConfig): Probot => { const parsedConfig = parseConfig(config); - return new Probot({ ...parsedConfig, // Spread the parsed configuration properties }); @@ -42,7 +40,6 @@ export const createProbot = (config: ProbotConfig): Probot => { */ export const createSmee = (config: ProbotConfig): SmeeClient => { const parsedConfig = parseConfig(config); - return new SmeeClient({ source: parsedConfig.webhookProxy as string, target: parsedConfig.webhookPath as string, diff --git a/packages/plugins/integration-github/src/probot.module.ts b/packages/plugins/integration-github/src/probot.module.ts index a4b83ba9636..575482034e2 100644 --- a/packages/plugins/integration-github/src/probot.module.ts +++ b/packages/plugins/integration-github/src/probot.module.ts @@ -1,3 +1,4 @@ +import { DiscoveryModule } from '@nestjs/core'; import { DynamicModule, Module } from '@nestjs/common'; import { ProbotModuleOptions, @@ -6,7 +7,6 @@ import { } from './probot.types'; import { ProbotDiscovery } from './probot.discovery'; import { getControllerClass } from './hook.controller'; -import { DiscoveryModule } from '@nestjs/core'; import { HookMetadataAccessor } from './hook-metadata.accessor'; import { GitHubService } from './github.service'; @@ -14,6 +14,11 @@ import { GitHubService } from './github.service'; imports: [DiscoveryModule], }) export class ProbotModule { + /** + * Register the Probot module. + * @param options - Configuration options for the Probot module. + * @returns A dynamic module configuration. + */ static forRoot(options: ProbotModuleOptions): DynamicModule { const HookController = getControllerClass({ path: options.path }); return { @@ -33,6 +38,11 @@ export class ProbotModule { }; } + /** + * Register the Probot module asynchronously. + * @param options - Configuration options for the Probot module. + * @returns A dynamic module configuration. + */ static forRootAsync(options: ProbotModuleAsyncOptions): DynamicModule { const HookController = getControllerClass({ path: options.path }); return { @@ -49,9 +59,7 @@ export class ProbotModule { ProbotDiscovery, GitHubService, ], - exports: [ - GitHubService - ], + exports: [GitHubService], }; } } diff --git a/yarn.lock b/yarn.lock index bae9c5e64f7..3c34fa8e565 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7134,9 +7134,9 @@ integrity sha512-/IsuXp3B9R//uRLi40VlIYoMp7OzhkunPe2fDu7jGfQXI9y3CDCx6FC4juRLSqrpmLst3vgsiK536AAGJFl4Ww== "@types/aws-lambda@^8.10.83": - version "8.10.119" - resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.119.tgz#aaf010a9c892b3e29a290e5c49bfe8bcec82c455" - integrity sha512-Vqm22aZrCvCd6I5g1SvpW151jfqwTzEZ7XJ3yZ6xaZG31nUEOEyzzVImjRcsN8Wi/QyPxId/x8GTtgIbsy8kEw== + version "8.10.121" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.121.tgz#2702b77a77fadea98bbef43502a289882361854b" + integrity sha512-Y/jsUwO18HuC0a39BuMQkSOd/kMGATh/h5LNksw8FlTafbQ3Ge3578ZoT8w8gSOsWl2qH1p/SS/R61vc0X5jIQ== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.7": version "7.20.0" From 81e3a47fe7fb1688a20f134251c8e9f0863ef720 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Thu, 21 Sep 2023 19:51:53 +0530 Subject: [PATCH 048/104] fix: #6734 add integration relationId --- .../integration/github/github.controller.ts | 2 +- .../src/integration/github/github.module.ts | 6 ++++-- .../src/integration/github/github.service.ts | 20 +++++++++++++++++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/packages/core/src/integration/github/github.controller.ts b/packages/core/src/integration/github/github.controller.ts index 169bc239426..e3dde0ab172 100644 --- a/packages/core/src/integration/github/github.controller.ts +++ b/packages/core/src/integration/github/github.controller.ts @@ -27,7 +27,7 @@ export class GitHubController { * @param body * @returns */ - @Post('oauth/access_token') + @Post('oauth') async oAuthEndpointAuthorization(@Body() input: IGithubAppInstallInput) { return await this._githubService.oAuthEndpointAuthorization(input); } diff --git a/packages/core/src/integration/github/github.module.ts b/packages/core/src/integration/github/github.module.ts index 36bde14915a..cc914a20694 100644 --- a/packages/core/src/integration/github/github.module.ts +++ b/packages/core/src/integration/github/github.module.ts @@ -1,9 +1,10 @@ -import { Module } from '@nestjs/common'; +import { Module, forwardRef } from '@nestjs/common'; import { HttpModule } from '@nestjs/axios'; import { CqrsModule } from '@nestjs/cqrs'; import { ProbotModule } from '@gauzy/integration-github'; import { TenantModule } from './../../tenant/tenant.module'; import { UserModule } from './../../user/user.module'; +import { IntegrationModule } from './../../integration/integration.module'; import { GithubService } from './github.service'; import { GitHubController } from './github.controller'; import { GitHubEventsController } from './github.events.controller'; @@ -14,7 +15,8 @@ import { GitHubEventsController } from './github.events.controller'; TenantModule, UserModule, ProbotModule, - CqrsModule + CqrsModule, + forwardRef(() => IntegrationModule) ], controllers: [ GitHubController, diff --git a/packages/core/src/integration/github/github.service.ts b/packages/core/src/integration/github/github.service.ts index c1706db9c2a..55eac8ec123 100644 --- a/packages/core/src/integration/github/github.service.ts +++ b/packages/core/src/integration/github/github.service.ts @@ -6,6 +6,7 @@ import { Context } from 'probot'; import { catchError, lastValueFrom, switchMap } from 'rxjs'; import { filter } from 'rxjs/operators'; import { IntegrationTenantCreateCommand } from 'integration-tenant/commands'; +import { IntegrationService } from 'integration/integration.service'; import { RequestContext } from './../../core/context'; import { GITHUB_ACCESS_TOKEN_URL } from './github.config'; @@ -13,7 +14,8 @@ import { GITHUB_ACCESS_TOKEN_URL } from './github.config'; export class GithubService { constructor( private readonly _httpService: HttpService, - private readonly _commandBus: CommandBus + private readonly _commandBus: CommandBus, + private readonly _integrationService: IntegrationService ) { } /** @@ -79,9 +81,16 @@ export class GithubService { const tenantId = RequestContext.currentTenantId() || input.tenantId; const { installation_id, setup_action, organizationId } = input; + const integration = await this._integrationService.findOneByOptions({ + where: { + name: IntegrationEnum.GITHUB + } + }); + return await this._commandBus.execute( new IntegrationTenantCreateCommand({ name: IntegrationEnum.GITHUB, + integration, tenantId, organizationId, entitySettings: [], @@ -116,6 +125,12 @@ export class GithubService { throw new UnauthorizedException(); } + const integration = await this._integrationService.findOneByOptions({ + where: { + name: IntegrationEnum.GITHUB + } + }); + const urlParams = new URLSearchParams(); urlParams.append('client_id', process.env.GITHUB_CLIENT_ID); urlParams.append('client_secret', process.env.GITHUB_CLIENT_SECRET); @@ -129,9 +144,10 @@ export class GithubService { filter(({ data }) => !!data.error), switchMap(({ data }) => this._commandBus.execute( new IntegrationTenantCreateCommand({ + name: IntegrationEnum.GITHUB, + integration, tenantId, organizationId, - name: IntegrationEnum.GITHUB, entitySettings: [], settings: [ { From 1851fe5cd7393a52ec1150e2ce227013e2780fb0 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Thu, 21 Sep 2023 21:23:30 +0530 Subject: [PATCH 049/104] fix: #6734 github integration redirect url --- .scripts/configure.ts | 4 ++-- .scripts/env.ts | 4 ++-- .../components/authorization/authorization.component.ts | 2 +- apps/gauzy/src/environments/model.ts | 2 +- packages/core/src/app.module.ts | 2 +- .../core/src/integration/github/github.events.controller.ts | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.scripts/configure.ts b/.scripts/configure.ts index 588e17ed45f..15ccbb7abc5 100644 --- a/.scripts/configure.ts +++ b/.scripts/configure.ts @@ -146,7 +146,7 @@ if (!env.IS_DOCKER) { GITHUB_APP_NAME: '${env.GITHUB_APP_NAME}', GITHUB_APP_ID: '${env.GITHUB_APP_ID}', GITHUB_CLIENT_ID: '${env.GITHUB_CLIENT_ID}', - GITHUB_POST_INSTALLATION_URL: '${env.GITHUB_POST_INSTALLATION_URL}', + GITHUB_REDIRECT_URL: '${env.GITHUB_REDIRECT_URL}', }; `; } else { @@ -249,7 +249,7 @@ if (!env.IS_DOCKER) { GITHUB_APP_NAME: '${env.GITHUB_APP_NAME}', GITHUB_APP_ID: '${env.GITHUB_APP_ID}', GITHUB_CLIENT_ID: '${env.GITHUB_CLIENT_ID}', - GITHUB_POST_INSTALLATION_URL: '${env.GITHUB_POST_INSTALLATION_URL}', + GITHUB_REDIRECT_URL: '${env.GITHUB_REDIRECT_URL}', }; `; } diff --git a/.scripts/env.ts b/.scripts/env.ts index 32b4f3bcc73..46485b6db8f 100644 --- a/.scripts/env.ts +++ b/.scripts/env.ts @@ -69,7 +69,7 @@ export type Env = Readonly<{ GITHUB_APP_NAME: string; GITHUB_APP_ID: string; GITHUB_CLIENT_ID: string; - GITHUB_POST_INSTALLATION_URL: string; + GITHUB_REDIRECT_URL: string; }>; export const env: Env = cleanEnv( @@ -127,7 +127,7 @@ export const env: Env = cleanEnv( GITHUB_APP_NAME: str({ default: '' }), GITHUB_APP_ID: str({ default: '' }), GITHUB_CLIENT_ID: str({ default: '' }), - GITHUB_POST_INSTALLATION_URL: str({ default: '' }), + GITHUB_REDIRECT_URL: str({ default: '' }), }, { strict: true, dotEnvPath: __dirname + '/../.env' } ); diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts index 3d77b455b4f..c1499591d4e 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts @@ -39,7 +39,7 @@ export class GithubAuthorizationComponent implements OnInit { * Redirect the user to GitHub for authorization */ private oAuthAppAuthorization() { - const redirect_uri = environment.GITHUB_POST_INSTALLATION_URL; + const redirect_uri = environment.GITHUB_REDIRECT_URL; const client_id = environment.GITHUB_CLIENT_ID; // Define your query parameters diff --git a/apps/gauzy/src/environments/model.ts b/apps/gauzy/src/environments/model.ts index 02c25be5a32..742b70ab14f 100644 --- a/apps/gauzy/src/environments/model.ts +++ b/apps/gauzy/src/environments/model.ts @@ -74,5 +74,5 @@ export interface Environment { GITHUB_APP_NAME: string; GITHUB_APP_ID: string; GITHUB_CLIENT_ID: string; - GITHUB_POST_INSTALLATION_URL: string; + GITHUB_REDIRECT_URL: string; } diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index 57dfda6b3bb..19399b781ce 100644 --- a/packages/core/src/app.module.ts +++ b/packages/core/src/app.module.ts @@ -256,7 +256,7 @@ if (environment.sentry && environment.sentry.dsn) { ...(github && environment.github.APP_ID ? [ ProbotModule.forRoot({ - path: 'integration/github/events', // Webhook URL in GitHub will be: https://api.gauzy.co/api/integration/github/events + path: 'integration/github/webhook', // Webhook URL in GitHub will be: https://api.gauzy.co/api/integration/github/webhook config: { /** Client Configuration */ clientId: environment.github.CLIENT_ID, diff --git a/packages/core/src/integration/github/github.events.controller.ts b/packages/core/src/integration/github/github.events.controller.ts index 70c07d49046..ffd76793ec2 100644 --- a/packages/core/src/integration/github/github.events.controller.ts +++ b/packages/core/src/integration/github/github.events.controller.ts @@ -5,7 +5,7 @@ import { Hook } from '@gauzy/integration-github'; import { GithubService } from './github.service'; @Public() -@Controller('events') +@Controller('webhook') export class GitHubEventsController { constructor( private readonly _githubService: GithubService From 483a4bc4da6792f310f77dab9cf96e4d2763ab9d Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Thu, 21 Sep 2023 21:30:38 +0530 Subject: [PATCH 050/104] fix: rename gauzy-ai integration API route --- apps/gauzy/src/app/@core/services/gauzy-ai/gauzy-ai.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gauzy/src/app/@core/services/gauzy-ai/gauzy-ai.service.ts b/apps/gauzy/src/app/@core/services/gauzy-ai/gauzy-ai.service.ts index b4d81c85bee..72c65f3b884 100644 --- a/apps/gauzy/src/app/@core/services/gauzy-ai/gauzy-ai.service.ts +++ b/apps/gauzy/src/app/@core/services/gauzy-ai/gauzy-ai.service.ts @@ -19,6 +19,6 @@ export class GauzyAIService { * @returns */ addIntegration(input: IIntegrationKeySecretPairInput): Observable { - return this._http.post(`${API_PREFIX}/integrations/gauzy-ai`, input); + return this._http.post(`${API_PREFIX}/integration/gauzy-ai`, input); } } From 0b7a4f56220e3715f46cea8082534ca10403d8e3 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Thu, 21 Sep 2023 21:39:49 +0530 Subject: [PATCH 051/104] fix: gauzy AI integration API group by integration module --- packages/core/src/app.module.ts | 2 -- .../core/src/integration/gauzy-ai/integration-ai.module.ts | 3 --- packages/core/src/integration/integration.module.ts | 3 +++ 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index 22457000237..82ef39424ce 100644 --- a/packages/core/src/app.module.ts +++ b/packages/core/src/app.module.ts @@ -85,7 +85,6 @@ import { OrganizationEmploymentTypeModule } from './organization-employment-type import { TimeTrackingModule } from './time-tracking/time-tracking.module'; import { ExpenseCategoriesModule } from './expense-categories/expense-categories.module'; import { UpworkModule } from './upwork/upwork.module'; -import { IntegrationAIModule } from './integration/gauzy-ai/integration-ai.module'; import { CandidateModule } from './candidate/candidate.module'; import { ProductCategoryModule } from './product-category/product-category.module'; import { ProductTypeModule } from './product-type/product-type.module'; @@ -380,7 +379,6 @@ if (environment.sentry && environment.sentry.dsn) { FeatureModule, ReportModule, UpworkModule, - IntegrationAIModule, ExpenseCategoriesModule, ProductCategoryModule, ProductTypeModule, diff --git a/packages/core/src/integration/gauzy-ai/integration-ai.module.ts b/packages/core/src/integration/gauzy-ai/integration-ai.module.ts index 54668b532bf..49f124334b4 100644 --- a/packages/core/src/integration/gauzy-ai/integration-ai.module.ts +++ b/packages/core/src/integration/gauzy-ai/integration-ai.module.ts @@ -13,9 +13,6 @@ import { EmployeeJobPostController } from './../../employee-job/employee-job.con @Module({ imports: [ - RouterModule.forRoutes([ - { path: '/integrations/gauzy-ai', module: IntegrationAIModule } - ]), TenantModule, UserModule, IntegrationModule, diff --git a/packages/core/src/integration/integration.module.ts b/packages/core/src/integration/integration.module.ts index b276b1a04ee..9e3b9d85a9b 100644 --- a/packages/core/src/integration/integration.module.ts +++ b/packages/core/src/integration/integration.module.ts @@ -11,6 +11,7 @@ import { IntegrationService } from './integration.service'; import { IntegrationController } from './integration.controller'; import { CommandHandlers } from './commands/handlers'; import { IntegrationTenantModule } from '../integration-tenant/integration-tenant.module'; +import { IntegrationAIModule } from './gauzy-ai/integration-ai.module'; @Module({ imports: [ @@ -19,6 +20,7 @@ import { IntegrationTenantModule } from '../integration-tenant/integration-tenan path: '/integration', module: IntegrationModule, children: [ { path: '/hubstaff', module: HubstaffModule }, + { path: '/gauzy-ai', module: IntegrationAIModule }, { path: '/', module: IntegrationModule } ] }, @@ -32,6 +34,7 @@ import { IntegrationTenantModule } from '../integration-tenant/integration-tenan TenantModule, UserModule, HubstaffModule, + IntegrationAIModule, CqrsModule ], controllers: [ From bde4381c7dfbc4bae1abba52930552688ec2a014 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Thu, 21 Sep 2023 22:02:11 +0530 Subject: [PATCH 052/104] fix: circular dependency for integrations modules --- .../integration/gauzy-ai/integration-ai.module.ts | 4 ++-- .../gauzy-ai/integration-ai.service.ts | 2 +- .../src/integration/hubstaff/hubstaff.module.ts | 4 +++- .../src/integration/hubstaff/hubstaff.service.ts | 15 ++++++++++++--- .../core/src/integration/integration.module.ts | 6 +++--- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/packages/core/src/integration/gauzy-ai/integration-ai.module.ts b/packages/core/src/integration/gauzy-ai/integration-ai.module.ts index 49f124334b4..9281d1f3e61 100644 --- a/packages/core/src/integration/gauzy-ai/integration-ai.module.ts +++ b/packages/core/src/integration/gauzy-ai/integration-ai.module.ts @@ -1,4 +1,4 @@ -import { MiddlewareConsumer, Module, NestModule, RequestMethod } from '@nestjs/common'; +import { MiddlewareConsumer, Module, NestModule, RequestMethod, forwardRef } from '@nestjs/common'; import { CqrsModule } from '@nestjs/cqrs'; import { RouterModule } from 'nest-router'; import { GauzyAIModule } from '@gauzy/integration-ai'; @@ -15,7 +15,7 @@ import { EmployeeJobPostController } from './../../employee-job/employee-job.con imports: [ TenantModule, UserModule, - IntegrationModule, + forwardRef(() => IntegrationModule), IntegrationTenantModule, CqrsModule, GauzyAIModule.forRoot() diff --git a/packages/core/src/integration/gauzy-ai/integration-ai.service.ts b/packages/core/src/integration/gauzy-ai/integration-ai.service.ts index ce582a356f8..ea297ec109d 100644 --- a/packages/core/src/integration/gauzy-ai/integration-ai.service.ts +++ b/packages/core/src/integration/gauzy-ai/integration-ai.service.ts @@ -28,7 +28,7 @@ export class IntegrationAIService { const integration = await this._integrationService.findOneByOptions({ where: { - name: IntegrationEnum.GAUZY_AI + provider: IntegrationEnum.GAUZY_AI } }); diff --git a/packages/core/src/integration/hubstaff/hubstaff.module.ts b/packages/core/src/integration/hubstaff/hubstaff.module.ts index c64df3b9c92..6282e9501b5 100644 --- a/packages/core/src/integration/hubstaff/hubstaff.module.ts +++ b/packages/core/src/integration/hubstaff/hubstaff.module.ts @@ -1,4 +1,4 @@ -import { Module } from '@nestjs/common'; +import { Module, forwardRef } from '@nestjs/common'; import { HttpModule } from '@nestjs/axios'; import { CqrsModule } from '@nestjs/cqrs'; import { HUBSTAFF_API_URL } from '@gauzy/integration-hubstaff'; @@ -9,6 +9,7 @@ import { TenantModule } from 'tenant/tenant.module'; import { OrganizationModule } from 'organization/organization.module'; import { IntegrationEntitySettingModule } from 'integration-entity-setting/integration-entity-setting.module'; import { IntegrationEntitySettingTiedModule } from 'integration-entity-setting-tied/integration-entity-setting-tied.module'; +import { IntegrationModule } from 'integration/integration.module'; import { IntegrationMapModule } from 'integration-map/integration-map.module'; import { IntegrationTenantModule } from 'integration-tenant/integration-tenant.module'; import { IntegrationSettingModule } from 'integration-setting/integration-setting.module'; @@ -27,6 +28,7 @@ import { HubstaffAuthorizationController } from './hubstaff-authorization.contro UserModule, OrganizationModule, OrganizationProjectModule, + forwardRef(() => IntegrationModule), IntegrationTenantModule, IntegrationSettingModule, IntegrationEntitySettingModule, diff --git a/packages/core/src/integration/hubstaff/hubstaff.service.ts b/packages/core/src/integration/hubstaff/hubstaff.service.ts index 13c5030ac3c..94e4c603983 100644 --- a/packages/core/src/integration/hubstaff/hubstaff.service.ts +++ b/packages/core/src/integration/hubstaff/hubstaff.service.ts @@ -68,6 +68,7 @@ import { } from 'integration-map/commands'; import { IntegrationTenantService } from 'integration-tenant/integration-tenant.service'; import { IntegrationTenantCreateCommand } from 'integration-tenant/commands'; +import { IntegrationService } from 'integration/integration.service'; @Injectable() export class HubstaffService { @@ -79,7 +80,8 @@ export class HubstaffService { private readonly _roleService: RoleService, private readonly _organizationService: OrganizationService, private readonly _userService: UserService, - private readonly _commandBus: CommandBus + private readonly _commandBus: CommandBus, + private readonly _integrationService: IntegrationService ) { } async fetchIntegration(url: string, token: string): Promise { @@ -189,7 +191,6 @@ export class HubstaffService { async addIntegration( body: ICreateHubstaffIntegrationInput ): Promise { - const tenantId = RequestContext.currentTenantId(); const { client_id, client_secret, code, redirect_uri, organizationId } = body; @@ -221,6 +222,13 @@ export class HubstaffService { : settingEntity ) as IIntegrationEntitySetting[]; + /** */ + const integration = await this._integrationService.findOneByOptions({ + where: { + provider: IntegrationEnum.HUBSTAFF + } + }); + const tokens$ = this._httpService.post(`${HUBSTAFF_AUTHORIZATION_URL}/access_tokens`, urlParams, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' @@ -228,9 +236,10 @@ export class HubstaffService { }).pipe( switchMap(({ data }) => this._commandBus.execute( new IntegrationTenantCreateCommand({ + name: IntegrationEnum.HUBSTAFF, + integration, organizationId, tenantId, - name: IntegrationEnum.HUBSTAFF, entitySettings: entitySettings, settings: [ { diff --git a/packages/core/src/integration/integration.module.ts b/packages/core/src/integration/integration.module.ts index 9e3b9d85a9b..61702314a0e 100644 --- a/packages/core/src/integration/integration.module.ts +++ b/packages/core/src/integration/integration.module.ts @@ -1,4 +1,4 @@ -import { Module } from '@nestjs/common'; +import { Module, forwardRef } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { RouterModule } from 'nest-router'; import { CqrsModule } from '@nestjs/cqrs'; @@ -33,8 +33,8 @@ import { IntegrationAIModule } from './gauzy-ai/integration-ai.module'; IntegrationTenantModule, TenantModule, UserModule, - HubstaffModule, - IntegrationAIModule, + forwardRef(() => HubstaffModule), + forwardRef(() => IntegrationAIModule), CqrsModule ], controllers: [ From 3e58752e966962fbe094dfaa1606e94e1dd53dc9 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:37:53 +0530 Subject: [PATCH 053/104] fix: task linked issue for crud update action --- .../dto/create-task-linked-issue.dto.ts | 6 ++-- .../core/src/tasks/linked-issue/dto/index.ts | 1 + .../dto/update-task-linked-issue.dto.ts | 4 +++ .../task-linked-issue.controller.ts | 35 +++++++++++++++---- 4 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 packages/core/src/tasks/linked-issue/dto/update-task-linked-issue.dto.ts diff --git a/packages/core/src/tasks/linked-issue/dto/create-task-linked-issue.dto.ts b/packages/core/src/tasks/linked-issue/dto/create-task-linked-issue.dto.ts index 55e8223242a..63db69ab3d4 100644 --- a/packages/core/src/tasks/linked-issue/dto/create-task-linked-issue.dto.ts +++ b/packages/core/src/tasks/linked-issue/dto/create-task-linked-issue.dto.ts @@ -1,6 +1,4 @@ -import { ITaskLinkedIssue } from '@gauzy/contracts'; +import { ITaskLinkedIssueCreateInput } from '@gauzy/contracts'; import { TaskLinkedIssueDTO } from './task-linked-issue.dto'; -export class CreateTaskLinkedIssueDTO - extends TaskLinkedIssueDTO - implements ITaskLinkedIssue {} +export class CreateTaskLinkedIssueDTO extends TaskLinkedIssueDTO implements ITaskLinkedIssueCreateInput { } diff --git a/packages/core/src/tasks/linked-issue/dto/index.ts b/packages/core/src/tasks/linked-issue/dto/index.ts index e2d16606913..1bfb16afd92 100644 --- a/packages/core/src/tasks/linked-issue/dto/index.ts +++ b/packages/core/src/tasks/linked-issue/dto/index.ts @@ -1 +1,2 @@ export * from './create-task-linked-issue.dto'; +export * from './update-task-linked-issue.dto'; diff --git a/packages/core/src/tasks/linked-issue/dto/update-task-linked-issue.dto.ts b/packages/core/src/tasks/linked-issue/dto/update-task-linked-issue.dto.ts new file mode 100644 index 00000000000..8e0e5b23dd9 --- /dev/null +++ b/packages/core/src/tasks/linked-issue/dto/update-task-linked-issue.dto.ts @@ -0,0 +1,4 @@ +import { ITaskLinkedIssueUpdateInput } from '@gauzy/contracts'; +import { TaskLinkedIssueDTO } from './task-linked-issue.dto'; + +export class UpdateTaskLinkedIssueDTO extends TaskLinkedIssueDTO implements ITaskLinkedIssueUpdateInput { } diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts index 1c1713443b7..9d2cd13c374 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts @@ -3,19 +3,22 @@ import { Controller, HttpCode, HttpStatus, + Param, Post, + Put, UseGuards, UsePipes, ValidationPipe, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { ITaskLinkedIssue, PermissionsEnum } from '@gauzy/contracts'; -import { PermissionGuard, TenantPermissionGuard } from './../../shared/guards'; +import { PermissionGuard, TenantPermissionGuard } from 'shared/guards'; +import { UUIDValidationPipe } from 'shared/pipes'; +import { Permissions } from 'shared/decorators'; +import { CrudController } from 'core/crud'; import { TaskLinkedIssue } from './task-linked-issue.entity'; import { TaskLinkedIssueService } from './task-linked-issue.service'; -import { Permissions } from './../../shared/decorators'; -import { CrudController } from './../../core/crud'; -import { CreateTaskLinkedIssueDTO } from './dto'; +import { CreateTaskLinkedIssueDTO, UpdateTaskLinkedIssueDTO } from './dto'; @ApiTags('Linked Issue') @UseGuards(TenantPermissionGuard, PermissionGuard) @@ -35,8 +38,7 @@ export class TaskLinkedIssueController extends CrudController { * @returns */ @HttpCode(HttpStatus.CREATED) - @UseGuards(PermissionGuard) - @Permissions(PermissionsEnum.ORG_TASK_ADD) + @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.ORG_TASK_ADD) @Post() @UsePipes(new ValidationPipe({ whitelist: true })) async create( @@ -44,4 +46,25 @@ export class TaskLinkedIssueController extends CrudController { ): Promise { return await this.taskLinkedIssueService.create(entity); } + + /** + * Update existing Linked Issue + * + * @param id + * @param entity + * @returns + */ + @HttpCode(HttpStatus.ACCEPTED) + @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.ORG_TASK_EDIT) + @Put(':id') + @UsePipes(new ValidationPipe({ whitelist: true })) + async update( + @Param('id', UUIDValidationPipe) id: ITaskLinkedIssue['id'], + @Body() entity: UpdateTaskLinkedIssueDTO + ): Promise { + return await this.taskLinkedIssueService.create({ + ...entity, + id + }); + } } From b39a63f4259fb6905ffcdf8a5b335af8014d1695 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Fri, 22 Sep 2023 12:05:19 +0530 Subject: [PATCH 054/104] fix: #6734 integration dynamic redirection --- .../hubstaff-authorize/hubstaff-authorize.component.ts | 6 +++--- .../gauzy-ai-authorize/gauzy-ai-authorize.component.ts | 2 +- .../upwork-authorize/upwork-authorize.component.ts | 5 ++++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/gauzy/src/app/pages/hubstaff/components/hubstaff-authorize/hubstaff-authorize.component.ts b/apps/gauzy/src/app/pages/hubstaff/components/hubstaff-authorize/hubstaff-authorize.component.ts index cf1c79dace0..fa738b04506 100644 --- a/apps/gauzy/src/app/pages/hubstaff/components/hubstaff-authorize/hubstaff-authorize.component.ts +++ b/apps/gauzy/src/app/pages/hubstaff/components/hubstaff-authorize/hubstaff-authorize.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { Validators, FormGroup, FormBuilder } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { filter, tap } from 'rxjs/operators'; -import { IIntegrationTenant, IOrganization, IntegrationEnum } from '@gauzy/contracts'; +import { IIntegration, IIntegrationTenant, IOrganization, IntegrationEnum } from '@gauzy/contracts'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { HubstaffService, IntegrationsService, Store } from './../../../../@core/services'; @@ -113,8 +113,8 @@ export class HubstaffAuthorizeComponent implements OnInit, OnDestroy { /** * Hubstaff integration remember state API call */ - private _redirectToHubstaffIntegration(integrationId) { - this._router.navigate(['pages/integrations/hubstaff', integrationId]); + private _redirectToHubstaffIntegration(integrationId: IIntegration['id']) { + this._router.navigate([this._router.url, integrationId]); } authorizeHubstaff() { diff --git a/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts b/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts index 53ec4c37447..31d0633241b 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts +++ b/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts @@ -117,7 +117,7 @@ export class GauzyAIAuthorizeComponent implements AfterViewInit, OnInit, OnDestr * Gauzy AI integration remember state API call */ private _redirectToGauzyAIIntegration(integrationId: string) { - this._router.navigate(['pages/integrations/gauzy-ai', integrationId]); + this._router.navigate([this._router.url, integrationId]); } /** diff --git a/apps/gauzy/src/app/pages/upwork/components/upwork-authorize/upwork-authorize.component.ts b/apps/gauzy/src/app/pages/upwork/components/upwork-authorize/upwork-authorize.component.ts index 9629e88b828..0c6aedbd21c 100644 --- a/apps/gauzy/src/app/pages/upwork/components/upwork-authorize/upwork-authorize.component.ts +++ b/apps/gauzy/src/app/pages/upwork/components/upwork-authorize/upwork-authorize.component.ts @@ -127,7 +127,10 @@ export class UpworkAuthorizeComponent implements OnInit, OnDestroy { * @param integrationId */ private _redirectToUpworkIntegration(integrationId: string) { - this._router.navigate(['pages/integrations/upwork', integrationId]); + this._router.navigate([ + this._router.url, + integrationId + ]); } ngOnDestroy(): void { } From 7f38662e6d1cfe4e5bc79c034a7ac5842949cd49 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Fri, 22 Sep 2023 12:36:54 +0530 Subject: [PATCH 055/104] fix: useHash true for angular routing --- apps/gauzy/src/app/app-routing.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gauzy/src/app/app-routing.module.ts b/apps/gauzy/src/app/app-routing.module.ts index f7e5decc424..7e9cada813f 100644 --- a/apps/gauzy/src/app/app-routing.module.ts +++ b/apps/gauzy/src/app/app-routing.module.ts @@ -42,7 +42,7 @@ const routes: Routes = [ ]; const config: ExtraOptions = { - useHash: false + useHash: true }; @NgModule({ From f6069c499e789bab07b2470cbb1f23f28569cbdd Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Fri, 22 Sep 2023 14:37:00 +0530 Subject: [PATCH 056/104] fix: #6734 github integration redirection after post install --- .../@core/services/github/github.service.ts | 2 +- .../installations.component.html | 8 ++-- .../installations/installations.component.ts | 38 +++++++++++-------- .../common/src/interfaces/IGithubConfig.ts | 3 ++ packages/config/src/config.service.ts | 4 +- .../src/environments/environment.prod.ts | 4 ++ .../config/src/environments/environment.ts | 4 ++ .../github/github-install.controller.ts | 38 +++++++++++++++++++ .../integration/github/github.controller.ts | 4 +- .../src/integration/github/github.module.ts | 2 + .../src/integration/github/github.service.ts | 3 +- 11 files changed, 86 insertions(+), 24 deletions(-) create mode 100644 packages/core/src/integration/github/github-install.controller.ts diff --git a/apps/gauzy/src/app/@core/services/github/github.service.ts b/apps/gauzy/src/app/@core/services/github/github.service.ts index dd03ca1f82d..042464238a2 100644 --- a/apps/gauzy/src/app/@core/services/github/github.service.ts +++ b/apps/gauzy/src/app/@core/services/github/github.service.ts @@ -20,7 +20,7 @@ export class GithubService { */ async addInstallationApp(input: IGithubAppInstallInput): Promise { return firstValueFrom( - this._http.post(`${API_PREFIX}/integration/github/install`, input) + this._http.post(`${API_PREFIX}/integration/github/app-install`, input) ); } } diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.html b/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.html index 72e52909053..2df16eb37a6 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.html +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.html @@ -1,8 +1,10 @@ -
-

Installing. Please wait...

-
+ +
+

Installing. Please wait...

+
+
diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts b/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts index 49f40281f2c..7c85a8a0483 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { tap } from 'rxjs/operators'; +import { filter, tap } from 'rxjs/operators'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { IGithubAppInstallInput, IOrganization } from '@gauzy/contracts'; import { GithubService } from './../../../../../../@core/services'; @@ -12,6 +12,7 @@ import { GithubService } from './../../../../../../@core/services'; }) export class GithubInstallationsComponent implements OnInit { + public isLoading: boolean = true; public organization: IOrganization; constructor( @@ -22,36 +23,43 @@ export class GithubInstallationsComponent implements OnInit { ngOnInit() { this._route.queryParams .pipe( - tap(({ installation_id, setup_action, state }: IGithubAppInstallInput) => { - this.verifyGitHubAppAuthorization({ + filter(({ installation_id, setup_action }) => !!installation_id && !!setup_action), + tap(async ({ installation_id, setup_action, state }: IGithubAppInstallInput) => + await this.verifyGitHubAppAuthorization({ installation_id, setup_action, state }) - }), + ), untilDestroyed(this) ) .subscribe() } /** - * * * @param input */ async verifyGitHubAppAuthorization(input: IGithubAppInstallInput) { const { installation_id, setup_action, state } = input; - const [organizationId, tenantId] = state.split('|'); + if (installation_id && setup_action && state) { + const [organizationId, tenantId] = state.split('|'); + try { + await this._githubService.addInstallationApp({ + installation_id, + setup_action, + organizationId, + tenantId + }); + this.isLoading = false; - try { - await this._githubService.addInstallationApp({ - installation_id, - setup_action, - organizationId, - tenantId - }); - } catch (error) { - console.log('Error while install github app: %s', installation_id); + /** Close current window */ + window.opener = null; + window.open("", "_self"); + window.close(); + } catch (error) { + console.log('Error while install github app: %s', installation_id); + } } } } diff --git a/packages/common/src/interfaces/IGithubConfig.ts b/packages/common/src/interfaces/IGithubConfig.ts index 7700f258430..6a98ee9d1c3 100644 --- a/packages/common/src/interfaces/IGithubConfig.ts +++ b/packages/common/src/interfaces/IGithubConfig.ts @@ -11,6 +11,9 @@ export interface IGithubIntegrationConfig { readonly APP_NAME: string; readonly APP_PRIVATE_KEY: string; + /** */ + readonly POST_INSTALL_URL: string; + /** */ readonly WEBHOOK_URL: string; readonly WEBHOOK_SECRET: string; diff --git a/packages/config/src/config.service.ts b/packages/config/src/config.service.ts index c0600bddb6d..abfcc96f0fb 100644 --- a/packages/config/src/config.service.ts +++ b/packages/config/src/config.service.ts @@ -41,8 +41,8 @@ export class ConfigService { return this.config.assetOptions; } - get(key: keyof IEnvironment): IEnvironment[keyof IEnvironment] { - return this.environment[key]; + get(key: keyof IEnvironment): IEnvironment[keyof IEnvironment] { + return this.environment[key] as T; } isProd(): boolean { diff --git a/packages/config/src/environments/environment.prod.ts b/packages/config/src/environments/environment.prod.ts index c1cad8d6646..a60b734e359 100644 --- a/packages/config/src/environments/environment.prod.ts +++ b/packages/config/src/environments/environment.prod.ts @@ -121,6 +121,10 @@ export const environment: IEnvironment = { APP_ID: process.env.GITHUB_APP_ID, APP_NAME: process.env.GITHUB_APP_NAME, APP_PRIVATE_KEY: process.env.GITHUB_APP_PRIVATE_KEY, + + /** Github App Post Install Configuration */ + POST_INSTALL_URL: process.env.GITHUB_POST_INSTALL_URL, + WEBHOOK_SECRET: process.env.GITHUB_WEBHOOK_SECRET, WEBHOOK_URL: process.env.GITHUB_WEBHOOK_URL }, diff --git a/packages/config/src/environments/environment.ts b/packages/config/src/environments/environment.ts index 783512305d3..0a7b195faa2 100644 --- a/packages/config/src/environments/environment.ts +++ b/packages/config/src/environments/environment.ts @@ -125,6 +125,10 @@ export const environment: IEnvironment = { APP_ID: process.env.GITHUB_APP_ID, APP_NAME: process.env.GITHUB_APP_NAME, APP_PRIVATE_KEY: process.env.GITHUB_APP_PRIVATE_KEY, + + /** Github App Post Install Configuration */ + POST_INSTALL_URL: process.env.GITHUB_POST_INSTALL_URL, + WEBHOOK_SECRET: process.env.GITHUB_WEBHOOK_SECRET, WEBHOOK_URL: process.env.GITHUB_WEBHOOK_URL }, diff --git a/packages/core/src/integration/github/github-install.controller.ts b/packages/core/src/integration/github/github-install.controller.ts new file mode 100644 index 00000000000..f89be77e950 --- /dev/null +++ b/packages/core/src/integration/github/github-install.controller.ts @@ -0,0 +1,38 @@ + +import { Controller, Get, Query, Res } from '@nestjs/common'; +import { Response } from 'express'; +import { ConfigService } from '@gauzy/config'; +import { IGithubAppInstallInput } from '@gauzy/contracts'; +import { IGithubConfig, Public } from '@gauzy/common'; + +@Public() +@Controller() +export class GitHubPostInstallController { + constructor( + private readonly _config: ConfigService + ) { } + + /** + * + * @param query + * @param response + */ + @Get('app-install') + async appInstallCallback( + @Query() query: IGithubAppInstallInput, + @Res() response: Response + ) { + const github = this._config.get('github') as IGithubConfig; + try { + const urlParams = new URLSearchParams(); + urlParams.append('installation_id', query.installation_id); + urlParams.append('setup_action', query.setup_action); + urlParams.append('state', query.state); + + /** Redirected to the UI */ + return response.redirect(`${github.POST_INSTALL_URL}?${urlParams.toString()}`); + } catch (error) { + return response.redirect(`${github.POST_INSTALL_URL}`); + } + } +} diff --git a/packages/core/src/integration/github/github.controller.ts b/packages/core/src/integration/github/github.controller.ts index e3dde0ab172..354de23c09c 100644 --- a/packages/core/src/integration/github/github.controller.ts +++ b/packages/core/src/integration/github/github.controller.ts @@ -17,8 +17,8 @@ export class GitHubController { * @param body * @returns */ - @Post('install') - async addInstallationApp(@Body() input: IGithubAppInstallInput) { + @Post('app-install') + async addInstallApp(@Body() input: IGithubAppInstallInput) { return await this._githubService.addInstallationApp(input); } diff --git a/packages/core/src/integration/github/github.module.ts b/packages/core/src/integration/github/github.module.ts index cc914a20694..9a81c7b89e5 100644 --- a/packages/core/src/integration/github/github.module.ts +++ b/packages/core/src/integration/github/github.module.ts @@ -8,6 +8,7 @@ import { IntegrationModule } from './../../integration/integration.module'; import { GithubService } from './github.service'; import { GitHubController } from './github.controller'; import { GitHubEventsController } from './github.events.controller'; +import { GitHubPostInstallController } from './github-install.controller'; @Module({ imports: [ @@ -20,6 +21,7 @@ import { GitHubEventsController } from './github.events.controller'; ], controllers: [ GitHubController, + GitHubPostInstallController, GitHubEventsController ], providers: [GithubService], diff --git a/packages/core/src/integration/github/github.service.ts b/packages/core/src/integration/github/github.service.ts index 55eac8ec123..a9ea07bed87 100644 --- a/packages/core/src/integration/github/github.service.ts +++ b/packages/core/src/integration/github/github.service.ts @@ -87,6 +87,7 @@ export class GithubService { } }); + /** */ return await this._commandBus.execute( new IntegrationTenantCreateCommand({ name: IntegrationEnum.GITHUB, @@ -106,7 +107,7 @@ export class GithubService { settingsValue: setup_action, tenantId, organizationId - } + }, ] }) ); From a0203a94cbd1b54e33c32c5275809ee7c113a5b3 Mon Sep 17 00:00:00 2001 From: Ruslan Konviser Date: Fri, 22 Sep 2023 12:22:42 +0200 Subject: [PATCH 057/104] chore: disable NX builds cache --- .deploy/api/Dockerfile | 3 +++ .deploy/webapp/Dockerfile | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.deploy/api/Dockerfile b/.deploy/api/Dockerfile index bbba4273051..fac9063068c 100644 --- a/.deploy/api/Dockerfile +++ b/.deploy/api/Dockerfile @@ -192,6 +192,9 @@ ENV DEMO=${DEMO:-false} ENV IS_DOCKER=true +# Temporary disable caching in NX Cloud for builds +ENV NX_NO_CLOUD=true + RUN yarn build:package:api RUN yarn build:api:prod:docker diff --git a/.deploy/webapp/Dockerfile b/.deploy/webapp/Dockerfile index 740215ed754..f050805535b 100644 --- a/.deploy/webapp/Dockerfile +++ b/.deploy/webapp/Dockerfile @@ -92,6 +92,9 @@ ENV NODE_ENV=${NODE_ENV:-production} ENV IS_DOCKER=true +# Temporary disable caching in NX Cloud for builds +ENV NX_NO_CLOUD=true + RUN yarn build:package:gauzy RUN yarn build:gauzy:prod:docker From 0aa83d7efaeb153778c1984f9b5a7460e5adf9a0 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Fri, 22 Sep 2023 17:12:29 +0530 Subject: [PATCH 058/104] fix: #6734 github integration redirection based on closed popup --- .../authorization.component.html | 10 ++ .../authorization/authorization.component.ts | 92 ++++++++++++++----- .../src/app/pages/pages-routing.module.ts | 4 +- .../github/github-install.controller.ts | 2 + 4 files changed, 85 insertions(+), 23 deletions(-) diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.html b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.html index e69de29bb2d..2df16eb37a6 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.html +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.html @@ -0,0 +1,10 @@ + + + + +
+

Installing. Please wait...

+
+
+
+
diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts index c1499591d4e..317390b3990 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts @@ -1,20 +1,23 @@ import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; import { debounceTime, filter, tap } from 'rxjs/operators'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { environment } from '@env/environment'; -import { IOrganization, IntegrationEnum } from '@gauzy/contracts'; +import { IOrganization } from '@gauzy/contracts'; import { distinctUntilChange, toParams } from '@gauzy/common-angular'; import { Store } from '../../../../../../@core/services'; import { GITHUB_AUTHORIZATION_URL } from '../../github.config'; -import { Router } from '@angular/router'; @UntilDestroy() @Component({ - template: '' + templateUrl: './authorization.component.html' }) export class GithubAuthorizationComponent implements OnInit { public organization: IOrganization; + public isLoading: boolean = true; + // save a reference to the window so we can close it + private window = null; constructor( private readonly router: Router, @@ -35,10 +38,10 @@ export class GithubAuthorizationComponent implements OnInit { } /** - * Authorize a client for Github integration. - * Redirect the user to GitHub for authorization + * Authorize a client for GitHub integration. + * Redirect the user to GitHub for authorization. */ - private oAuthAppAuthorization() { + private async oAuthAppAuthorization() { const redirect_uri = environment.GITHUB_REDIRECT_URL; const client_id = environment.GITHUB_CLIENT_ID; @@ -57,29 +60,76 @@ export class GithubAuthorizationComponent implements OnInit { } /** - * + * Handle GitHub App installation flow. */ - private githubAppInstallation() { + private async githubAppInstallation() { if (!this.organization) { return; } + try { + if (!this.window || this.window.closed) { + // If no window is open or the existing window is closed, open a new one + this.openPopupWindow(); + } else { + // If a window is already open, you can handle it here (e.g., focus or bring it to the front) + this.window.focus(); + } + this.checkPopupWindowStatus(); + } catch (error) { + console.log('Error while setup GitHub integration: %s', error?.message); + } + } + + /** + * Open a popup window for GitHub App installation. + */ + private async openPopupWindow() { const { id: organizationId, tenantId } = this.organization; - const state = organizationId + '|' + tenantId; + const state = `${organizationId}|${tenantId}`; - const width = 600, height = 600; - const left = window.innerWidth / 2 - width / 2; + const width = 800, height = 800; + const left = window.innerWidth - width; // Adjust the left position to place it on the right side const top = window.innerHeight / 2 - height / 2; - /** Navigate to the external URL with query parameters */ - window.open(`https://github.com/apps/${environment.GITHUB_APP_NAME}/installations/new?state=${state.toString()}`, "", `width=${width}, height=${height}, top=${top}, left=${left}`); - - /** - * - */ - this.router.navigate(['/pages/integrations/new'], { - queryParams: { - provider: IntegrationEnum.GITHUB + // Specify a unique window name to identify the window + const windowName = 'githubAppInstallationWindow'; + + // Check if a window with the same name is already open + if (window.frames[windowName] && !window.frames[windowName].closed) { + // A window with the same name is already open, so focus on it + window.frames[windowName].focus(); + } else { + /** Navigate to the external URL with query parameters */ + this.window = window.open(`https://github.com/apps/${environment.GITHUB_APP_NAME}/installations/new?state=${state.toString()}`, windowName, `width=${width}, height=${height}, top=${top}, left=${left}`); + } + } + + /** + * Check the status of the popup window. + */ + private async checkPopupWindowStatus() { + const timer = setInterval(() => { + console.log(this.window); + if (this.window == null || this.window.closed) { + clearInterval(timer); // Stop checking when the window is closed + /** */ + this.handleClosedPopupWindow(); + } else { + console.log('Popup window still open'); } - }); + }, 1000); // Check every second (adjust the interval as needed) + } + + /** + * Handle the case when the popup window is closed. + */ + private handleClosedPopupWindow() { + this.isLoading = false; + console.log('Popup window closed'); + + // Delay navigation by 5 seconds before redirecting + setTimeout(() => { + this.router.navigate(['/pages/integrations/new']); + }, 5000); // 5000 milliseconds = 5 seconds } } diff --git a/apps/gauzy/src/app/pages/pages-routing.module.ts b/apps/gauzy/src/app/pages/pages-routing.module.ts index 3a9a6b2c16d..48933309793 100644 --- a/apps/gauzy/src/app/pages/pages-routing.module.ts +++ b/apps/gauzy/src/app/pages/pages-routing.module.ts @@ -690,8 +690,8 @@ const routes: Routes = [ project: false, team: false, employee: false, - organization: false, - date: false + date: false, + organization: true, } } }, diff --git a/packages/core/src/integration/github/github-install.controller.ts b/packages/core/src/integration/github/github-install.controller.ts index f89be77e950..3f47c6fbcba 100644 --- a/packages/core/src/integration/github/github-install.controller.ts +++ b/packages/core/src/integration/github/github-install.controller.ts @@ -22,8 +22,10 @@ export class GitHubPostInstallController { @Query() query: IGithubAppInstallInput, @Res() response: Response ) { + /** Github Config Options */ const github = this._config.get('github') as IGithubConfig; try { + /** */ const urlParams = new URLSearchParams(); urlParams.append('installation_id', query.installation_id); urlParams.append('setup_action', query.setup_action); From 3d4b23f418e3c2c199bb1b430ff40ae7e4056647 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Fri, 22 Sep 2023 17:23:07 +0530 Subject: [PATCH 059/104] fix: window popup will be closed after install github app --- .../installations/installations.component.ts | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts b/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts index 7c85a8a0483..0a0b2de9962 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts @@ -51,15 +51,27 @@ export class GithubInstallationsComponent implements OnInit { organizationId, tenantId }); - this.isLoading = false; - - /** Close current window */ - window.opener = null; - window.open("", "_self"); - window.close(); + this.handleClosedPopupWindow(); } catch (error) { console.log('Error while install github app: %s', installation_id); } } } + + /** + * Handle the case when the popup window is closed. + */ + private handleClosedPopupWindow() { + this.isLoading = false; + console.log('Popup window closed after github app installed!'); + + // Delay navigation by 2 seconds before close window + setTimeout(() => { + /** Close current window */ + window.opener = null; + window.open("", "_self"); + window.close(); + + }, 2000); // 2000 milliseconds = 5 seconds + } } From b98c782a7a7dc55695ffc39b190a627b63330304 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sat, 23 Sep 2023 12:41:41 +0530 Subject: [PATCH 060/104] fix: #6734 open new window with some predefined properties --- .../authorization/authorization.component.ts | 41 ++++++++++++++----- packages/contracts/src/integration.model.ts | 4 +- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts index 317390b3990..977e5d0c306 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts +++ b/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts @@ -3,12 +3,12 @@ import { Router } from '@angular/router'; import { debounceTime, filter, tap } from 'rxjs/operators'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { environment } from '@env/environment'; -import { IOrganization } from '@gauzy/contracts'; +import { IOrganization, IntegrationEnum } from '@gauzy/contracts'; import { distinctUntilChange, toParams } from '@gauzy/common-angular'; import { Store } from '../../../../../../@core/services'; import { GITHUB_AUTHORIZATION_URL } from '../../github.config'; -@UntilDestroy() +@UntilDestroy({ checkProperties: true }) @Component({ templateUrl: './authorization.component.html' }) @@ -87,7 +87,7 @@ export class GithubAuthorizationComponent implements OnInit { const { id: organizationId, tenantId } = this.organization; const state = `${organizationId}|${tenantId}`; - const width = 800, height = 800; + const width = 600, height = 600; const left = window.innerWidth - width; // Adjust the left position to place it on the right side const top = window.innerHeight / 2 - height / 2; @@ -99,8 +99,24 @@ export class GithubAuthorizationComponent implements OnInit { // A window with the same name is already open, so focus on it window.frames[windowName].focus(); } else { + /** Navigate to the target external URL */ + const url = `https://github.com/apps/${environment.GITHUB_APP_NAME}/installations/new?state=${state.toString()}`; + /** Navigate to the external URL with query parameters */ - this.window = window.open(`https://github.com/apps/${environment.GITHUB_APP_NAME}/installations/new?state=${state.toString()}`, windowName, `width=${width}, height=${height}, top=${top}, left=${left}`); + this.window = window.open( + url, + windowName, + `width=${width}, + height=${height}, + top=${top}, + left=${left}, + toolbar=no, + location=no, + status=no, + menubar=no, + scrollbars=yes, + resizable=yes, + `); } } @@ -109,27 +125,32 @@ export class GithubAuthorizationComponent implements OnInit { */ private async checkPopupWindowStatus() { const timer = setInterval(() => { - console.log(this.window); if (this.window == null || this.window.closed) { clearInterval(timer); // Stop checking when the window is closed /** */ this.handleClosedPopupWindow(); } else { - console.log('Popup window still open'); + console.log('popup window still open'); } }, 1000); // Check every second (adjust the interval as needed) } /** * Handle the case when the popup window is closed. + * + * @param ms */ - private handleClosedPopupWindow() { + private handleClosedPopupWindow(ms: number = 1000) { this.isLoading = false; - console.log('Popup window closed'); + console.log('popup window closed'); // Delay navigation by 5 seconds before redirecting setTimeout(() => { - this.router.navigate(['/pages/integrations/new']); - }, 5000); // 5000 milliseconds = 5 seconds + this.router.navigate(['/pages/integrations/new'], { + queryParams: { + provider: IntegrationEnum.GITHUB + } + }); + }, ms); // 5000 milliseconds = 5 seconds } } diff --git a/packages/contracts/src/integration.model.ts b/packages/contracts/src/integration.model.ts index 5b184265666..20bd304511f 100644 --- a/packages/contracts/src/integration.model.ts +++ b/packages/contracts/src/integration.model.ts @@ -146,7 +146,9 @@ export interface IIntegrationTenantCreateInput extends IBasePerTenantAndOrganiza settings?: IIntegrationSetting[]; } -export interface IIntegrationTenantUpdateInput extends Pick { } +export interface IIntegrationTenantUpdateInput extends Pick { + id?: IIntegrationTenant['id']; +} export enum IntegrationEnum { IMPORT_EXPORT = 'Import_Export', From 011e0a239347b70899adb49eb7b3d1108381ae9d Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sat, 23 Sep 2023 14:56:52 +0530 Subject: [PATCH 061/104] feat: #6734 probot plugin improvement --- packages/core/src/app.module.ts | 1 + .../plugins/integration-github/package.json | 5 +- .../src/hook-metadata.accessor.ts | 9 +- .../integration-github/src/hook.controller.ts | 9 ++ .../integration-github/src/hook.decorator.ts | 3 +- .../plugins/integration-github/src/index.ts | 2 +- .../{github.service.ts => octokit.service.ts} | 26 ++-- .../src/probot.discovery.ts | 130 +++++++++++++----- .../integration-github/src/probot.helpers.ts | 13 +- .../integration-github/src/probot.module.ts | 12 +- .../integration-github/src/probot.types.ts | 21 ++- 11 files changed, 164 insertions(+), 67 deletions(-) rename packages/plugins/integration-github/src/{github.service.ts => octokit.service.ts} (83%) diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index 9fffab239a5..c1fe03f8b7a 100644 --- a/packages/core/src/app.module.ts +++ b/packages/core/src/app.module.ts @@ -255,6 +255,7 @@ if (environment.sentry && environment.sentry.dsn) { ...(github && environment.github.APP_ID ? [ ProbotModule.forRoot({ + isGlobal: true, path: 'integration/github/webhook', // Webhook URL in GitHub will be: https://api.gauzy.co/api/integration/github/webhook config: { /** Client Configuration */ diff --git a/packages/plugins/integration-github/package.json b/packages/plugins/integration-github/package.json index 2e2d6aea63b..09964c588b1 100644 --- a/packages/plugins/integration-github/package.json +++ b/packages/plugins/integration-github/package.json @@ -28,12 +28,15 @@ }, "keywords": [], "dependencies": { + "@nestjs/common": "^9.2.1", + "@nestjs/core": "^9.2.1", "@octokit/rest": "^18.0.0", "octokit": "2.1.0", "pino-std-serializers": "^6.2.2", "probot": "^12.3.1", "smee-client": "^1.2.3", - "underscore": "^1.13.3" + "underscore": "^1.13.3", + "express": "^4.17.2" }, "devDependencies": { "@types/node": "^17.0.33", diff --git a/packages/plugins/integration-github/src/hook-metadata.accessor.ts b/packages/plugins/integration-github/src/hook-metadata.accessor.ts index c3935f5e97d..970e8538624 100644 --- a/packages/plugins/integration-github/src/hook-metadata.accessor.ts +++ b/packages/plugins/integration-github/src/hook-metadata.accessor.ts @@ -4,9 +4,16 @@ import { EmitterWebhookEventName } from '@octokit/webhooks/dist-types/types'; @Injectable() export class HookMetadataAccessor { - constructor(private readonly reflector: Reflector) {} + constructor(private readonly reflector: Reflector) { } + /** + * Get the webhook events associated with a target. + * @param target A function or constructor representing the target class or controller. + * @returns An array of EmitterWebhookEventName that represent the webhook events. + */ getWebhookEvents(target: () => any): EmitterWebhookEventName[] { + // Retrieve the metadata for HOOK_EVENTS, if available, from the target. + // HOOK_EVENTS metadata should contain eventOrEvents property. return this.reflector.get('HOOK_EVENTS', target)?.eventOrEvents; } } diff --git a/packages/plugins/integration-github/src/hook.controller.ts b/packages/plugins/integration-github/src/hook.controller.ts index 76498746789..9654fcd4ba7 100644 --- a/packages/plugins/integration-github/src/hook.controller.ts +++ b/packages/plugins/integration-github/src/hook.controller.ts @@ -3,14 +3,23 @@ import { Public } from '@gauzy/common'; import { Request } from 'express'; import { ProbotDiscovery } from './probot.discovery'; +/** + * Factory function to create a NestJS controller class for handling webhook hooks. + * @param path The path at which the controller should listen for webhook requests. + */ export function getControllerClass({ path }): Type { @Public() @Controller() class HookController { constructor(private readonly probotDiscovery: ProbotDiscovery) { } + /** + * Endpoint for receiving webhook requests. + * @param req The Express request object. + */ @Post([path]) async hooks(@Req() req: Request) { + // Forward the request to ProbotDiscovery for processing. return await this.probotDiscovery.receiveHook(req); } } diff --git a/packages/plugins/integration-github/src/hook.decorator.ts b/packages/plugins/integration-github/src/hook.decorator.ts index 674017621f6..b104ae3fe20 100644 --- a/packages/plugins/integration-github/src/hook.decorator.ts +++ b/packages/plugins/integration-github/src/hook.decorator.ts @@ -3,9 +3,10 @@ import { EmitterWebhookEventName } from '@octokit/webhooks/dist-types/types'; /** * Sets up hook trigger on functions. + * @param eventOrEvents The GitHub webhook event(s) to trigger this function. */ export function Hook( - eventOrEvents: EmitterWebhookEventName[] + eventOrEvents: EmitterWebhookEventName | EmitterWebhookEventName[] ): MethodDecorator { return applyDecorators(SetMetadata('HOOK_EVENTS', { eventOrEvents })); } diff --git a/packages/plugins/integration-github/src/index.ts b/packages/plugins/integration-github/src/index.ts index 76787101aa9..65c4483a49e 100644 --- a/packages/plugins/integration-github/src/index.ts +++ b/packages/plugins/integration-github/src/index.ts @@ -4,4 +4,4 @@ export * from './hook.decorator'; export * from './probot.helpers'; export * from './probot.discovery'; -export * from './github.service'; +export * from './octokit.service'; diff --git a/packages/plugins/integration-github/src/github.service.ts b/packages/plugins/integration-github/src/octokit.service.ts similarity index 83% rename from packages/plugins/integration-github/src/github.service.ts rename to packages/plugins/integration-github/src/octokit.service.ts index 23309563b10..d834ddee7c6 100644 --- a/packages/plugins/integration-github/src/github.service.ts +++ b/packages/plugins/integration-github/src/octokit.service.ts @@ -1,23 +1,31 @@ import { Inject, Injectable } from '@nestjs/common'; -import { ModuleProviders, ProbotConfig } from './probot.types'; import { App } from 'octokit'; +import { ModuleProviders, ProbotConfig } from './probot.types'; @Injectable() -export class GitHubService { +export class OctokitService { + /** */ private readonly app: App; constructor( @Inject(ModuleProviders.ProbotConfig) private readonly config: ProbotConfig ) { - // TODO: BUG - // ENV variable is not working here, hence getting error (" secretOrPrivateKey must be an asymmetric key when using RS256") this.app = new App({ appId: this.config.appId, privateKey: this.config.privateKey, }); } + /** + * + * @param installationId + */ + async getGithubMetadata(installationId: number) { + const octokit = await this.app.getInstallationOctokit(installationId); + console.log(octokit); + } + async openIssue( title: string, body: string, @@ -26,20 +34,12 @@ export class GitHubService { installationId: number ) { const octokit = await this.app.getInstallationOctokit(installationId); - octokit .request('POST /repos/{owner}/{repo}/issues', { owner, repo, title, body, - - // TODO: - // pass dynamic values as required - // Add all the fields that we have - // Ex. - // labels: ['bug', 'GauzyAPI'], - headers: { 'X-GitHub-Api-Version': '2022-11-28', }, @@ -51,6 +51,7 @@ export class GitHubService { console.log('error', error); }); } + async editIssue( issueNumber: number, title: string, @@ -60,7 +61,6 @@ export class GitHubService { installationId: number ) { const octokit = await this.app.getInstallationOctokit(installationId); - octokit .request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', { owner, diff --git a/packages/plugins/integration-github/src/probot.discovery.ts b/packages/plugins/integration-github/src/probot.discovery.ts index 83ad94792fb..eb29f1e9e42 100644 --- a/packages/plugins/integration-github/src/probot.discovery.ts +++ b/packages/plugins/integration-github/src/probot.discovery.ts @@ -35,110 +35,172 @@ export class ProbotDiscovery implements OnModuleInit, OnApplicationBootstrap, On this.probot = createProbot(this.config); } + /** + * + */ public async onModuleInit() { - this.explore(); + this.discoverInstanceWrappers(); } + /** + * Implementation for onApplicationBootstrap + * This method is called when the application is fully initialized. + * You can perform setup tasks here. + */ onApplicationBootstrap(): any { + // Check if webhookProxy is configured if (!_.isEmpty(this.config.webhookProxy)) { + // Create and start a SmeeClient if webhookProxy is configured this.smee = createSmee(this.config); this.smee.start(); } + // Mount the webhook event listeners this.mountHooks(); } + /** + * Implementation for onApplicationShutdown + * This method is called when the application is about to shut down. + * You can perform cleanup tasks here. + * @param signal + */ onApplicationShutdown(signal?: string): any { // TODO clear probot event handlers on shutdown } + /** + * Initialize and mount event listeners for Probot hooks. + */ mountHooks() { this.probot - .load( - (app: { - on: ( - arg0: any, - arg1: (context: any) => Promise - ) => any; - }) => { - this.hooks.forEach((hook) => { - app.on( - hook.eventOrEvents, - this.initContext(hook.target) - ); - }); - } - ) + .load((app: { + on: (eventName: any, callback: (context: any) => Promise) => any; + }) => { + // Iterate through registered hooks and add event listeners + this.hooks.forEach((hook) => { + app.on( + hook.eventOrEvents, // The event name or names to listen for + this.initContext(hook.target) // The callback function for the event + ); + }); + }) .then(() => { + // Log a message when hook event listeners are initialized this.logger.log('Hook event listeners initialized'); }) - .catch(this.logger.error); + .catch(this.logger.error); // Handle any errors that occur during initialization } + /** + * Create an asynchronous context wrapper for a function. + * @param fn The original function to be wrapped. + * @returns An asynchronous function that calls the original function. + */ initContext(fn: (context: any) => any) { return async (context: any) => { - await fn(context); + await fn(context); // Call the original function with the provided context. }; } - explore() { + + /** + * Explore and analyze methods of instance wrappers (controllers and providers). + */ + discoverInstanceWrappers() { + // Get all instance wrappers for controllers and providers const instanceWrappers: InstanceWrapper[] = [ ...this.discoveryService.getControllers(), ...this.discoveryService.getProviders(), ]; - instanceWrappers - .filter((wrapper: InstanceWrapper) => wrapper.isDependencyTreeStatic()) - .forEach((wrapper: InstanceWrapper) => { - const { instance } = wrapper; - if (!instance || !Object.getPrototypeOf(instance)) { - return; - } + // Filter instance wrappers with static dependency trees + const staticInstanceWrappers = instanceWrappers.filter( + (wrapper: InstanceWrapper) => wrapper.isDependencyTreeStatic() + ); - const prototype = Object.getPrototypeOf(instance); - const methodNames = this.metadataScanner.getAllMethodNames(prototype); + // Iterate through static instance wrappers and explore methods + staticInstanceWrappers.forEach((wrapper: InstanceWrapper) => { + const { instance } = wrapper; + + // Skip if instance or its prototype is missing + if (!instance || !Object.getPrototypeOf(instance)) { + return; + } - methodNames.forEach((key: string) => this.lookupHooks(instance, key)); + // Get the prototype of the instance + const instancePrototype = Object.getPrototypeOf(instance); + + // Get all method names from the prototype + const methodNames = this.metadataScanner.getAllMethodNames(instancePrototype); + + // Iterate through method names and lookup hooks + methodNames.forEach((methodName: string) => { + this.lookupHooks(instance, methodName); }); + }); } + /** + * Look up and process webhook hooks associated with a method of an instance. + * @param instance The instance to examine. + * @param key The method name to inspect. + * @returns The stored hook information or null if no webhook event definition. + */ lookupHooks(instance: Record any>, key: string) { + // Get the method reference from the instance const methodRef = instance[key]; + // Get webhook event metadata for the method const hookMetadata = this.metadataAccessor.getWebhookEvents(methodRef); + // Wrap the method in try-catch blocks if needed const hookFn = this.wrapFunctionInTryCatchBlocks(methodRef, instance); - // filter functions that do not have a webhook event definition + // If no webhook event definition, skip if (_.isEmpty(hookMetadata)) { return null; } + // Generate a unique key and store the hook information return this.hooks.set(v4(), { target: hookFn, eventOrEvents: hookMetadata, }); } + /** + * Wrap a method reference in try-catch blocks to handle errors and log them. + * @param methodRef The method reference to wrap. + * @param instance The instance to which the method belongs. + * @returns An asynchronous function that handles errors and logs them. + */ private wrapFunctionInTryCatchBlocks( methodRef: () => any, instance: Record ) { + // Return an asynchronous function that wraps the method reference return async (...args: unknown[]) => { try { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore + // Call the method reference with the provided instance and arguments await methodRef.call(instance, ...args); } catch (error) { + // Handle and log any errors using the logger this.logger.error(error); } }; } - receiveHook(request: any) { + /** + * Receive and process a GitHub webhook request. + * @param request The incoming webhook request. + * @returns A promise that resolves when the webhook is processed. + */ + public receiveHook(request: any) { + // Extract relevant information from the request const id = request.headers['x-github-delivery'] as string; const event = request.headers['x-github-event']; const body = request.body; - console.log({ id, event, body }); + // Call the probot's receive method with extracted information return this.probot.receive({ id, name: event, payload: body }); } } diff --git a/packages/plugins/integration-github/src/probot.helpers.ts b/packages/plugins/integration-github/src/probot.helpers.ts index 39820342fba..5cbd4b58a6b 100644 --- a/packages/plugins/integration-github/src/probot.helpers.ts +++ b/packages/plugins/integration-github/src/probot.helpers.ts @@ -53,14 +53,17 @@ export const createSmee = (config: ProbotConfig): SmeeClient => { * @returns An Octokit instance. */ export const createOctokit = (config: OctokitConfig): Octokit => { + /** Parsed Probot Config */ + const probot = parseConfig(config.probot); + /** return an Octokit instance. */ return new Octokit({ authStrategy: createAppAuth, - baseUrl: config.probot.ghUrl, + baseUrl: probot.ghUrl, auth: { - appId: config.probot.appId, - privateKey: config.probot.privateKey, - clientId: config.probot.clientId, - clientSecret: config.probot.clientSecret, + appId: probot.appId, + privateKey: probot.privateKey, + clientId: probot.clientId, + clientSecret: probot.clientSecret, ...config.auth, // Include other auth options if needed }, }); diff --git a/packages/plugins/integration-github/src/probot.module.ts b/packages/plugins/integration-github/src/probot.module.ts index 575482034e2..9669a05b967 100644 --- a/packages/plugins/integration-github/src/probot.module.ts +++ b/packages/plugins/integration-github/src/probot.module.ts @@ -8,10 +8,10 @@ import { import { ProbotDiscovery } from './probot.discovery'; import { getControllerClass } from './hook.controller'; import { HookMetadataAccessor } from './hook-metadata.accessor'; -import { GitHubService } from './github.service'; +import { OctokitService } from './octokit.service'; @Module({ - imports: [DiscoveryModule], + imports: [DiscoveryModule] }) export class ProbotModule { /** @@ -32,9 +32,9 @@ export class ProbotModule { }, HookMetadataAccessor, ProbotDiscovery, - GitHubService, + OctokitService, ], - exports: [GitHubService], + exports: [OctokitService], }; } @@ -57,9 +57,9 @@ export class ProbotModule { }, HookMetadataAccessor, ProbotDiscovery, - GitHubService, + OctokitService, ], - exports: [GitHubService], + exports: [OctokitService], }; } } diff --git a/packages/plugins/integration-github/src/probot.types.ts b/packages/plugins/integration-github/src/probot.types.ts index 7147d5422c6..f1f528d447d 100644 --- a/packages/plugins/integration-github/src/probot.types.ts +++ b/packages/plugins/integration-github/src/probot.types.ts @@ -1,42 +1,53 @@ import { ModuleMetadata } from '@nestjs/common'; +// Define interfaces for Probot and Octokit configuration + export interface ProbotConfig { + // GitHub App configuration options appId: string; privateKey: string; - webhookSecret?: string; webhookPath?: string; - ghUrl?: string; - clientId: string; clientSecret: string; - webhookProxy?: string; } export interface OctokitConfig { - auth: Record; + // Octokit library configuration options + auth?: Record; probot: ProbotConfig; } +// Define options for the Probot module + export interface ProbotModuleOptions { + // Specifies if the Probot module should be global isGlobal?: boolean; + // The path at which the module is mounted path: string; + // Probot configuration options config: ProbotConfig; } export interface ProbotModuleAsyncOptions extends Pick { + // Specifies if the Probot module should be global isGlobal?: boolean; + // The path at which the module is mounted path: string; + // Factory function to asynchronously provide Probot configuration useFactory: (...args: any[]) => Promise | ProbotConfig; + // Optional list of dependencies to inject into the factory function inject?: any[]; } +// Define enums for metadata and module providers export enum ProbotMetadata { name = 'probot/metadata/hook', } export enum ModuleProviders { + // Provider key for Probot configuration ProbotConfig = 'probot/provider/config', } From f5671e7b31d51250f14ec309ff9766fc4935a38b Mon Sep 17 00:00:00 2001 From: Ruslan K Date: Sat, 23 Sep 2023 12:41:18 +0200 Subject: [PATCH 062/104] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 32d9b0c845a..7a6a4c4ca82 100644 --- a/README.md +++ b/README.md @@ -305,6 +305,10 @@ You can also view a full list of our [contributors tracked by Github](https://gi +## ⭐ Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=ever-co/ever-gauzy&type=Date)](https://star-history.com/#ever-co/ever-gauzy&Date) + ## ❤️ Powered By

From d2a90c9995e238be656235318e556bfac62a1d3c Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sat, 23 Sep 2023 17:52:20 +0530 Subject: [PATCH 063/104] fix: #6734 environment private key configuration issue --- packages/common/src/interfaces/IGithubConfig.ts | 3 +++ packages/config/src/environments/environment.prod.ts | 6 ++++-- packages/config/src/environments/environment.ts | 6 ++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/common/src/interfaces/IGithubConfig.ts b/packages/common/src/interfaces/IGithubConfig.ts index 6a98ee9d1c3..d698be86075 100644 --- a/packages/common/src/interfaces/IGithubConfig.ts +++ b/packages/common/src/interfaces/IGithubConfig.ts @@ -17,4 +17,7 @@ export interface IGithubIntegrationConfig { /** */ readonly WEBHOOK_URL: string; readonly WEBHOOK_SECRET: string; + + /** */ + readonly API_VERSION: string; } diff --git a/packages/config/src/environments/environment.prod.ts b/packages/config/src/environments/environment.prod.ts index a60b734e359..6d64377f148 100644 --- a/packages/config/src/environments/environment.prod.ts +++ b/packages/config/src/environments/environment.prod.ts @@ -120,13 +120,15 @@ export const environment: IEnvironment = { /** Github App Install Configuration */ APP_ID: process.env.GITHUB_APP_ID, APP_NAME: process.env.GITHUB_APP_NAME, - APP_PRIVATE_KEY: process.env.GITHUB_APP_PRIVATE_KEY, + APP_PRIVATE_KEY: process.env.GITHUB_APP_PRIVATE_KEY.replace(/\\n/g, '\n'), /** Github App Post Install Configuration */ POST_INSTALL_URL: process.env.GITHUB_POST_INSTALL_URL, WEBHOOK_SECRET: process.env.GITHUB_WEBHOOK_SECRET, - WEBHOOK_URL: process.env.GITHUB_WEBHOOK_URL + WEBHOOK_URL: process.env.GITHUB_WEBHOOK_URL, + + API_VERSION: process.env.GITHUB_API_VERSION }, microsoftConfig: { diff --git a/packages/config/src/environments/environment.ts b/packages/config/src/environments/environment.ts index 0a7b195faa2..2c56f9f711c 100644 --- a/packages/config/src/environments/environment.ts +++ b/packages/config/src/environments/environment.ts @@ -124,13 +124,15 @@ export const environment: IEnvironment = { /** Github App Install Configuration */ APP_ID: process.env.GITHUB_APP_ID, APP_NAME: process.env.GITHUB_APP_NAME, - APP_PRIVATE_KEY: process.env.GITHUB_APP_PRIVATE_KEY, + APP_PRIVATE_KEY: process.env.GITHUB_APP_PRIVATE_KEY.replace(/\\n/g, '\n'), /** Github App Post Install Configuration */ POST_INSTALL_URL: process.env.GITHUB_POST_INSTALL_URL, WEBHOOK_SECRET: process.env.GITHUB_WEBHOOK_SECRET, - WEBHOOK_URL: process.env.GITHUB_WEBHOOK_URL + WEBHOOK_URL: process.env.GITHUB_WEBHOOK_URL, + + API_VERSION: process.env.GITHUB_API_VERSION }, microsoftConfig: { From ca1fde5199b6861fe66be551b535e6d59f8fa20e Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:04:49 +0530 Subject: [PATCH 064/104] fix: #6734 github installation metadata API --- .../commands/handlers/index.ts | 4 +- ...gration-tenant-update-or-create.handler.ts | 20 ++ .../src/integration-tenant/commands/index.ts | 1 + ...gration-tenant-update-or-create.command.ts | 13 + .../integration-tenant.service.ts | 4 +- .../github/github-authorization.controller.ts | 46 ++++ .../github/github-install.controller.ts | 40 --- .../github/github-integration.controller.ts | 38 +++ .../integration/github/github.controller.ts | 34 ++- .../github/github.events.controller.ts | 10 +- .../github/github.events.service.ts | 16 ++ .../src/integration/github/github.module.ts | 31 ++- .../src/integration/github/github.service.ts | 240 ++++++++---------- .../integration-github/src/octokit.service.ts | 33 ++- 14 files changed, 327 insertions(+), 203 deletions(-) create mode 100644 packages/core/src/integration-tenant/commands/handlers/integration-tenant-update-or-create.handler.ts create mode 100644 packages/core/src/integration-tenant/commands/integration-tenant-update-or-create.command.ts create mode 100644 packages/core/src/integration/github/github-authorization.controller.ts delete mode 100644 packages/core/src/integration/github/github-install.controller.ts create mode 100644 packages/core/src/integration/github/github-integration.controller.ts create mode 100644 packages/core/src/integration/github/github.events.service.ts diff --git a/packages/core/src/integration-tenant/commands/handlers/index.ts b/packages/core/src/integration-tenant/commands/handlers/index.ts index ceb131ba8e0..76510b8388b 100644 --- a/packages/core/src/integration-tenant/commands/handlers/index.ts +++ b/packages/core/src/integration-tenant/commands/handlers/index.ts @@ -1,3 +1,4 @@ +import { IntegrationTenantUpdateOrCreateHandler } from './integration-tenant-update-or-create.handler'; import { IntegrationTenantCreateHandler } from './integration-tenant.create.handler'; import { IntegrationTenantGetHandler } from './integration-tenant.get.handler'; import { IntegrationTenantUpdateHandler } from './integration-tenant.update.handler'; @@ -5,5 +6,6 @@ import { IntegrationTenantUpdateHandler } from './integration-tenant.update.hand export const CommandHandlers = [ IntegrationTenantCreateHandler, IntegrationTenantGetHandler, - IntegrationTenantUpdateHandler + IntegrationTenantUpdateHandler, + IntegrationTenantUpdateOrCreateHandler ]; diff --git a/packages/core/src/integration-tenant/commands/handlers/integration-tenant-update-or-create.handler.ts b/packages/core/src/integration-tenant/commands/handlers/integration-tenant-update-or-create.handler.ts new file mode 100644 index 00000000000..b5deaf72fd1 --- /dev/null +++ b/packages/core/src/integration-tenant/commands/handlers/integration-tenant-update-or-create.handler.ts @@ -0,0 +1,20 @@ +import { forwardRef, Inject } from '@nestjs/common'; +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { IntegrationTenantService } from 'integration-tenant/integration-tenant.service'; +import { IntegrationTenantUpdateOrCreateCommand } from '../integration-tenant-update-or-create.command'; + +@CommandHandler(IntegrationTenantUpdateOrCreateCommand) +export class IntegrationTenantUpdateOrCreateHandler implements ICommandHandler { + + constructor( + @Inject(forwardRef(() => IntegrationTenantService)) + private readonly _integrationTenantService: IntegrationTenantService + ) { } + + public async execute( + event: IntegrationTenantUpdateOrCreateCommand + ) { + const { options, input } = event; + console.log({ options, input }); + } +} diff --git a/packages/core/src/integration-tenant/commands/index.ts b/packages/core/src/integration-tenant/commands/index.ts index e3c04db500e..83e6a0643cf 100644 --- a/packages/core/src/integration-tenant/commands/index.ts +++ b/packages/core/src/integration-tenant/commands/index.ts @@ -1,3 +1,4 @@ export * from './integration-tenant.create.command'; export * from './integration-tenant.get.command'; export * from './integration-tenant.update.command'; +export * from './integration-tenant-update-or-create.command'; diff --git a/packages/core/src/integration-tenant/commands/integration-tenant-update-or-create.command.ts b/packages/core/src/integration-tenant/commands/integration-tenant-update-or-create.command.ts new file mode 100644 index 00000000000..33fdae384d6 --- /dev/null +++ b/packages/core/src/integration-tenant/commands/integration-tenant-update-or-create.command.ts @@ -0,0 +1,13 @@ +import { ICommand } from '@nestjs/cqrs'; +import { FindOptionsWhere } from 'typeorm'; +import { IIntegrationTenant, } from '@gauzy/contracts'; +import { IntegrationTenant } from './../integration-tenant.entity'; + +export class IntegrationTenantUpdateOrCreateCommand implements ICommand { + static readonly type = '[Update Or Create] Integration Tenant'; + + constructor( + public readonly options: FindOptionsWhere, + public readonly input: IIntegrationTenant + ) { } +} diff --git a/packages/core/src/integration-tenant/integration-tenant.service.ts b/packages/core/src/integration-tenant/integration-tenant.service.ts index de0df7c6c2e..197ccb0e806 100644 --- a/packages/core/src/integration-tenant/integration-tenant.service.ts +++ b/packages/core/src/integration-tenant/integration-tenant.service.ts @@ -9,8 +9,8 @@ import { IIntegrationTenantFindInput, IntegrationEnum } from '@gauzy/contracts'; -import { RequestContext } from './../core/context'; -import { TenantAwareCrudService } from './../core/crud'; +import { RequestContext } from 'core/context'; +import { TenantAwareCrudService } from 'core/crud'; import { IntegrationTenant } from './integration-tenant.entity'; @Injectable() diff --git a/packages/core/src/integration/github/github-authorization.controller.ts b/packages/core/src/integration/github/github-authorization.controller.ts new file mode 100644 index 00000000000..fb38d92b429 --- /dev/null +++ b/packages/core/src/integration/github/github-authorization.controller.ts @@ -0,0 +1,46 @@ +import { Controller, Get, HttpException, HttpStatus, Query, Res } from '@nestjs/common'; +import { Response } from 'express'; +import { ConfigService } from '@gauzy/config'; +import { IGithubAppInstallInput } from '@gauzy/contracts'; +import { IGithubConfig, Public } from '@gauzy/common'; + +@Public() +@Controller() +export class GitHubAuthorizationController { + constructor( + private readonly _config: ConfigService + ) { } + + /** + * + * @param query + * @param response + */ + @Get('callback') + async githubIntegrationPostInstallCallback( + @Query() query: IGithubAppInstallInput, + @Res() response: Response + ) { + try { + // Validate the input data (You can use class-validator for validation) + if (!query || !query.installation_id || !query.setup_action || !query.state) { + throw new HttpException('Invalid installation query data', HttpStatus.BAD_REQUEST); + } + + /** Github Config Options */ + const github = this._config.get('github') as IGithubConfig; + + /** Construct the redirect URL with query parameters */ + const urlParams = new URLSearchParams(); + urlParams.append('installation_id', query.installation_id); + urlParams.append('setup_action', query.setup_action); + urlParams.append('state', query.state); + + /** Redirect to the URL */ + return response.redirect(`${github.POST_INSTALL_URL}?${urlParams.toString()}`); + } catch (error) { + // Handle errors and return an appropriate error response + throw new HttpException(`Failed to add GitHub installation: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/packages/core/src/integration/github/github-install.controller.ts b/packages/core/src/integration/github/github-install.controller.ts deleted file mode 100644 index 3f47c6fbcba..00000000000 --- a/packages/core/src/integration/github/github-install.controller.ts +++ /dev/null @@ -1,40 +0,0 @@ - -import { Controller, Get, Query, Res } from '@nestjs/common'; -import { Response } from 'express'; -import { ConfigService } from '@gauzy/config'; -import { IGithubAppInstallInput } from '@gauzy/contracts'; -import { IGithubConfig, Public } from '@gauzy/common'; - -@Public() -@Controller() -export class GitHubPostInstallController { - constructor( - private readonly _config: ConfigService - ) { } - - /** - * - * @param query - * @param response - */ - @Get('app-install') - async appInstallCallback( - @Query() query: IGithubAppInstallInput, - @Res() response: Response - ) { - /** Github Config Options */ - const github = this._config.get('github') as IGithubConfig; - try { - /** */ - const urlParams = new URLSearchParams(); - urlParams.append('installation_id', query.installation_id); - urlParams.append('setup_action', query.setup_action); - urlParams.append('state', query.state); - - /** Redirected to the UI */ - return response.redirect(`${github.POST_INSTALL_URL}?${urlParams.toString()}`); - } catch (error) { - return response.redirect(`${github.POST_INSTALL_URL}`); - } - } -} diff --git a/packages/core/src/integration/github/github-integration.controller.ts b/packages/core/src/integration/github/github-integration.controller.ts new file mode 100644 index 00000000000..7e55dcbd9bd --- /dev/null +++ b/packages/core/src/integration/github/github-integration.controller.ts @@ -0,0 +1,38 @@ +import { Controller, Get, HttpException, HttpStatus, Logger, Query, UseGuards } from '@nestjs/common'; +import { OctokitResponse, OctokitService } from '@gauzy/integration-github'; +import { IGithubAppInstallInput, PermissionsEnum } from '@gauzy/contracts'; +import { PermissionGuard, TenantPermissionGuard } from 'shared/guards'; +import { Permissions } from 'shared/decorators'; + +@UseGuards(TenantPermissionGuard, PermissionGuard) +@Permissions(PermissionsEnum.INTEGRATION_VIEW) +@Controller('installation') +export class GitHubIntegrationController { + private readonly logger = new Logger('GitHubIntegrationController'); + + constructor( + private readonly _octokitService: OctokitService + ) { } + + /** + * + * @param query + * @param response + */ + @Get('metadata') + async getInstallationMetadata( + @Query() query: IGithubAppInstallInput, + ): Promise> { + try { + const installationId = parseInt(query.installation_id); + const metadata = await this._octokitService.getInstallationMetadata(installationId); + console.log(metadata, 'Github Metadata'); + + return metadata; + } catch (error) { + // Handle errors and return an appropriate error respons + this.logger.error('Error while retrieve github installation metadata', error.message); + throw new HttpException(`Error while retrieve github installation metadata: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/packages/core/src/integration/github/github.controller.ts b/packages/core/src/integration/github/github.controller.ts index 354de23c09c..7b69377ac4b 100644 --- a/packages/core/src/integration/github/github.controller.ts +++ b/packages/core/src/integration/github/github.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Post, Body, UseGuards } from '@nestjs/common'; +import { Controller, Post, Body, UseGuards, HttpException, HttpStatus, HttpCode } from '@nestjs/common'; import { IGithubAppInstallInput, PermissionsEnum } from '@gauzy/contracts'; import { PermissionGuard, TenantPermissionGuard } from 'shared/guards'; import { Permissions } from 'shared/decorators'; @@ -17,9 +17,22 @@ export class GitHubController { * @param body * @returns */ - @Post('app-install') - async addInstallApp(@Body() input: IGithubAppInstallInput) { - return await this._githubService.addInstallationApp(input); + @Post('install') + @HttpCode(HttpStatus.CREATED) + // ToDo - Create Class Validation DTO to validate request + async addGithubAppInstallation(@Body() input: IGithubAppInstallInput) { + try { + // Validate the input data (You can use class-validator for validation) + if (!input || !input.installation_id || !input.setup_action) { + throw new HttpException('Invalid input data', HttpStatus.BAD_REQUEST); + } + + // Add the GitHub installation using the service + return await this._githubService.addGithubAppInstallation(input); + } catch (error) { + // Handle errors and return an appropriate error response + throw new HttpException(`Failed to add GitHub integration: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); + } } /** @@ -28,7 +41,18 @@ export class GitHubController { * @returns */ @Post('oauth') + @HttpCode(HttpStatus.CREATED) + // ToDo - Create Class Validation DTO to validate request async oAuthEndpointAuthorization(@Body() input: IGithubAppInstallInput) { - return await this._githubService.oAuthEndpointAuthorization(input); + try { + // Validate the input data (You can use class-validator for validation) + if (!input || !input.code) { + throw new HttpException('Invalid input data', HttpStatus.BAD_REQUEST); + } + return await this._githubService.oAuthEndpointAuthorization(input); + } catch (error) { + // Handle errors and return an appropriate error response + throw new HttpException(`Failed to add GitHub integration: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); + } } } diff --git a/packages/core/src/integration/github/github.events.controller.ts b/packages/core/src/integration/github/github.events.controller.ts index ffd76793ec2..19af413c1b2 100644 --- a/packages/core/src/integration/github/github.events.controller.ts +++ b/packages/core/src/integration/github/github.events.controller.ts @@ -2,13 +2,13 @@ import { Controller } from '@nestjs/common'; import { Context } from 'probot'; import { Public } from '@gauzy/common'; import { Hook } from '@gauzy/integration-github'; -import { GithubService } from './github.service'; +import { GithubHooksService } from './github.events.service'; @Public() @Controller('webhook') -export class GitHubEventsController { +export class GitHubHooksController { constructor( - private readonly _githubService: GithubService + private readonly _githubHooksService: GithubHooksService ) { } /** @@ -17,7 +17,7 @@ export class GitHubEventsController { */ @Hook(['issues.opened']) async issuesOpened(context: Context) { - await this._githubService.issuesOpened(context); + await this._githubHooksService.issuesOpened(context); } /** @@ -26,6 +26,6 @@ export class GitHubEventsController { */ @Hook(['issues.edited']) async issuesEdited(context: Context) { - await this._githubService.issuesEdited(context); + await this._githubHooksService.issuesEdited(context); } } diff --git a/packages/core/src/integration/github/github.events.service.ts b/packages/core/src/integration/github/github.events.service.ts new file mode 100644 index 00000000000..88c940ef696 --- /dev/null +++ b/packages/core/src/integration/github/github.events.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@nestjs/common'; +import { Context } from 'probot'; + +@Injectable() +export class GithubHooksService { + + constructor() { } + + async issuesOpened(context: Context) { + console.log('Issue Created: ', context.payload); + } + + async issuesEdited(context: Context) { + console.log('Issue Edited', context.payload); + } +} diff --git a/packages/core/src/integration/github/github.module.ts b/packages/core/src/integration/github/github.module.ts index 9a81c7b89e5..4e86cd71225 100644 --- a/packages/core/src/integration/github/github.module.ts +++ b/packages/core/src/integration/github/github.module.ts @@ -1,30 +1,37 @@ import { Module, forwardRef } from '@nestjs/common'; import { HttpModule } from '@nestjs/axios'; import { CqrsModule } from '@nestjs/cqrs'; -import { ProbotModule } from '@gauzy/integration-github'; -import { TenantModule } from './../../tenant/tenant.module'; -import { UserModule } from './../../user/user.module'; -import { IntegrationModule } from './../../integration/integration.module'; -import { GithubService } from './github.service'; +import { TenantModule } from 'tenant/tenant.module'; +import { UserModule } from 'user/user.module'; +import { IntegrationModule } from 'integration/integration.module'; +import { GitHubAuthorizationController } from './github-authorization.controller'; import { GitHubController } from './github.controller'; -import { GitHubEventsController } from './github.events.controller'; -import { GitHubPostInstallController } from './github-install.controller'; +import { GitHubIntegrationController } from './github-integration.controller'; +import { GitHubHooksController } from './github.events.controller'; +import { GithubService } from './github.service'; +import { GithubHooksService } from './github.events.service'; @Module({ imports: [ HttpModule, TenantModule, UserModule, - ProbotModule, CqrsModule, forwardRef(() => IntegrationModule) ], controllers: [ + GitHubAuthorizationController, GitHubController, - GitHubPostInstallController, - GitHubEventsController + GitHubIntegrationController, + GitHubHooksController + ], + providers: [ + GithubService, + GithubHooksService + ], + exports: [ + GithubService, + GithubHooksService ], - providers: [GithubService], - exports: [GithubService], }) export class GithubModule { } diff --git a/packages/core/src/integration/github/github.service.ts b/packages/core/src/integration/github/github.service.ts index a9ea07bed87..ac63a85c4af 100644 --- a/packages/core/src/integration/github/github.service.ts +++ b/packages/core/src/integration/github/github.service.ts @@ -1,116 +1,33 @@ -import { IGithubAppInstallInput, IIntegrationTenant, IntegrationEnum } from '@gauzy/contracts'; -import { HttpService } from '@nestjs/axios'; -import { BadRequestException, Injectable, UnauthorizedException } from '@nestjs/common'; +import { BadRequestException, HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'; import { CommandBus } from '@nestjs/cqrs'; -import { Context } from 'probot'; +import { HttpService } from '@nestjs/axios'; import { catchError, lastValueFrom, switchMap } from 'rxjs'; import { filter } from 'rxjs/operators'; +import { environment } from '@gauzy/config'; +import { IGithubAppInstallInput, IIntegrationTenant, IntegrationEnum } from '@gauzy/contracts'; import { IntegrationTenantCreateCommand } from 'integration-tenant/commands'; import { IntegrationService } from 'integration/integration.service'; -import { RequestContext } from './../../core/context'; +import { RequestContext } from '../../core/context'; import { GITHUB_ACCESS_TOKEN_URL } from './github.config'; +const { github } = environment; + @Injectable() export class GithubService { + private readonly logger = new Logger('GithubService'); + constructor( private readonly _httpService: HttpService, private readonly _commandBus: CommandBus, private readonly _integrationService: IntegrationService ) { } - /** - * ----- From GitHub to APIs ----- - */ - - async issuesOpened(context: Context) { - console.log('Issue Created: ', context.payload); - // TODO - // Handle event processing - // Find all the Projects connected to current repo and create new Task - } - async issuesEdited(context: Context) { - console.log('Issue Edited', context.payload); - // TODO - // Handle event processing - // Find all the Projects connected to current repo and edit task - // To edit task we need to save issue_number of GitHub in task table - } - - /** - * ----- From APIs to GitHub ----- - */ - async openIssue( - title: string, - body: string, - owner: string, - repo: string, - installationId: number - ) { - // await this.gitHubIntegrationService.openIssue( - // title, - // body, - // owner, - // repo, - // installationId - // ); - } - async editIssue( - issueNumber: number, - title: string, - body: string, - owner: string, - repo: string, - installationId: number - ) { - // await this.gitHubIntegrationService.editIssue( - // issueNumber, - // title, - // body, - // repo, - // owner, - // installationId - // ); + async openIssue({ title, body, owner, repo, installationI }) { + console.log({ title, body, owner, repo, installationI }); } - /** - * - * @param input - * @returns - */ - async addInstallationApp(input: IGithubAppInstallInput): Promise { - const tenantId = RequestContext.currentTenantId() || input.tenantId; - const { installation_id, setup_action, organizationId } = input; - - const integration = await this._integrationService.findOneByOptions({ - where: { - name: IntegrationEnum.GITHUB - } - }); - - /** */ - return await this._commandBus.execute( - new IntegrationTenantCreateCommand({ - name: IntegrationEnum.GITHUB, - integration, - tenantId, - organizationId, - entitySettings: [], - settings: [ - { - settingsName: 'installation_id', - settingsValue: installation_id, - tenantId, - organizationId - }, - { - settingsName: 'setup_action', - settingsValue: setup_action, - tenantId, - organizationId - }, - ] - }) - ); + async editIssue({ issueNumber, title, body, owner, repo, installationI }) { + console.log({ issueNumber, title, body, owner, repo, installationI }); } /** @@ -118,32 +35,24 @@ export class GithubService { * @param input * @returns */ - async oAuthEndpointAuthorization(input: IGithubAppInstallInput): Promise { - const tenantId = RequestContext.currentTenantId() || input.tenantId; - const { code, organizationId } = input; - - if (!code) { - throw new UnauthorizedException(); - } - - const integration = await this._integrationService.findOneByOptions({ - where: { - name: IntegrationEnum.GITHUB + async addGithubAppInstallation(input: IGithubAppInstallInput) { + try { + // Validate the input data (You can use class-validator for validation) + if (!input || !input.installation_id || !input.setup_action) { + throw new HttpException('Invalid input data', HttpStatus.BAD_REQUEST); } - }); - const urlParams = new URLSearchParams(); - urlParams.append('client_id', process.env.GITHUB_CLIENT_ID); - urlParams.append('client_secret', process.env.GITHUB_CLIENT_SECRET); - urlParams.append('code', code); + const tenantId = RequestContext.currentTenantId() || input.tenantId; + const { installation_id, setup_action, organizationId } = input; - const tokens$ = this._httpService.post(GITHUB_ACCESS_TOKEN_URL, urlParams, { - headers: { - 'accept': 'application/json' - } - }).pipe( - filter(({ data }) => !!data.error), - switchMap(({ data }) => this._commandBus.execute( + /** Find the GitHub integration */ + const integration = await this._integrationService.findOneByOptions({ + where: { + provider: IntegrationEnum.GITHUB + } + }); + /** Execute the command to create the integration tenant settings */ + return await this._commandBus.execute( new IntegrationTenantCreateCommand({ name: IntegrationEnum.GITHUB, integration, @@ -152,24 +61,89 @@ export class GithubService { entitySettings: [], settings: [ { - settingsName: 'token_type', - settingsValue: data.token_type + settingsName: 'installation_id', + settingsValue: installation_id, + tenantId, + organizationId }, { - settingsName: 'access_token', - settingsValue: data.access_token + settingsName: 'setup_action', + settingsValue: setup_action, + tenantId, + organizationId }, - { - settingsName: 'scope', - settingsValue: data.scope - } ] }) - )), - catchError((err) => { - throw new BadRequestException(err); - }) - ); - return await lastValueFrom(tokens$); + ); + } catch (error) { + this.logger.error('Error while creating GitHub integration settings', error?.message); + throw new Error('Failed to add GitHub App Installation'); + } + } + + /** + * + * @param input + * @returns + */ + async oAuthEndpointAuthorization(input: IGithubAppInstallInput): Promise { + try { + // Validate the input data (You can use class-validator for validation) + if (!input || !input.code) { + throw new HttpException('Invalid input data', HttpStatus.BAD_REQUEST); + } + + const tenantId = RequestContext.currentTenantId() || input.tenantId; + const { code, organizationId } = input; + + const integration = await this._integrationService.findOneByOptions({ + where: { + name: IntegrationEnum.GITHUB + } + }); + + const urlParams = new URLSearchParams(); + urlParams.append('client_id', github.CLIENT_ID); + urlParams.append('client_secret', github.CLIENT_SECRET); + urlParams.append('code', code); + + const tokens$ = this._httpService.post(GITHUB_ACCESS_TOKEN_URL, urlParams, { + headers: { + 'accept': 'application/json' + } + }).pipe( + filter(({ data }) => !!data.error), + switchMap(({ data }) => this._commandBus.execute( + new IntegrationTenantCreateCommand({ + name: IntegrationEnum.GITHUB, + integration, + tenantId, + organizationId, + entitySettings: [], + settings: [ + { + settingsName: 'token_type', + settingsValue: data.token_type + }, + { + settingsName: 'access_token', + settingsValue: data.access_token + }, + { + settingsName: 'scope', + settingsValue: data.scope + } + ] + }) + )), + catchError((error) => { + throw new BadRequestException(error); + }) + ); + return await lastValueFrom(tokens$); + } catch (error) { + this.logger.error('Error while creating GitHub integration settings', error?.message); + throw new Error('Failed to add GitHub App Installation'); + } } } diff --git a/packages/plugins/integration-github/src/octokit.service.ts b/packages/plugins/integration-github/src/octokit.service.ts index d834ddee7c6..7d90130f4cc 100644 --- a/packages/plugins/integration-github/src/octokit.service.ts +++ b/packages/plugins/integration-github/src/octokit.service.ts @@ -1,6 +1,16 @@ import { Inject, Injectable } from '@nestjs/common'; import { App } from 'octokit'; import { ModuleProviders, ProbotConfig } from './probot.types'; +import { ResponseHeaders as OctokitResponseHeaders } from "@octokit/types"; + +const GITHUB_API_VERSION = process.env.GITHUB_API_VERSION; + +export interface OctokitResponse { + data: T; // The response data received from the GitHub API. + status: number; // The HTTP status code of the response (e.g., 200, 404, etc.). + headers: OctokitResponseHeaders; // The headers included in the response. + [key: string]: any; // Additional properties may be present depending on the specific response. +} @Injectable() export class OctokitService { @@ -18,12 +28,25 @@ export class OctokitService { } /** - * - * @param installationId + * Get GitHub metadata for a specific installation. + * @param installation_id The installation ID for the GitHub App. + * @returns GitHub metadata. */ - async getGithubMetadata(installationId: number) { - const octokit = await this.app.getInstallationOctokit(installationId); - console.log(octokit); + public async getInstallationMetadata(installation_id: number): Promise> { + try { + // Get an Octokit instance for the installation + const octokit = await this.app.getInstallationOctokit(installation_id); + + // Send a request to the GitHub API to get metadata + return await octokit.request('GET /app/installations/{installation_id}', { + installation_id, + headers: { + 'X-GitHub-Api-Version': GITHUB_API_VERSION + } + }); + } catch (error) { + throw new Error('Failed to fetch GitHub metadata'); + } } async openIssue( From 93f38eff714ced86cc5d0a42c7e490392e535d95 Mon Sep 17 00:00:00 2001 From: Ruslan K Date: Sat, 23 Sep 2023 14:45:03 +0200 Subject: [PATCH 065/104] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7a6a4c4ca82..464518d667d 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ It's built with React / ReactNative (Expo) stack and connects to headless Ever G ## 🌟 What is it -[Ever® Gauzy™][uri_gauzy] - **Open-Source Business Management Platform** for On-Demand and Sharing Economies. +[Ever® Gauzy™][uri_gauzy] - **Open Business Management Platform** for Collaborative, On-Demand and Sharing Economies. - **Enterprise Resource Planning** (ERP) software. - **Customer Relationship Management** (CRM) software. @@ -25,7 +25,7 @@ It's built with React / ReactNative (Expo) stack and connects to headless Ever G ![overview](https://docs.gauzy.co/docs/assets/overview.png) -Ever® Gauzy™ Platform is a part of our larger Open Platform for **On-Demand and Sharing Economies** - [Ever® Platform™](https://ever.co). +Ever® Gauzy™ Platform is a part of our larger Open Platform for **Collaborative, On-Demand and Sharing Economies** - [Ever® Platform™](https://ever.co). ## ✨ Features From 032d24276744af5894cf5570135be8abc15bffc0 Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Sat, 23 Sep 2023 15:06:16 +0200 Subject: [PATCH 066/104] feat: build task number if not generated --- .../task-render-cell/task-render-cell.component.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render-cell/task-render-cell.component.ts b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render-cell/task-render-cell.component.ts index 6529d14f88a..c4a9a157ac7 100644 --- a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render-cell/task-render-cell.component.ts +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render-cell/task-render-cell.component.ts @@ -16,7 +16,7 @@ export class TaskRenderCellComponent extends TaskRenderComponent { } public get number(): string { - return `#${this.task.taskNumber}`; + return `#${this.task.taskNumber || this.buildTaskNumber()}`; } public get status(): string { @@ -37,4 +37,12 @@ export class TaskRenderCellComponent extends TaskRenderComponent { untilDestroyed(this) ); } + + private buildTaskNumber() { + if (!this.task.prefix || !this.task.number) return; + return this.task.prefix + .concat('-') + .concat(String(this.task.number)) + .toUpperCase(); + } } From 0747f9c98fe72c25e0ac51e9b5cee9c16d9c25a9 Mon Sep 17 00:00:00 2001 From: Govindram Date: Sat, 23 Sep 2023 19:35:36 +0530 Subject: [PATCH 067/104] Translation Added --- apps/gauzy/src/assets/i18n/bg.json | 1 + apps/gauzy/src/assets/i18n/en.json | 1 + apps/gauzy/src/assets/i18n/es.json | 1 + apps/gauzy/src/assets/i18n/he.json | 1 + apps/gauzy/src/assets/i18n/ru.json | 1 + 5 files changed, 5 insertions(+) diff --git a/apps/gauzy/src/assets/i18n/bg.json b/apps/gauzy/src/assets/i18n/bg.json index 0ae47a9f95b..c1281b25a7a 100644 --- a/apps/gauzy/src/assets/i18n/bg.json +++ b/apps/gauzy/src/assets/i18n/bg.json @@ -522,6 +522,7 @@ "UPLOADER_PLACEHOLDER": "Снимка", "UPLOADER_DOCUMENT_PLACEHOLDER": "URL", "ADD_REMOVE_PROJECTS": "Добавяне или премахване на проекти", + "ADD_REMOVE_TEAMS": "Добавяне или премахване на екипи", "ADD_REMOVE_EMPLOYEES": "Добави или премахни служители", "ADD_REMOVE_CANDIDATE": "Add Candidate", "ADD_REMOVE_EMPLOYEE": "Add Interviewer", diff --git a/apps/gauzy/src/assets/i18n/en.json b/apps/gauzy/src/assets/i18n/en.json index 0e8ba8cdec8..3548def8cb1 100644 --- a/apps/gauzy/src/assets/i18n/en.json +++ b/apps/gauzy/src/assets/i18n/en.json @@ -526,6 +526,7 @@ "UPLOADER_DOCUMENT_PLACEHOLDER": "URL", "ADD_REMOVE_EMPLOYEES": "Add or Remove Employees", "ADD_REMOVE_PROJECTS": "Add or Remove Projects", + "ADD_REMOVE_TEAMS": "Add or Remove Teams", "ADD_REMOVE_CANDIDATE": "Add Candidate", "ADD_REMOVE_EMPLOYEE": "Add Interviewer", "ADD_REMOVE_INTERVIEWER": "Select interviewer", diff --git a/apps/gauzy/src/assets/i18n/es.json b/apps/gauzy/src/assets/i18n/es.json index 7809fe30b8e..392c3a8edb8 100644 --- a/apps/gauzy/src/assets/i18n/es.json +++ b/apps/gauzy/src/assets/i18n/es.json @@ -526,6 +526,7 @@ "UPLOADER_DOCUMENT_PLACEHOLDER": "URL (Uniform Resource Locator)", "ADD_REMOVE_EMPLOYEES": "Agregar o eliminar empleados.", "ADD_REMOVE_PROJECTS": "Agregar o quitar proyecto", + "ADD_REMOVE_TEAMS": "Agregar o eliminar equipos", "ADD_REMOVE_CANDIDATE": "Agregar candidato", "ADD_REMOVE_EMPLOYEE": "Añadir entrevistador", "ADD_REMOVE_INTERVIEWER": "Seleccionar entrevistador", diff --git a/apps/gauzy/src/assets/i18n/he.json b/apps/gauzy/src/assets/i18n/he.json index 6b2147432e6..3ca66742093 100644 --- a/apps/gauzy/src/assets/i18n/he.json +++ b/apps/gauzy/src/assets/i18n/he.json @@ -523,6 +523,7 @@ "UPLOADER_DOCUMENT_PLACEHOLDER": "URL", "ADD_REMOVE_EMPLOYEES": "Add or Remove Employees", "ADD_REMOVE_PROJECTS": "הוסף או הסר פרויקט", + "ADD_REMOVE_TEAMS": "הוסף או הסר צוותים", "ADD_REMOVE_CANDIDATE": "Add Candidate", "ADD_REMOVE_EMPLOYEE": "Add Interviewer", "ADD_REMOVE_INTERVIEWER": "Select interviewer", diff --git a/apps/gauzy/src/assets/i18n/ru.json b/apps/gauzy/src/assets/i18n/ru.json index db282aa352a..dbdf9731122 100644 --- a/apps/gauzy/src/assets/i18n/ru.json +++ b/apps/gauzy/src/assets/i18n/ru.json @@ -523,6 +523,7 @@ "UPLOADER_DOCUMENT_PLACEHOLDER": "URL", "ADD_REMOVE_EMPLOYEES": "Добавление или удаление сотрудников", "ADD_REMOVE_PROJECTS": "Добавить или удалить проект", + "ADD_REMOVE_TEAMS": "Добавить или удалить команды", "ADD_REMOVE_CANDIDATE": "Добавить Кандидата", "ADD_REMOVE_EMPLOYEE": "Добавить интервьюера", "ADD_REMOVE_INTERVIEWER": "Выбрать интервьюера", From 58c7dc5767d887fdcbde714b32bdf29e3631eaee Mon Sep 17 00:00:00 2001 From: Govindram Date: Sat, 23 Sep 2023 19:42:38 +0530 Subject: [PATCH 068/104] Adding Team while project create --- .../team-select/team/team.component.html | 6 ++- .../team-select/team/team.component.ts | 7 ++- .../projects-mutation.component.html | 7 +++ .../projects-mutation.component.ts | 23 +++++++-- .../pages/projects/projects.component.html | 1 + .../app/pages/projects/projects.component.ts | 47 +++++++++++++++++-- .../src/app/pages/projects/projects.module.ts | 4 +- 7 files changed, 81 insertions(+), 14 deletions(-) diff --git a/apps/gauzy/src/app/@shared/team-select/team/team.component.html b/apps/gauzy/src/app/@shared/team-select/team/team.component.html index 89caaec97cc..8a5a686fd0f 100644 --- a/apps/gauzy/src/app/@shared/team-select/team/team.component.html +++ b/apps/gauzy/src/app/@shared/team-select/team/team.component.html @@ -1,7 +1,9 @@ + diff --git a/apps/gauzy/src/app/@shared/team-select/team/team.component.ts b/apps/gauzy/src/app/@shared/team-select/team/team.component.ts index 6023098aa49..c0d25d9c443 100644 --- a/apps/gauzy/src/app/@shared/team-select/team/team.component.ts +++ b/apps/gauzy/src/app/@shared/team-select/team/team.component.ts @@ -4,7 +4,6 @@ import { OnDestroy, Input, forwardRef, - AfterViewInit, Output, EventEmitter } from '@angular/core'; @@ -26,7 +25,6 @@ import { } from '@gauzy/common-angular'; import { ALL_TEAM_SELECTED } from './default-team'; import { - OrganizationTeamsService, Store, ToastrService @@ -48,7 +46,7 @@ import { OrganizationTeamStore } from '../../../@core/services/organization-team ] }) export class TeamSelectorComponent - implements OnInit, OnDestroy, AfterViewInit { + implements OnInit, OnDestroy { teams: IOrganizationTeam[] = []; selectedTeam: IOrganizationTeam; hasAddTeam$: Observable; @@ -61,6 +59,7 @@ export class TeamSelectorComponent @Input() shortened = false; @Input() disabled = false; @Input() multiple = false; + @Input() label = null; private _teamId: string | string[]; get teamId(): string | string[] { @@ -71,7 +70,7 @@ export class TeamSelectorComponent this.onChange(val); this.onTouched(val); } - //----------------------------------------------- + private _employeeId: string; public get employeeId() { return this._employeeId; diff --git a/apps/gauzy/src/app/pages/projects/projects-mutation/projects-mutation.component.html b/apps/gauzy/src/app/pages/projects/projects-mutation/projects-mutation.component.html index 0d517f7db39..d2b62122d42 100644 --- a/apps/gauzy/src/app/pages/projects/projects-mutation/projects-mutation.component.html +++ b/apps/gauzy/src/app/pages/projects/projects-mutation/projects-mutation.component.html @@ -222,6 +222,13 @@ " >

+
+ + +
diff --git a/apps/gauzy/src/app/pages/projects/projects-mutation/projects-mutation.component.ts b/apps/gauzy/src/app/pages/projects/projects-mutation/projects-mutation.component.ts index c6f4122e23b..815978a0044 100644 --- a/apps/gauzy/src/app/pages/projects/projects-mutation/projects-mutation.component.ts +++ b/apps/gauzy/src/app/pages/projects/projects-mutation/projects-mutation.component.ts @@ -4,7 +4,6 @@ import { IEmployee, IOrganization, IOrganizationContact, - IOrganizationProject, ProjectBillingEnum, ITag, ProjectOwnerEnum, @@ -12,7 +11,9 @@ import { ContactType, ICurrency, OrganizationProjectBudgetTypeEnum, - IImageAsset + IImageAsset, + IOrganizationTeam, + IOrganizationProject, } from '@gauzy/contracts'; import { TranslateService } from '@ngx-translate/core'; import { Router } from '@angular/router'; @@ -39,11 +40,14 @@ import { ckEditorConfig } from "../../../@shared/ckeditor.config"; export class ProjectsMutationComponent extends TranslationBaseComponent implements OnInit { + @Input() teams: IOrganizationTeam[] = []; + FormHelpers: typeof FormHelpers = FormHelpers; OrganizationProjectBudgetTypeEnum = OrganizationProjectBudgetTypeEnum; TaskListTypeEnum = TaskListTypeEnum; members: string[] = []; selectedEmployeeIds: string[] = []; + selectedTeamIds: string[] = []; billings: string[] = Object.values(ProjectBillingEnum); owners: ProjectOwnerEnum[] = Object.values(ProjectOwnerEnum); taskViewModeTypes: TaskListTypeEnum[] = Object.values(TaskListTypeEnum); @@ -58,10 +62,11 @@ export class ProjectsMutationComponent extends TranslationBaseComponent */ public form: FormGroup = ProjectsMutationComponent.buildForm(this.fb); static buildForm(fb: FormBuilder): FormGroup { - return fb.group({ + const form = fb.group({ imageUrl: [], imageId: [], tags: [], + teams: [], public: [], billable: [], name: ['', Validators.required], @@ -91,6 +96,8 @@ export class ProjectsMutationComponent extends TranslationBaseComponent CompareDateValidator.validateDate('startDate', 'endDate') ] }); + form.get('teams').setValue([]); + return form; } /* @@ -187,6 +194,7 @@ export class ProjectsMutationComponent extends TranslationBaseComponent this.selectedEmployeeIds = project.members.map( (member: IEmployee) => member.id ); + this.members = this.selectedEmployeeIds; this.form.patchValue({ imageUrl: project.imageUrl || null, @@ -210,6 +218,8 @@ export class ProjectsMutationComponent extends TranslationBaseComponent openSource: project.openSource || null, projectUrl: project.projectUrl || null, openSourceProjectUrl: project.openSourceProjectUrl || null, + teams: this.project.teams.map((team: IOrganizationTeam) => team.id), + }); this.form.updateValueAndValidity(); } @@ -245,6 +255,11 @@ export class ProjectsMutationComponent extends TranslationBaseComponent this.members = members; } + onTeamsSelected(teams: IOrganizationTeam[]) { + this.form.get('teams').setValue(teams); + this.form.get('teams').updateValueAndValidity(); + } + cancel() { this.canceled.emit(); } @@ -283,7 +298,7 @@ export class ProjectsMutationComponent extends TranslationBaseComponent startDate: startDate, endDate: endDate, members: this.members.map((id) => this.employees.find((e) => e.id === id)).filter((e) => !!e), - + teams: this.form.get('teams').value.map((id) => this.teams.find((e) => e.id === id)).filter((e) => !!e), // Description Step description: description, tags: tags || [], diff --git a/apps/gauzy/src/app/pages/projects/projects.component.html b/apps/gauzy/src/app/pages/projects/projects.component.html index fa02817eb92..de44b25f25f 100644 --- a/apps/gauzy/src/app/pages/projects/projects.component.html +++ b/apps/gauzy/src/app/pages/projects/projects.component.html @@ -129,6 +129,7 @@

= this.subject$; + teams$: Subject = new Subject(); + private _refresh$: Subject = new Subject(); projectsTable: Ng2SmartTableComponent; @@ -87,6 +93,7 @@ export class ProjectsComponent extends PaginationFilterBaseComponent implements constructor( private readonly organizationContactService: OrganizationContactService, private readonly organizationProjectsService: OrganizationProjectsService, + private readonly organizationTeamService: OrganizationTeamsService, private readonly toastrService: ToastrService, private readonly store: Store, public readonly translateService: TranslateService, @@ -109,6 +116,14 @@ export class ProjectsComponent extends PaginationFilterBaseComponent implements untilDestroyed(this) ) .subscribe(); + + this.teams$.pipe( + debounceTime(300), + tap(() => this._refresh$.next(true)), + tap(() => this.loadOrganizationTeams()), + untilDestroyed(this) + ).subscribe(); + const storeOrganization$ = this.store.selectedOrganization$; const storeEmployee$ = this.store.selectedEmployee$; combineLatest([storeOrganization$, storeEmployee$]) @@ -121,6 +136,7 @@ export class ProjectsComponent extends PaginationFilterBaseComponent implements }), tap(() => this._refresh$.next(true)), tap(() => this.project$.next(true)), + tap(() => this.teams$.next(true)), untilDestroyed(this) ) .subscribe(); @@ -267,7 +283,7 @@ export class ProjectsComponent extends PaginationFilterBaseComponent implements this.smartTableSource = new ServerDataSource(this.httpClient, { endPoint: `${API_PREFIX}/organization-projects/pagination`, - relations: ['organizationContact', 'organization', 'members', 'members.user', 'tags'], + relations: ['organizationContact', 'organization', 'members', 'members.user', 'tags', 'teams'], join: { alias: 'organization_project', leftJoin: { @@ -323,6 +339,31 @@ export class ProjectsComponent extends PaginationFilterBaseComponent implements } } + /** + * load organization projects + * + * @returns + */ + private async loadOrganizationTeams(): Promise { + if (!this.organization || !this.store.hasAnyPermission( + PermissionsEnum.ALL_ORG_VIEW, + PermissionsEnum.ORG_TEAM_VIEW + )) { + return; + } + + const { tenantId } = this.store.user; + const { id: organizationId } = this.organization; + + this.teams = (await this.organizationTeamService.getAll( + [], + { + organizationId, + tenantId + } + )).items; + } + private async loadGridLayoutData() { if (this._isGridCardLayout) { await this.smartTableSource.getElements(); diff --git a/apps/gauzy/src/app/pages/projects/projects.module.ts b/apps/gauzy/src/app/pages/projects/projects.module.ts index 5834f76326d..b46894e0a20 100644 --- a/apps/gauzy/src/app/pages/projects/projects.module.ts +++ b/apps/gauzy/src/app/pages/projects/projects.module.ts @@ -43,6 +43,7 @@ import { HeaderTitleModule } from '../../@shared/components/header-title/header- import { GauzyButtonActionModule } from '../../@shared/gauzy-button-action/gauzy-button-action.module'; import { PaginationModule } from '../../@shared/pagination/pagination.module'; import { CardGridModule } from '../../@shared/card-grid/card-grid.module'; +import { TeamSelectModule } from '../../@shared/team-select/team-select.module'; @NgModule({ imports: [ @@ -85,9 +86,10 @@ import { CardGridModule } from '../../@shared/card-grid/card-grid.module'; GauzyButtonActionModule, NbTooltipModule, PaginationModule, + TeamSelectModule, CardGridModule ], declarations: [ProjectsComponent, ProjectsMutationComponent], providers: [OrganizationProjectsService, OrganizationContactService] }) -export class ProjectsModule {} +export class ProjectsModule { } From 9daff50855a4f18b694b2c12e6d2d775309b5482 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sat, 23 Sep 2023 21:35:56 +0530 Subject: [PATCH 069/104] fix: #6734 get metadata for github installation app --- .../github/github-integration.controller.ts | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/core/src/integration/github/github-integration.controller.ts b/packages/core/src/integration/github/github-integration.controller.ts index 7e55dcbd9bd..3a4c8fbef2e 100644 --- a/packages/core/src/integration/github/github-integration.controller.ts +++ b/packages/core/src/integration/github/github-integration.controller.ts @@ -1,12 +1,13 @@ -import { Controller, Get, HttpException, HttpStatus, Logger, Query, UseGuards } from '@nestjs/common'; +import { Controller, Get, HttpException, HttpStatus, Logger, Param, Query, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common'; import { OctokitResponse, OctokitService } from '@gauzy/integration-github'; -import { IGithubAppInstallInput, PermissionsEnum } from '@gauzy/contracts'; +import { PermissionsEnum } from '@gauzy/contracts'; import { PermissionGuard, TenantPermissionGuard } from 'shared/guards'; import { Permissions } from 'shared/decorators'; +import { TenantOrganizationBaseDTO } from 'core/dto'; @UseGuards(TenantPermissionGuard, PermissionGuard) @Permissions(PermissionsEnum.INTEGRATION_VIEW) -@Controller('installation') +@Controller() export class GitHubIntegrationController { private readonly logger = new Logger('GitHubIntegrationController'); @@ -19,16 +20,21 @@ export class GitHubIntegrationController { * @param query * @param response */ - @Get('metadata') + + @Get(':installation_id/metadata') + @UsePipes(new ValidationPipe({ transform: true })) async getInstallationMetadata( - @Query() query: IGithubAppInstallInput, + @Param('installation_id') installation_id: number, + @Query() query: TenantOrganizationBaseDTO, ): Promise> { try { - const installationId = parseInt(query.installation_id); - const metadata = await this._octokitService.getInstallationMetadata(installationId); - console.log(metadata, 'Github Metadata'); - - return metadata; + // Validate the input data (You can use class-validator for validation) + if (!query || !query.organizationId) { + throw new HttpException('Invalid installation query parameter', HttpStatus.BAD_REQUEST); + } + // Get installation metadata + const metadata = await this._octokitService.getInstallationMetadata(installation_id); + return metadata.data; } catch (error) { // Handle errors and return an appropriate error respons this.logger.error('Error while retrieve github installation metadata', error.message); From bbdbf8c25864f4ec236edffcc356e9dbb8fc8fdc Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sat, 23 Sep 2023 21:59:35 +0530 Subject: [PATCH 070/104] feat: #6734 get github repositories --- .../github/github-integration.controller.ts | 47 +++++++++++++++---- .../integration-github/src/octokit.service.ts | 40 ++++++++++++++-- 2 files changed, 74 insertions(+), 13 deletions(-) diff --git a/packages/core/src/integration/github/github-integration.controller.ts b/packages/core/src/integration/github/github-integration.controller.ts index 3a4c8fbef2e..62f5eaecb35 100644 --- a/packages/core/src/integration/github/github-integration.controller.ts +++ b/packages/core/src/integration/github/github-integration.controller.ts @@ -7,7 +7,7 @@ import { TenantOrganizationBaseDTO } from 'core/dto'; @UseGuards(TenantPermissionGuard, PermissionGuard) @Permissions(PermissionsEnum.INTEGRATION_VIEW) -@Controller() +@Controller(':installation_id') export class GitHubIntegrationController { private readonly logger = new Logger('GitHubIntegrationController'); @@ -16,24 +16,26 @@ export class GitHubIntegrationController { ) { } /** + * Retrieve installation metadata for a GitHub App installation. * - * @param query - * @param response + * @param installation_id The installation ID for the GitHub App. + * @param organizationId The organization ID to query for metadata. + * @returns {Promise>} A promise that resolves with the installation metadata. + * @throws {HttpException} If the query parameters are invalid or an error occurs during retrieval. */ - - @Get(':installation_id/metadata') + @Get('/metadata') @UsePipes(new ValidationPipe({ transform: true })) - async getInstallationMetadata( + async getGithubInstallationMetadata( @Param('installation_id') installation_id: number, @Query() query: TenantOrganizationBaseDTO, ): Promise> { try { // Validate the input data (You can use class-validator for validation) if (!query || !query.organizationId) { - throw new HttpException('Invalid installation query parameter', HttpStatus.BAD_REQUEST); + throw new HttpException('Invalid query parameter', HttpStatus.BAD_REQUEST); } // Get installation metadata - const metadata = await this._octokitService.getInstallationMetadata(installation_id); + const metadata = await this._octokitService.getGithubInstallationMetadata(installation_id); return metadata.data; } catch (error) { // Handle errors and return an appropriate error respons @@ -41,4 +43,33 @@ export class GitHubIntegrationController { throw new HttpException(`Error while retrieve github installation metadata: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); } } + + /** + * Get GitHub repositories for a specific installation. + * + * @param {number} installation_id - The installation ID for the GitHub App. + * @param {TenantOrganizationBaseDTO} query - Query parameters, including organizationId. + * @returns {Promise>} A promise that resolves with the GitHub repositories. + * @throws {HttpException} If the query parameters are invalid or if there's an error retrieving the repositories. + */ + @Get('/repositories') + @UsePipes(new ValidationPipe({ transform: true })) + async getGithubRepositories( + @Param('installation_id') installation_id: number, + @Query() query: TenantOrganizationBaseDTO, + ): Promise> { + try { + // Validate the input data (You can use class-validator for validation) + if (!query || !query.organizationId) { + throw new HttpException('Invalid query parameter', HttpStatus.BAD_REQUEST); + } + // Get installation repositories + const repositories = await this._octokitService.getGithubRepositories(installation_id); + return repositories.data; + } catch (error) { + // Handle errors and return an appropriate error respons + this.logger.error('Error while retrieving GitHub installation repositories', error.message); + throw new HttpException(`Error while retrieving GitHub installation repositories: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); + } + } } diff --git a/packages/plugins/integration-github/src/octokit.service.ts b/packages/plugins/integration-github/src/octokit.service.ts index 7d90130f4cc..f832ae39609 100644 --- a/packages/plugins/integration-github/src/octokit.service.ts +++ b/packages/plugins/integration-github/src/octokit.service.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Inject, Injectable, Logger } from '@nestjs/common'; import { App } from 'octokit'; import { ModuleProviders, ProbotConfig } from './probot.types'; import { ResponseHeaders as OctokitResponseHeaders } from "@octokit/types"; @@ -14,6 +14,8 @@ export interface OctokitResponse { @Injectable() export class OctokitService { + private readonly logger = new Logger('OctokitService'); + /** */ private readonly app: App; @@ -29,15 +31,17 @@ export class OctokitService { /** * Get GitHub metadata for a specific installation. + * * @param installation_id The installation ID for the GitHub App. - * @returns GitHub metadata. + * @returns {Promise>} A promise that resolves with the GitHub metadata. + * @throws {Error} If the request to fetch metadata fails. */ - public async getInstallationMetadata(installation_id: number): Promise> { + public async getGithubInstallationMetadata(installation_id: number): Promise> { try { // Get an Octokit instance for the installation const octokit = await this.app.getInstallationOctokit(installation_id); - // Send a request to the GitHub API to get metadata + // Send a request to the GitHub API to get installation metadata return await octokit.request('GET /app/installations/{installation_id}', { installation_id, headers: { @@ -45,7 +49,33 @@ export class OctokitService { } }); } catch (error) { - throw new Error('Failed to fetch GitHub metadata'); + this.logger.error('Failed to fetch GitHub installation metadata', error.message); + throw new Error('Failed to fetch GitHub installation metadata'); + } + } + + /** + * Get GitHub repositories for a specific installation. + * + * @param installation_id The installation ID for the GitHub App. + * @returns {Promise>} A promise that resolves with the GitHub repositories. + * @throws {Error} If the request to fetch repositories fails. + */ + public async getGithubRepositories(installation_id: number): Promise> { + try { + // Get an Octokit instance for the installation + const octokit = await this.app.getInstallationOctokit(installation_id); + + // Send a request to the GitHub API to get repositories + return await octokit.request('GET /installation/repositories', { + installation_id, + headers: { + 'X-GitHub-Api-Version': GITHUB_API_VERSION + } + }); + } catch (error) { + this.logger.error('Failed to fetch GitHub installation repositories', error.message); + throw new Error('Failed to fetch GitHub installation repositories'); } } From 02be4f2882de4264e10b0dd882ebb28f61bca642 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sat, 23 Sep 2023 22:41:33 +0530 Subject: [PATCH 071/104] fix: #6734 hubstaff integration post install URL redirection --- .../common/src/interfaces/IHubstaffConfig.ts | 5 ++ packages/common/src/interfaces/index.ts | 1 + .../src/environments/environment.prod.ts | 34 ++++++-------- .../config/src/environments/environment.ts | 34 ++++++-------- .../config/src/environments/ienvironment.ts | 2 + .../hubstaff-authorization.controller.ts | 47 ++++++++++++------- 6 files changed, 67 insertions(+), 56 deletions(-) create mode 100644 packages/common/src/interfaces/IHubstaffConfig.ts diff --git a/packages/common/src/interfaces/IHubstaffConfig.ts b/packages/common/src/interfaces/IHubstaffConfig.ts new file mode 100644 index 00000000000..38c4f15fb76 --- /dev/null +++ b/packages/common/src/interfaces/IHubstaffConfig.ts @@ -0,0 +1,5 @@ +export interface IHubstaffConfig { + readonly CLIENT_ID: string; + readonly CLIENT_SECRET: string; + readonly POST_INSTALL_URL: string; +} diff --git a/packages/common/src/interfaces/index.ts b/packages/common/src/interfaces/index.ts index 39629d72af3..758058db10e 100644 --- a/packages/common/src/interfaces/index.ts +++ b/packages/common/src/interfaces/index.ts @@ -21,3 +21,4 @@ export * from './ITwitterConfig'; export * from './IUnleashConfig'; export * from './IUpworkConfig'; export * from './IWasabiConfig'; +export * from './IHubstaffConfig'; diff --git a/packages/config/src/environments/environment.prod.ts b/packages/config/src/environments/environment.prod.ts index 6d64377f148..ebc4fefb1ca 100644 --- a/packages/config/src/environments/environment.prod.ts +++ b/packages/config/src/environments/environment.prod.ts @@ -97,18 +97,14 @@ export const environment: IEnvironment = { clientId: process.env.FACEBOOK_CLIENT_ID, clientSecret: process.env.FACEBOOK_CLIENT_SECRET, fbGraphVersion: process.env.FACEBOOK_GRAPH_VERSION, - oauthRedirectUri: - process.env.FACEBOOK_CALLBACK_URL || - `${process.env.API_HOST}:${process.env.API_PORT}/api/auth/facebook/callback`, + oauthRedirectUri: process.env.FACEBOOK_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/facebook/callback`, state: '{fbstate}', }, googleConfig: { clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, - callbackUrl: - process.env.GOOGLE_CALLBACK_URL || - `${process.env.API_HOST}:${process.env.API_PORT}/api/auth/google/callback`, + callbackUrl: process.env.GOOGLE_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/google/callback`, }, github: { @@ -136,25 +132,19 @@ export const environment: IEnvironment = { clientSecret: process.env.MICROSOFT_CLIENT_SECRET, resource: process.env.MICROSOFT_RESOURCE, tenant: process.env.MICROSOFT_TENANT, - callbackUrl: - process.env.MICROSOFT_CALLBACK_URL || - `http://${process.env.API_HOST}:${process.env.API_PORT}/api/auth/microsoft/callback`, + callbackUrl: process.env.MICROSOFT_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/microsoft/callback`, }, linkedinConfig: { clientId: process.env.LINKEDIN_CLIENT_ID, clientSecret: process.env.LINKEDIN_CLIENT_SECRET, - callbackUrl: - process.env.LINKEDIN_CALLBACK_URL || - `http://${process.env.API_HOST}:${process.env.API_PORT}/api/auth/linked/callback`, + callbackUrl: process.env.LINKEDIN_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/linked/callback`, }, twitterConfig: { clientId: process.env.TWITTER_CLIENT_ID, clientSecret: process.env.TWITTER_CLIENT_SECRET, - callbackUrl: - process.env.TWITTER_CALLBACK_URL || - `http://${process.env.API_HOST}:${process.env.API_PORT}/api/auth/twitter/callback`, + callbackUrl: process.env.TWITTER_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/twitter/callback`, }, fiverrConfig: { @@ -180,17 +170,23 @@ export const environment: IEnvironment = { dsn: process.env.SENTRY_DSN, }, - defaultIntegratedUserPass: - process.env.INTEGRATED_USER_DEFAULT_PASS || '123456', + defaultIntegratedUserPass: process.env.INTEGRATED_USER_DEFAULT_PASS || '123456', upworkConfig: { callbackUrl: process.env.UPWORK_REDIRECT_URL || `${process.env.API_BASE_URL}/api/integrations/upwork/callback`, }, + hubstaff: { + /** Hubstaff Integration Configuration */ + CLIENT_ID: process.env.HUBSTAFF_CLIENT_ID, + CLIENT_SECRET: process.env.HUBSTAFF_CLIENT_SECRET, + /** Hubstaff Integration Post Install URL */ + POST_INSTALL_URL: process.env.HUBSTAFF_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/wizard/hubstaff`, + }, + isElectron: process.env.IS_ELECTRON === 'true' ? true : false, gauzyUserPath: process.env.GAUZY_USER_PATH, - allowSuperAdminRole: - process.env.ALLOW_SUPER_ADMIN_ROLE === 'false' ? false : true, + allowSuperAdminRole: process.env.ALLOW_SUPER_ADMIN_ROLE === 'false' ? false : true, /** * Endpoint for Gauzy AI API (optional), e.g.: http://localhost:3005/graphql diff --git a/packages/config/src/environments/environment.ts b/packages/config/src/environments/environment.ts index 2c56f9f711c..7e39d101a87 100644 --- a/packages/config/src/environments/environment.ts +++ b/packages/config/src/environments/environment.ts @@ -91,8 +91,7 @@ export const environment: IEnvironment = { api_key: process.env.CLOUDINARY_API_KEY, api_secret: process.env.CLOUDINARY_API_SECRET, secure: process.env.CLOUDINARY_API_SECURE === 'false' ? false : true, - delivery_url: - process.env.CLOUDINARY_CDN_URL || `https://res.cloudinary.com`, + delivery_url: process.env.CLOUDINARY_CDN_URL || `https://res.cloudinary.com`, }, facebookConfig: { @@ -101,18 +100,14 @@ export const environment: IEnvironment = { clientId: process.env.FACEBOOK_CLIENT_ID, clientSecret: process.env.FACEBOOK_CLIENT_SECRET, fbGraphVersion: process.env.FACEBOOK_GRAPH_VERSION, - oauthRedirectUri: - process.env.FACEBOOK_CALLBACK_URL || - `${process.env.API_HOST}:${process.env.API_PORT}/api/auth/facebook/callback`, + oauthRedirectUri: process.env.FACEBOOK_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/facebook/callback`, state: '{fbstate}', }, googleConfig: { clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, - callbackUrl: - process.env.GOOGLE_CALLBACK_URL || - `http://${process.env.API_HOST}:${process.env.API_PORT}/api/auth/google/callback`, + callbackUrl: process.env.GOOGLE_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/google/callback`, }, github: { @@ -140,25 +135,19 @@ export const environment: IEnvironment = { clientSecret: process.env.MICROSOFT_CLIENT_SECRET, resource: process.env.MICROSOFT_RESOURCE, tenant: process.env.MICROSOFT_TENANT, - callbackUrl: - process.env.MICROSOFT_CALLBACK_URL || - `http://${process.env.API_HOST}:${process.env.API_PORT}/api/auth/microsoft/callback`, + callbackUrl: process.env.MICROSOFT_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/microsoft/callback`, }, linkedinConfig: { clientId: process.env.LINKEDIN_CLIENT_ID, clientSecret: process.env.LINKEDIN_CLIENT_SECRET, - callbackUrl: - process.env.LINKEDIN_CALLBACK_URL || - `http://${process.env.API_HOST}:${process.env.API_PORT}/api/auth/linked/callback`, + callbackUrl: process.env.LINKEDIN_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/linked/callback`, }, twitterConfig: { clientId: process.env.TWITTER_CLIENT_ID, clientSecret: process.env.TWITTER_CLIENT_SECRET, - callbackUrl: - process.env.TWITTER_CALLBACK_URL || - `http://${process.env.API_HOST}:${process.env.API_PORT}/api/auth/twitter/callback`, + callbackUrl: process.env.TWITTER_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/twitter/callback`, }, fiverrConfig: { @@ -191,10 +180,17 @@ export const environment: IEnvironment = { callbackUrl: process.env.UPWORK_REDIRECT_URL || `${process.env.API_BASE_URL}/api/integrations/upwork/callback`, }, + hubstaff: { + /** Hubstaff Integration Configuration */ + CLIENT_ID: process.env.HUBSTAFF_CLIENT_ID, + CLIENT_SECRET: process.env.HUBSTAFF_CLIENT_SECRET, + /** Hubstaff Integration Post Install URL */ + POST_INSTALL_URL: process.env.HUBSTAFF_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/wizard/hubstaff`, + }, + isElectron: process.env.IS_ELECTRON === 'true' ? true : false, gauzyUserPath: process.env.GAUZY_USER_PATH, - allowSuperAdminRole: - process.env.ALLOW_SUPER_ADMIN_ROLE === 'false' ? false : true, + allowSuperAdminRole: process.env.ALLOW_SUPER_ADMIN_ROLE === 'false' ? false : true, /** * Endpoint for Gauzy AI API (optional), e.g.: http://localhost:3005/graphql diff --git a/packages/config/src/environments/ienvironment.ts b/packages/config/src/environments/ienvironment.ts index bcfd0bcb3da..0056f97c4f4 100644 --- a/packages/config/src/environments/ienvironment.ts +++ b/packages/config/src/environments/ienvironment.ts @@ -12,6 +12,7 @@ import { IFiverrConfig, IGithubConfig, IGoogleConfig, + IHubstaffConfig, IKeycloakConfig, ILinkedinConfig, IMicrosoftConfig, @@ -119,6 +120,7 @@ export interface IEnvironment { defaultIntegratedUserPass?: string; upworkConfig?: IUpworkConfig; + hubstaff?: IHubstaffConfig; isElectron?: boolean; gauzyUserPath?: string; allowSuperAdminRole?: boolean; diff --git a/packages/core/src/integration/hubstaff/hubstaff-authorization.controller.ts b/packages/core/src/integration/hubstaff/hubstaff-authorization.controller.ts index 7619ac0e7d4..ddbe1fe1bea 100644 --- a/packages/core/src/integration/hubstaff/hubstaff-authorization.controller.ts +++ b/packages/core/src/integration/hubstaff/hubstaff-authorization.controller.ts @@ -1,9 +1,12 @@ -import { Controller, Get, Query, Res } from '@nestjs/common'; +import { Controller, Get, HttpException, HttpStatus, Query, Res } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; +import { Response } from 'express'; import { ConfigService } from '@gauzy/config'; -import { Public } from '@gauzy/common'; +import { IHubstaffConfig, Public } from '@gauzy/common'; +import { IntegrationEnum } from '@gauzy/contracts'; @ApiTags('Hubstaff Integrations') +@Public() @Controller() export class HubstaffAuthorizationController { constructor( @@ -11,27 +14,35 @@ export class HubstaffAuthorizationController { ) { } /** - * Hubstaff Integration Authorization Flow Callback - * - * @param code - * @param state - * @param res - * @returns - */ - @Public() + * Handle the callback from the Hubstaff integration. + * + * @param {any} query - The query parameters from the callback. + * @param {Response} response - Express Response object. + */ @Get('callback') - async hubstaffCallback( - @Query('code') code: string, - @Query('state') state: string, - @Res() res: any + async hubstaffIntegrationCallback( + @Query() query: any, + @Res() response: Response ) { try { - if (code && state) { - return res.redirect(`${this._config.get('clientBaseUrl')}/#/pages/integrations/hubstaff?code=${code}&state=${state}`); + // Validate the input data (You can use class-validator for validation) + if (!query || !query.code || !query.state) { + throw new HttpException('Invalid query parameters', HttpStatus.BAD_REQUEST); } - return res.redirect(`${this._config.get('clientBaseUrl')}/#/pages/integrations/hubstaff`); + + /** Hubstaff Config Options */ + const hubstaff = this._config.get('hubstaff') as IHubstaffConfig; + + /** Construct the redirect URL with query parameters */ + const urlParams = new URLSearchParams(); + urlParams.append('code', query.code); + urlParams.append('state', query.state); + + /** Redirect to the URL */ + return response.redirect(`${hubstaff.POST_INSTALL_URL}?${urlParams.toString()}`); } catch (error) { - return res.redirect(`${this._config.get('clientBaseUrl')}/#/pages/integrations/hubstaff`); + // Handle errors and return an appropriate error response + throw new HttpException(`Failed to add ${IntegrationEnum.HUBSTAFF} integration: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); } } } From c39f06c3a010a4bb1e691aee44ae29df1e0833b0 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sun, 24 Sep 2023 14:12:12 +0530 Subject: [PATCH 072/104] fix: #6734 upwork integration callback configuration --- .../components/header/header.component.ts | 2 +- .../hubstaff-authorize.component.ts | 2 +- .../components/hubstaff/hubstaff.component.ts | 2 +- .../gauzy-ai-authorize.component.ts | 2 +- .../upwork-authorize.component.ts | 5 +- .../components/upwork/upwork.component.ts | 4 +- .../common/src/interfaces/IUpworkConfig.ts | 7 +-- .../src/environments/environment.prod.ts | 7 ++- .../config/src/environments/environment.ts | 10 ++-- .../config/src/environments/ienvironment.ts | 4 +- .../upwork/upwork-authorization.controller.ts | 47 ++++++++++++------- packages/core/src/upwork/upwork.service.ts | 2 +- 12 files changed, 55 insertions(+), 39 deletions(-) diff --git a/apps/gauzy/src/app/@theme/components/header/header.component.ts b/apps/gauzy/src/app/@theme/components/header/header.component.ts index db5c8882bc4..b44bd5b3eac 100644 --- a/apps/gauzy/src/app/@theme/components/header/header.component.ts +++ b/apps/gauzy/src/app/@theme/components/header/header.component.ts @@ -529,7 +529,7 @@ export class HeaderComponent extends TranslationBaseComponent implements OnInit, { title: this.getTranslation('CONTEXT_MENU.CONTRACT'), icon: 'file-text-outline', - link: 'pages/integrations/upwork' + link: 'pages/integrations/wizard/upwork' } ] : []), diff --git a/apps/gauzy/src/app/pages/hubstaff/components/hubstaff-authorize/hubstaff-authorize.component.ts b/apps/gauzy/src/app/pages/hubstaff/components/hubstaff-authorize/hubstaff-authorize.component.ts index fa738b04506..ac056bb6b01 100644 --- a/apps/gauzy/src/app/pages/hubstaff/components/hubstaff-authorize/hubstaff-authorize.component.ts +++ b/apps/gauzy/src/app/pages/hubstaff/components/hubstaff-authorize/hubstaff-authorize.component.ts @@ -114,7 +114,7 @@ export class HubstaffAuthorizeComponent implements OnInit, OnDestroy { * Hubstaff integration remember state API call */ private _redirectToHubstaffIntegration(integrationId: IIntegration['id']) { - this._router.navigate([this._router.url, integrationId]); + this._router.navigate(['pages/integrations/wizard/hubstaff', integrationId]); } authorizeHubstaff() { diff --git a/apps/gauzy/src/app/pages/hubstaff/components/hubstaff/hubstaff.component.ts b/apps/gauzy/src/app/pages/hubstaff/components/hubstaff/hubstaff.component.ts index dc4d7730ca5..c86a018b976 100644 --- a/apps/gauzy/src/app/pages/hubstaff/components/hubstaff/hubstaff.component.ts +++ b/apps/gauzy/src/app/pages/hubstaff/components/hubstaff/hubstaff.component.ts @@ -258,7 +258,7 @@ export class HubstaffComponent extends TranslationBaseComponent implements OnIni { title: this.getTranslation('INTEGRATIONS.RE_INTEGRATE'), icon: 'text-outline', - link: `pages/integrations/hubstaff/regenerate` + link: `pages/integrations/wizard/hubstaff/regenerate` }, { title: this.getTranslation('INTEGRATIONS.SETTINGS'), diff --git a/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts b/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts index 31d0633241b..1f4d0789c7e 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts +++ b/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts @@ -117,7 +117,7 @@ export class GauzyAIAuthorizeComponent implements AfterViewInit, OnInit, OnDestr * Gauzy AI integration remember state API call */ private _redirectToGauzyAIIntegration(integrationId: string) { - this._router.navigate([this._router.url, integrationId]); + this._router.navigate(['pages/integrations/wizard/gauzy-ai', integrationId]); } /** diff --git a/apps/gauzy/src/app/pages/upwork/components/upwork-authorize/upwork-authorize.component.ts b/apps/gauzy/src/app/pages/upwork/components/upwork-authorize/upwork-authorize.component.ts index 0c6aedbd21c..e2a4d42fe0c 100644 --- a/apps/gauzy/src/app/pages/upwork/components/upwork-authorize/upwork-authorize.component.ts +++ b/apps/gauzy/src/app/pages/upwork/components/upwork-authorize/upwork-authorize.component.ts @@ -127,10 +127,7 @@ export class UpworkAuthorizeComponent implements OnInit, OnDestroy { * @param integrationId */ private _redirectToUpworkIntegration(integrationId: string) { - this._router.navigate([ - this._router.url, - integrationId - ]); + this._router.navigate(['pages/integrations/wizard/upwork', integrationId]); } ngOnDestroy(): void { } diff --git a/apps/gauzy/src/app/pages/upwork/components/upwork/upwork.component.ts b/apps/gauzy/src/app/pages/upwork/components/upwork/upwork.component.ts index 40ef0eae1f6..2a9270cc4d9 100644 --- a/apps/gauzy/src/app/pages/upwork/components/upwork/upwork.component.ts +++ b/apps/gauzy/src/app/pages/upwork/components/upwork/upwork.component.ts @@ -108,12 +108,12 @@ export class UpworkComponent { title: this.getTranslation('INTEGRATIONS.RE_INTEGRATE'), icon: 'text-outline', - link: `pages/integrations/upwork/regenerate` + link: `pages/integrations/wizard/upwork/regenerate` }, { title: this.getTranslation('INTEGRATIONS.SETTINGS'), icon: 'settings-2-outline', - link: `pages/integrations/upwork/${this.integrationId}/settings` + link: `pages/integrations/wizard/upwork/${this.integrationId}/settings` } ]; } diff --git a/packages/common/src/interfaces/IUpworkConfig.ts b/packages/common/src/interfaces/IUpworkConfig.ts index 6689e1f21e6..a34094db082 100644 --- a/packages/common/src/interfaces/IUpworkConfig.ts +++ b/packages/common/src/interfaces/IUpworkConfig.ts @@ -1,5 +1,6 @@ export interface IUpworkConfig { - apiKey?: string; - apiSecret?: string; - callbackUrl: string; + readonly API_KEY: string; + readonly API_SECRET: string; + readonly CALLBACK_URL: string; + readonly POST_INSTALL_URL: string; } diff --git a/packages/config/src/environments/environment.prod.ts b/packages/config/src/environments/environment.prod.ts index ebc4fefb1ca..62b616485df 100644 --- a/packages/config/src/environments/environment.prod.ts +++ b/packages/config/src/environments/environment.prod.ts @@ -172,8 +172,11 @@ export const environment: IEnvironment = { defaultIntegratedUserPass: process.env.INTEGRATED_USER_DEFAULT_PASS || '123456', - upworkConfig: { - callbackUrl: process.env.UPWORK_REDIRECT_URL || `${process.env.API_BASE_URL}/api/integrations/upwork/callback`, + upwork: { + API_KEY: process.env.UPWORK_API_KEY, + API_SECRET: process.env.UPWORK_API_SECRET, + CALLBACK_URL: process.env.UPWORK_REDIRECT_URL || `${process.env.API_BASE_URL}/api/integrations/upwork/callback`, + POST_INSTALL_URL: process.env.UPWORK_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/wizard/upwork`, }, hubstaff: { diff --git a/packages/config/src/environments/environment.ts b/packages/config/src/environments/environment.ts index 7e39d101a87..5c25d0c1d5a 100644 --- a/packages/config/src/environments/environment.ts +++ b/packages/config/src/environments/environment.ts @@ -173,11 +173,13 @@ export const environment: IEnvironment = { dsn: process.env.SENTRY_DSN, }, - defaultIntegratedUserPass: - process.env.INTEGRATED_USER_DEFAULT_PASS || '123456', + defaultIntegratedUserPass: process.env.INTEGRATED_USER_DEFAULT_PASS || '123456', - upworkConfig: { - callbackUrl: process.env.UPWORK_REDIRECT_URL || `${process.env.API_BASE_URL}/api/integrations/upwork/callback`, + upwork: { + API_KEY: process.env.UPWORK_API_KEY, + API_SECRET: process.env.UPWORK_API_SECRET, + CALLBACK_URL: process.env.UPWORK_REDIRECT_URL || `${process.env.API_BASE_URL}/api/integrations/upwork/callback`, + POST_INSTALL_URL: process.env.UPWORK_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/wizard/upwork`, }, hubstaff: { diff --git a/packages/config/src/environments/ienvironment.ts b/packages/config/src/environments/ienvironment.ts index 0056f97c4f4..9ad255ad5ed 100644 --- a/packages/config/src/environments/ienvironment.ts +++ b/packages/config/src/environments/ienvironment.ts @@ -119,8 +119,10 @@ export interface IEnvironment { */ defaultIntegratedUserPass?: string; - upworkConfig?: IUpworkConfig; + /** Third Party Integrations */ + upwork?: IUpworkConfig; hubstaff?: IHubstaffConfig; + isElectron?: boolean; gauzyUserPath?: string; allowSuperAdminRole?: boolean; diff --git a/packages/core/src/upwork/upwork-authorization.controller.ts b/packages/core/src/upwork/upwork-authorization.controller.ts index b0d90e90fdb..0561c178d5f 100644 --- a/packages/core/src/upwork/upwork-authorization.controller.ts +++ b/packages/core/src/upwork/upwork-authorization.controller.ts @@ -1,9 +1,12 @@ -import { Controller, Get, Query, Res } from '@nestjs/common'; +import { Controller, Get, HttpException, HttpStatus, Query, Res } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; +import { Response } from 'express'; import { ConfigService } from '@gauzy/config'; -import { Public } from '@gauzy/common'; +import { IUpworkConfig, Public } from '@gauzy/common'; +import { IntegrationEnum } from '@gauzy/contracts'; @ApiTags('Upwork Integrations') +@Public() @Controller() export class UpworkAuthorizationController { @@ -12,27 +15,35 @@ export class UpworkAuthorizationController { ) { } /** - * Upwork Integration Authorization Flow Callback - * - * @param oauth_token - * @param oauth_verifier - * @param res - * @returns - */ - @Public() + * Handle the callback from the Upwork integration. + * + * @param {any} query - The query parameters from the callback. + * @param {Response} response - Express Response object. + */ @Get('callback') - async upworkCallback( - @Query('oauth_token') oauth_token: string, - @Query('oauth_verifier') oauth_verifier: string, - @Res() res: any + async upworkIntegrationCallback( + @Query() query: any, + @Res() response: Response ) { try { - if (oauth_token && oauth_verifier) { - return res.redirect(`${this._config.get('clientBaseUrl')}/#/pages/integrations/upwork?oauth_token=${oauth_token}&oauth_verifier=${oauth_verifier}`); + // Validate the input data (You can use class-validator for validation) + if (!query || !query.oauth_token || !query.oauth_verifier) { + throw new HttpException('Invalid query parameters', HttpStatus.BAD_REQUEST); } - return res.redirect(`${this._config.get('clientBaseUrl')}/#/pages/integrations/upwork`); + + /** Upwork Config Options */ + const upwork = this._config.get('upwork') as IUpworkConfig; + + /** Construct the redirect URL with query parameters */ + const urlParams = new URLSearchParams(); + urlParams.append('oauth_token', query.oauth_token); + urlParams.append('oauth_verifier', query.oauth_verifier); + + /** Redirect to the URL */ + return response.redirect(`${upwork.POST_INSTALL_URL}?${urlParams.toString()}`); } catch (error) { - return res.redirect(`${this._config.get('clientBaseUrl')}/#/pages/integrations/upwork`); + // Handle errors and return an appropriate error response + throw new HttpException(`Failed to add ${IntegrationEnum.UPWORK} integration: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); } } } diff --git a/packages/core/src/upwork/upwork.service.ts b/packages/core/src/upwork/upwork.service.ts index 9b6c3ee381a..7b1b56f3aea 100644 --- a/packages/core/src/upwork/upwork.service.ts +++ b/packages/core/src/upwork/upwork.service.ts @@ -165,7 +165,7 @@ export class UpworkService { this._upworkApi = new UpworkApi(config); - const authUrl = environment.upworkConfig.callbackUrl; + const authUrl = environment.upwork.CALLBACK_URL; console.log(`Upwork callback URL: ${authUrl}`); From d4714354cd61a319dbdc9c3ea63f787898f792d6 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sun, 24 Sep 2023 15:03:17 +0530 Subject: [PATCH 073/104] fix: #6734 removed wizard step --- .../components/header/header.component.ts | 2 +- .../hubstaff-authorize.component.ts | 2 +- .../components/hubstaff/hubstaff.component.ts | 2 +- .../gauzy-ai-authorize.component.html | 0 .../gauzy-ai-authorize.component.scss | 0 .../gauzy-ai-authorize.component.spec.ts | 0 .../gauzy-ai-authorize.component.ts | 4 +- .../gauzy-ai/gauzy-ai-routing.module.ts | 0 .../{wizard => }/gauzy-ai/gauzy-ai.module.ts | 4 +- .../installation/installation.component.html} | 0 .../installation/installation.component.ts} | 10 ++-- .../components/wizard/wizard.component.html} | 0 .../components/wizard/wizard.component.scss} | 0 .../wizard/wizard.component.spec.ts} | 12 ++--- .../components/wizard/wizard.component.ts} | 9 ++-- .../github/github-routing.module.ts | 31 +++++++++++ .../{wizard => }/github/github.config.ts | 0 .../{wizard => }/github/github.module.ts | 10 ++-- .../integrations-routing.module.ts | 51 +++++++++---------- .../integrations/integrations.component.html | 2 +- .../wizard/github/github-routing.module.ts | 31 ----------- .../upwork-authorize.component.ts | 2 +- .../components/upwork/upwork.component.ts | 4 +- .../src/environments/environment.prod.ts | 4 +- .../config/src/environments/environment.ts | 4 +- ...gration-tenant-update-or-create.handler.ts | 38 +++++++++++++- .../src/integration/github/github.service.ts | 9 +++- 27 files changed, 132 insertions(+), 99 deletions(-) rename apps/gauzy/src/app/pages/integrations/{wizard => }/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.html (100%) rename apps/gauzy/src/app/pages/integrations/{wizard => }/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.scss (100%) rename apps/gauzy/src/app/pages/integrations/{wizard => }/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.spec.ts (100%) rename apps/gauzy/src/app/pages/integrations/{wizard => }/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts (97%) rename apps/gauzy/src/app/pages/integrations/{wizard => }/gauzy-ai/gauzy-ai-routing.module.ts (100%) rename apps/gauzy/src/app/pages/integrations/{wizard => }/gauzy-ai/gauzy-ai.module.ts (82%) rename apps/gauzy/src/app/pages/integrations/{wizard/github/components/authorization/authorization.component.html => github/components/installation/installation.component.html} (100%) rename apps/gauzy/src/app/pages/integrations/{wizard/github/components/installations/installations.component.ts => github/components/installation/installation.component.ts} (87%) rename apps/gauzy/src/app/pages/integrations/{wizard/github/components/installations/installations.component.html => github/components/wizard/wizard.component.html} (100%) rename apps/gauzy/src/app/pages/integrations/{wizard/github/components/authorization/authorization.component.scss => github/components/wizard/wizard.component.scss} (100%) rename apps/gauzy/src/app/pages/integrations/{wizard/github/components/authorization/authorization.component.spec.ts => github/components/wizard/wizard.component.spec.ts} (52%) rename apps/gauzy/src/app/pages/integrations/{wizard/github/components/authorization/authorization.component.ts => github/components/wizard/wizard.component.ts} (94%) create mode 100644 apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts rename apps/gauzy/src/app/pages/integrations/{wizard => }/github/github.config.ts (100%) rename apps/gauzy/src/app/pages/integrations/{wizard => }/github/github.module.ts (50%) delete mode 100644 apps/gauzy/src/app/pages/integrations/wizard/github/github-routing.module.ts diff --git a/apps/gauzy/src/app/@theme/components/header/header.component.ts b/apps/gauzy/src/app/@theme/components/header/header.component.ts index b44bd5b3eac..db5c8882bc4 100644 --- a/apps/gauzy/src/app/@theme/components/header/header.component.ts +++ b/apps/gauzy/src/app/@theme/components/header/header.component.ts @@ -529,7 +529,7 @@ export class HeaderComponent extends TranslationBaseComponent implements OnInit, { title: this.getTranslation('CONTEXT_MENU.CONTRACT'), icon: 'file-text-outline', - link: 'pages/integrations/wizard/upwork' + link: 'pages/integrations/upwork' } ] : []), diff --git a/apps/gauzy/src/app/pages/hubstaff/components/hubstaff-authorize/hubstaff-authorize.component.ts b/apps/gauzy/src/app/pages/hubstaff/components/hubstaff-authorize/hubstaff-authorize.component.ts index ac056bb6b01..90efe1b588a 100644 --- a/apps/gauzy/src/app/pages/hubstaff/components/hubstaff-authorize/hubstaff-authorize.component.ts +++ b/apps/gauzy/src/app/pages/hubstaff/components/hubstaff-authorize/hubstaff-authorize.component.ts @@ -114,7 +114,7 @@ export class HubstaffAuthorizeComponent implements OnInit, OnDestroy { * Hubstaff integration remember state API call */ private _redirectToHubstaffIntegration(integrationId: IIntegration['id']) { - this._router.navigate(['pages/integrations/wizard/hubstaff', integrationId]); + this._router.navigate(['pages/integrations/hubstaff', integrationId]); } authorizeHubstaff() { diff --git a/apps/gauzy/src/app/pages/hubstaff/components/hubstaff/hubstaff.component.ts b/apps/gauzy/src/app/pages/hubstaff/components/hubstaff/hubstaff.component.ts index c86a018b976..dc4d7730ca5 100644 --- a/apps/gauzy/src/app/pages/hubstaff/components/hubstaff/hubstaff.component.ts +++ b/apps/gauzy/src/app/pages/hubstaff/components/hubstaff/hubstaff.component.ts @@ -258,7 +258,7 @@ export class HubstaffComponent extends TranslationBaseComponent implements OnIni { title: this.getTranslation('INTEGRATIONS.RE_INTEGRATE'), icon: 'text-outline', - link: `pages/integrations/wizard/hubstaff/regenerate` + link: `pages/integrations/hubstaff/regenerate` }, { title: this.getTranslation('INTEGRATIONS.SETTINGS'), diff --git a/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.html b/apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.html similarity index 100% rename from apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.html rename to apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.html diff --git a/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.scss b/apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.scss similarity index 100% rename from apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.scss rename to apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.scss diff --git a/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.spec.ts b/apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.spec.ts similarity index 100% rename from apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.spec.ts rename to apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.spec.ts diff --git a/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts b/apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts similarity index 97% rename from apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts rename to apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts index 1f4d0789c7e..18e683bc2f3 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts +++ b/apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai-authorize/gauzy-ai-authorize.component.ts @@ -4,7 +4,7 @@ import { FormGroup, FormBuilder, Validators, FormGroupDirective } from '@angular import { filter, tap } from 'rxjs/operators'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { IIntegrationSetting, IIntegrationTenant, IOrganization, IntegrationEnum } from '@gauzy/contracts'; -import { GauzyAIService, IntegrationsService, Store } from './../../../../../@core/services'; +import { GauzyAIService, IntegrationsService, Store } from './../../../../@core/services'; @UntilDestroy({ checkProperties: true }) @Component({ @@ -117,7 +117,7 @@ export class GauzyAIAuthorizeComponent implements AfterViewInit, OnInit, OnDestr * Gauzy AI integration remember state API call */ private _redirectToGauzyAIIntegration(integrationId: string) { - this._router.navigate(['pages/integrations/wizard/gauzy-ai', integrationId]); + this._router.navigate(['pages/integrations/gauzy-ai', integrationId]); } /** diff --git a/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-routing.module.ts b/apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai-routing.module.ts similarity index 100% rename from apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai-routing.module.ts rename to apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai-routing.module.ts diff --git a/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai.module.ts b/apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai.module.ts similarity index 82% rename from apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai.module.ts rename to apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai.module.ts index 94582e91310..3823730a392 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/gauzy-ai/gauzy-ai.module.ts +++ b/apps/gauzy/src/app/pages/integrations/gauzy-ai/gauzy-ai.module.ts @@ -2,8 +2,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { NbButtonModule, NbCardModule, NbIconModule, NbInputModule, NbTooltipModule } from '@nebular/theme'; -import { TranslateModule } from './../../../../@shared/translate/translate.module'; -import { BackNavigationModule } from './../../../../@shared/back-navigation'; +import { TranslateModule } from './../../../@shared/translate/translate.module'; +import { BackNavigationModule } from './../../../@shared/back-navigation'; import { GauzyAIRoutingModule } from './gauzy-ai-routing.module'; import { GauzyAIAuthorizeComponent } from './gauzy-ai-authorize/gauzy-ai-authorize.component'; diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.html b/apps/gauzy/src/app/pages/integrations/github/components/installation/installation.component.html similarity index 100% rename from apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.html rename to apps/gauzy/src/app/pages/integrations/github/components/installation/installation.component.html diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/installation/installation.component.ts similarity index 87% rename from apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts rename to apps/gauzy/src/app/pages/integrations/github/components/installation/installation.component.ts index 0a0b2de9962..f2e52f78e62 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/installation/installation.component.ts @@ -3,14 +3,13 @@ import { ActivatedRoute } from '@angular/router'; import { filter, tap } from 'rxjs/operators'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { IGithubAppInstallInput, IOrganization } from '@gauzy/contracts'; -import { GithubService } from './../../../../../../@core/services'; +import { GithubService } from '../../../../../@core/services'; @UntilDestroy({ checkProperties: true }) @Component({ - selector: 'github-integration-installations', - templateUrl: './installations.component.html' + templateUrl: './installation.component.html' }) -export class GithubInstallationsComponent implements OnInit { +export class GithubInstallationComponent implements OnInit { public isLoading: boolean = true; public organization: IOrganization; @@ -71,7 +70,6 @@ export class GithubInstallationsComponent implements OnInit { window.opener = null; window.open("", "_self"); window.close(); - - }, 2000); // 2000 milliseconds = 5 seconds + }, 2000); // 2000 milliseconds = 2 seconds } } diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.html b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.html similarity index 100% rename from apps/gauzy/src/app/pages/integrations/wizard/github/components/installations/installations.component.html rename to apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.html diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.scss b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.scss similarity index 100% rename from apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.scss rename to apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.scss diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.spec.ts b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.spec.ts similarity index 52% rename from apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.spec.ts rename to apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.spec.ts index b13ce5dfe93..825f7b073d3 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.spec.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.spec.ts @@ -1,19 +1,19 @@ import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; -import { GithubAuthorizationComponent } from './authorization.component'; +import { GithubWizardComponent } from './wizard.component'; -describe('GithubAuthorizationComponent', () => { - let component: GithubAuthorizationComponent; - let fixture: ComponentFixture; +describe('GithubWizardComponent', () => { + let component: GithubWizardComponent; + let fixture: ComponentFixture; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [GithubAuthorizationComponent], + declarations: [GithubWizardComponent], teardown: { destroyAfterEach: false } }).compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(GithubAuthorizationComponent); + fixture = TestBed.createComponent(GithubWizardComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts similarity index 94% rename from apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts rename to apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts index 977e5d0c306..8f2d08b9acb 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/github/components/authorization/authorization.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts @@ -1,18 +1,18 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { debounceTime, filter, tap } from 'rxjs/operators'; +import { filter, tap } from 'rxjs/operators'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { environment } from '@env/environment'; import { IOrganization, IntegrationEnum } from '@gauzy/contracts'; import { distinctUntilChange, toParams } from '@gauzy/common-angular'; -import { Store } from '../../../../../../@core/services'; +import { Store } from '../../../../../@core/services'; import { GITHUB_AUTHORIZATION_URL } from '../../github.config'; @UntilDestroy({ checkProperties: true }) @Component({ - templateUrl: './authorization.component.html' + templateUrl: './wizard.component.html' }) -export class GithubAuthorizationComponent implements OnInit { +export class GithubWizardComponent implements OnInit { public organization: IOrganization; public isLoading: boolean = true; @@ -27,7 +27,6 @@ export class GithubAuthorizationComponent implements OnInit { ngOnInit() { this.store.selectedOrganization$ .pipe( - debounceTime(100), distinctUntilChange(), filter((organization: IOrganization) => !!organization), tap((organization: IOrganization) => this.organization = organization), diff --git a/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts b/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts new file mode 100644 index 00000000000..a33c22c9a96 --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { GithubWizardComponent } from './components/wizard/wizard.component'; +import { GithubInstallationComponent } from './components/installation/installation.component'; + +const routes: Routes = [ + { + path: '', + children: [ + { + path: '', + redirectTo: 'wizard', + pathMatch: 'full' + }, + { + path: 'wizard', + component: GithubWizardComponent, + }, + { + path: 'installation', + component: GithubInstallationComponent + } + ] + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class GithubRoutingModule { } diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/github.config.ts b/apps/gauzy/src/app/pages/integrations/github/github.config.ts similarity index 100% rename from apps/gauzy/src/app/pages/integrations/wizard/github/github.config.ts rename to apps/gauzy/src/app/pages/integrations/github/github.config.ts diff --git a/apps/gauzy/src/app/pages/integrations/wizard/github/github.module.ts b/apps/gauzy/src/app/pages/integrations/github/github.module.ts similarity index 50% rename from apps/gauzy/src/app/pages/integrations/wizard/github/github.module.ts rename to apps/gauzy/src/app/pages/integrations/github/github.module.ts index 2a9b7afa06a..f964b689e48 100644 --- a/apps/gauzy/src/app/pages/integrations/wizard/github/github.module.ts +++ b/apps/gauzy/src/app/pages/integrations/github/github.module.ts @@ -1,15 +1,15 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { NbCardModule } from '@nebular/theme'; -import { TranslateModule } from './../../../../@shared/translate/translate.module'; +import { TranslateModule } from './../../../@shared/translate/translate.module'; import { GithubRoutingModule } from './github-routing.module'; -import { GithubAuthorizationComponent } from './components/authorization/authorization.component'; -import { GithubInstallationsComponent } from './components/installations/installations.component'; +import { GithubWizardComponent } from './components/wizard/wizard.component'; +import { GithubInstallationComponent } from './components/installation/installation.component'; @NgModule({ declarations: [ - GithubAuthorizationComponent, - GithubInstallationsComponent + GithubWizardComponent, + GithubInstallationComponent ], imports: [ CommonModule, diff --git a/apps/gauzy/src/app/pages/integrations/integrations-routing.module.ts b/apps/gauzy/src/app/pages/integrations/integrations-routing.module.ts index 7a6d3253800..96a8bb48c56 100644 --- a/apps/gauzy/src/app/pages/integrations/integrations-routing.module.ts +++ b/apps/gauzy/src/app/pages/integrations/integrations-routing.module.ts @@ -7,35 +7,30 @@ const routes: Routes = [ path: 'new', component: IntegrationsComponent }, - /** Integrations Wizard List */ + /** Integrations List */ { - path: 'wizard', - children: [ - { - path: 'upwork', - loadChildren: () => import('../upwork/upwork.module').then( - (m) => m.UpworkModule - ) - }, - { - path: 'hubstaff', - loadChildren: () => import('../hubstaff/hubstaff.module').then( - (m) => m.HubstaffModule - ) - }, - { - path: 'gauzy-ai', - loadChildren: () => import('./wizard/gauzy-ai/gauzy-ai.module').then( - (m) => m.GauzyAIModule - ) - }, - { - path: 'github', - loadChildren: () => import('./wizard/github/github.module').then( - (m) => m.GithubModule - ) - } - ] + path: 'upwork', + loadChildren: () => import('../upwork/upwork.module').then( + (m) => m.UpworkModule + ) + }, + { + path: 'hubstaff', + loadChildren: () => import('../hubstaff/hubstaff.module').then( + (m) => m.HubstaffModule + ) + }, + { + path: 'gauzy-ai', + loadChildren: () => import('./gauzy-ai/gauzy-ai.module').then( + (m) => m.GauzyAIModule + ) + }, + { + path: 'github', + loadChildren: () => import('./github/github.module').then( + (m) => m.GithubModule + ) } ]; diff --git a/apps/gauzy/src/app/pages/integrations/integrations.component.html b/apps/gauzy/src/app/pages/integrations/integrations.component.html index 8867d33485b..f69a53bf57d 100644 --- a/apps/gauzy/src/app/pages/integrations/integrations.component.html +++ b/apps/gauzy/src/app/pages/integrations/integrations.component.html @@ -88,7 +88,7 @@

{{ 'INTEGRATIONS.AVAILABLE_INTEGRATIONS' | translate }}

Date: Sun, 24 Sep 2023 15:07:45 +0530 Subject: [PATCH 074/104] fix: broken integrations router link --- .../src/app/pages/integrations/integrations.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gauzy/src/app/pages/integrations/integrations.component.html b/apps/gauzy/src/app/pages/integrations/integrations.component.html index f69a53bf57d..371e1ef39ae 100644 --- a/apps/gauzy/src/app/pages/integrations/integrations.component.html +++ b/apps/gauzy/src/app/pages/integrations/integrations.component.html @@ -88,7 +88,7 @@

{{ 'INTEGRATIONS.AVAILABLE_INTEGRATIONS' | translate }}

Date: Sun, 24 Sep 2023 17:35:58 +0530 Subject: [PATCH 075/104] fix: #6734 github app install API dto validation --- .../app/@core/services/github/github.service.ts | 2 +- .../1691494801748-SeedIntegrationTable.ts | 2 +- .../integration-entity-setting.entity.ts | 5 +++-- .../integration-map/integration-map.entity.ts | 3 ++- .../integration-setting.entity.ts | 5 +++-- .../integration-tenant.entity.ts | 6 +++++- .../github/dto/github-app-install.dto.ts | 17 +++++++++++++++++ .../core/src/integration/github/dto/index.ts | 1 + .../src/integration/github/github.controller.ts | 9 ++++++--- 9 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 packages/core/src/integration/github/dto/github-app-install.dto.ts create mode 100644 packages/core/src/integration/github/dto/index.ts diff --git a/apps/gauzy/src/app/@core/services/github/github.service.ts b/apps/gauzy/src/app/@core/services/github/github.service.ts index 042464238a2..dd03ca1f82d 100644 --- a/apps/gauzy/src/app/@core/services/github/github.service.ts +++ b/apps/gauzy/src/app/@core/services/github/github.service.ts @@ -20,7 +20,7 @@ export class GithubService { */ async addInstallationApp(input: IGithubAppInstallInput): Promise { return firstValueFrom( - this._http.post(`${API_PREFIX}/integration/github/app-install`, input) + this._http.post(`${API_PREFIX}/integration/github/install`, input) ); } } diff --git a/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts b/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts index 9b5c83da148..70b251f0484 100644 --- a/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts +++ b/packages/core/src/database/migrations/1691494801748-SeedIntegrationTable.ts @@ -64,7 +64,7 @@ export class SeedIntegrationTable1691494801748 implements MigrationInterface { "name", "imgSrc", "isComingSoon", "order" ) VALUES ( - $1, $2, $3, $4, $5 + $1, $2, $3, $4 ) ON CONFLICT(name) DO UPDATE SET diff --git a/packages/core/src/integration-entity-setting/integration-entity-setting.entity.ts b/packages/core/src/integration-entity-setting/integration-entity-setting.entity.ts index e962cc405d2..6e8f80c1429 100644 --- a/packages/core/src/integration-entity-setting/integration-entity-setting.entity.ts +++ b/packages/core/src/integration-entity-setting/integration-entity-setting.entity.ts @@ -44,8 +44,9 @@ export class IntegrationEntitySetting extends TenantOrganizationBaseEntity imple * IntegrationTenant */ @ApiPropertyOptional({ type: () => IntegrationTenant }) - @ManyToOne(() => IntegrationTenant, (integration) => integration.entitySettings, { - onDelete: 'CASCADE' + @ManyToOne(() => IntegrationTenant, (it) => it.entitySettings, { + /** Database cascade action on delete. */ + onDelete: 'CASCADE', }) @JoinColumn() integration?: IIntegrationTenant; diff --git a/packages/core/src/integration-map/integration-map.entity.ts b/packages/core/src/integration-map/integration-map.entity.ts index 3dd2a3c8ba3..e215ba1d7df 100644 --- a/packages/core/src/integration-map/integration-map.entity.ts +++ b/packages/core/src/integration-map/integration-map.entity.ts @@ -33,7 +33,8 @@ export class IntegrationMap extends TenantOrganizationBaseEntity implements IInt */ @ApiProperty({ type: () => IntegrationTenant }) @ManyToOne(() => IntegrationTenant, (it) => it.entityMaps, { - onDelete: 'CASCADE' + /** Database cascade action on delete. */ + onDelete: 'CASCADE', }) @JoinColumn() integration: IIntegrationTenant; diff --git a/packages/core/src/integration-setting/integration-setting.entity.ts b/packages/core/src/integration-setting/integration-setting.entity.ts index a68eea63c32..9c0632463f0 100644 --- a/packages/core/src/integration-setting/integration-setting.entity.ts +++ b/packages/core/src/integration-setting/integration-setting.entity.ts @@ -28,8 +28,9 @@ export class IntegrationSetting extends TenantOrganizationBaseEntity implements * IntegrationTenant */ @ApiProperty({ type: () => IntegrationTenant }) - @ManyToOne(() => IntegrationTenant, (integrationTenant) => integrationTenant.settings, { - onDelete: 'CASCADE' + @ManyToOne(() => IntegrationTenant, (it) => it.settings, { + /** Database cascade action on delete. */ + onDelete: 'CASCADE', }) @JoinColumn() integration?: IntegrationTenant; diff --git a/packages/core/src/integration-tenant/integration-tenant.entity.ts b/packages/core/src/integration-tenant/integration-tenant.entity.ts index 594d10f0aac..86c060c946b 100644 --- a/packages/core/src/integration-tenant/integration-tenant.entity.ts +++ b/packages/core/src/integration-tenant/integration-tenant.entity.ts @@ -29,8 +29,12 @@ export class IntegrationTenant extends TenantOrganizationBaseEntity implements I * Integration */ @ManyToOne(() => Integration, { + + /** Indicates if relation column value can be nullable or not. */ nullable: true, - onDelete: 'CASCADE' + + /** Database cascade action on delete. */ + onDelete: 'CASCADE', }) @JoinColumn() integration?: IIntegration; diff --git a/packages/core/src/integration/github/dto/github-app-install.dto.ts b/packages/core/src/integration/github/dto/github-app-install.dto.ts new file mode 100644 index 00000000000..accc990dede --- /dev/null +++ b/packages/core/src/integration/github/dto/github-app-install.dto.ts @@ -0,0 +1,17 @@ +import { IGithubAppInstallInput } from "@gauzy/contracts"; +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsString } from "class-validator"; +import { TenantOrganizationBaseDTO } from "core/dto"; + +export class GithubAppInstallDTO extends TenantOrganizationBaseDTO implements IGithubAppInstallInput { + + @ApiProperty({ type: () => String }) + @IsNotEmpty() + @IsString() + readonly installation_id: string; + + @ApiProperty({ type: () => String }) + @IsNotEmpty() + @IsString() + readonly setup_action: string; +} diff --git a/packages/core/src/integration/github/dto/index.ts b/packages/core/src/integration/github/dto/index.ts new file mode 100644 index 00000000000..95f85018e37 --- /dev/null +++ b/packages/core/src/integration/github/dto/index.ts @@ -0,0 +1 @@ +export * from './github-app-install.dto'; diff --git a/packages/core/src/integration/github/github.controller.ts b/packages/core/src/integration/github/github.controller.ts index 7b69377ac4b..0b79bbdf747 100644 --- a/packages/core/src/integration/github/github.controller.ts +++ b/packages/core/src/integration/github/github.controller.ts @@ -1,8 +1,9 @@ -import { Controller, Post, Body, UseGuards, HttpException, HttpStatus, HttpCode } from '@nestjs/common'; +import { Controller, Post, Body, UseGuards, HttpException, HttpStatus, HttpCode, UsePipes, ValidationPipe } from '@nestjs/common'; import { IGithubAppInstallInput, PermissionsEnum } from '@gauzy/contracts'; import { PermissionGuard, TenantPermissionGuard } from 'shared/guards'; import { Permissions } from 'shared/decorators'; import { GithubService } from './github.service'; +import { GithubAppInstallDTO } from './dto'; @UseGuards(TenantPermissionGuard, PermissionGuard) @Permissions(PermissionsEnum.INTEGRATION_VIEW) @@ -19,8 +20,10 @@ export class GitHubController { */ @Post('install') @HttpCode(HttpStatus.CREATED) - // ToDo - Create Class Validation DTO to validate request - async addGithubAppInstallation(@Body() input: IGithubAppInstallInput) { + @UsePipes(new ValidationPipe()) + async addGithubAppInstallation( + @Body() input: GithubAppInstallDTO + ) { try { // Validate the input data (You can use class-validator for validation) if (!input || !input.installation_id || !input.setup_action) { From d75a3d994d48b1511c5942106b38cecfd3c1ea40 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sun, 24 Sep 2023 18:37:12 +0530 Subject: [PATCH 076/104] fix: #6734 integration tenant create only one time --- ...840-SeedIntegrationsAndIntegrationTypes.ts | 2 + .../commands/handlers/index.ts | 4 +- ...egration-tenant-first-or-create.handler.ts | 33 +++++++++++ ...gration-tenant-update-or-create.handler.ts | 56 ------------------- .../integration-tenant.create.handler.ts | 6 +- .../integration-tenant.get.handler.ts | 20 +++++-- .../integration-tenant.update.handler.ts | 8 ++- .../src/integration-tenant/commands/index.ts | 2 +- ...gration-tenant-first-or-create.command.ts} | 6 +- .../integration-tenant.service.ts | 8 +-- .../src/integration/github/github.service.ts | 10 +++- packages/core/src/integration/utils.ts | 1 - 12 files changed, 76 insertions(+), 80 deletions(-) create mode 100644 packages/core/src/integration-tenant/commands/handlers/integration-tenant-first-or-create.handler.ts delete mode 100644 packages/core/src/integration-tenant/commands/handlers/integration-tenant-update-or-create.handler.ts rename packages/core/src/integration-tenant/commands/{integration-tenant-update-or-create.command.ts => integration-tenant-first-or-create.command.ts} (56%) diff --git a/packages/core/src/database/migrations/1695112275840-SeedIntegrationsAndIntegrationTypes.ts b/packages/core/src/database/migrations/1695112275840-SeedIntegrationsAndIntegrationTypes.ts index 6d1e4157ff0..56791f3e9d3 100644 --- a/packages/core/src/database/migrations/1695112275840-SeedIntegrationsAndIntegrationTypes.ts +++ b/packages/core/src/database/migrations/1695112275840-SeedIntegrationsAndIntegrationTypes.ts @@ -1,5 +1,6 @@ import { MigrationInterface, QueryRunner } from "typeorm"; +import { IntegrationTypeEnum } from "@gauzy/contracts"; import { DEFAULT_INTEGRATIONS, PROJECT_MANAGE_DEFAULT_INTEGRATIONS } from "./../../integration/default-integration"; import { IntegrationsUtils } from "./../../integration/utils"; @@ -13,6 +14,7 @@ export class SeedIntegrationsAndIntegrationTypes1695112275840 implements Migrati * @param queryRunner */ public async up(queryRunner: QueryRunner): Promise { + await IntegrationsUtils.upsertIntegrationTypes(queryRunner, [IntegrationTypeEnum.PROJECT_MANAGEMENT]); await IntegrationsUtils.upsertIntegrationsAndIntegrationTypes(queryRunner, PROJECT_MANAGE_DEFAULT_INTEGRATIONS); await IntegrationsUtils.upsertIntegrationsAndIntegrationTypes(queryRunner, DEFAULT_INTEGRATIONS); } diff --git a/packages/core/src/integration-tenant/commands/handlers/index.ts b/packages/core/src/integration-tenant/commands/handlers/index.ts index 76510b8388b..c17d21b0cb7 100644 --- a/packages/core/src/integration-tenant/commands/handlers/index.ts +++ b/packages/core/src/integration-tenant/commands/handlers/index.ts @@ -1,11 +1,11 @@ -import { IntegrationTenantUpdateOrCreateHandler } from './integration-tenant-update-or-create.handler'; import { IntegrationTenantCreateHandler } from './integration-tenant.create.handler'; +import { IntegrationTenantFirstOrCreateHandler } from './integration-tenant-first-or-create.handler'; import { IntegrationTenantGetHandler } from './integration-tenant.get.handler'; import { IntegrationTenantUpdateHandler } from './integration-tenant.update.handler'; export const CommandHandlers = [ IntegrationTenantCreateHandler, + IntegrationTenantFirstOrCreateHandler, IntegrationTenantGetHandler, IntegrationTenantUpdateHandler, - IntegrationTenantUpdateOrCreateHandler ]; diff --git a/packages/core/src/integration-tenant/commands/handlers/integration-tenant-first-or-create.handler.ts b/packages/core/src/integration-tenant/commands/handlers/integration-tenant-first-or-create.handler.ts new file mode 100644 index 00000000000..ddeea89d1dd --- /dev/null +++ b/packages/core/src/integration-tenant/commands/handlers/integration-tenant-first-or-create.handler.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { CommandBus, CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { IntegrationTenantCreateCommand } from '../integration-tenant.create.command'; +import { IntegrationTenantFirstOrCreateCommand } from '../integration-tenant-first-or-create.command'; +import { IntegrationTenantGetCommand } from '../integration-tenant.get.command'; + +@Injectable() +@CommandHandler(IntegrationTenantFirstOrCreateCommand) +export class IntegrationTenantFirstOrCreateHandler implements ICommandHandler { + + constructor( + private readonly _commandBus: CommandBus + ) { } + + public async execute( + command: IntegrationTenantFirstOrCreateCommand + ) { + const { options, input } = command; + try { + return await this._commandBus.execute( + new IntegrationTenantGetCommand({ + where: { + ...options + } + }) + ); + } catch (error) { + return await this._commandBus.execute( + new IntegrationTenantCreateCommand(input) + ); + } + } +} diff --git a/packages/core/src/integration-tenant/commands/handlers/integration-tenant-update-or-create.handler.ts b/packages/core/src/integration-tenant/commands/handlers/integration-tenant-update-or-create.handler.ts deleted file mode 100644 index 58b4c59ddb3..00000000000 --- a/packages/core/src/integration-tenant/commands/handlers/integration-tenant-update-or-create.handler.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { forwardRef, Inject } from '@nestjs/common'; -import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; -import { IntegrationTenantService } from 'integration-tenant/integration-tenant.service'; -import { IntegrationTenantUpdateOrCreateCommand } from '../integration-tenant-update-or-create.command'; - -@CommandHandler(IntegrationTenantUpdateOrCreateCommand) -export class IntegrationTenantUpdateOrCreateHandler implements ICommandHandler { - - constructor( - @Inject(forwardRef(() => IntegrationTenantService)) - private readonly _integrationTenantService: IntegrationTenantService - ) { } - - public async execute( - event: IntegrationTenantUpdateOrCreateCommand - ) { - const { options, input } = event; - console.log({ options, input }, this._integrationTenantService); - - // const { options, input = {} as IImportRecord } = event; - // const payload = Object.assign({}, options, input) as IImportRecord; - - // const { - // sourceId, - // destinationId, - // entityType, - // tenantId = RequestContext.currentTenantId() - // } = payload; - - // try { - // const record = await this._importRecordService.findOneByWhereOptions(options); - // if (record) { - // return { - // ...await this._importRecordService.create({ - // id: record.id, - // tenantId, - // sourceId, - // destinationId, - // entityType - // }), - // wasCreated: false - // }; - // } - // } catch (error) { - // return { - // ...await this._importRecordService.create({ - // tenantId, - // sourceId, - // destinationId, - // entityType - // }), - // wasCreated: true - // }; - // } - } -} diff --git a/packages/core/src/integration-tenant/commands/handlers/integration-tenant.create.handler.ts b/packages/core/src/integration-tenant/commands/handlers/integration-tenant.create.handler.ts index 68e1d04c9ae..e2760d6f0d0 100644 --- a/packages/core/src/integration-tenant/commands/handlers/integration-tenant.create.handler.ts +++ b/packages/core/src/integration-tenant/commands/handlers/integration-tenant.create.handler.ts @@ -1,4 +1,4 @@ -import { BadRequestException } from '@nestjs/common'; +import { HttpException, HttpStatus } from '@nestjs/common'; import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { IntegrationTenantCreateCommand } from '../../commands/integration-tenant.create.command'; import { IntegrationTenantService } from '../../integration-tenant.service'; @@ -18,7 +18,9 @@ export class IntegrationTenantCreateHandler implements ICommandHandler { - constructor(private _integrationTenantService: IntegrationTenantService) {} +export class IntegrationTenantGetHandler implements ICommandHandler { + + constructor( + private readonly _integrationTenantService: IntegrationTenantService + ) { } public async execute( command: IntegrationTenantGetCommand ): Promise { - const { input } = command; - return await this._integrationTenantService.findOneByOptions(input); + try { + const { input } = command; + console.log({ input }); + return await this._integrationTenantService.findOneByOptions(input); + } catch (error) { + console.log('Error while getting integration tenant: %s', error.message); + // Handle errors and return an appropriate error response + throw new HttpException(`Failed to get integration tenant: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); + } } } diff --git a/packages/core/src/integration-tenant/commands/handlers/integration-tenant.update.handler.ts b/packages/core/src/integration-tenant/commands/handlers/integration-tenant.update.handler.ts index 4f15ed012b8..22c0197d6e7 100644 --- a/packages/core/src/integration-tenant/commands/handlers/integration-tenant.update.handler.ts +++ b/packages/core/src/integration-tenant/commands/handlers/integration-tenant.update.handler.ts @@ -1,4 +1,4 @@ -import { BadRequestException } from '@nestjs/common'; +import { HttpException, HttpStatus } from '@nestjs/common'; import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { IntegrationTenantUpdateCommand } from '../../commands'; import { IntegrationTenantService } from '../../integration-tenant.service'; @@ -16,11 +16,13 @@ export class IntegrationTenantUpdateHandler implements ICommandHandler { try { const { id, input } = command; - await this._integrationTenantService.update(id, input); + return await this._integrationTenantService.findOneByIdString(id); } catch (error) { - throw new BadRequestException(error); + // Handle errors and return an appropriate error response + console.log(`Failed to update integration tenant: %s`, error.message); + throw new HttpException(`Failed to update integration tenant: ${error.message}`, HttpStatus.BAD_REQUEST); } } } diff --git a/packages/core/src/integration-tenant/commands/index.ts b/packages/core/src/integration-tenant/commands/index.ts index 83e6a0643cf..2418cc0e903 100644 --- a/packages/core/src/integration-tenant/commands/index.ts +++ b/packages/core/src/integration-tenant/commands/index.ts @@ -1,4 +1,4 @@ export * from './integration-tenant.create.command'; export * from './integration-tenant.get.command'; export * from './integration-tenant.update.command'; -export * from './integration-tenant-update-or-create.command'; +export * from './integration-tenant-first-or-create.command'; diff --git a/packages/core/src/integration-tenant/commands/integration-tenant-update-or-create.command.ts b/packages/core/src/integration-tenant/commands/integration-tenant-first-or-create.command.ts similarity index 56% rename from packages/core/src/integration-tenant/commands/integration-tenant-update-or-create.command.ts rename to packages/core/src/integration-tenant/commands/integration-tenant-first-or-create.command.ts index 33fdae384d6..aa58008bd08 100644 --- a/packages/core/src/integration-tenant/commands/integration-tenant-update-or-create.command.ts +++ b/packages/core/src/integration-tenant/commands/integration-tenant-first-or-create.command.ts @@ -1,10 +1,10 @@ import { ICommand } from '@nestjs/cqrs'; import { FindOptionsWhere } from 'typeorm'; import { IIntegrationTenant, } from '@gauzy/contracts'; -import { IntegrationTenant } from './../integration-tenant.entity'; +import { IntegrationTenant } from '../integration-tenant.entity'; -export class IntegrationTenantUpdateOrCreateCommand implements ICommand { - static readonly type = '[Update Or Create] Integration Tenant'; +export class IntegrationTenantFirstOrCreateCommand implements ICommand { + static readonly type = '[Integration Tenant] First Or Create'; constructor( public readonly options: FindOptionsWhere, diff --git a/packages/core/src/integration-tenant/integration-tenant.service.ts b/packages/core/src/integration-tenant/integration-tenant.service.ts index 197ccb0e806..66585ee4147 100644 --- a/packages/core/src/integration-tenant/integration-tenant.service.ts +++ b/packages/core/src/integration-tenant/integration-tenant.service.ts @@ -30,10 +30,9 @@ export class IntegrationTenantService extends TenantAwareCrudService { - const tenantId = RequestContext.currentTenantId(); try { - const { organizationId } = input; - let { entitySettings = [], settings = [] } = input; + const tenantId = RequestContext.currentTenantId() || input.tenantId; + let { organizationId, entitySettings = [], settings = [] } = input; settings = settings.map((item: IIntegrationSetting) => ({ ...item, @@ -50,11 +49,12 @@ export class IntegrationTenantService extends TenantAwareCrudService { try { - await this.upsertIntegrationTypes(queryRunner, integrationTypeNames); return await queryRunner.query(`SELECT * FROM "integration_type" WHERE "integration_type"."name" IN ('${integrationTypeNames.join("','")}')`); } catch (error) { console.log('Error while querying integration types:', error); From 9d6a5f0e7453233c0726b40eb816234ac3bb9786 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sun, 24 Sep 2023 19:15:13 +0530 Subject: [PATCH 077/104] fix: #6734 implemented integration tenant first or create --- .../integration-tenant.get.handler.ts | 2 - .../gauzy-ai/integration-ai.service.ts | 70 ++++++++++------- .../src/integration/github/github.service.ts | 75 +++++++++++-------- .../integration/hubstaff/hubstaff.service.ts | 17 ++++- packages/core/src/upwork/upwork.service.ts | 17 ++++- 5 files changed, 117 insertions(+), 64 deletions(-) diff --git a/packages/core/src/integration-tenant/commands/handlers/integration-tenant.get.handler.ts b/packages/core/src/integration-tenant/commands/handlers/integration-tenant.get.handler.ts index 02f2819b0f3..87324726cc9 100644 --- a/packages/core/src/integration-tenant/commands/handlers/integration-tenant.get.handler.ts +++ b/packages/core/src/integration-tenant/commands/handlers/integration-tenant.get.handler.ts @@ -16,10 +16,8 @@ export class IntegrationTenantGetHandler implements ICommandHandler { try { const { input } = command; - console.log({ input }); return await this._integrationTenantService.findOneByOptions(input); } catch (error) { - console.log('Error while getting integration tenant: %s', error.message); // Handle errors and return an appropriate error response throw new HttpException(`Failed to get integration tenant: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); } diff --git a/packages/core/src/integration/gauzy-ai/integration-ai.service.ts b/packages/core/src/integration/gauzy-ai/integration-ai.service.ts index ea297ec109d..5e2be9f4a19 100644 --- a/packages/core/src/integration/gauzy-ai/integration-ai.service.ts +++ b/packages/core/src/integration/gauzy-ai/integration-ai.service.ts @@ -1,12 +1,13 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { CommandBus } from '@nestjs/cqrs'; import { IIntegrationKeySecretPairInput, IIntegrationTenant, IntegrationEnum } from '@gauzy/contracts'; import { RequestContext } from '../../core/context'; -import { IntegrationTenantCreateCommand } from '../../integration-tenant/commands'; +import { IntegrationTenantFirstOrCreateCommand } from '../../integration-tenant/commands'; import { IntegrationService } from './../../integration/integration.service'; @Injectable() export class IntegrationAIService { + private readonly logger = new Logger('IntegrationAIService'); constructor( private readonly _commandBus: CommandBus, @@ -23,34 +24,53 @@ export class IntegrationAIService { input: IIntegrationKeySecretPairInput ): Promise { - const tenantId = RequestContext.currentTenantId(); - const { client_id, client_secret, organizationId } = input; + try { + const tenantId = RequestContext.currentTenantId(); + const { client_id, client_secret, organizationId } = input; - const integration = await this._integrationService.findOneByOptions({ - where: { - provider: IntegrationEnum.GAUZY_AI - } - }); + const integration = await this._integrationService.findOneByOptions({ + where: { + provider: IntegrationEnum.GAUZY_AI + } + }); - return await this._commandBus.execute( - new IntegrationTenantCreateCommand({ - name: IntegrationEnum.GAUZY_AI, - integration, - entitySettings: [], - settings: [ + /** Execute the command to create the integration tenant settings */ + return await this._commandBus.execute( + new IntegrationTenantFirstOrCreateCommand( { - settingsName: 'apiKey', - settingsValue: client_id, - + name: IntegrationEnum.GAUZY_AI, + integration: { + provider: IntegrationEnum.GAUZY_AI + }, + tenantId, + organizationId, }, { - settingsName: 'apiSecret', - settingsValue: client_secret + name: IntegrationEnum.GAUZY_AI, + integration, + organizationId, + tenantId, + entitySettings: [], + settings: [ + { + settingsName: 'apiKey', + settingsValue: client_id + }, + { + settingsName: 'apiSecret', + settingsValue: client_secret + } + ].map((setting) => ({ + ...setting, + tenantId, + organizationId, + })) } - ], - organizationId, - tenantId, - }) - ); + ) + ); + } catch (error) { + this.logger.error(`Error while creating ${IntegrationEnum.GAUZY_AI} integration settings`, error?.message); + throw new Error(`Failed to add ${IntegrationEnum.GAUZY_AI} integration`); + } } } diff --git a/packages/core/src/integration/github/github.service.ts b/packages/core/src/integration/github/github.service.ts index 963ff2ecad4..7665d3144ba 100644 --- a/packages/core/src/integration/github/github.service.ts +++ b/packages/core/src/integration/github/github.service.ts @@ -5,7 +5,7 @@ import { catchError, lastValueFrom, switchMap } from 'rxjs'; import { filter } from 'rxjs/operators'; import { environment } from '@gauzy/config'; import { IGithubAppInstallInput, IIntegrationTenant, IntegrationEnum } from '@gauzy/contracts'; -import { IntegrationTenantCreateCommand, IntegrationTenantFirstOrCreateCommand } from 'integration-tenant/commands'; +import { IntegrationTenantFirstOrCreateCommand } from 'integration-tenant/commands'; import { IntegrationService } from 'integration/integration.service'; import { RequestContext } from '../../core/context'; import { GITHUB_ACCESS_TOKEN_URL } from './github.config'; @@ -36,7 +36,6 @@ export class GithubService { * @returns */ async addGithubAppInstallation(input: IGithubAppInstallInput) { - console.log(input); try { // Validate the input data (You can use class-validator for validation) if (!input || !input.installation_id || !input.setup_action) { @@ -71,22 +70,22 @@ export class GithubService { settings: [ { settingsName: 'installation_id', - settingsValue: installation_id, - tenantId, - organizationId + settingsValue: installation_id }, { settingsName: 'setup_action', - settingsValue: setup_action, - tenantId, - organizationId + settingsValue: setup_action }, - ] + ].map((setting) => ({ + ...setting, + tenantId, + organizationId, + })) }) ); } catch (error) { - this.logger.error('Error while creating GitHub integration settings', error?.message); - throw new Error('Failed to add GitHub App Installation'); + this.logger.error(`Error while creating ${IntegrationEnum.GAUZY_AI} integration settings`, error?.message); + throw new Error(`Failed to add ${IntegrationEnum.GAUZY_AI} App Installation`); } } @@ -123,27 +122,41 @@ export class GithubService { }).pipe( filter(({ data }) => !!data.error), switchMap(({ data }) => this._commandBus.execute( - new IntegrationTenantCreateCommand({ - name: IntegrationEnum.GITHUB, - integration, - tenantId, - organizationId, - entitySettings: [], - settings: [ - { - settingsName: 'token_type', - settingsValue: data.token_type - }, - { - settingsName: 'access_token', - settingsValue: data.access_token + new IntegrationTenantFirstOrCreateCommand( + { + name: IntegrationEnum.GITHUB, + integration: { + provider: IntegrationEnum.GITHUB }, - { - settingsName: 'scope', - settingsValue: data.scope - } - ] - }) + tenantId, + organizationId, + }, + { + name: IntegrationEnum.GITHUB, + integration, + tenantId, + organizationId, + entitySettings: [], + settings: [ + { + settingsName: 'token_type', + settingsValue: data.token_type + }, + { + settingsName: 'access_token', + settingsValue: data.access_token + }, + { + settingsName: 'scope', + settingsValue: data.scope + } + ].map((setting) => ({ + ...setting, + tenantId, + organizationId, + })) + } + ) )), catchError((error) => { throw new BadRequestException(error); diff --git a/packages/core/src/integration/hubstaff/hubstaff.service.ts b/packages/core/src/integration/hubstaff/hubstaff.service.ts index 94e4c603983..fe4d55c8690 100644 --- a/packages/core/src/integration/hubstaff/hubstaff.service.ts +++ b/packages/core/src/integration/hubstaff/hubstaff.service.ts @@ -67,7 +67,7 @@ import { IntegrationMapSyncTimeSlotCommand } from 'integration-map/commands'; import { IntegrationTenantService } from 'integration-tenant/integration-tenant.service'; -import { IntegrationTenantCreateCommand } from 'integration-tenant/commands'; +import { IntegrationTenantFirstOrCreateCommand } from 'integration-tenant/commands'; import { IntegrationService } from 'integration/integration.service'; @Injectable() @@ -235,7 +235,14 @@ export class HubstaffService { } }).pipe( switchMap(({ data }) => this._commandBus.execute( - new IntegrationTenantCreateCommand({ + new IntegrationTenantFirstOrCreateCommand({ + name: IntegrationEnum.HUBSTAFF, + integration: { + provider: IntegrationEnum.HUBSTAFF + }, + tenantId, + organizationId, + }, { name: IntegrationEnum.HUBSTAFF, integration, organizationId, @@ -258,7 +265,11 @@ export class HubstaffService { settingsName: 'refresh_token', settingsValue: data.refresh_token } - ] + ].map((setting) => ({ + ...setting, + tenantId, + organizationId, + })) }) )), catchError((err) => { diff --git a/packages/core/src/upwork/upwork.service.ts b/packages/core/src/upwork/upwork.service.ts index 7b1b56f3aea..6919ab4e1d7 100644 --- a/packages/core/src/upwork/upwork.service.ts +++ b/packages/core/src/upwork/upwork.service.ts @@ -34,7 +34,7 @@ import { ITimeLog } from '@gauzy/contracts'; import { - IntegrationTenantCreateCommand, + IntegrationTenantFirstOrCreateCommand, IntegrationTenantGetCommand } from '../integration-tenant/commands'; import { @@ -179,7 +179,14 @@ export class UpworkService { } await this.commandBus.execute( - new IntegrationTenantCreateCommand({ + new IntegrationTenantFirstOrCreateCommand({ + name: IntegrationEnum.UPWORK, + integration: { + provider: IntegrationEnum.UPWORK + }, + tenantId, + organizationId, + }, { tenantId, organizationId, name: IntegrationEnum.UPWORK, @@ -201,7 +208,11 @@ export class UpworkService { settingsName: 'requestTokenSecret', settingsValue: requestTokenSecret } - ] + ].map((setting) => ({ + ...setting, + tenantId, + organizationId, + })) }) ); return resolve({ From 9b92bca4889ba305fc05f6724a299cf1e8068fa8 Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Sun, 24 Sep 2023 19:50:15 +0200 Subject: [PATCH 078/104] feat: add new attribute to entity task --- packages/contracts/src/task.model.ts | 17 ++-- packages/core/src/tasks/task.entity.ts | 108 ++++++++++++++++++++----- 2 files changed, 99 insertions(+), 26 deletions(-) diff --git a/packages/contracts/src/task.model.ts b/packages/contracts/src/task.model.ts index 4c47bffb193..222937cdd0c 100644 --- a/packages/contracts/src/task.model.ts +++ b/packages/contracts/src/task.model.ts @@ -9,9 +9,9 @@ import { IOrganizationSprint } from './organization-sprint.model'; import { IOrganizationTeam } from './organization-team.model'; import { ITag } from './tag.model'; import { IUser } from './user.model'; -import { TaskStatusEnum } from './task-status.model'; -import { TaskPriorityEnum } from './task-priority.model'; -import { TaskSizeEnum } from './task-size.model'; +import { ITaskStatus, TaskStatusEnum } from './task-status.model'; +import { ITaskPriority, TaskPriorityEnum } from './task-priority.model'; +import { ITaskSize, TaskSizeEnum } from './task-size.model'; export interface ITask extends IBasePerTenantAndOrganizationEntityModel { title: string; @@ -37,6 +37,13 @@ export interface ITask extends IBasePerTenantAndOrganizationEntityModel { parent?: ITask; parentId?: ITask['id']; // Optional field for specifying the parent task ID children?: ITask[]; + + taskStatus?: ITaskStatus; + taskSize?: ITaskSize; + taskPriority?: ITaskPriority; + taskStatusId?: ITaskStatus['id']; + taskSizeId?: ITaskSize['id']; + taskPriorityId?: ITaskPriority['id']; } export interface IGetTaskOptions @@ -48,14 +55,14 @@ export interface IGetTaskByEmployeeOptions extends IBaseRelationsEntityModel { where?: IGetTaskOptions; } -export interface IGetSprintsOptions extends IGetTaskOptions {} +export type IGetSprintsOptions = IGetTaskOptions; export enum TaskParticipantEnum { EMPLOYEES = 'employees', TEAMS = 'teams', } -export interface ITaskCreateInput extends ITask {} +export type ITaskCreateInput = ITask; export interface ITaskUpdateInput extends ITaskCreateInput { id?: string; diff --git a/packages/core/src/tasks/task.entity.ts b/packages/core/src/tasks/task.entity.ts index 85b01e1a2b4..61ea93518ef 100644 --- a/packages/core/src/tasks/task.entity.ts +++ b/packages/core/src/tasks/task.entity.ts @@ -1,13 +1,13 @@ import { - Entity, Column, - ManyToOne, + Entity, + Index, JoinColumn, - RelationId, - OneToMany, - ManyToMany, JoinTable, - Index, + ManyToMany, + ManyToOne, + OneToMany, + RelationId, } from 'typeorm'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { @@ -29,6 +29,9 @@ import { IOrganizationTeam, ITag, ITask, + ITaskPriority, + ITaskSize, + ITaskStatus, ITimeLog, IUser, TaskPriorityEnum, @@ -42,13 +45,16 @@ import { OrganizationProject, OrganizationSprint, OrganizationTeam, + OrganizationTeamEmployee, Tag, + TaskEstimation, + TaskLinkedIssue, + TaskPriority, + TaskSize, + TaskStatus, TenantOrganizationBaseEntity, TimeLog, User, - TaskEstimation, - TaskLinkedIssue, - OrganizationTeamEmployee, } from '../core/entities/internal'; @Entity('task') @@ -143,10 +149,10 @@ export class Task extends TenantOrganizationBaseEntity implements ITask { taskNumber?: string; /* - |-------------------------------------------------------------------------- - | @ManyToOne - |-------------------------------------------------------------------------- - */ + |-------------------------------------------------------------------------- + | @ManyToOne + |-------------------------------------------------------------------------- + */ // Define the parent-child relationship @ApiPropertyOptional({ type: () => Task }) @@ -217,11 +223,71 @@ export class Task extends TenantOrganizationBaseEntity implements ITask { @Column({ nullable: true }) organizationSprintId?: IOrganizationSprint['id']; + /** + * Task Status + */ + @ApiPropertyOptional({ type: () => Object }) + @IsOptional() + @IsObject() + @ManyToOne(() => TaskStatus, { + onDelete: 'SET NULL', + }) + @JoinColumn() + taskStatus?: ITaskStatus; + + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @IsUUID() + @RelationId((it: Task) => it.taskStatus) + @Index() + @Column({ nullable: true, type: 'varchar' }) + taskStatusId?: ITaskStatus['id']; + + /** + * Task Size + */ + @ApiPropertyOptional({ type: () => Object }) + @IsOptional() + @IsObject() + @ManyToOne(() => TaskSize, { + onDelete: 'SET NULL', + }) + @JoinColumn() + taskSize?: ITaskSize; + + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @IsUUID() + @RelationId((it: Task) => it.taskSize) + @Index() + @Column({ nullable: true, type: 'varchar' }) + taskSizeId?: ITaskSize['id']; + + /** + * Task Priority + */ + @ApiPropertyOptional({ type: () => Object }) + @IsOptional() + @IsObject() + @ManyToOne(() => TaskPriority, { + onDelete: 'SET NULL', + }) + @JoinColumn() + taskPriority?: ITaskPriority; + + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @IsUUID() + @RelationId((it: Task) => it.taskPriority) + @Index() + @Column({ nullable: true, type: 'varchar' }) + taskPriorityId?: ITaskPriority['id']; + /* - |-------------------------------------------------------------------------- - | @OneToMany - |-------------------------------------------------------------------------- - */ + |-------------------------------------------------------------------------- + | @OneToMany + |-------------------------------------------------------------------------- + */ /** * Organization Team Employees @@ -270,10 +336,10 @@ export class Task extends TenantOrganizationBaseEntity implements ITask { linkedIssues?: TaskLinkedIssue[]; /* - |-------------------------------------------------------------------------- - | @ManyToMany - |-------------------------------------------------------------------------- - */ + |-------------------------------------------------------------------------- + | @ManyToMany + |-------------------------------------------------------------------------- + */ /** * Tags From 04ac8b612c4484193105d9077452937360b75e08 Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Sun, 24 Sep 2023 19:51:36 +0200 Subject: [PATCH 079/104] feat: alter table task and generate migration file --- .../1695570009125-AlterTableTask.ts | 347 ++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 packages/core/src/database/migrations/1695570009125-AlterTableTask.ts diff --git a/packages/core/src/database/migrations/1695570009125-AlterTableTask.ts b/packages/core/src/database/migrations/1695570009125-AlterTableTask.ts new file mode 100644 index 00000000000..64d176c8948 --- /dev/null +++ b/packages/core/src/database/migrations/1695570009125-AlterTableTask.ts @@ -0,0 +1,347 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterTableTask1695570009125 implements MigrationInterface { + name = 'AlterTableTask1695570009125'; + + /** + * Up Migration + * + * @param queryRunner + */ + public async up(queryRunner: QueryRunner): Promise { + if (queryRunner.connection.options.type === 'sqlite') { + await this.sqliteUpQueryRunner(queryRunner); + } else { + await this.postgresUpQueryRunner(queryRunner); + } + } + + /** + * Down Migration + * + * @param queryRunner + */ + public async down(queryRunner: QueryRunner): Promise { + if (queryRunner.connection.options.type === 'sqlite') { + await this.sqliteDownQueryRunner(queryRunner); + } else { + await this.postgresDownQueryRunner(queryRunner); + } + } + + /** + * PostgresDB Up Migration + * + * @param queryRunner + */ + public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "task" ADD "taskStatusId" uuid`); + await queryRunner.query(`ALTER TABLE "task" ADD "taskSizeId" uuid`); + await queryRunner.query(`ALTER TABLE "task" ADD "taskPriorityId" uuid`); + await queryRunner.query( + `CREATE INDEX "IDX_0cbe714983eb0aae5feeee8212" ON "task" ("taskStatusId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_2f4bdd2593fd6038aaa91fd107" ON "task" ("taskSizeId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_b8616deefe44d0622233e73fbf" ON "task" ("taskPriorityId") ` + ); + await queryRunner.query( + `ALTER TABLE "task" ADD CONSTRAINT "FK_0cbe714983eb0aae5feeee8212b" FOREIGN KEY ("taskStatusId") REFERENCES "task_status"("id") ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE "task" ADD CONSTRAINT "FK_2f4bdd2593fd6038aaa91fd1076" FOREIGN KEY ("taskSizeId") REFERENCES "task_size"("id") ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE "task" ADD CONSTRAINT "FK_b8616deefe44d0622233e73fbf9" FOREIGN KEY ("taskPriorityId") REFERENCES "task_priority"("id") ON DELETE SET NULL ON UPDATE NO ACTION` + ); + } + + /** + * PostgresDB Down Migration + * + * @param queryRunner + */ + public async postgresDownQueryRunner( + queryRunner: QueryRunner + ): Promise { + await queryRunner.query( + `ALTER TABLE "task" DROP CONSTRAINT "FK_b8616deefe44d0622233e73fbf9"` + ); + await queryRunner.query( + `ALTER TABLE "task" DROP CONSTRAINT "FK_2f4bdd2593fd6038aaa91fd1076"` + ); + await queryRunner.query( + `ALTER TABLE "task" DROP CONSTRAINT "FK_0cbe714983eb0aae5feeee8212b"` + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_b8616deefe44d0622233e73fbf"` + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_2f4bdd2593fd6038aaa91fd107"` + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_0cbe714983eb0aae5feeee8212"` + ); + await queryRunner.query( + `ALTER TABLE "task" DROP COLUMN "taskPriorityId"` + ); + await queryRunner.query(`ALTER TABLE "task" DROP COLUMN "taskSizeId"`); + await queryRunner.query( + `ALTER TABLE "task" DROP COLUMN "taskStatusId"` + ); + } + + /** + * SqliteDB Up Migration + * + * @param queryRunner + */ + public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX "taskNumber"`); + await queryRunner.query(`DROP INDEX "IDX_1e1f64696aa3a26d3e12c840e5"`); + await queryRunner.query(`DROP INDEX "IDX_94fe6b3a5aec5f85427df4f8cd"`); + await queryRunner.query(`DROP INDEX "IDX_3797a20ef5553ae87af126bc2f"`); + await queryRunner.query(`DROP INDEX "IDX_5b0272d923a31c972bed1a1ac4"`); + await queryRunner.query(`DROP INDEX "IDX_e91cbff3d206f150ccc14d0c3a"`); + await queryRunner.query(`DROP INDEX "IDX_2fe7a278e6f08d2be55740a939"`); + await queryRunner.query(`DROP INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1"`); + await queryRunner.query(`DROP INDEX "IDX_7127880d6fae956ecc1c84ac31"`); + await queryRunner.query(`DROP INDEX "IDX_ed5441fb13e82854a994da5a78"`); + await queryRunner.query( + `CREATE TABLE "temporary_task" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "title" varchar NOT NULL, "description" varchar, "status" varchar, "estimate" integer, "dueDate" datetime, "projectId" varchar, "creatorId" varchar, "organizationSprintId" varchar, "number" integer, "prefix" varchar, "priority" varchar, "size" varchar, "public" boolean DEFAULT (1), "startDate" datetime, "resolvedAt" datetime, "version" varchar, "issueType" varchar, "parentId" varchar, "taskStatusId" varchar, "taskSizeId" varchar, "taskPriorityId" varchar, CONSTRAINT "FK_8c9920b5fb32c3d8453f64b705c" FOREIGN KEY ("parentId") REFERENCES "task" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_1e1f64696aa3a26d3e12c840e55" FOREIGN KEY ("organizationSprintId") REFERENCES "organization_sprint" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_94fe6b3a5aec5f85427df4f8cd7" FOREIGN KEY ("creatorId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_3797a20ef5553ae87af126bc2fe" FOREIGN KEY ("projectId") REFERENCES "organization_project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_5b0272d923a31c972bed1a1ac4d" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_e91cbff3d206f150ccc14d0c3a1" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_task"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId" FROM "task"` + ); + await queryRunner.query(`DROP TABLE "task"`); + await queryRunner.query( + `ALTER TABLE "temporary_task" RENAME TO "task"` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "taskNumber" ON "task" ("projectId", "number") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_1e1f64696aa3a26d3e12c840e5" ON "task" ("organizationSprintId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_94fe6b3a5aec5f85427df4f8cd" ON "task" ("creatorId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_3797a20ef5553ae87af126bc2f" ON "task" ("projectId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_5b0272d923a31c972bed1a1ac4" ON "task" ("organizationId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_e91cbff3d206f150ccc14d0c3a" ON "task" ("tenantId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_2fe7a278e6f08d2be55740a939" ON "task" ("status") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1" ON "task" ("priority") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_7127880d6fae956ecc1c84ac31" ON "task" ("size") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_ed5441fb13e82854a994da5a78" ON "task" ("issueType") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_0cbe714983eb0aae5feeee8212" ON "task" ("taskStatusId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_2f4bdd2593fd6038aaa91fd107" ON "task" ("taskSizeId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_b8616deefe44d0622233e73fbf" ON "task" ("taskPriorityId") ` + ); + await queryRunner.query(`DROP INDEX "taskNumber"`); + await queryRunner.query(`DROP INDEX "IDX_1e1f64696aa3a26d3e12c840e5"`); + await queryRunner.query(`DROP INDEX "IDX_94fe6b3a5aec5f85427df4f8cd"`); + await queryRunner.query(`DROP INDEX "IDX_3797a20ef5553ae87af126bc2f"`); + await queryRunner.query(`DROP INDEX "IDX_5b0272d923a31c972bed1a1ac4"`); + await queryRunner.query(`DROP INDEX "IDX_e91cbff3d206f150ccc14d0c3a"`); + await queryRunner.query(`DROP INDEX "IDX_2fe7a278e6f08d2be55740a939"`); + await queryRunner.query(`DROP INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1"`); + await queryRunner.query(`DROP INDEX "IDX_7127880d6fae956ecc1c84ac31"`); + await queryRunner.query(`DROP INDEX "IDX_ed5441fb13e82854a994da5a78"`); + await queryRunner.query(`DROP INDEX "IDX_0cbe714983eb0aae5feeee8212"`); + await queryRunner.query(`DROP INDEX "IDX_2f4bdd2593fd6038aaa91fd107"`); + await queryRunner.query(`DROP INDEX "IDX_b8616deefe44d0622233e73fbf"`); + await queryRunner.query( + `CREATE TABLE "temporary_task" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "title" varchar NOT NULL, "description" varchar, "status" varchar, "estimate" integer, "dueDate" datetime, "projectId" varchar, "creatorId" varchar, "organizationSprintId" varchar, "number" integer, "prefix" varchar, "priority" varchar, "size" varchar, "public" boolean DEFAULT (1), "startDate" datetime, "resolvedAt" datetime, "version" varchar, "issueType" varchar, "parentId" varchar, "taskStatusId" varchar, "taskSizeId" varchar, "taskPriorityId" varchar, CONSTRAINT "FK_8c9920b5fb32c3d8453f64b705c" FOREIGN KEY ("parentId") REFERENCES "task" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_1e1f64696aa3a26d3e12c840e55" FOREIGN KEY ("organizationSprintId") REFERENCES "organization_sprint" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_94fe6b3a5aec5f85427df4f8cd7" FOREIGN KEY ("creatorId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_3797a20ef5553ae87af126bc2fe" FOREIGN KEY ("projectId") REFERENCES "organization_project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_5b0272d923a31c972bed1a1ac4d" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_e91cbff3d206f150ccc14d0c3a1" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_0cbe714983eb0aae5feeee8212b" FOREIGN KEY ("taskStatusId") REFERENCES "task_status" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_2f4bdd2593fd6038aaa91fd1076" FOREIGN KEY ("taskSizeId") REFERENCES "task_size" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_b8616deefe44d0622233e73fbf9" FOREIGN KEY ("taskPriorityId") REFERENCES "task_priority" ("id") ON DELETE SET NULL ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_task"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId", "taskStatusId", "taskSizeId", "taskPriorityId") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId", "taskStatusId", "taskSizeId", "taskPriorityId" FROM "task"` + ); + await queryRunner.query(`DROP TABLE "task"`); + await queryRunner.query( + `ALTER TABLE "temporary_task" RENAME TO "task"` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "taskNumber" ON "task" ("projectId", "number") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_1e1f64696aa3a26d3e12c840e5" ON "task" ("organizationSprintId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_94fe6b3a5aec5f85427df4f8cd" ON "task" ("creatorId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_3797a20ef5553ae87af126bc2f" ON "task" ("projectId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_5b0272d923a31c972bed1a1ac4" ON "task" ("organizationId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_e91cbff3d206f150ccc14d0c3a" ON "task" ("tenantId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_2fe7a278e6f08d2be55740a939" ON "task" ("status") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1" ON "task" ("priority") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_7127880d6fae956ecc1c84ac31" ON "task" ("size") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_ed5441fb13e82854a994da5a78" ON "task" ("issueType") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_0cbe714983eb0aae5feeee8212" ON "task" ("taskStatusId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_2f4bdd2593fd6038aaa91fd107" ON "task" ("taskSizeId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_b8616deefe44d0622233e73fbf" ON "task" ("taskPriorityId") ` + ); + } + + /** + * SqliteDB Down Migration + * + * @param queryRunner + */ + public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX "IDX_b8616deefe44d0622233e73fbf"`); + await queryRunner.query(`DROP INDEX "IDX_2f4bdd2593fd6038aaa91fd107"`); + await queryRunner.query(`DROP INDEX "IDX_0cbe714983eb0aae5feeee8212"`); + await queryRunner.query(`DROP INDEX "IDX_ed5441fb13e82854a994da5a78"`); + await queryRunner.query(`DROP INDEX "IDX_7127880d6fae956ecc1c84ac31"`); + await queryRunner.query(`DROP INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1"`); + await queryRunner.query(`DROP INDEX "IDX_2fe7a278e6f08d2be55740a939"`); + await queryRunner.query(`DROP INDEX "IDX_e91cbff3d206f150ccc14d0c3a"`); + await queryRunner.query(`DROP INDEX "IDX_5b0272d923a31c972bed1a1ac4"`); + await queryRunner.query(`DROP INDEX "IDX_3797a20ef5553ae87af126bc2f"`); + await queryRunner.query(`DROP INDEX "IDX_94fe6b3a5aec5f85427df4f8cd"`); + await queryRunner.query(`DROP INDEX "IDX_1e1f64696aa3a26d3e12c840e5"`); + await queryRunner.query(`DROP INDEX "taskNumber"`); + await queryRunner.query( + `ALTER TABLE "task" RENAME TO "temporary_task"` + ); + await queryRunner.query( + `CREATE TABLE "task" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "title" varchar NOT NULL, "description" varchar, "status" varchar, "estimate" integer, "dueDate" datetime, "projectId" varchar, "creatorId" varchar, "organizationSprintId" varchar, "number" integer, "prefix" varchar, "priority" varchar, "size" varchar, "public" boolean DEFAULT (1), "startDate" datetime, "resolvedAt" datetime, "version" varchar, "issueType" varchar, "parentId" varchar, "taskStatusId" varchar, "taskSizeId" varchar, "taskPriorityId" varchar, CONSTRAINT "FK_8c9920b5fb32c3d8453f64b705c" FOREIGN KEY ("parentId") REFERENCES "task" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_1e1f64696aa3a26d3e12c840e55" FOREIGN KEY ("organizationSprintId") REFERENCES "organization_sprint" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_94fe6b3a5aec5f85427df4f8cd7" FOREIGN KEY ("creatorId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_3797a20ef5553ae87af126bc2fe" FOREIGN KEY ("projectId") REFERENCES "organization_project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_5b0272d923a31c972bed1a1ac4d" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_e91cbff3d206f150ccc14d0c3a1" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "task"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId", "taskStatusId", "taskSizeId", "taskPriorityId") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId", "taskStatusId", "taskSizeId", "taskPriorityId" FROM "temporary_task"` + ); + await queryRunner.query(`DROP TABLE "temporary_task"`); + await queryRunner.query( + `CREATE INDEX "IDX_b8616deefe44d0622233e73fbf" ON "task" ("taskPriorityId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_2f4bdd2593fd6038aaa91fd107" ON "task" ("taskSizeId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_0cbe714983eb0aae5feeee8212" ON "task" ("taskStatusId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_ed5441fb13e82854a994da5a78" ON "task" ("issueType") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_7127880d6fae956ecc1c84ac31" ON "task" ("size") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1" ON "task" ("priority") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_2fe7a278e6f08d2be55740a939" ON "task" ("status") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_e91cbff3d206f150ccc14d0c3a" ON "task" ("tenantId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_5b0272d923a31c972bed1a1ac4" ON "task" ("organizationId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_3797a20ef5553ae87af126bc2f" ON "task" ("projectId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_94fe6b3a5aec5f85427df4f8cd" ON "task" ("creatorId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_1e1f64696aa3a26d3e12c840e5" ON "task" ("organizationSprintId") ` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "taskNumber" ON "task" ("projectId", "number") ` + ); + await queryRunner.query(`DROP INDEX "IDX_b8616deefe44d0622233e73fbf"`); + await queryRunner.query(`DROP INDEX "IDX_2f4bdd2593fd6038aaa91fd107"`); + await queryRunner.query(`DROP INDEX "IDX_0cbe714983eb0aae5feeee8212"`); + await queryRunner.query(`DROP INDEX "IDX_ed5441fb13e82854a994da5a78"`); + await queryRunner.query(`DROP INDEX "IDX_7127880d6fae956ecc1c84ac31"`); + await queryRunner.query(`DROP INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1"`); + await queryRunner.query(`DROP INDEX "IDX_2fe7a278e6f08d2be55740a939"`); + await queryRunner.query(`DROP INDEX "IDX_e91cbff3d206f150ccc14d0c3a"`); + await queryRunner.query(`DROP INDEX "IDX_5b0272d923a31c972bed1a1ac4"`); + await queryRunner.query(`DROP INDEX "IDX_3797a20ef5553ae87af126bc2f"`); + await queryRunner.query(`DROP INDEX "IDX_94fe6b3a5aec5f85427df4f8cd"`); + await queryRunner.query(`DROP INDEX "IDX_1e1f64696aa3a26d3e12c840e5"`); + await queryRunner.query(`DROP INDEX "taskNumber"`); + await queryRunner.query( + `ALTER TABLE "task" RENAME TO "temporary_task"` + ); + await queryRunner.query( + `CREATE TABLE "task" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "title" varchar NOT NULL, "description" varchar, "status" varchar, "estimate" integer, "dueDate" datetime, "projectId" varchar, "creatorId" varchar, "organizationSprintId" varchar, "number" integer, "prefix" varchar, "priority" varchar, "size" varchar, "public" boolean DEFAULT (1), "startDate" datetime, "resolvedAt" datetime, "version" varchar, "issueType" varchar, "parentId" varchar, CONSTRAINT "FK_8c9920b5fb32c3d8453f64b705c" FOREIGN KEY ("parentId") REFERENCES "task" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_1e1f64696aa3a26d3e12c840e55" FOREIGN KEY ("organizationSprintId") REFERENCES "organization_sprint" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_94fe6b3a5aec5f85427df4f8cd7" FOREIGN KEY ("creatorId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_3797a20ef5553ae87af126bc2fe" FOREIGN KEY ("projectId") REFERENCES "organization_project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_5b0272d923a31c972bed1a1ac4d" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_e91cbff3d206f150ccc14d0c3a1" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "task"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId" FROM "temporary_task"` + ); + await queryRunner.query(`DROP TABLE "temporary_task"`); + await queryRunner.query( + `CREATE INDEX "IDX_ed5441fb13e82854a994da5a78" ON "task" ("issueType") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_7127880d6fae956ecc1c84ac31" ON "task" ("size") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1" ON "task" ("priority") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_2fe7a278e6f08d2be55740a939" ON "task" ("status") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_e91cbff3d206f150ccc14d0c3a" ON "task" ("tenantId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_5b0272d923a31c972bed1a1ac4" ON "task" ("organizationId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_3797a20ef5553ae87af126bc2f" ON "task" ("projectId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_94fe6b3a5aec5f85427df4f8cd" ON "task" ("creatorId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_1e1f64696aa3a26d3e12c840e5" ON "task" ("organizationSprintId") ` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "taskNumber" ON "task" ("projectId", "number") ` + ); + } +} From f9321b73514e3449eb1ee15bc0b90f3e8efc3aaf Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Sun, 24 Sep 2023 19:52:32 +0200 Subject: [PATCH 080/104] feat: create new util class --- .../src/app/@core/utils/color-adapter.ts | 49 +++++++++++++++++++ apps/gauzy/src/app/@core/utils/index.ts | 12 +++-- 2 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 apps/gauzy/src/app/@core/utils/color-adapter.ts diff --git a/apps/gauzy/src/app/@core/utils/color-adapter.ts b/apps/gauzy/src/app/@core/utils/color-adapter.ts new file mode 100644 index 00000000000..6abcfa9ffae --- /dev/null +++ b/apps/gauzy/src/app/@core/utils/color-adapter.ts @@ -0,0 +1,49 @@ +import { Color, rgbString } from '@kurkle/color'; + +export class ColorAdapter { + public static hex2Rgb(hex: string) { + hex = this.normalize(hex); + return rgbString({ + r: parseInt(hex.slice(1, 3), 16), + g: parseInt(hex.slice(3, 5), 16), + b: parseInt(hex.slice(5, 7), 16), + a: 1, + }); + } + + public static normalize(hex: string): string { + const regex = /^#[0-9A-F]{6}$/i; + if (regex.test(hex)) { + return hex; + } else { + hex = '#' + hex; + return regex.test(hex) ? hex : '#000000'; + } + } + + public static contrast(bgColor: string) { + let color = new Color(bgColor); + color = color.valid ? color : new Color(this.hex2Rgb(bgColor)); + const MIN_THRESHOLD = 128; + const MAX_THRESHOLD = 186; + const contrast = color.rgb + ? color.rgb.r * 0.299 + color.rgb.g * 0.587 + color.rgb.b * 0.114 + : null; + if (contrast < MIN_THRESHOLD) { + return '#ffffff'; + } else if (contrast > MAX_THRESHOLD) { + return '#000000'; + } + } + + public static background(bgColor: string) { + const color = new Color(bgColor); + return color.valid ? bgColor : this.normalize(bgColor); + } + + public static hexToHsl(hexColor: string): string { + let color = new Color(hexColor); + color = color.valid ? color : new Color(this.hex2Rgb(hexColor)); + return color.hslString(); + } +} diff --git a/apps/gauzy/src/app/@core/utils/index.ts b/apps/gauzy/src/app/@core/utils/index.ts index 5606da56f19..7239b215f58 100644 --- a/apps/gauzy/src/app/@core/utils/index.ts +++ b/apps/gauzy/src/app/@core/utils/index.ts @@ -3,11 +3,13 @@ import { AnalyticsService } from './analytics.service'; import { PlayerService } from './player.service'; import { StateService } from './state.service'; import { SeoService } from './seo.service'; +import { ColorAdapter } from './color-adapter'; export { - LayoutService, - AnalyticsService, - PlayerService, - SeoService, - StateService + LayoutService, + AnalyticsService, + PlayerService, + SeoService, + StateService, + ColorAdapter, }; From d91e7ef31a60f603b223b809b32b629d4cc1b95b Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Sun, 24 Sep 2023 19:56:22 +0200 Subject: [PATCH 081/104] feat: improve tasks dialogs using dynamics status, size and priority --- apps/gauzy/src/app/@shared/shared.module.ts | 21 +- .../status-view/status-view.component.html | 20 +- .../add-task-dialog.component.html | 24 +- .../add-task-dialog.component.ts | 93 ++-- .../task-badge-view.component.html | 14 + .../task-badge-view.component.scss | 18 + .../task-badge-view.component.ts | 47 ++ .../task-priority-select.component.html | 20 +- .../task-priority-select.component.ts | 147 +++--- .../task-priority-select.module.ts | 18 +- .../task-size-select.component.html | 21 +- .../task-size-select.component.ts | 93 ++-- .../task-status-select.component.html | 13 +- .../task-status-select.component.ts | 129 +++--- .../my-task-dialog.component.html | 75 ++-- .../my-task-dialog.component.ts | 135 +++--- .../tasks/components/task/task.component.ts | 420 +++++++++--------- .../team-task-dialog.component.html | 78 ++-- .../team-task-dialog.component.ts | 119 +++-- 19 files changed, 890 insertions(+), 615 deletions(-) create mode 100644 apps/gauzy/src/app/@shared/tasks/task-badge-view/task-badge-view.component.html create mode 100644 apps/gauzy/src/app/@shared/tasks/task-badge-view/task-badge-view.component.scss create mode 100644 apps/gauzy/src/app/@shared/tasks/task-badge-view/task-badge-view.component.ts diff --git a/apps/gauzy/src/app/@shared/shared.module.ts b/apps/gauzy/src/app/@shared/shared.module.ts index 971669c3a2f..5da702ffe12 100644 --- a/apps/gauzy/src/app/@shared/shared.module.ts +++ b/apps/gauzy/src/app/@shared/shared.module.ts @@ -6,27 +6,34 @@ import { Components } from './components'; import { RouterModule } from '@angular/router'; import { AlertModalModule } from './alert-modal'; import { NgxPermissionsModule } from 'ngx-permissions'; -import { DirectivesModule } from "./directives/directives.module"; -import {EmployeeStartWorkModule} from "./employee/employee-start-work/employee-start-work.module"; +import { DirectivesModule } from './directives/directives.module'; +import { EmployeeStartWorkModule } from './employee/employee-start-work/employee-start-work.module'; +import { TaskBadgeViewComponent } from './tasks/task-badge-view/task-badge-view.component'; -const Modules = [NgxPermissionsModule, BackNavigationModule, DirectivesModule, EmployeeStartWorkModule]; +const Modules = [ + NgxPermissionsModule, + BackNavigationModule, + DirectivesModule, + EmployeeStartWorkModule, +]; @NgModule({ - declarations: [...Pipes, ...Components], + declarations: [...Pipes, ...Components, TaskBadgeViewComponent], imports: [CommonModule, RouterModule, ...Modules], exports: [ AlertModalModule, ...Pipes, ...Components, - ...Modules + ...Modules, + TaskBadgeViewComponent, ], - providers: [...Pipes] + providers: [...Pipes], }) export class SharedModule { static forRoot(): ModuleWithProviders { return { ngModule: SharedModule, - providers: [] + providers: [], }; } } diff --git a/apps/gauzy/src/app/@shared/table-components/status-view/status-view.component.html b/apps/gauzy/src/app/@shared/table-components/status-view/status-view.component.html index c11c4612ed8..fe142781931 100644 --- a/apps/gauzy/src/app/@shared/table-components/status-view/status-view.component.html +++ b/apps/gauzy/src/app/@shared/table-components/status-view/status-view.component.html @@ -1,6 +1,14 @@ - + + + + + + diff --git a/apps/gauzy/src/app/@shared/tasks/add-task-dialog/add-task-dialog.component.html b/apps/gauzy/src/app/@shared/tasks/add-task-dialog/add-task-dialog.component.html index 3ea2ef1ecb5..eaaadf49b3e 100644 --- a/apps/gauzy/src/app/@shared/tasks/add-task-dialog/add-task-dialog.component.html +++ b/apps/gauzy/src/app/@shared/tasks/add-task-dialog/add-task-dialog.component.html @@ -49,8 +49,10 @@

@@ -99,7 +101,9 @@
[selected]="selectedTeams" (selectedChange)="onTeamsSelected($event)" fullWidth - [placeholder]="'FORM.PLACEHOLDERS.CHOOSE_TEAMS' | translate" + [placeholder]=" + 'FORM.PLACEHOLDERS.CHOOSE_TEAMS' | translate + " >
@@ -148,7 +154,7 @@
@@ -238,7 +244,11 @@
- +
diff --git a/apps/gauzy/src/app/@shared/tasks/add-task-dialog/add-task-dialog.component.ts b/apps/gauzy/src/app/@shared/tasks/add-task-dialog/add-task-dialog.component.ts index fdd78d7cb4c..8d20459aa6f 100644 --- a/apps/gauzy/src/app/@shared/tasks/add-task-dialog/add-task-dialog.component.ts +++ b/apps/gauzy/src/app/@shared/tasks/add-task-dialog/add-task-dialog.component.ts @@ -1,16 +1,16 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { - ITask, - IOrganizationProject, IEmployee, + IOrganization, + IOrganizationProject, IOrganizationTeam, + ISelectedEmployee, ITag, + ITask, TaskParticipantEnum, - IOrganization, TaskStatusEnum, - ISelectedEmployee } from '@gauzy/contracts'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { NbDialogRef } from '@nebular/theme'; import { TranslateService } from '@ngx-translate/core'; import * as moment from 'moment'; @@ -34,9 +34,10 @@ import { richTextCKEditorConfig } from '../../ckeditor.config'; templateUrl: './add-task-dialog.component.html', styleUrls: ['./add-task-dialog.component.scss'], }) -export class AddTaskDialogComponent extends TranslationBaseComponent - implements OnInit { - +export class AddTaskDialogComponent + extends TranslationBaseComponent + implements OnInit +{ employees: IEmployee[] = []; teams: IOrganizationTeam[] = []; selectedMembers: string[] = []; @@ -46,25 +47,24 @@ export class AddTaskDialogComponent extends TranslationBaseComponent taskParticipantEnum = TaskParticipantEnum; participants = TaskParticipantEnum.EMPLOYEES; public ckConfig: CKEditor4.Config = richTextCKEditorConfig; - @Input() createTask = false; - - /* - * Getter & Setter for task - */ - _task: ITask; - get task(): ITask { - return this._task; - } - @Input() set task(value: ITask) { - this.selectedTask = value; - this._task = value; - } - /* * Payment Mutation Form */ public form: FormGroup = AddTaskDialogComponent.buildForm(this.fb); + + constructor( + public readonly dialogRef: NbDialogRef, + private readonly fb: FormBuilder, + private readonly store: Store, + public readonly translateService: TranslateService, + private readonly employeesService: EmployeesService, + private readonly tasksService: TasksService, + private readonly organizationTeamsService: OrganizationTeamsService + ) { + super(translateService); + } + static buildForm(fb: FormBuilder): FormGroup { return fb.group({ number: [{ value: '', disabled: true }], @@ -82,23 +82,30 @@ export class AddTaskDialogComponent extends TranslationBaseComponent description: [], tags: [], teams: [], + taskStatus: [], + taskSize: [], + taskPriority: [], }); } - constructor( - public readonly dialogRef: NbDialogRef, - private readonly fb: FormBuilder, - private readonly store: Store, - public readonly translateService: TranslateService, - private readonly employeesService: EmployeesService, - private readonly tasksService: TasksService, - private readonly organizationTeamsService: OrganizationTeamsService - ) { - super(translateService); + /* + * Getter & Setter for task + */ + _task: ITask; + + get task(): ITask { + return this._task; + } + + @Input() set task(value: ITask) { + this.selectedTask = value; + this._task = value; } ngOnInit() { - this.ckConfig.editorplaceholder = this.translateService.instant('FORM.PLACEHOLDERS.DESCRIPTION'); + this.ckConfig.editorplaceholder = this.translateService.instant( + 'FORM.PLACEHOLDERS.DESCRIPTION' + ); const storeOrganization$ = this.store.selectedOrganization$; const storeEmployee$ = this.store.selectedEmployee$; const storeProject$ = this.store.selectedProject$; @@ -161,7 +168,10 @@ export class AddTaskDialogComponent extends TranslationBaseComponent teams, title, priority, - size + size, + taskStatus, + taskSize, + taskPriority, } = this.selectedTask; const duration = moment.duration(estimate, 'seconds'); @@ -186,6 +196,9 @@ export class AddTaskDialogComponent extends TranslationBaseComponent description, tags, teams: this.selectedTeams, + taskStatus, + taskSize, + taskPriority, }); } } @@ -206,7 +219,15 @@ export class AddTaskDialogComponent extends TranslationBaseComponent .map((id) => this.teams.find((e) => e.id === id)) .filter((e) => !!e) ); - + this.form + .get('status') + .setValue(this.form.get('taskStatus').value?.name); + this.form + .get('priority') + .setValue(this.form.get('taskPriority').value?.name); + this.form + .get('size') + .setValue(this.form.get('taskSize').value?.name); const { estimateDays, estimateHours, estimateMinutes } = this.form.value; diff --git a/apps/gauzy/src/app/@shared/tasks/task-badge-view/task-badge-view.component.html b/apps/gauzy/src/app/@shared/tasks/task-badge-view/task-badge-view.component.html new file mode 100644 index 00000000000..f8fc522a16a --- /dev/null +++ b/apps/gauzy/src/app/@shared/tasks/task-badge-view/task-badge-view.component.html @@ -0,0 +1,14 @@ +
+
+
+
+ badge +
+
{{ name | replace : '-' : ' ' | titlecase }}
+
+
+
diff --git a/apps/gauzy/src/app/@shared/tasks/task-badge-view/task-badge-view.component.scss b/apps/gauzy/src/app/@shared/tasks/task-badge-view/task-badge-view.component.scss new file mode 100644 index 00000000000..a76cfac5321 --- /dev/null +++ b/apps/gauzy/src/app/@shared/tasks/task-badge-view/task-badge-view.component.scss @@ -0,0 +1,18 @@ +.badge-color { + display: flex; + padding: 4px; + width: fit-content; + line-height: 1; + border-radius: var(--border-radius); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + gap: 4px; + align-items: center; + font-weight: 600; + + .badge-img { + width: 18px; + height: 100%; + } +} diff --git a/apps/gauzy/src/app/@shared/tasks/task-badge-view/task-badge-view.component.ts b/apps/gauzy/src/app/@shared/tasks/task-badge-view/task-badge-view.component.ts new file mode 100644 index 00000000000..2a8e0524e67 --- /dev/null +++ b/apps/gauzy/src/app/@shared/tasks/task-badge-view/task-badge-view.component.ts @@ -0,0 +1,47 @@ +import { Component, Input } from '@angular/core'; +import { ITaskPriority, ITaskSize, ITaskStatus } from '@gauzy/contracts'; +import { ColorAdapter } from '../../../@core'; + +export type ITaskBadge = ITaskStatus | ITaskSize | ITaskPriority; + +@Component({ + selector: 'gauzy-task-badge-view', + templateUrl: './task-badge-view.component.html', + styleUrls: ['./task-badge-view.component.scss'], +}) +export class TaskBadgeViewComponent { + constructor() { + this._taskBadge = null; + } + + private _taskBadge: ITaskBadge; + + public get taskBadge(): ITaskBadge { + return this._taskBadge; + } + + @Input() + public set taskBadge(value: ITaskBadge) { + this._taskBadge = value; + } + + public get textColor() { + return ColorAdapter.contrast(this.taskBadge.color); + } + + public get backgroundColor() { + return ColorAdapter.background(this.taskBadge.color); + } + + public get icon() { + return this.taskBadge.fullIconUrl; + } + + public get name() { + return this.taskBadge.name; + } + + public get imageFilter() { + return ColorAdapter.hexToHsl(this.taskBadge.color); + } +} diff --git a/apps/gauzy/src/app/@shared/tasks/task-priority-select/task-priority-select.component.html b/apps/gauzy/src/app/@shared/tasks/task-priority-select/task-priority-select.component.html index 3a67a819f6c..e1bce604aa1 100644 --- a/apps/gauzy/src/app/@shared/tasks/task-priority-select/task-priority-select.component.html +++ b/apps/gauzy/src/app/@shared/tasks/task-priority-select/task-priority-select.component.html @@ -1,17 +1,19 @@ - - {{ item.name | replace: '_':' ' | replace: '-':' ' | titlecase }} - + + + + + + diff --git a/apps/gauzy/src/app/@shared/tasks/task-priority-select/task-priority-select.component.ts b/apps/gauzy/src/app/@shared/tasks/task-priority-select/task-priority-select.component.ts index 515ec72f995..318f75c9eab 100644 --- a/apps/gauzy/src/app/@shared/tasks/task-priority-select/task-priority-select.component.ts +++ b/apps/gauzy/src/app/@shared/tasks/task-priority-select/task-priority-select.component.ts @@ -1,12 +1,12 @@ import { + AfterViewInit, Component, - OnInit, - OnDestroy, - Input, - forwardRef, EventEmitter, + forwardRef, + Input, + OnDestroy, + OnInit, Output, - AfterViewInit, } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; @@ -19,11 +19,15 @@ import { IPagination, ITaskPriority, ITaskPriorityFindInput, - TaskPriorityEnum + TaskPriorityEnum, } from '@gauzy/contracts'; import { distinctUntilChange, sluggable } from '@gauzy/common-angular'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { Store, TaskPrioritiesService, ToastrService } from '../../../@core/services'; +import { + Store, + TaskPrioritiesService, + ToastrService, +} from '../../../@core/services'; import { TranslationBaseComponent } from '../../language-base/translation-base.component'; @UntilDestroy({ checkProperties: true }) @@ -38,54 +42,73 @@ import { TranslationBaseComponent } from '../../language-base/translation-base.c }, ], }) -export class TaskPrioritySelectComponent extends TranslationBaseComponent - implements AfterViewInit, OnInit, OnDestroy { - +export class TaskPrioritySelectComponent + extends TranslationBaseComponent + implements AfterViewInit, OnInit, OnDestroy +{ private subject$: Subject = new Subject(); - public organization: IOrganization; - public priorities$: BehaviorSubject = new BehaviorSubject([]); - /** * Default global task priorities */ - private _priorities: Array<{ name: string; value: TaskPriorityEnum & any }> = [ + private _priorities: Array<{ + name: string; + value: TaskPriorityEnum & any; + }> = [ { name: TaskPriorityEnum.URGENT, - value: sluggable(TaskPriorityEnum.URGENT) + value: sluggable(TaskPriorityEnum.URGENT), }, { name: TaskPriorityEnum.HIGH, - value: sluggable(TaskPriorityEnum.HIGH) + value: sluggable(TaskPriorityEnum.HIGH), }, { name: TaskPriorityEnum.MEDIUM, - value: sluggable(TaskPriorityEnum.MEDIUM) + value: sluggable(TaskPriorityEnum.MEDIUM), }, { name: TaskPriorityEnum.LOW, - value: sluggable(TaskPriorityEnum.LOW) + value: sluggable(TaskPriorityEnum.LOW), }, ]; + public organization: IOrganization; + public priorities$: BehaviorSubject = new BehaviorSubject( + [] + ); + @Output() onChanged = new EventEmitter(); + + constructor( + public readonly translateService: TranslateService, + public readonly store: Store, + public readonly taskPrioritiesService: TaskPrioritiesService, + private readonly toastrService: ToastrService + ) { + super(translateService); + } /* - * Getter & Setter for selected organization project - */ + * Getter & Setter for selected organization project + */ private _projectId: IOrganizationProject['id']; + get projectId(): IOrganizationProject['id'] { return this._projectId; } + @Input() set projectId(value: IOrganizationProject['id']) { this._projectId = value; this.subject$.next(true); } /* - * Getter & Setter for dynamic add tag option - */ + * Getter & Setter for dynamic add tag option + */ private _addTag: boolean = true; + get addTag(): boolean { return this._addTag; } + @Input() set addTag(value: boolean) { this._addTag = value; } @@ -94,9 +117,11 @@ export class TaskPrioritySelectComponent extends TranslationBaseComponent * Getter & Setter for dynamic placeholder */ private _placeholder: string; + get placeholder(): string { return this._placeholder; } + @Input() set placeholder(value: string) { this._placeholder = value; } @@ -104,29 +129,21 @@ export class TaskPrioritySelectComponent extends TranslationBaseComponent /* * Getter & Setter for priority */ - private _priority: TaskPriorityEnum | string; - set priority(val: TaskPriorityEnum | string) { + private _priority: ITaskPriority; + + get priority(): ITaskPriority { + return this._priority; + } + + set priority(val: ITaskPriority) { this._priority = val; this.onChange(val); this.onTouched(val); } - get priority(): TaskPriorityEnum | string { - return this._priority; - } - - onChange: any = () => { }; - onTouched: any = () => { }; - @Output() onChanged = new EventEmitter(); + onChange: any = () => {}; - constructor( - public readonly translateService: TranslateService, - public readonly store: Store, - public readonly taskPrioritiesService: TaskPrioritiesService, - private readonly toastrService: ToastrService - ) { - super(translateService); - } + onTouched: any = () => {}; ngOnInit(): void { this.subject$ @@ -155,8 +172,8 @@ export class TaskPrioritySelectComponent extends TranslationBaseComponent .subscribe(); } - writeValue(value: TaskPriorityEnum) { - this._priority = value; + writeValue(value: ITaskPriority) { + this.priority = value; } registerOnChange(fn: (rating: number) => void): void { @@ -167,8 +184,8 @@ export class TaskPrioritySelectComponent extends TranslationBaseComponent this.onTouched = fn; } - selectPriority(event: { label: string; value: TaskPriorityEnum }) { - this.onChanged.emit(event ? event.value : null); + selectPriority(priority: ITaskPriority) { + this.onChanged.emit(priority); } /** @@ -182,20 +199,26 @@ export class TaskPrioritySelectComponent extends TranslationBaseComponent const { tenantId } = this.store.user; const { id: organizationId } = this.organization; - this.taskPrioritiesService.get({ - tenantId, - organizationId, - ...(this.projectId - ? { - projectId: this.projectId - } - : {}), - }).pipe( - map(({ items, total }: IPagination) => total > 0 ? items : this._priorities), - tap((priorities: ITaskPriority[]) => this.priorities$.next(priorities)), - untilDestroyed(this) - ) - .subscribe(); + this.taskPrioritiesService + .get({ + tenantId, + organizationId, + ...(this.projectId + ? { + projectId: this.projectId, + } + : {}), + }) + .pipe( + map(({ items, total }: IPagination) => + total > 0 ? items : this._priorities + ), + tap((priorities: ITaskPriority[]) => + this.priorities$.next(priorities) + ), + untilDestroyed(this) + ) + .subscribe(); } /** @@ -218,13 +241,13 @@ export class TaskPrioritySelectComponent extends TranslationBaseComponent name, ...(this.projectId ? { - projectId: this.projectId - } + projectId: this.projectId, + } : {}), }); const priority: ITaskPriority = await firstValueFrom(source); - if (priority.value) { - this.priority = priority.value; + if (priority) { + this.priority = priority; } } catch (error) { this.toastrService.error(error); @@ -233,5 +256,5 @@ export class TaskPrioritySelectComponent extends TranslationBaseComponent } }; - ngOnDestroy(): void { } + ngOnDestroy(): void {} } diff --git a/apps/gauzy/src/app/@shared/tasks/task-priority-select/task-priority-select.module.ts b/apps/gauzy/src/app/@shared/tasks/task-priority-select/task-priority-select.module.ts index 4e306fbee65..7ee1a571153 100644 --- a/apps/gauzy/src/app/@shared/tasks/task-priority-select/task-priority-select.module.ts +++ b/apps/gauzy/src/app/@shared/tasks/task-priority-select/task-priority-select.module.ts @@ -8,21 +8,17 @@ import { TranslateModule } from '../../translate/translate.module'; import { SharedModule } from '../../shared.module'; @NgModule({ - declarations: [ - TaskPrioritySelectComponent - ], - exports: [ - TaskPrioritySelectComponent - ], + declarations: [TaskPrioritySelectComponent], + exports: [TaskPrioritySelectComponent], imports: [ CommonModule, FormsModule, TranslateModule, NgSelectModule, - SharedModule + SharedModule, + NgSelectModule, + NgSelectModule, ], - providers: [ - TaskPrioritiesService - ] + providers: [TaskPrioritiesService], }) -export class TaskPrioritySelectModule { } +export class TaskPrioritySelectModule {} diff --git a/apps/gauzy/src/app/@shared/tasks/task-size-select/task-size-select.component.html b/apps/gauzy/src/app/@shared/tasks/task-size-select/task-size-select.component.html index dca413320ec..881e192e4e5 100644 --- a/apps/gauzy/src/app/@shared/tasks/task-size-select/task-size-select.component.html +++ b/apps/gauzy/src/app/@shared/tasks/task-size-select/task-size-select.component.html @@ -1,17 +1,18 @@ - - {{ item.name | replace: '_':' ' | replace: '-':' ' | titlecase }} - + + + + + + diff --git a/apps/gauzy/src/app/@shared/tasks/task-size-select/task-size-select.component.ts b/apps/gauzy/src/app/@shared/tasks/task-size-select/task-size-select.component.ts index dae0a42c595..20f472c154d 100644 --- a/apps/gauzy/src/app/@shared/tasks/task-size-select/task-size-select.component.ts +++ b/apps/gauzy/src/app/@shared/tasks/task-size-select/task-size-select.component.ts @@ -19,11 +19,15 @@ import { IPagination, ITaskSize, ITaskSizeFindInput, - TaskSizeEnum + TaskSizeEnum, } from '@gauzy/contracts'; import { distinctUntilChange, sluggable } from '@gauzy/common-angular'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { Store, TaskSizesService, ToastrService } from '../../../@core/services'; +import { + Store, + TaskSizesService, + ToastrService, +} from '../../../@core/services'; import { TranslationBaseComponent } from '../../language-base/translation-base.component'; @UntilDestroy({ checkProperties: true }) @@ -38,9 +42,10 @@ import { TranslationBaseComponent } from '../../language-base/translation-base.c }, ], }) -export class TaskSizeSelectComponent extends TranslationBaseComponent - implements AfterViewInit, OnInit, OnDestroy { - +export class TaskSizeSelectComponent + extends TranslationBaseComponent + implements AfterViewInit, OnInit, OnDestroy +{ private subject$: Subject = new Subject(); public organization: IOrganization; public sizes$: BehaviorSubject = new BehaviorSubject([]); @@ -51,29 +56,29 @@ export class TaskSizeSelectComponent extends TranslationBaseComponent private _sizes: Array<{ name: string; value: TaskSizeEnum & any }> = [ { name: TaskSizeEnum.X_LARGE, - value: sluggable(TaskSizeEnum.X_LARGE) + value: sluggable(TaskSizeEnum.X_LARGE), }, { name: TaskSizeEnum.LARGE, - value: sluggable(TaskSizeEnum.LARGE) + value: sluggable(TaskSizeEnum.LARGE), }, { name: TaskSizeEnum.MEDIUM, - value: sluggable(TaskSizeEnum.MEDIUM) + value: sluggable(TaskSizeEnum.MEDIUM), }, { name: TaskSizeEnum.SMALL, - value: sluggable(TaskSizeEnum.SMALL) + value: sluggable(TaskSizeEnum.SMALL), }, { name: TaskSizeEnum.TINY, - value: sluggable(TaskSizeEnum.TINY) + value: sluggable(TaskSizeEnum.TINY), }, ]; /* - * Getter & Setter for selected organization project - */ + * Getter & Setter for selected organization project + */ private _projectId: IOrganizationProject['id']; get projectId(): IOrganizationProject['id'] { return this._projectId; @@ -84,8 +89,8 @@ export class TaskSizeSelectComponent extends TranslationBaseComponent } /* - * Getter & Setter for dynamic add tag option - */ + * Getter & Setter for dynamic add tag option + */ private _addTag: boolean = true; get addTag(): boolean { return this._addTag; @@ -108,20 +113,20 @@ export class TaskSizeSelectComponent extends TranslationBaseComponent /* * Getter & Setter for size */ - private _size: TaskSizeEnum | string; - set size(val: TaskSizeEnum | string) { + private _size: ITaskSize; + set size(val: ITaskSize) { this._size = val; this.onChange(val); this.onTouched(val); } - get size(): TaskSizeEnum | string { + get size(): ITaskSize { return this._size; } - onChange: any = () => { }; - onTouched: any = () => { }; + onChange: any = () => {}; + onTouched: any = () => {}; - @Output() onChanged = new EventEmitter(); + @Output() onChanged = new EventEmitter(); constructor( public readonly translateService: TranslateService, @@ -159,8 +164,8 @@ export class TaskSizeSelectComponent extends TranslationBaseComponent .subscribe(); } - writeValue(value: TaskSizeEnum) { - this._size = value; + writeValue(value: ITaskSize) { + this.size = value; } registerOnChange(fn: (rating: number) => void): void { @@ -171,8 +176,8 @@ export class TaskSizeSelectComponent extends TranslationBaseComponent this.onTouched = fn; } - selectSize(event: { label: string; value: TaskSizeEnum }) { - this.onChanged.emit(event ? event.value : null); + selectSize(size: ITaskSize) { + this.onChanged.emit(size); } /** @@ -186,20 +191,24 @@ export class TaskSizeSelectComponent extends TranslationBaseComponent const { tenantId } = this.store.user; const { id: organizationId } = this.organization; - this.taskSizesService.get({ - tenantId, - organizationId, - ...(this.projectId - ? { - projectId: this.projectId - } - : {}), - }).pipe( - map(({ items, total }: IPagination) => total > 0 ? items : this._sizes), - tap((sizes: ITaskSize[]) => this.sizes$.next(sizes)), - untilDestroyed(this) - ) - .subscribe(); + this.taskSizesService + .get({ + tenantId, + organizationId, + ...(this.projectId + ? { + projectId: this.projectId, + } + : {}), + }) + .pipe( + map(({ items, total }: IPagination) => + total > 0 ? items : this._sizes + ), + tap((sizes: ITaskSize[]) => this.sizes$.next(sizes)), + untilDestroyed(this) + ) + .subscribe(); } /** @@ -222,13 +231,13 @@ export class TaskSizeSelectComponent extends TranslationBaseComponent name, ...(this.projectId ? { - projectId: this.projectId - } + projectId: this.projectId, + } : {}), }); const size: ITaskSize = await firstValueFrom(source); if (size.value) { - this.size = size.value; + this.size = size; } } catch (error) { this.toastrService.error(error); @@ -237,5 +246,5 @@ export class TaskSizeSelectComponent extends TranslationBaseComponent } }; - ngOnDestroy(): void { } + ngOnDestroy(): void {} } diff --git a/apps/gauzy/src/app/@shared/tasks/task-status-select/task-status-select.component.html b/apps/gauzy/src/app/@shared/tasks/task-status-select/task-status-select.component.html index 2ba47508324..3b73d161388 100644 --- a/apps/gauzy/src/app/@shared/tasks/task-status-select/task-status-select.component.html +++ b/apps/gauzy/src/app/@shared/tasks/task-status-select/task-status-select.component.html @@ -1,11 +1,16 @@ - - {{ item.name | replace: '_':' ' | replace: '-':' ' | titlecase }} - + + + + + + diff --git a/apps/gauzy/src/app/@shared/tasks/task-status-select/task-status-select.component.ts b/apps/gauzy/src/app/@shared/tasks/task-status-select/task-status-select.component.ts index f3b43f7f626..45bf94a8745 100644 --- a/apps/gauzy/src/app/@shared/tasks/task-status-select/task-status-select.component.ts +++ b/apps/gauzy/src/app/@shared/tasks/task-status-select/task-status-select.component.ts @@ -1,22 +1,33 @@ import { + AfterViewInit, Component, - OnInit, - OnDestroy, - Input, - forwardRef, EventEmitter, + forwardRef, + Input, + OnDestroy, + OnInit, Output, - AfterViewInit, } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; import { combineLatest, debounceTime, firstValueFrom, Subject } from 'rxjs'; import { filter, map, tap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; -import { IOrganization, IOrganizationProject, IPagination, ITaskStatus, ITaskStatusFindInput, TaskStatusEnum } from '@gauzy/contracts'; +import { + IOrganization, + IOrganizationProject, + IPagination, + ITaskStatus, + ITaskStatusFindInput, + TaskStatusEnum, +} from '@gauzy/contracts'; import { distinctUntilChange, sluggable } from '@gauzy/common-angular'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { TaskStatusesService, Store, ToastrService } from '../../../@core/services'; +import { + Store, + TaskStatusesService, + ToastrService, +} from '../../../@core/services'; import { TranslationBaseComponent } from '../../language-base/translation-base.component'; @UntilDestroy({ checkProperties: true }) @@ -31,13 +42,11 @@ import { TranslationBaseComponent } from '../../language-base/translation-base.c }, ], }) -export class TaskStatusSelectComponent extends TranslationBaseComponent - implements AfterViewInit, OnInit, OnDestroy { - +export class TaskStatusSelectComponent + extends TranslationBaseComponent + implements AfterViewInit, OnInit, OnDestroy +{ private subject$: Subject = new Subject(); - public organization: IOrganization; - public statuses$: BehaviorSubject = new BehaviorSubject([]); - /** * Default global task statuses */ @@ -67,26 +76,42 @@ export class TaskStatusSelectComponent extends TranslationBaseComponent value: sluggable(TaskStatusEnum.COMPLETED), }, ]; + public organization: IOrganization; + public statuses$: BehaviorSubject = new BehaviorSubject([]); + @Output() onChanged = new EventEmitter(); + + constructor( + public readonly translateService: TranslateService, + public readonly store: Store, + public readonly taskStatusesService: TaskStatusesService, + private readonly toastrService: ToastrService + ) { + super(translateService); + } /* - * Getter & Setter for selected organization project - */ + * Getter & Setter for selected organization project + */ private _projectId: IOrganizationProject['id']; + get projectId(): IOrganizationProject['id'] { return this._projectId; } + @Input() set projectId(value: IOrganizationProject['id']) { this._projectId = value; this.subject$.next(true); } /* - * Getter & Setter for dynamic add tag option - */ + * Getter & Setter for dynamic add tag option + */ private _addTag: boolean = true; + get addTag(): boolean { return this._addTag; } + @Input() set addTag(value: boolean) { this._addTag = value; } @@ -95,9 +120,11 @@ export class TaskStatusSelectComponent extends TranslationBaseComponent * Getter & Setter for dynamic placeholder */ private _placeholder: string; + get placeholder(): string { return this._placeholder; } + @Input() set placeholder(value: string) { this._placeholder = value; } @@ -105,29 +132,21 @@ export class TaskStatusSelectComponent extends TranslationBaseComponent /* * Getter & Setter for status */ - private _status: TaskStatusEnum; - set status(val: TaskStatusEnum) { + private _status: ITaskStatus; + + get status(): ITaskStatus { + return this._status; + } + + set status(val: ITaskStatus) { this._status = val; this.onChange(val); this.onTouched(val); } - get status(): TaskStatusEnum { - return this._status; - } onChange: any = () => {}; - onTouched: any = () => {}; - - @Output() onChanged = new EventEmitter(); - constructor( - public readonly translateService: TranslateService, - public readonly store: Store, - public readonly taskStatusesService: TaskStatusesService, - private readonly toastrService: ToastrService - ) { - super(translateService); - } + onTouched: any = () => {}; ngOnInit(): void { this.subject$ @@ -156,8 +175,8 @@ export class TaskStatusSelectComponent extends TranslationBaseComponent .subscribe(); } - writeValue(value: TaskStatusEnum) { - this._status = value; + writeValue(value: ITaskStatus) { + this.status = value; } registerOnChange(fn: (rating: number) => void): void { @@ -168,8 +187,8 @@ export class TaskStatusSelectComponent extends TranslationBaseComponent this.onTouched = fn; } - selectStatus(event: { label: string; value: TaskStatusEnum }) { - this.onChanged.emit(event ? event.value : null); + selectStatus(status: ITaskStatus) { + this.onChanged.emit(status); } /** @@ -183,20 +202,24 @@ export class TaskStatusSelectComponent extends TranslationBaseComponent const { tenantId } = this.store.user; const { id: organizationId } = this.organization; - this.taskStatusesService.get({ - tenantId, - organizationId, - ...(this.projectId - ? { - projectId: this.projectId - } - : {}), - }).pipe( - map(({ items, total }: IPagination) => total > 0 ? items : this._statuses), - tap((statuses: ITaskStatus[]) => this.statuses$.next(statuses)), - untilDestroyed(this) - ) - .subscribe(); + this.taskStatusesService + .get({ + tenantId, + organizationId, + ...(this.projectId + ? { + projectId: this.projectId, + } + : {}), + }) + .pipe( + map(({ items, total }: IPagination) => + total > 0 ? items : this._statuses + ), + tap((statuses: ITaskStatus[]) => this.statuses$.next(statuses)), + untilDestroyed(this) + ) + .subscribe(); } /** @@ -219,7 +242,7 @@ export class TaskStatusSelectComponent extends TranslationBaseComponent name, ...(this.projectId ? { - projectId: this.projectId + projectId: this.projectId, } : {}), }); @@ -227,7 +250,7 @@ export class TaskStatusSelectComponent extends TranslationBaseComponent } catch (error) { this.toastrService.error(error); } finally { - this.subject$.next(true) + this.subject$.next(true); } }; diff --git a/apps/gauzy/src/app/pages/tasks/components/my-task-dialog/my-task-dialog.component.html b/apps/gauzy/src/app/pages/tasks/components/my-task-dialog/my-task-dialog.component.html index d93ddb9d4a6..87c20036ce0 100644 --- a/apps/gauzy/src/app/pages/tasks/components/my-task-dialog/my-task-dialog.component.html +++ b/apps/gauzy/src/app/pages/tasks/components/my-task-dialog/my-task-dialog.component.html @@ -1,7 +1,7 @@
{{ @@ -18,10 +18,10 @@
@@ -33,12 +33,12 @@
'CONTEXT_MENU.PROJECT' | translate }} @@ -48,9 +48,11 @@
'TASKS_PAGE.TASKS_STATUS' | translate }} @@ -59,8 +61,8 @@
@@ -76,11 +78,11 @@
@@ -90,9 +92,11 @@
{{ 'TASKS_PAGE.TASK_PRIORITY' | translate }} @@ -102,18 +106,18 @@
{{ 'TASKS_PAGE.TASK_SIZE' | translate }}
@@ -122,19 +126,19 @@
-
@@ -146,47 +150,47 @@
}}
@@ -197,8 +201,11 @@
- - +
@@ -206,18 +213,18 @@
diff --git a/apps/gauzy/src/app/pages/tasks/components/my-task-dialog/my-task-dialog.component.ts b/apps/gauzy/src/app/pages/tasks/components/my-task-dialog/my-task-dialog.component.ts index 78d4cd3bffc..c150ecb5eae 100644 --- a/apps/gauzy/src/app/pages/tasks/components/my-task-dialog/my-task-dialog.component.ts +++ b/apps/gauzy/src/app/pages/tasks/components/my-task-dialog/my-task-dialog.component.ts @@ -1,12 +1,12 @@ import { Component, Input, OnInit } from '@angular/core'; import { - ITask, - IOrganizationProject, IEmployee, + IOrganizationProject, ITag, + ITask, TaskStatusEnum, } from '@gauzy/contracts'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { NbDialogRef } from '@nebular/theme'; import { TranslateService } from '@ngx-translate/core'; import * as moment from 'moment'; @@ -22,7 +22,6 @@ import { import { CKEditor4 } from 'ckeditor4-angular/ckeditor'; import { richTextCKEditorConfig } from 'apps/gauzy/src/app/@shared/ckeditor.config'; - const initialTaskValue = { title: '', project: null, @@ -40,10 +39,10 @@ const initialTaskValue = { templateUrl: './my-task-dialog.component.html', styleUrls: ['./my-task-dialog.component.scss'], }) -export class MyTaskDialogComponent extends TranslationBaseComponent - implements OnInit { - - form: FormGroup; +export class MyTaskDialogComponent + extends TranslationBaseComponent + implements OnInit +{ selectedTaskId: string; projects: IOrganizationProject[]; employees: IEmployee[] = []; @@ -56,6 +55,7 @@ export class MyTaskDialogComponent extends TranslationBaseComponent tags: ITag[] = []; @Input() task: Partial = {}; public ckConfig: CKEditor4.Config = richTextCKEditorConfig; + public form: FormGroup = MyTaskDialogComponent.buildForm(this.fb); constructor( public readonly dialogRef: NbDialogRef, @@ -71,13 +71,27 @@ export class MyTaskDialogComponent extends TranslationBaseComponent super(translateService); } - ngOnInit() { - this.ckConfig.editorplaceholder = this.translateService.instant('FORM.PLACEHOLDERS.DESCRIPTION'); - this.loadProjects(); - this.loadEmployees(); - this.initializeForm( - Object.assign({}, initialTaskValue, this.selectedTask || this.task) - ); + static buildForm(fb: FormBuilder): FormGroup { + return fb.group({ + number: [{ value: '', disabled: true }], + title: [null, Validators.required], + project: [], + projectId: [], + status: [TaskStatusEnum.OPEN], + priority: [], + size: [], + members: [], + estimateDays: [], + estimateHours: [null, [Validators.min(0), Validators.max(23)]], + estimateMinutes: [null, [Validators.min(0), Validators.max(59)]], + dueDate: [], + description: [], + tags: [], + teams: [], + taskStatus: [], + taskSize: [], + taskPriority: [], + }); } private async loadProjects() { @@ -93,6 +107,32 @@ export class MyTaskDialogComponent extends TranslationBaseComponent if (items) this.projects = items; } + private async loadEmployees() { + const organizationId = this._organizationsStore.selectedOrganization.id; + if (!organizationId) { + return; + } + + const { items } = await firstValueFrom( + this.employeesService.getAll(['user'], { + organization: { id: organizationId }, + }) + ); + + this.employees = items; + } + + async ngOnInit() { + this.ckConfig.editorplaceholder = this.translateService.instant( + 'FORM.PLACEHOLDERS.DESCRIPTION' + ); + await this.loadProjects(); + await this.loadEmployees(); + this.initializeForm( + Object.assign({}, initialTaskValue, this.selectedTask || this.task) + ); + } + initializeForm({ title, description, @@ -103,7 +143,10 @@ export class MyTaskDialogComponent extends TranslationBaseComponent dueDate, tags, priority, - size + size, + taskStatus, + taskSize, + taskPriority, }: ITask) { const duration = moment.duration(estimate, 'seconds'); // select members from database of default value @@ -117,27 +160,23 @@ export class MyTaskDialogComponent extends TranslationBaseComponent if (members === null) { this.selectedMembers = [this.employeeId]; } - this.form = this.fb.group({ - number: [{ value: '', disabled: true }], - title: [title, Validators.required], - project: [project], + this.form.patchValue({ + title, + project, projectId: project ? project.id : null, - status: [status ? status : TaskStatusEnum.OPEN], - priority: [priority ? priority : null], - size: [size ? size : null], - members: [members], - estimateDays: [duration.days() || ''], - estimateHours: [ - duration.hours() || '', - [Validators.min(0), Validators.max(23)], - ], - estimateMinutes: [ - duration.minutes() || '', - [Validators.min(0), Validators.max(59)], - ], - dueDate: [dueDate], - description: [description], - tags: [tags], + status, + priority, + size, + estimateDays: duration.days(), + estimateHours: duration.hours(), + estimateMinutes: duration.minutes(), + dueDate: dueDate ? new Date(dueDate) : null, + description, + tags, + members: [this.selectedMembers], + taskStatus, + taskSize, + taskPriority, teams: [], }); this.tags = this.form.get('tags').value || []; @@ -163,6 +202,15 @@ export class MyTaskDialogComponent extends TranslationBaseComponent onSave() { if (this.form.valid) { + this.form + .get('status') + .setValue(this.form.get('taskStatus').value?.name); + this.form + .get('priority') + .setValue(this.form.get('taskPriority').value?.name); + this.form + .get('size') + .setValue(this.form.get('taskSize').value?.name); this.form .get('members') .setValue( @@ -181,19 +229,4 @@ export class MyTaskDialogComponent extends TranslationBaseComponent selectedProject(project: IOrganizationProject) { this.form.patchValue({ project }); } - - private async loadEmployees() { - const organizationId = this._organizationsStore.selectedOrganization.id; - if (!organizationId) { - return; - } - - const { items } = await firstValueFrom( - this.employeesService.getAll(['user'], { - organization: { id: organizationId }, - }) - ); - - this.employees = items; - } } diff --git a/apps/gauzy/src/app/pages/tasks/components/task/task.component.ts b/apps/gauzy/src/app/pages/tasks/components/task/task.component.ts index 27ac2fbfbed..9ab425af5ef 100644 --- a/apps/gauzy/src/app/pages/tasks/components/task/task.component.ts +++ b/apps/gauzy/src/app/pages/tasks/components/task/task.component.ts @@ -1,19 +1,19 @@ -import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; -import { Router, ActivatedRoute } from '@angular/router'; +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; import { HttpClient } from '@angular/common/http'; import { combineLatest, firstValueFrom, Observable, Subject } from 'rxjs'; -import { first, tap, filter, debounceTime } from 'rxjs/operators'; +import { debounceTime, filter, first, tap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { Ng2SmartTableComponent } from 'ng2-smart-table'; import { NbDialogService } from '@nebular/theme'; import { - ITask, - IOrganizationProject, ComponentLayoutStyleEnum, - TaskListTypeEnum, IOrganization, + IOrganizationProject, ISelectedEmployee, + ITask, PermissionsEnum, + TaskListTypeEnum, } from '@gauzy/contracts'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { distinctUntilChange } from '@gauzy/common-angular'; @@ -61,6 +61,8 @@ export class TaskComponent extends PaginationFilterBaseComponent implements OnInit, OnDestroy { + private _refresh$: Subject = new Subject(); + private _tasks: ITask[] = []; settingsSmartTable: object; loading: boolean = false; disableButton: boolean = true; @@ -81,16 +83,7 @@ export class TaskComponent selectedEmployee: ISelectedEmployee; selectedEmployeeId: ISelectedEmployee['id']; selectedProject: IOrganizationProject; - private _refresh$: Subject = new Subject(); - private _tasks: ITask[] = []; - tasksTable: Ng2SmartTableComponent; - @ViewChild('tasksTable') set content(content: Ng2SmartTableComponent) { - if (content) { - this.tasksTable = content; - this.onChangedSource(); - } - } constructor( private readonly dialogService: NbDialogService, @@ -110,6 +103,205 @@ export class TaskComponent this.setView(); } + @ViewChild('tasksTable') set content(content: Ng2SmartTableComponent) { + if (content) { + this.tasksTable = content; + this.onChangedSource(); + } + } + + /** + * If, default project is selected from header + * + * @returns + */ + get isDefaultProject() { + if (this.selectedProject) { + return this.selectedProject.id === this.defaultProject.id; + } + return true; + } + + /** + * return store instance as per page + */ + get storeInstance() { + if (this.isTasksPage()) { + return this._taskStore; + } else if (this.isMyTasksPage()) { + return this._myTaskStore; + } else if (this.isTeamTaskPage()) { + return this._teamTaskStore; + } + } + + private initTasks(): void { + const path = this._activatedRoute.snapshot.url[0].path; + if (path === 'me') { + this.viewComponentName = ComponentEnum.MY_TASKS; + this.availableTasks$ = this.myTasks$; + return; + } + if (path === 'team') { + this.viewComponentName = ComponentEnum.TEAM_TASKS; + this.availableTasks$ = this.teamTasks$; + return; + } + this.viewComponentName = ComponentEnum.ALL_TASKS; + this.availableTasks$ = this.tasks$; + return; + } + + private _applyTranslationOnSmartTable() { + this.translateService.onLangChange + .pipe( + tap(() => this._loadSmartTableSettings()), + untilDestroyed(this) + ) + .subscribe(); + } + + private _loadSmartTableSettings() { + const pagination: IPaginationBase = this.getPagination(); + this.settingsSmartTable = { + actions: false, + pager: { + display: false, + perPage: pagination ? pagination.itemsPerPage : 10, + }, + noDataMessage: this.getTranslation('SM_TABLE.NO_DATA.TASK'), + columns: { + taskNumber: { + title: this.getTranslation('TASKS_PAGE.TASK_ID'), + type: 'string', + width: '10%', + filter: { + type: 'custom', + component: InputFilterComponent, + }, + filterFunction: (prefix: string) => { + this.setFilter({ field: 'prefix', search: prefix }); + }, + }, + description: { + title: this.getTranslation('TASKS_PAGE.TASKS_TITLE'), + type: 'custom', + class: 'align-row', + renderComponent: NotesWithTagsComponent, + filter: { + type: 'custom', + component: InputFilterComponent, + }, + filterFunction: (value) => { + this.setFilter({ field: 'title', search: value }); + }, + }, + project: { + title: this.getTranslation('TASKS_PAGE.TASKS_PROJECT'), + type: 'custom', + renderComponent: ProjectComponent, + filter: false, + }, + createdAt: { + title: this.getTranslation('SM_TABLE.CREATED_AT'), + type: 'custom', + filter: false, + renderComponent: CreatedAtComponent, + }, + creator: { + title: this.getTranslation('TASKS_PAGE.TASKS_CREATOR'), + type: 'custom', + renderComponent: CreateByComponent, + filter: { + type: 'custom', + component: InputFilterComponent, + }, + filterFunction: (value: string) => { + this.setFilter({ + field: 'creator.firstName', + search: value, + }); + }, + }, + ...this.getColumnsByPage(), + dueDate: { + title: this.getTranslation('TASKS_PAGE.DUE_DATE'), + type: 'custom', + filter: { + type: 'custom', + component: InputFilterComponent, + }, + filterFunction: (value) => { + this.setFilter({ field: 'dueDate', search: value }); + }, + renderComponent: DateViewComponent, + }, + status: { + title: this.getTranslation('TASKS_PAGE.TASKS_STATUS'), + type: 'custom', + width: '10%', + renderComponent: StatusViewComponent, + filter: { + type: 'custom', + component: TaskStatusFilterComponent, + }, + filterFunction: (value) => { + this.setFilter({ + field: 'status', + search: value?.name, + }); + }, + }, + }, + }; + } + + private getColumnsByPage() { + if (this.isTasksPage()) { + return { + employeesMergedTeams: { + title: + this.getTranslation('TASKS_PAGE.TASK_MEMBERS') + + '/' + + this.getTranslation('TASKS_PAGE.TASK_TEAMS'), + type: 'custom', + filter: false, + renderComponent: EmployeesMergedTeamsComponent, + }, + }; + } else if (this.isMyTasksPage()) { + return { + assignTo: { + title: this.getTranslation('TASKS_PAGE.TASK_ASSIGNED_TO'), + type: 'custom', + filter: false, + renderComponent: AssignedToComponent, + }, + }; + } else if (this.isTeamTaskPage()) { + return { + assignTo: { + title: this.getTranslation('TASKS_PAGE.TASK_ASSIGNED_TO'), + type: 'custom', + width: '12%', + renderComponent: AssignedToComponent, + filter: { + type: 'custom', + component: OrganizationTeamFilterComponent, + }, + filterFunction: (value) => { + this.setFilter({ + field: 'teams', + search: value ? [value.id] : [], + }); + }, + }, + }; + } else { + return {}; + } + } + ngOnInit() { this._loadSmartTableSettings(); this._applyTranslationOnSmartTable(); @@ -174,23 +366,6 @@ export class TaskComponent .subscribe(); } - private initTasks(): void { - const path = this._activatedRoute.snapshot.url[0].path; - if (path === 'me') { - this.viewComponentName = ComponentEnum.MY_TASKS; - this.availableTasks$ = this.myTasks$; - return; - } - if (path === 'team') { - this.viewComponentName = ComponentEnum.TEAM_TASKS; - this.availableTasks$ = this.teamTasks$; - return; - } - this.viewComponentName = ComponentEnum.ALL_TASKS; - this.availableTasks$ = this.tasks$; - return; - } - setView() { this._store .componentLayout$(this.viewComponentName) @@ -224,15 +399,6 @@ export class TaskComponent .subscribe(); } - private _applyTranslationOnSmartTable() { - this.translateService.onLangChange - .pipe( - tap(() => this._loadSmartTableSettings()), - untilDestroyed(this) - ) - .subscribe(); - } - /* * Register Smart Table Source Config */ @@ -247,13 +413,13 @@ export class TaskComponent const { id: organizationId } = this.organization; this.smartTableSource = new ServerDataSource(this.httpClient, { - ...(this.viewComponentName == ComponentEnum.ALL_TASKS + ...(this.viewComponentName === ComponentEnum.ALL_TASKS ? { endPoint: `${API_PREFIX}/tasks/pagination` } : {}), - ...(this.viewComponentName == ComponentEnum.TEAM_TASKS + ...(this.viewComponentName === ComponentEnum.TEAM_TASKS ? { endPoint: `${API_PREFIX}/tasks/team` } : {}), - ...(this.viewComponentName == ComponentEnum.MY_TASKS + ...(this.viewComponentName === ComponentEnum.MY_TASKS ? { endPoint: `${API_PREFIX}/tasks/me` } : {}), relations: [ @@ -265,6 +431,9 @@ export class TaskComponent 'teams.members.employee.user', 'creator', 'organizationSprint', + 'taskStatus', + 'taskSize', + 'taskPriority', ], join: { alias: 'task', @@ -341,144 +510,6 @@ export class TaskComponent } } - private _loadSmartTableSettings() { - const pagination: IPaginationBase = this.getPagination(); - this.settingsSmartTable = { - actions: false, - pager: { - display: false, - perPage: pagination ? pagination.itemsPerPage : 10, - }, - noDataMessage: this.getTranslation('SM_TABLE.NO_DATA.TASK'), - columns: { - taskNumber: { - title: this.getTranslation('TASKS_PAGE.TASK_ID'), - type: 'string', - width: '10%', - filter: { - type: 'custom', - component: InputFilterComponent, - }, - filterFunction: (prefix: string) => { - this.setFilter({ field: 'prefix', search: prefix }); - }, - }, - description: { - title: this.getTranslation('TASKS_PAGE.TASKS_TITLE'), - type: 'custom', - class: 'align-row', - renderComponent: NotesWithTagsComponent, - filter: { - type: 'custom', - component: InputFilterComponent, - }, - filterFunction: (value) => { - this.setFilter({ field: 'title', search: value }); - }, - }, - project: { - title: this.getTranslation('TASKS_PAGE.TASKS_PROJECT'), - type: 'custom', - renderComponent: ProjectComponent, - filter: false, - }, - createdAt: { - title: this.getTranslation('SM_TABLE.CREATED_AT'), - type: 'custom', - filter: false, - renderComponent: CreatedAtComponent, - }, - creator: { - title: this.getTranslation('TASKS_PAGE.TASKS_CREATOR'), - type: 'custom', - renderComponent: CreateByComponent, - filter: { - type: 'custom', - component: InputFilterComponent, - }, - filterFunction: (value: string) => { - this.setFilter({ - field: 'creator.firstName', - search: value, - }); - }, - }, - ...this.getColumnsByPage(), - dueDate: { - title: this.getTranslation('TASKS_PAGE.DUE_DATE'), - type: 'custom', - filter: { - type: 'custom', - component: InputFilterComponent, - }, - filterFunction: (value) => { - this.setFilter({ field: 'dueDate', search: value }); - }, - renderComponent: DateViewComponent, - }, - status: { - title: this.getTranslation('TASKS_PAGE.TASKS_STATUS'), - type: 'custom', - width: '10%', - renderComponent: StatusViewComponent, - filter: { - type: 'custom', - component: TaskStatusFilterComponent, - }, - filterFunction: (value) => { - this.setFilter({ field: 'status', search: value }); - }, - }, - }, - }; - } - - private getColumnsByPage() { - if (this.isTasksPage()) { - return { - employeesMergedTeams: { - title: - this.getTranslation('TASKS_PAGE.TASK_MEMBERS') + - '/' + - this.getTranslation('TASKS_PAGE.TASK_TEAMS'), - type: 'custom', - filter: false, - renderComponent: EmployeesMergedTeamsComponent, - }, - }; - } else if (this.isMyTasksPage()) { - return { - assignTo: { - title: this.getTranslation('TASKS_PAGE.TASK_ASSIGNED_TO'), - type: 'custom', - filter: false, - renderComponent: AssignedToComponent, - }, - }; - } else if (this.isTeamTaskPage()) { - return { - assignTo: { - title: this.getTranslation('TASKS_PAGE.TASK_ASSIGNED_TO'), - type: 'custom', - width: '12%', - renderComponent: AssignedToComponent, - filter: { - type: 'custom', - component: OrganizationTeamFilterComponent, - }, - filterFunction: (value) => { - this.setFilter({ - field: 'teams', - search: value ? [value.id] : [], - }); - }, - }, - }; - } else { - return {}; - } - } - async createTaskDialog() { let dialog; if (this.isTasksPage()) { @@ -708,31 +739,6 @@ export class TaskComponent }); } - /** - * If, default project is selected from header - * - * @returns - */ - get isDefaultProject() { - if (this.selectedProject) { - return this.selectedProject.id === this.defaultProject.id; - } - return true; - } - - /** - * return store instance as per page - */ - get storeInstance() { - if (this.isTasksPage()) { - return this._taskStore; - } else if (this.isMyTasksPage()) { - return this._myTaskStore; - } else if (this.isTeamTaskPage()) { - return this._teamTaskStore; - } - } - /* * Clear selected item */ diff --git a/apps/gauzy/src/app/pages/tasks/components/team-task-dialog/team-task-dialog.component.html b/apps/gauzy/src/app/pages/tasks/components/team-task-dialog/team-task-dialog.component.html index f1f6cc1c8c1..151d419e4f1 100644 --- a/apps/gauzy/src/app/pages/tasks/components/team-task-dialog/team-task-dialog.component.html +++ b/apps/gauzy/src/app/pages/tasks/components/team-task-dialog/team-task-dialog.component.html @@ -1,7 +1,7 @@
- +
{{ @@ -18,10 +18,10 @@
@@ -33,12 +33,12 @@
'CONTEXT_MENU.PROJECT' | translate }} @@ -48,9 +48,11 @@
'TASKS_PAGE.TASKS_STATUS' | translate }} @@ -62,11 +64,11 @@
'TASKS_PAGE.TASK_TEAMS' | translate }} @@ -104,9 +106,11 @@
{{ 'TASKS_PAGE.TASK_PRIORITY' | translate }} @@ -116,18 +120,18 @@
{{ 'TASKS_PAGE.TASK_SIZE' | translate }}
@@ -136,19 +140,19 @@
-
@@ -160,47 +164,47 @@
}}
@@ -211,7 +215,11 @@
- +
@@ -219,18 +227,18 @@
diff --git a/apps/gauzy/src/app/pages/tasks/components/team-task-dialog/team-task-dialog.component.ts b/apps/gauzy/src/app/pages/tasks/components/team-task-dialog/team-task-dialog.component.ts index 0fb1a8db45b..d139536915c 100644 --- a/apps/gauzy/src/app/pages/tasks/components/team-task-dialog/team-task-dialog.component.ts +++ b/apps/gauzy/src/app/pages/tasks/components/team-task-dialog/team-task-dialog.component.ts @@ -1,13 +1,13 @@ import { Component, Input, OnInit } from '@angular/core'; import { - ITask, - IOrganizationProject, IEmployee, + IOrganizationProject, IOrganizationTeam, ITag, + ITask, TaskStatusEnum, } from '@gauzy/contracts'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { NbDialogRef } from '@nebular/theme'; import { TranslateService } from '@ngx-translate/core'; import * as moment from 'moment'; @@ -20,7 +20,7 @@ import { ToastrService, } from '../../../../@core/services'; import { CKEditor4 } from 'ckeditor4-angular/ckeditor'; -import { richTextCKEditorConfig } from "../../../../@shared/ckeditor.config"; +import { richTextCKEditorConfig } from '../../../../@shared/ckeditor.config'; const initialTaskValue = { title: '', @@ -32,6 +32,9 @@ const initialTaskValue = { dueDate: null, description: '', tags: null, + taskStatus: null, + taskSize: null, + taskPriority: null, }; @Component({ @@ -39,10 +42,10 @@ const initialTaskValue = { templateUrl: './team-task-dialog.component.html', styleUrls: ['./team-task-dialog.component.scss'], }) -export class TeamTaskDialogComponent extends TranslationBaseComponent - implements OnInit { - - form: FormGroup; +export class TeamTaskDialogComponent + extends TranslationBaseComponent + implements OnInit +{ selectedTaskId: string; projects: IOrganizationProject[]; employees: IEmployee[] = []; @@ -56,6 +59,8 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent public ckConfig: CKEditor4.Config = richTextCKEditorConfig; @Input() task: Partial = {}; + public form: FormGroup = TeamTaskDialogComponent.buildForm(this.fb); + constructor( public readonly dialogRef: NbDialogRef, private readonly fb: FormBuilder, @@ -70,16 +75,27 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent super(translateService); } - ngOnInit() { - this.ckConfig.editorplaceholder = this.translateService.instant('FORM.PLACEHOLDERS.DESCRIPTION'); - this.tenantId = this.store.user.tenantId; - this.organizationId = this._organizationsStore.selectedOrganization.id; - - this.loadProjects(); - this.loadTeams(); - this.initializeForm( - Object.assign({}, initialTaskValue, this.selectedTask || this.task) - ); + static buildForm(fb: FormBuilder): FormGroup { + return fb.group({ + number: [{ value: '', disabled: true }], + title: [null, Validators.required], + project: [], + projectId: [], + status: [TaskStatusEnum.OPEN], + priority: [], + size: [], + members: [], + estimateDays: [], + estimateHours: [null, [Validators.min(0), Validators.max(23)]], + estimateMinutes: [null, [Validators.min(0), Validators.max(59)]], + dueDate: [], + description: [], + tags: [], + teams: [], + taskStatus: [], + taskSize: [], + taskPriority: [], + }); } private async loadProjects() { @@ -92,6 +108,20 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent if (items) this.projects = items; } + async ngOnInit() { + this.ckConfig.editorplaceholder = this.translateService.instant( + 'FORM.PLACEHOLDERS.DESCRIPTION' + ); + this.tenantId = this.store.user.tenantId; + this.organizationId = this._organizationsStore.selectedOrganization.id; + + await this.loadProjects(); + await this.loadTeams(); + this.initializeForm( + Object.assign({}, initialTaskValue, this.selectedTask || this.task) + ); + } + initializeForm({ title, description, @@ -103,7 +133,10 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent dueDate, tags, priority, - size + size, + taskStatus, + taskSize, + taskPriority, }: ITask) { const duration = moment.duration(estimate, 'seconds'); this.selectedTeams = (teams || []).map((team) => team.id); @@ -116,30 +149,25 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent // if (teams === null) { // this.selectedTeams = [this.employeeId]; // } - this.form = this.fb.group({ - number: [{ value: '', disabled: true }], - title: [title, Validators.required], - project: [project], + this.form.patchValue({ + title, + project, projectId: project ? project.id : null, - status: [status ? status : TaskStatusEnum.OPEN], - priority: [priority ? priority : null], - size: [size ? size : null], - members: [members], - estimateDays: [duration.days() || ''], - estimateHours: [ - duration.hours() || '', - [Validators.min(0), Validators.max(23)], - ], - estimateMinutes: [ - duration.minutes() || '', - [Validators.min(0), Validators.max(59)], - ], - dueDate: [dueDate], - description: [description], - tags: [tags], - teams: [this.selectedTeams], + status, + priority, + size, + estimateDays: duration.days(), + estimateHours: duration.hours(), + estimateMinutes: duration.minutes(), + dueDate: dueDate ? new Date(dueDate) : null, + members, + description, + tags, + teams: this.selectedTeams, + taskStatus, + taskSize, + taskPriority, }); - this.tags = this.form.get('tags').value || []; } @@ -169,6 +197,15 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent .map((id) => this.teams.find((e) => e.id === id)) .filter((e) => !!e) ); + this.form + .get('status') + .setValue(this.form.get('taskStatus').value?.name); + this.form + .get('priority') + .setValue(this.form.get('taskPriority').value?.name); + this.form + .get('size') + .setValue(this.form.get('taskSize').value?.name); this.dialogRef.close(this.form.value); } } From a1a041340de475e62762cff11e220b8e878b3700 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:08:50 +0530 Subject: [PATCH 082/104] fix: #6734 get github repositories and displayed --- .../@core/services/github/github.service.ts | 17 +++- .../components/view/view.component.html | 16 ++++ .../components/view/view.component.scss | 6 ++ .../components/view/view.component.spec.ts | 24 +++++ .../github/components/view/view.component.ts | 87 +++++++++++++++++++ .../components/wizard/wizard.component.ts | 3 +- .../github/github-routing.module.ts | 16 +++- .../integrations/github/github.component.ts | 32 +++++++ .../integrations/github/github.module.ts | 13 ++- .../integrations/integration.resolver.ts | 33 +++++++ packages/contracts/src/github.model.ts | 16 ++++ .../github/github-integration.controller.ts | 4 +- 12 files changed, 257 insertions(+), 10 deletions(-) create mode 100644 apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html create mode 100644 apps/gauzy/src/app/pages/integrations/github/components/view/view.component.scss create mode 100644 apps/gauzy/src/app/pages/integrations/github/components/view/view.component.spec.ts create mode 100644 apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts create mode 100644 apps/gauzy/src/app/pages/integrations/github/github.component.ts create mode 100644 apps/gauzy/src/app/pages/integrations/integration.resolver.ts diff --git a/apps/gauzy/src/app/@core/services/github/github.service.ts b/apps/gauzy/src/app/@core/services/github/github.service.ts index dd03ca1f82d..992657e2c1e 100644 --- a/apps/gauzy/src/app/@core/services/github/github.service.ts +++ b/apps/gauzy/src/app/@core/services/github/github.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { IGithubAppInstallInput, IIntegrationTenant } from '@gauzy/contracts'; -import { firstValueFrom } from 'rxjs'; +import { IBasePerTenantAndOrganizationEntityModel, IGithubAppInstallInput, IGithubRepositoryResponse, IIntegrationTenant } from '@gauzy/contracts'; +import { Observable, firstValueFrom } from 'rxjs'; +import { toParams } from '@gauzy/common-angular'; import { API_PREFIX } from '../../constants'; @Injectable({ @@ -23,4 +24,16 @@ export class GithubService { this._http.post(`${API_PREFIX}/integration/github/install`, input) ); } + + /** + * + */ + getRepositories( + installation_id: string, + query: IBasePerTenantAndOrganizationEntityModel + ): Observable { + return this._http.get(`${API_PREFIX}/integration/github/${installation_id}/repositories`, { + params: toParams(query) + }); + } } diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html new file mode 100644 index 00000000000..71585b41ba6 --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html @@ -0,0 +1,16 @@ + + + +
+
+ +
+
+
diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.scss b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.scss new file mode 100644 index 00000000000..0b27efbff7d --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.scss @@ -0,0 +1,6 @@ +:host { + nb-card, + nb-card-body { + background-color: var(--gauzy-card-2); + } +} diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.spec.ts b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.spec.ts new file mode 100644 index 00000000000..0d0503049f9 --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.spec.ts @@ -0,0 +1,24 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { GithubViewComponent } from './view.component'; + +describe('GithubViewComponent', () => { + let component: GithubViewComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GithubViewComponent], + teardown: { destroyAfterEach: false } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(GithubViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts new file mode 100644 index 00000000000..fd961efb6f8 --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts @@ -0,0 +1,87 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { debounceTime, of } from 'rxjs'; +import { Observable } from 'rxjs/internal/Observable'; +import { catchError, filter, finalize, map, switchMap, tap } from 'rxjs/operators'; +import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; +import { IGithubRepository, IIntegrationSetting, IIntegrationTenant, IOrganization } from '@gauzy/contracts'; +import { distinctUntilChange } from '@gauzy/common-angular'; +import { + ErrorHandlingService, + GithubService, + IntegrationsService, + Store +} from './../../../../../@core/services'; + +@UntilDestroy({ checkProperties: true }) +@Component({ + templateUrl: './view.component.html' +}) +export class GithubViewComponent implements OnInit { + + public organization: IOrganization; + public loading: boolean = true; + public repositories: IGithubRepository[] = []; + public repositories$: Observable; + + constructor( + private readonly _activatedRoute: ActivatedRoute, + private readonly _errorHandlingService: ErrorHandlingService, + private readonly _store: Store, + private readonly _integrationsService: IntegrationsService, + private readonly _githubService: GithubService, + ) { } + + ngOnInit() { + this._store.selectedOrganization$ + .pipe( + debounceTime(200), + distinctUntilChange(), + filter((organization: IOrganization) => !!organization), + tap((organization: IOrganization) => this.organization = organization), + tap(() => this.getRepositories()), + untilDestroyed(this) + ) + .subscribe(); + } + + /** + * + */ + async getRepositories() { + if (!this.organization) { + return; + } + + const integrationId = this._activatedRoute.snapshot.paramMap.get('integrationId'); + const { id: organizationId, tenantId } = this.organization; + + this.repositories$ = this._integrationsService.fetchIntegrationTenant(integrationId, { + relations: ['settings'] + }).pipe( + filter((integration: IIntegrationTenant) => !!integration.id), + filter(({ settings }: IIntegrationTenant) => !!settings), + map(({ settings }: IIntegrationTenant) => { + const setting: IIntegrationSetting = settings.find( + (setting: IIntegrationSetting) => setting.settingsName === 'installation_id' + ); + const installation_id = setting.settingsValue; + return installation_id; + }), + switchMap((installation_id) => this._githubService.getRepositories( + installation_id, + { organizationId, tenantId } + )), + map(({ repositories }) => { + this.repositories = repositories; + return repositories; + }), + untilDestroyed(this), + catchError((error) => { + this._errorHandlingService.handleError(error); + return of([]); + }), + finalize(() => (this.loading = false)), + ); + } +} diff --git a/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts index 8f2d08b9acb..8701445158c 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { filter, tap } from 'rxjs/operators'; +import { debounceTime, filter, tap } from 'rxjs/operators'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { environment } from '@env/environment'; import { IOrganization, IntegrationEnum } from '@gauzy/contracts'; @@ -27,6 +27,7 @@ export class GithubWizardComponent implements OnInit { ngOnInit() { this.store.selectedOrganization$ .pipe( + debounceTime(200), distinctUntilChange(), filter((organization: IOrganization) => !!organization), tap((organization: IOrganization) => this.organization = organization), diff --git a/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts b/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts index a33c22c9a96..1135019a233 100644 --- a/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts +++ b/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts @@ -1,16 +1,26 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { IntegrationEnum } from '@gauzy/contracts'; +import { IntergrationResolver } from './../integration.resolver'; import { GithubWizardComponent } from './components/wizard/wizard.component'; import { GithubInstallationComponent } from './components/installation/installation.component'; +import { GithubComponent } from './github.component'; +import { GithubViewComponent } from './components/view/view.component'; const routes: Routes = [ { path: '', + component: GithubComponent, + data: { + integration: IntegrationEnum.GITHUB + }, + resolve: { + integration: IntergrationResolver + }, children: [ { - path: '', - redirectTo: 'wizard', - pathMatch: 'full' + path: ':integrationId', + component: GithubViewComponent, }, { path: 'wizard', diff --git a/apps/gauzy/src/app/pages/integrations/github/github.component.ts b/apps/gauzy/src/app/pages/integrations/github/github.component.ts new file mode 100644 index 00000000000..2d204063c8d --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/github/github.component.ts @@ -0,0 +1,32 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Data, Router } from '@angular/router'; +import { debounceTime, tap } from 'rxjs/operators'; + +@Component({ + template: `` +}) +export class GithubComponent implements OnInit { + + constructor( + private readonly _router: Router, + private readonly _activatedRoute: ActivatedRoute, + ) { } + + /** + * + */ + ngOnInit() { + this._activatedRoute.data + .pipe( + debounceTime(100), + tap(({ integration }: Data) => { + if (integration) { + this._router.navigate(['/pages/integrations/github', integration.id]); + } else { + this._router.navigate(['/pages/integrations/github/wizard']); + } + }) + ) + .subscribe(); + } +} diff --git a/apps/gauzy/src/app/pages/integrations/github/github.module.ts b/apps/gauzy/src/app/pages/integrations/github/github.module.ts index f964b689e48..1b3a3c1f89b 100644 --- a/apps/gauzy/src/app/pages/integrations/github/github.module.ts +++ b/apps/gauzy/src/app/pages/integrations/github/github.module.ts @@ -1,19 +1,28 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { NbCardModule } from '@nebular/theme'; +import { NbCardModule, NbSpinnerModule } from '@nebular/theme'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { NgSelectModule } from '@ng-select/ng-select'; import { TranslateModule } from './../../../@shared/translate/translate.module'; import { GithubRoutingModule } from './github-routing.module'; +import { GithubComponent } from './github.component'; import { GithubWizardComponent } from './components/wizard/wizard.component'; import { GithubInstallationComponent } from './components/installation/installation.component'; +import { GithubViewComponent } from './components/view/view.component'; @NgModule({ declarations: [ + GithubComponent, GithubWizardComponent, - GithubInstallationComponent + GithubInstallationComponent, + GithubViewComponent ], imports: [ CommonModule, NbCardModule, + NbSpinnerModule, + Ng2SmartTableModule, + NgSelectModule, GithubRoutingModule, TranslateModule ] diff --git a/apps/gauzy/src/app/pages/integrations/integration.resolver.ts b/apps/gauzy/src/app/pages/integrations/integration.resolver.ts new file mode 100644 index 00000000000..d68b0c3e4b6 --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/integration.resolver.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { catchError, Observable, of } from 'rxjs'; +import { IIntegrationTenant } from '@gauzy/contracts'; +import { IntegrationsService, Store } from './../../@core/services'; + +@Injectable({ + providedIn: 'root' +}) +export class IntergrationResolver implements Resolve> { + constructor( + private readonly _integrationsService: IntegrationsService, + private readonly _store: Store, + ) { } + + /** + * + * @param route + * @returns + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const integration = route.data['integration']; + const { id: organizationId, tenantId } = this._store.selectedOrganization; + + return this._integrationsService.checkRememberState({ + organizationId, + tenantId, + name: integration + }).pipe( + catchError(error => of(error)) + ); + } +} diff --git a/packages/contracts/src/github.model.ts b/packages/contracts/src/github.model.ts index 98bf87b2943..64d3aba2771 100644 --- a/packages/contracts/src/github.model.ts +++ b/packages/contracts/src/github.model.ts @@ -10,3 +10,19 @@ export interface IOAuthAppInstallInput extends IBasePerTenantAndOrganizationEnti provider?: string; code?: string; } + +/** */ +export interface IGithubRepository { + id: string; + node_id: string; + name: string; + full_name: string; + private: boolean; + [x: string]: any; +} + +export interface IGithubRepositoryResponse { + total_count: number; + repository_selection: string; + repositories: IGithubRepository[] +} diff --git a/packages/core/src/integration/github/github-integration.controller.ts b/packages/core/src/integration/github/github-integration.controller.ts index 62f5eaecb35..6ef98ab4678 100644 --- a/packages/core/src/integration/github/github-integration.controller.ts +++ b/packages/core/src/integration/github/github-integration.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get, HttpException, HttpStatus, Logger, Param, Query, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common'; import { OctokitResponse, OctokitService } from '@gauzy/integration-github'; -import { PermissionsEnum } from '@gauzy/contracts'; +import { IGithubRepositoryResponse, PermissionsEnum } from '@gauzy/contracts'; import { PermissionGuard, TenantPermissionGuard } from 'shared/guards'; import { Permissions } from 'shared/decorators'; import { TenantOrganizationBaseDTO } from 'core/dto'; @@ -57,7 +57,7 @@ export class GitHubIntegrationController { async getGithubRepositories( @Param('installation_id') installation_id: number, @Query() query: TenantOrganizationBaseDTO, - ): Promise> { + ): Promise> { try { // Validate the input data (You can use class-validator for validation) if (!query || !query.organizationId) { From 52048df80ec4baea5f386c2d71c6d06565b41d05 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:19:52 +0530 Subject: [PATCH 083/104] fix: #6734 display tables for issues of specific repository --- .../components/view/view.component.html | 16 ++++- .../github/components/view/view.component.ts | 59 +++++++++++++++++-- apps/gauzy/src/assets/i18n/en.json | 3 + 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html index 71585b41ba6..bd4f44bcb94 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html @@ -1,5 +1,12 @@ - - + + +
{{ 'INTEGRATIONS.GITHUB_PAGE.NAME' | translate }}
+
@@ -11,6 +18,11 @@ [placeholder]="'Select Repository'" appendTo="body" > +
diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts index fd961efb6f8..b084f1b72ba 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts @@ -1,8 +1,9 @@ -import { Component, OnInit } from '@angular/core'; +import { AfterViewInit, Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { debounceTime, of } from 'rxjs'; import { Observable } from 'rxjs/internal/Observable'; import { catchError, filter, finalize, map, switchMap, tap } from 'rxjs/operators'; +import { TranslateService } from '@ngx-translate/core'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { IGithubRepository, IIntegrationSetting, IIntegrationTenant, IOrganization } from '@gauzy/contracts'; import { distinctUntilChange } from '@gauzy/common-angular'; @@ -12,27 +13,39 @@ import { IntegrationsService, Store } from './../../../../../@core/services'; +import { TranslationBaseComponent } from 'apps/gauzy/src/app/@shared/language-base'; @UntilDestroy({ checkProperties: true }) @Component({ templateUrl: './view.component.html' }) -export class GithubViewComponent implements OnInit { +export class GithubViewComponent extends TranslationBaseComponent implements AfterViewInit, OnInit { + public settingsSmartTable: object; public organization: IOrganization; public loading: boolean = true; public repositories: IGithubRepository[] = []; public repositories$: Observable; + public issues$: Observable; + public issues: any[] = []; constructor( + public readonly _translateService: TranslateService, private readonly _activatedRoute: ActivatedRoute, private readonly _errorHandlingService: ErrorHandlingService, private readonly _store: Store, private readonly _integrationsService: IntegrationsService, private readonly _githubService: GithubService, - ) { } + ) { + super(_translateService); + } + + ngOnInit(): void { + this._loadSmartTableSettings(); + this._applyTranslationOnSmartTable(); + } - ngOnInit() { + ngAfterViewInit(): void { this._store.selectedOrganization$ .pipe( debounceTime(200), @@ -84,4 +97,42 @@ export class GithubViewComponent implements OnInit { finalize(() => (this.loading = false)), ); } + + /** + * + */ + private _applyTranslationOnSmartTable() { + this.translateService.onLangChange + .pipe( + tap(() => this._loadSmartTableSettings()), + untilDestroyed(this) + ) + .subscribe(); + } + + /** + * + */ + private _loadSmartTableSettings() { + this.settingsSmartTable = { + selectedRowIndex: -1, + selectMode: 'multi', + actions: { + add: false, + edit: false, + delete: false, + select: true + }, + columns: { + name: { + title: this.getTranslation('SM_TABLE.NAME'), + type: 'string' + }, + description: { + title: this.getTranslation('SM_TABLE.DESCRIPTION'), + type: 'string' + } + } + }; + } } diff --git a/apps/gauzy/src/assets/i18n/en.json b/apps/gauzy/src/assets/i18n/en.json index 0e8ba8cdec8..6221f0308f6 100644 --- a/apps/gauzy/src/assets/i18n/en.json +++ b/apps/gauzy/src/assets/i18n/en.json @@ -1211,6 +1211,9 @@ "API_KEY": "Gauzy AI key", "API_SECRET": "Gauzy AI Secret" }, + "GITHUB_PAGE": { + "NAME": "GitHub" + }, "COMING_SOON": "Coming soon", "RE_INTEGRATE": "Re-integrate", "SETTINGS": "Settings", From a87b8b8fcc49ef90a1cbc45dd7887654f571a37c Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:09:05 +0200 Subject: [PATCH 084/104] feat: improve tasks backlog --- .../task/task.component.html | 25 +++- .../task/task.component.scss | 16 ++- .../tasks-sprint-view/task/task.component.ts | 118 +++++++++++++++--- .../gauzy/src/app/pages/tasks/tasks.module.ts | 12 +- 4 files changed, 143 insertions(+), 28 deletions(-) diff --git a/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/task/task.component.html b/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/task/task.component.html index b7facd0d656..eb724b5b845 100644 --- a/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/task/task.component.html +++ b/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/task/task.component.html @@ -1,4 +1,4 @@ -
+
@@ -16,7 +16,7 @@ @@ -24,13 +24,26 @@
+ +
+ + + +
+
diff --git a/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/task/task.component.scss b/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/task/task.component.scss index 7a678ddada7..55718e481e7 100644 --- a/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/task/task.component.scss +++ b/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/task/task.component.scss @@ -11,4 +11,18 @@ nb-card { .selected { background-color: var(--gauzy-sidebar-background-3); box-shadow: -6px 0 0 0 rgba(0 0 0 / 10%); -} \ No newline at end of file +} + +.view { + display: flex; + flex-direction: column; + gap: 4px; + padding: 4px; +} +.status-view { + cursor: pointer; +} + +::ng-deep nb-popover > span.arrow { + display: none; +} diff --git a/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/task/task.component.ts b/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/task/task.component.ts index 3c00a980875..3534ec52a82 100644 --- a/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/task/task.component.ts +++ b/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/task/task.component.ts @@ -1,29 +1,47 @@ import { + AfterViewInit, Component, - Input, EventEmitter, - Output, + Input, + OnDestroy, OnInit, - OnDestroy + Output, } from '@angular/core'; -import { ITask, TaskStatusEnum } from '@gauzy/contracts'; +import { + IOrganization, + IOrganizationProject, + IPagination, + ITask, + ITaskStatus, + ITaskStatusFindInput, + TaskStatusEnum, +} from '@gauzy/contracts'; import { NbMenuService } from '@nebular/theme'; -import { tap, filter, map, takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; +import { filter, map, takeUntil, tap } from 'rxjs/operators'; +import { combineLatest, debounceTime, Subject } from 'rxjs'; import { TranslationBaseComponent } from 'apps/gauzy/src/app/@shared/language-base/translation-base.component'; import { TranslateService } from '@ngx-translate/core'; +import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; +import { Store, TaskStatusesService } from '../../../../../../../@core'; +import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; +import { distinctUntilChange } from '@gauzy/common-angular'; +@UntilDestroy({ checkProperties: true }) @Component({ selector: 'ga-sprint-task', templateUrl: './task.component.html', - styleUrls: ['./task.component.scss'] + styleUrls: ['./task.component.scss'], }) export class SprintTaskComponent extends TranslationBaseComponent - implements OnInit, OnDestroy + implements OnInit, AfterViewInit, OnDestroy { + private onDestroy$ = new Subject(); + private subject$: Subject = new Subject(); + private organization: IOrganization; + private projectId: IOrganizationProject['id']; @Input() task: any; - @Input() isSelected: boolean = false; + @Input() isSelected = false; @Output() taskActionEvent: EventEmitter<{ action: string; task: ITask; @@ -32,25 +50,34 @@ export class SprintTaskComponent new EventEmitter(); taskStatusList: any; taskActions: any; - private onDestroy$ = new Subject(); + public statuses$: BehaviorSubject = new BehaviorSubject([]); constructor( private nbMenuService: NbMenuService, - readonly translate: TranslateService + readonly translate: TranslateService, + private readonly store: Store, + private readonly taskStatusesService: TaskStatusesService ) { super(translate); } ngOnInit(): void { + this.subject$ + .pipe( + debounceTime(200), + tap(() => this.getStatuses()), + untilDestroyed(this) + ) + .subscribe(); this.taskActions = [ { title: this.getTranslation('TASKS_PAGE.EDIT_TASK'), - action: 'EDIT_TASK' + action: 'EDIT_TASK', }, { title: this.getTranslation('TASKS_PAGE.DELETE_TASK'), - action: 'DELETE_TASK' - } + action: 'DELETE_TASK', + }, ]; this.taskStatusList = this.getStatusList(this.task.status); @@ -68,13 +95,13 @@ export class SprintTaskComponent this.changeStatusEvent.emit({ status: item.title, id: this.task.id, - title: this.task.title + title: this.task.title, }); break; case 'updateTask': this.taskActionEvent.emit({ action: item.action, - task: this.task + task: this.task, }); } }), @@ -83,6 +110,23 @@ export class SprintTaskComponent .subscribe(); } + ngAfterViewInit(): void { + const storeOrganization$ = this.store.selectedOrganization$; + const storeProject$ = this.store.selectedProject$; + combineLatest([storeOrganization$, storeProject$]) + .pipe( + distinctUntilChange(), + filter(([organization]) => !!organization), + tap(([organization, project]) => { + this.organization = organization; + this.projectId = project ? project.id : null; + }), + tap(() => this.subject$.next(true)), + untilDestroyed(this) + ) + .subscribe(); + } + getStatusList(filterOption: string): { title: TaskStatusEnum }[] { return Object.values(TaskStatusEnum) .filter((status) => status !== filterOption) @@ -97,6 +141,48 @@ export class SprintTaskComponent this.changeStatusEvent.emit(evt); } + getStatuses() { + if (!this.organization) { + return; + } + + const { tenantId } = this.store.user; + const { id: organizationId } = this.organization; + + this.taskStatusesService + .get({ + tenantId, + organizationId, + ...(this.projectId + ? { + projectId: this.projectId, + } + : {}), + }) + .pipe( + map(({ items, total }: IPagination) => + total > 0 ? items : this.taskStatusList + ), + tap((statuses: ITaskStatus[]) => this.statuses$.next(statuses)), + untilDestroyed(this) + ) + .subscribe(); + } + + public updateStatus(taskStatus: ITaskStatus) { + this.changeStatusEvent.emit({ + status: taskStatus.name as TaskStatusEnum, + id: this.task.id, + title: this.task.title, + taskStatus, + }); + this.task = { + ...this.task, + taskStatus, + status: taskStatus.name as TaskStatusEnum, + }; + } + ngOnDestroy() { this.onDestroy$.next(); this.onDestroy$.complete(); diff --git a/apps/gauzy/src/app/pages/tasks/tasks.module.ts b/apps/gauzy/src/app/pages/tasks/tasks.module.ts index 3038ed67bc8..2e326ee9b31 100644 --- a/apps/gauzy/src/app/pages/tasks/tasks.module.ts +++ b/apps/gauzy/src/app/pages/tasks/tasks.module.ts @@ -21,7 +21,8 @@ import { NbTabsetModule, NbActionsModule, NbContextMenuModule, - NbTooltipModule + NbTooltipModule, + NbPopoverModule, } from '@nebular/theme'; import { NgxPermissionsModule } from 'ngx-permissions'; import { ThemeModule } from '../../@theme/theme.module'; @@ -62,7 +63,7 @@ import { CKEditorModule } from 'ckeditor4-angular'; TaskSettingsComponent, ProjectViewComponent, TasksSprintViewComponent, - SprintTaskComponent + SprintTaskComponent, ], imports: [ NbTooltipModule, @@ -110,7 +111,8 @@ import { CKEditorModule } from 'ckeditor4-angular'; TaskNumberFieldModule, NgxPermissionsModule.forChild(), DirectivesModule, - CKEditorModule - ] + CKEditorModule, + NbPopoverModule, + ], }) -export class TasksModule { } +export class TasksModule {} From 50eb8e83da922257da78693d234661897ddef9cc Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:26:16 +0200 Subject: [PATCH 085/104] feat: add taskStatus parameter to payload on update --- .../tasks-sprint-view.component.ts | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/tasks-sprint-view.component.ts b/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/tasks-sprint-view.component.ts index 6177e310df3..86c116c2c57 100644 --- a/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/tasks-sprint-view.component.ts +++ b/apps/gauzy/src/app/pages/tasks/components/task/tasks-layouts/tasks-sprint-view/tasks-sprint-view.component.ts @@ -5,20 +5,20 @@ import { Output, EventEmitter, SimpleChanges, - OnChanges + OnChanges, } from '@angular/core'; import { ITask, IOrganizationSprint, IOrganizationProject, - IOrganization + IOrganization, } from '@gauzy/contracts'; import { Observable } from 'rxjs'; import { map, tap, filter, take } from 'rxjs/operators'; import { CdkDragDrop, moveItemInArray, - transferArrayItem + transferArrayItem, } from '@angular/cdk/drag-drop'; import { NbDialogService } from '@nebular/theme'; import { TranslateService } from '@ngx-translate/core'; @@ -27,14 +27,14 @@ import { GauzyEditableGridComponent } from '../../../../../../@shared/components import { SprintStoreService, Store, - TasksStoreService + TasksStoreService, } from './../../../../../../@core/services'; @UntilDestroy({ checkProperties: true }) @Component({ selector: 'ga-tasks-sprint-view', templateUrl: './tasks-sprint-view.component.html', - styleUrls: ['./tasks-sprint-view.component.scss'] + styleUrls: ['./tasks-sprint-view.component.scss'], }) export class TasksSprintViewComponent extends GauzyEditableGridComponent @@ -61,7 +61,7 @@ export class TasksSprintViewComponent tap((sprints: IOrganizationSprint[]) => { this.sprintIds = [ ...sprints.map((sprint: IOrganizationSprint) => sprint.id), - 'backlog' + 'backlog', ]; }) ); @@ -87,7 +87,7 @@ export class TasksSprintViewComponent this.backlogTasks = this.tasks; this.sprintActions = [ { title: this.getTranslation('TASKS_PAGE.EDIT_SPRINT') }, - { title: this.getTranslation('TASKS_PAGE.DELETE_SPRINT') } + { title: this.getTranslation('TASKS_PAGE.DELETE_SPRINT') }, ]; } @@ -102,7 +102,7 @@ export class TasksSprintViewComponent this.store$.fetchSprints({ organizationId, projectId: project.id, - tenantId + tenantId, }); }), untilDestroyed(this) @@ -121,7 +121,7 @@ export class TasksSprintViewComponent ) => { acc[sprint.id] = { ...sprint, - tasks: sprint.tasks || [] + tasks: sprint.tasks || [], }; return acc; }, @@ -169,7 +169,7 @@ export class TasksSprintViewComponent this.selectedTask = task === this.selectedTask ? null : task; this.selectedTaskEvent.emit({ data: task, - isSelected: this.isSelected + isSelected: this.isSelected, }); } @@ -188,7 +188,7 @@ export class TasksSprintViewComponent organizationSprint: this.sprints.find( (sprint) => sprint.id === event.container.id - ) || null + ) || null, }) .pipe(untilDestroyed(this)) .subscribe(); @@ -213,13 +213,14 @@ export class TasksSprintViewComponent } } - changeTaskStatus({ id, status, title }: Partial): void { + changeTaskStatus({ id, taskStatus, status, title }: Partial): void { this.taskStore .editTask({ id, status, title, - organizationId: this.organizationId + organizationId: this.organizationId, + taskStatus, }) .pipe(untilDestroyed(this)) .subscribe(); @@ -230,7 +231,7 @@ export class TasksSprintViewComponent this.store$ .updateSprint({ ...sprint, - isActive: false + isActive: false, }) .pipe(take(1), untilDestroyed(this)) .subscribe(); From f371693273f08cdeb45c87ba2e850414db286f40 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 25 Sep 2023 15:29:15 +0530 Subject: [PATCH 086/104] fix: #6734 get github repositories using integration ID --- .../@core/services/github/github.service.ts | 24 +++++---- .../github/components/view/view.component.ts | 51 ++++++++---------- .../github/github-integration.controller.ts | 29 ++++++---- .../integration/github/github.middleware.ts | 53 +++++++++++++++++++ .../src/integration/github/github.module.ts | 30 ++++++++--- 5 files changed, 129 insertions(+), 58 deletions(-) create mode 100644 packages/core/src/integration/github/github.middleware.ts diff --git a/apps/gauzy/src/app/@core/services/github/github.service.ts b/apps/gauzy/src/app/@core/services/github/github.service.ts index 992657e2c1e..399f11df888 100644 --- a/apps/gauzy/src/app/@core/services/github/github.service.ts +++ b/apps/gauzy/src/app/@core/services/github/github.service.ts @@ -15,25 +15,27 @@ export class GithubService { ) { } /** - * - * @param input - * @returns + * Add a GitHub app installation. + * @param input The input data for the GitHub app installation. + * @returns A promise that resolves to the integration tenant object. */ async addInstallationApp(input: IGithubAppInstallInput): Promise { - return firstValueFrom( - this._http.post(`${API_PREFIX}/integration/github/install`, input) - ); + const url = `${API_PREFIX}/integration/github/install`; + return firstValueFrom(this._http.post(url, input)); } /** - * + * Fetches repositories for a given integration and organization. + * @param integrationId The ID of the integration. + * @param query Query parameters for the request. */ getRepositories( - installation_id: string, + integrationId: IIntegrationTenant['id'], query: IBasePerTenantAndOrganizationEntityModel ): Observable { - return this._http.get(`${API_PREFIX}/integration/github/${installation_id}/repositories`, { - params: toParams(query) - }); + const url = `${API_PREFIX}/integration/github/${integrationId}/repositories`; + const params = toParams(query); + + return this._http.get(url, { params }); } } diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts index b084f1b72ba..7f30f0fec85 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts @@ -2,18 +2,17 @@ import { AfterViewInit, Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { debounceTime, of } from 'rxjs'; import { Observable } from 'rxjs/internal/Observable'; -import { catchError, filter, finalize, map, switchMap, tap } from 'rxjs/operators'; +import { catchError, filter, map, switchMap, tap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { IGithubRepository, IIntegrationSetting, IIntegrationTenant, IOrganization } from '@gauzy/contracts'; +import { IGithubRepository, IGithubRepositoryResponse, IOrganization } from '@gauzy/contracts'; import { distinctUntilChange } from '@gauzy/common-angular'; +import { TranslationBaseComponent } from './../../../../../@shared/language-base'; import { ErrorHandlingService, GithubService, - IntegrationsService, Store } from './../../../../../@core/services'; -import { TranslationBaseComponent } from 'apps/gauzy/src/app/@shared/language-base'; @UntilDestroy({ checkProperties: true }) @Component({ @@ -34,7 +33,6 @@ export class GithubViewComponent extends TranslationBaseComponent implements Aft private readonly _activatedRoute: ActivatedRoute, private readonly _errorHandlingService: ErrorHandlingService, private readonly _store: Store, - private readonly _integrationsService: IntegrationsService, private readonly _githubService: GithubService, ) { super(_translateService); @@ -59,42 +57,39 @@ export class GithubViewComponent extends TranslationBaseComponent implements Aft } /** - * + * Fetches repositories for a given integration and organization. */ - async getRepositories() { + private getRepositories() { + // Ensure there is a valid organization if (!this.organization) { return; } - const integrationId = this._activatedRoute.snapshot.paramMap.get('integrationId'); + // Extract organization properties const { id: organizationId, tenantId } = this.organization; - - this.repositories$ = this._integrationsService.fetchIntegrationTenant(integrationId, { - relations: ['settings'] - }).pipe( - filter((integration: IIntegrationTenant) => !!integration.id), - filter(({ settings }: IIntegrationTenant) => !!settings), - map(({ settings }: IIntegrationTenant) => { - const setting: IIntegrationSetting = settings.find( - (setting: IIntegrationSetting) => setting.settingsName === 'installation_id' - ); - const installation_id = setting.settingsValue; - return installation_id; + this.repositories$ = this._activatedRoute.params.pipe( + // Get the 'integrationId' route parameter + switchMap(({ integrationId }) => { + return this._githubService.getRepositories(integrationId, { + organizationId, + tenantId + }); }), - switchMap((installation_id) => this._githubService.getRepositories( - installation_id, - { organizationId, tenantId } - )), - map(({ repositories }) => { + // Update component state with fetched repositories + tap(({ repositories }: IGithubRepositoryResponse) => { this.repositories = repositories; - return repositories; }), - untilDestroyed(this), + map(({ repositories }) => repositories), catchError((error) => { + // Handle and log errors this._errorHandlingService.handleError(error); return of([]); }), - finalize(() => (this.loading = false)), + tap(() => { + this.loading = false; + }), + // Handle component lifecycle to avoid memory leaks + untilDestroyed(this), ); } diff --git a/packages/core/src/integration/github/github-integration.controller.ts b/packages/core/src/integration/github/github-integration.controller.ts index 6ef98ab4678..844155c710c 100644 --- a/packages/core/src/integration/github/github-integration.controller.ts +++ b/packages/core/src/integration/github/github-integration.controller.ts @@ -1,4 +1,5 @@ -import { Controller, Get, HttpException, HttpStatus, Logger, Param, Query, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common'; +import { Controller, Get, HttpException, HttpStatus, Logger, Query, Req, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common'; +import { Request } from 'express'; import { OctokitResponse, OctokitService } from '@gauzy/integration-github'; import { IGithubRepositoryResponse, PermissionsEnum } from '@gauzy/contracts'; import { PermissionGuard, TenantPermissionGuard } from 'shared/guards'; @@ -7,7 +8,7 @@ import { TenantOrganizationBaseDTO } from 'core/dto'; @UseGuards(TenantPermissionGuard, PermissionGuard) @Permissions(PermissionsEnum.INTEGRATION_VIEW) -@Controller(':installation_id') +@Controller(':integrationId') export class GitHubIntegrationController { private readonly logger = new Logger('GitHubIntegrationController'); @@ -26,17 +27,20 @@ export class GitHubIntegrationController { @Get('/metadata') @UsePipes(new ValidationPipe({ transform: true })) async getGithubInstallationMetadata( - @Param('installation_id') installation_id: number, + @Req() request: Request, @Query() query: TenantOrganizationBaseDTO, - ): Promise> { + ): Promise | void> { try { // Validate the input data (You can use class-validator for validation) if (!query || !query.organizationId) { throw new HttpException('Invalid query parameter', HttpStatus.BAD_REQUEST); } // Get installation metadata - const metadata = await this._octokitService.getGithubInstallationMetadata(installation_id); - return metadata.data; + const installation_id = request['integration']['settings']['installation_id']; + if (installation_id) { + const metadata = await this._octokitService.getGithubInstallationMetadata(installation_id); + return metadata.data; + } } catch (error) { // Handle errors and return an appropriate error respons this.logger.error('Error while retrieve github installation metadata', error.message); @@ -55,17 +59,20 @@ export class GitHubIntegrationController { @Get('/repositories') @UsePipes(new ValidationPipe({ transform: true })) async getGithubRepositories( - @Param('installation_id') installation_id: number, + @Req() request: Request, @Query() query: TenantOrganizationBaseDTO, - ): Promise> { + ): Promise | void> { try { // Validate the input data (You can use class-validator for validation) if (!query || !query.organizationId) { throw new HttpException('Invalid query parameter', HttpStatus.BAD_REQUEST); } - // Get installation repositories - const repositories = await this._octokitService.getGithubRepositories(installation_id); - return repositories.data; + const installation_id = request['integration']['settings']['installation_id']; + if (installation_id) { + // Get installation repositories + const repositories = await this._octokitService.getGithubRepositories(installation_id); + return repositories.data; + } } catch (error) { // Handle errors and return an appropriate error respons this.logger.error('Error while retrieving GitHub installation repositories', error.message); diff --git a/packages/core/src/integration/github/github.middleware.ts b/packages/core/src/integration/github/github.middleware.ts new file mode 100644 index 00000000000..b34d18c4b1f --- /dev/null +++ b/packages/core/src/integration/github/github.middleware.ts @@ -0,0 +1,53 @@ +import { Injectable, NestMiddleware } from '@nestjs/common'; +import { Request, Response, NextFunction } from 'express'; +import { isNotEmpty } from '@gauzy/common'; +import { IntegrationEnum } from '@gauzy/contracts'; +import { arrayToObject } from 'core/utils'; +import { IntegrationTenantService } from 'integration-tenant/integration-tenant.service'; + +@Injectable() +export class GithubMiddleware implements NestMiddleware { + + constructor( + private readonly _integrationTenantService: IntegrationTenantService, + ) { } + + async use( + request: Request, + response: Response, + next: NextFunction + ) { + try { + const integrationId = request.params['integrationId']; + const queryParameters = request.query; + + const tenantId = queryParameters.tenantId ? queryParameters.tenantId.toString() : request.header('Tenant-Id'); + const organizationId = queryParameters.organizationId ? queryParameters.organizationId.toString() : request.header('Organization-Id'); + + // Check if tenant and organization IDs are not empty + if (isNotEmpty(tenantId) && isNotEmpty(organizationId)) { + // Fetch integration settings from the service + const { settings = [] } = await this._integrationTenantService.findOneByIdString(integrationId, { + where: { + tenantId, + organizationId + }, + relations: { + settings: true + } + }); + /** */ + request['integration'] = new Object({ + name: IntegrationEnum.GITHUB, + // Convert settings array to an object + settings: arrayToObject(settings, 'settingsName', 'settingsValue') + }); + } + } catch (error) { + console.log(`Error while getting integration (${IntegrationEnum.GITHUB}) tenant: %s`, error?.message); + } + + // Continue to the next middleware or route handler + next(); + } +} diff --git a/packages/core/src/integration/github/github.module.ts b/packages/core/src/integration/github/github.module.ts index 4e86cd71225..861f61778a8 100644 --- a/packages/core/src/integration/github/github.module.ts +++ b/packages/core/src/integration/github/github.module.ts @@ -1,15 +1,18 @@ -import { Module, forwardRef } from '@nestjs/common'; +import { MiddlewareConsumer, Module, NestModule, forwardRef } from '@nestjs/common'; import { HttpModule } from '@nestjs/axios'; import { CqrsModule } from '@nestjs/cqrs'; +import { RouterModule } from 'nest-router'; import { TenantModule } from 'tenant/tenant.module'; import { UserModule } from 'user/user.module'; import { IntegrationModule } from 'integration/integration.module'; +import { IntegrationTenantModule } from 'integration-tenant/integration-tenant.module'; import { GitHubAuthorizationController } from './github-authorization.controller'; import { GitHubController } from './github.controller'; import { GitHubIntegrationController } from './github-integration.controller'; import { GitHubHooksController } from './github.events.controller'; import { GithubService } from './github.service'; import { GithubHooksService } from './github.events.service'; +import { GithubMiddleware } from './github.middleware'; @Module({ imports: [ @@ -17,7 +20,8 @@ import { GithubHooksService } from './github.events.service'; TenantModule, UserModule, CqrsModule, - forwardRef(() => IntegrationModule) + forwardRef(() => IntegrationModule), + forwardRef(() => IntegrationTenantModule) ], controllers: [ GitHubAuthorizationController, @@ -27,11 +31,21 @@ import { GithubHooksService } from './github.events.service'; ], providers: [ GithubService, - GithubHooksService - ], - exports: [ - GithubService, - GithubHooksService + GithubHooksService, + // Define middleware heres + GithubMiddleware ], + exports: [], }) -export class GithubModule { } +export class GithubModule implements NestModule { + /** + * + * @param consumer + */ + configure(consumer: MiddlewareConsumer) { + // Apply middlewares to specific controllers + consumer.apply(GithubMiddleware).forRoutes( + RouterModule.resolvePath(GitHubIntegrationController) // Apply to GitHubIntegrationController + ) + } +} From aaf87de7f0df2d39c6b98be3edbcdd1851a4f873 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 25 Sep 2023 17:28:44 +0530 Subject: [PATCH 087/104] fix: #6734 sync tasks/issues from github --- .../@core/services/github/github.service.ts | 30 ++++++- .../components/view/view.component.html | 5 +- .../components/view/view.component.scss | 15 ++++ .../github/components/view/view.component.ts | 78 +++++++++++++++-- .../components/wizard/wizard.component.ts | 7 +- .../github/github-routing.module.ts | 18 ++-- .../integrations/github/github.component.ts | 2 +- apps/gauzy/src/assets/i18n/en.json | 6 +- packages/contracts/src/github.model.ts | 9 ++ .../github/github-integration.controller.ts | 87 +++++++++++++++++-- .../integration-github/src/octokit.service.ts | 35 ++++++++ 11 files changed, 256 insertions(+), 36 deletions(-) diff --git a/apps/gauzy/src/app/@core/services/github/github.service.ts b/apps/gauzy/src/app/@core/services/github/github.service.ts index 399f11df888..1f9044b24cb 100644 --- a/apps/gauzy/src/app/@core/services/github/github.service.ts +++ b/apps/gauzy/src/app/@core/services/github/github.service.ts @@ -4,6 +4,7 @@ import { IBasePerTenantAndOrganizationEntityModel, IGithubAppInstallInput, IGith import { Observable, firstValueFrom } from 'rxjs'; import { toParams } from '@gauzy/common-angular'; import { API_PREFIX } from '../../constants'; +import { IGithubIssue } from 'packages/contracts/dist'; @Injectable({ providedIn: 'root', @@ -25,9 +26,11 @@ export class GithubService { } /** - * Fetches repositories for a given integration and organization. - * @param integrationId The ID of the integration. - * @param query Query parameters for the request. + * Get GitHub repositories for a specific integration. + * + * @param {string} integrationId - The ID of the integration. + * @param {IBasePerTenantAndOrganizationEntityModel} query - Query parameters for the request. + * @returns {Observable} An observable that emits GitHub repositories. */ getRepositories( integrationId: IIntegrationTenant['id'], @@ -38,4 +41,25 @@ export class GithubService { return this._http.get(url, { params }); } + + /** + * Get GitHub repository issues for a specific integration, owner, and repository. + * + * @param {string} integrationId - The ID of the integration. + * @param {string} owner - The owner (username or organization) of the repository. + * @param {string} repo - The name of the repository. + * @param {IBasePerTenantAndOrganizationEntityModel} query - Query parameters for the request. + * @returns {Observable} An observable that emits GitHub issues. + */ + getRepositoryIssues( + integrationId: IIntegrationTenant['id'], + owner: string, + repo: string, + query: IBasePerTenantAndOrganizationEntityModel + ): Observable { + const url = `${API_PREFIX}/integration/github/${integrationId}/${owner}/${repo}/issues`; + const params = toParams(query); + + return this._http.get(url, { params }); + } } diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html index bd4f44bcb94..4429c13d659 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html @@ -9,13 +9,14 @@
{{ 'INTEGRATIONS.GITHUB_PAGE.NAME' | translate }}
-
+
; public issues$: Observable; @@ -31,6 +36,7 @@ export class GithubViewComponent extends TranslationBaseComponent implements Aft constructor( public readonly _translateService: TranslateService, private readonly _activatedRoute: ActivatedRoute, + private readonly _titlecasePipe: TitleCasePipe, private readonly _errorHandlingService: ErrorHandlingService, private readonly _store: Store, private readonly _githubService: GithubService, @@ -65,6 +71,7 @@ export class GithubViewComponent extends TranslationBaseComponent implements Aft return; } + this.loading = true; // Extract organization properties const { id: organizationId, tenantId } = this.organization; this.repositories$ = this._activatedRoute.params.pipe( @@ -93,6 +100,50 @@ export class GithubViewComponent extends TranslationBaseComponent implements Aft ); } + /** + * + * @param repository + */ + selectRepository(repository: IGithubRepository) { + this.issues$ = repository ? this.getRepositoryIssue(repository) : of([]); + } + + private getRepositoryIssue(repository: IGithubRepository) { + // Ensure there is a valid organization + if (!this.organization) { + return; + } + const owner = repository.owner['login']; + const repo = repository.name; + + this.loading = true; + // Extract organization properties + const { id: organizationId, tenantId } = this.organization; + return this._activatedRoute.params.pipe( + // Get the 'integrationId' route parameter + switchMap(({ integrationId }) => { + return this._githubService.getRepositoryIssues(integrationId, owner, repo, { + organizationId, + tenantId, + }); + }), + // Update component state with fetched issues + tap((issues: IGithubIssue[]) => { + this.issues = issues; + }), + catchError((error) => { + // Handle and log errors + this._errorHandlingService.handleError(error); + return of([]); + }), + tap(() => { + this.loading = false; + }), + // Handle component lifecycle to avoid memory leaks + untilDestroyed(this), + ); + } + /** * */ @@ -119,13 +170,24 @@ export class GithubViewComponent extends TranslationBaseComponent implements Aft select: true }, columns: { - name: { - title: this.getTranslation('SM_TABLE.NAME'), - type: 'string' + number: { + title: this.getTranslation('SM_TABLE.NUMBER'), + type: 'number', + valuePrepareFunction: (data: string) => { + console.log(data); + return data; + } }, - description: { - title: this.getTranslation('SM_TABLE.DESCRIPTION'), + title: { + title: this.getTranslation('SM_TABLE.TITLE'), type: 'string' + }, + state: { + title: this.getTranslation('SM_TABLE.STATUS'), + type: 'string', + valuePrepareFunction: (data: string) => { + return this._titlecasePipe.transform(data); + } } } }; diff --git a/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts index 8701445158c..270580d0226 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts @@ -125,6 +125,7 @@ export class GithubWizardComponent implements OnInit { */ private async checkPopupWindowStatus() { const timer = setInterval(() => { + console.log(this.window); if (this.window == null || this.window.closed) { clearInterval(timer); // Stop checking when the window is closed /** */ @@ -146,11 +147,7 @@ export class GithubWizardComponent implements OnInit { // Delay navigation by 5 seconds before redirecting setTimeout(() => { - this.router.navigate(['/pages/integrations/new'], { - queryParams: { - provider: IntegrationEnum.GITHUB - } - }); + this.router.navigate(['/pages/integrations/new']); }, ms); // 5000 milliseconds = 5 seconds } } diff --git a/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts b/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts index 1135019a233..385c85a02be 100644 --- a/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts +++ b/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts @@ -23,13 +23,19 @@ const routes: Routes = [ component: GithubViewComponent, }, { - path: 'wizard', - component: GithubWizardComponent, + path: 'setup', + component: GithubViewComponent, + children: [ + { + path: 'wizard', + component: GithubWizardComponent, + }, + { + path: 'installation', + component: GithubInstallationComponent + } + ] }, - { - path: 'installation', - component: GithubInstallationComponent - } ] }, ]; diff --git a/apps/gauzy/src/app/pages/integrations/github/github.component.ts b/apps/gauzy/src/app/pages/integrations/github/github.component.ts index 2d204063c8d..b23718d685d 100644 --- a/apps/gauzy/src/app/pages/integrations/github/github.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/github.component.ts @@ -23,7 +23,7 @@ export class GithubComponent implements OnInit { if (integration) { this._router.navigate(['/pages/integrations/github', integration.id]); } else { - this._router.navigate(['/pages/integrations/github/wizard']); + this._router.navigate(['/pages/integrations/github/setup/wizard']); } }) ) diff --git a/apps/gauzy/src/assets/i18n/en.json b/apps/gauzy/src/assets/i18n/en.json index 6221f0308f6..00fc4c307c2 100644 --- a/apps/gauzy/src/assets/i18n/en.json +++ b/apps/gauzy/src/assets/i18n/en.json @@ -292,7 +292,8 @@ "REJECTED_DATE": "Rejected Date", "TIME_TRACKING": "Time Tracking", "CREATED_AT": "Created At", - "SCREEN_CAPTURE": "Screen Capture" + "SCREEN_CAPTURE": "Screen Capture", + "NUMBER": "Number" }, "FORM": { "USERNAME": "Username", @@ -1212,7 +1213,8 @@ "API_SECRET": "Gauzy AI Secret" }, "GITHUB_PAGE": { - "NAME": "GitHub" + "NAME": "GitHub", + "SELECT_REPOSITORY": "Select Repository" }, "COMING_SOON": "Coming soon", "RE_INTEGRATE": "Re-integrate", diff --git a/packages/contracts/src/github.model.ts b/packages/contracts/src/github.model.ts index 64d3aba2771..966db139fac 100644 --- a/packages/contracts/src/github.model.ts +++ b/packages/contracts/src/github.model.ts @@ -21,6 +21,15 @@ export interface IGithubRepository { [x: string]: any; } +export interface IGithubIssue { + id: string; + node_id: string; + number: string; + title: string; + state: string; + [x: string]: any; +} + export interface IGithubRepositoryResponse { total_count: number; repository_selection: string; diff --git a/packages/core/src/integration/github/github-integration.controller.ts b/packages/core/src/integration/github/github-integration.controller.ts index 844155c710c..9cf408b28a6 100644 --- a/packages/core/src/integration/github/github-integration.controller.ts +++ b/packages/core/src/integration/github/github-integration.controller.ts @@ -1,7 +1,7 @@ -import { Controller, Get, HttpException, HttpStatus, Logger, Query, Req, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common'; +import { Controller, Get, HttpException, HttpStatus, Logger, Param, Query, Req, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common'; import { Request } from 'express'; import { OctokitResponse, OctokitService } from '@gauzy/integration-github'; -import { IGithubRepositoryResponse, PermissionsEnum } from '@gauzy/contracts'; +import { IGithubIssue, IGithubRepositoryResponse, PermissionsEnum } from '@gauzy/contracts'; import { PermissionGuard, TenantPermissionGuard } from 'shared/guards'; import { Permissions } from 'shared/decorators'; import { TenantOrganizationBaseDTO } from 'core/dto'; @@ -17,10 +17,12 @@ export class GitHubIntegrationController { ) { } /** - * Retrieve installation metadata for a GitHub App installation. + * Retrieve installation metadata for a GitHub App installation associated with a specific organization. * - * @param installation_id The installation ID for the GitHub App. - * @param organizationId The organization ID to query for metadata. + * This endpoint allows you to fetch metadata related to a GitHub App installation within a specific organization. + * + * @param {number} installation_id - The installation ID for the GitHub App. + * @param {TenantOrganizationBaseDTO} query - Query parameters containing organization information. * @returns {Promise>} A promise that resolves with the installation metadata. * @throws {HttpException} If the query parameters are invalid or an error occurs during retrieval. */ @@ -35,12 +37,21 @@ export class GitHubIntegrationController { if (!query || !query.organizationId) { throw new HttpException('Invalid query parameter', HttpStatus.BAD_REQUEST); } - // Get installation metadata + + // Check if the request contains integration settings + const settings = request['integration']?.settings; + if (!settings || !settings.installation_id) { + throw new HttpException('Invalid request parameter: Missing or unauthorized integration', HttpStatus.UNAUTHORIZED); + } + const installation_id = request['integration']['settings']['installation_id']; if (installation_id) { + // Get installation metadata const metadata = await this._octokitService.getGithubInstallationMetadata(installation_id); return metadata.data; } + + throw new HttpException('Invalid query parameter', HttpStatus.BAD_REQUEST); } catch (error) { // Handle errors and return an appropriate error respons this.logger.error('Error while retrieve github installation metadata', error.message); @@ -49,11 +60,13 @@ export class GitHubIntegrationController { } /** - * Get GitHub repositories for a specific installation. + * Get GitHub repositories associated with a specific GitHub App installation within a given organization. + * + * This endpoint allows you to retrieve a list of GitHub repositories associated with a GitHub App installation within a specific organization. * * @param {number} installation_id - The installation ID for the GitHub App. - * @param {TenantOrganizationBaseDTO} query - Query parameters, including organizationId. - * @returns {Promise>} A promise that resolves with the GitHub repositories. + * @param {TenantOrganizationBaseDTO} query - Query parameters containing organization information. + * @returns {Promise>} A promise that resolves with the GitHub repositories. * @throws {HttpException} If the query parameters are invalid or if there's an error retrieving the repositories. */ @Get('/repositories') @@ -67,16 +80,72 @@ export class GitHubIntegrationController { if (!query || !query.organizationId) { throw new HttpException('Invalid query parameter', HttpStatus.BAD_REQUEST); } + + // Check if the request contains integration settings + const settings = request['integration']?.settings; + if (!settings || !settings.installation_id) { + throw new HttpException('Invalid request parameter: Missing or unauthorized integration', HttpStatus.UNAUTHORIZED); + } + const installation_id = request['integration']['settings']['installation_id']; if (installation_id) { // Get installation repositories const repositories = await this._octokitService.getGithubRepositories(installation_id); return repositories.data; } + + throw new HttpException('Invalid request parameter', HttpStatus.UNAUTHORIZED); } catch (error) { // Handle errors and return an appropriate error respons this.logger.error('Error while retrieving GitHub installation repositories', error.message); throw new HttpException(`Error while retrieving GitHub installation repositories: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); } } + + /** + * Get GitHub repository issues for a specific GitHub App installation within a given organization, owner, and repository. + * + * This endpoint allows you to retrieve issues associated with a GitHub repository for a GitHub App installation within a specific organization. + * + * @param {number} installation_id - The installation ID for the GitHub App. + * @param {TenantOrganizationBaseDTO} query - Query parameters containing organization information. + * @param {string} owner - The owner (username or organization) of the repository. + * @param {string} repo - The name of the repository. + * @returns {Promise>} A promise that resolves with the GitHub repository issues. + * @throws {HttpException} If the query parameters are invalid or if there's an error retrieving the issues. + */ + @Get('/:owner/:repo/issues') + @UsePipes(new ValidationPipe({ transform: true })) + async getGithubRepositoryIssues( + @Req() request: Request, + @Query() query: TenantOrganizationBaseDTO, + @Param('owner') owner: string, + @Param('repo') repo: string, + ): Promise | void> { + try { + // Validate the input data (You can use class-validator for validation) + if (!query || !query.organizationId) { + throw new HttpException('Invalid query parameter', HttpStatus.BAD_REQUEST); + } + + // Check if the request contains integration settings + const settings = request['integration']?.settings; + if (!settings || !settings.installation_id) { + throw new HttpException('Invalid request parameter: Missing or unauthorized integration', HttpStatus.UNAUTHORIZED); + } + + const installation_id = request['integration']['settings']['installation_id']; + if (installation_id) { + // Get installation repositories + const issues = await this._octokitService.getGithubRepositoryIssues(installation_id, { owner, repo }); + return issues.data; + } + + throw new HttpException('Invalid request parameter', HttpStatus.UNAUTHORIZED); + } catch (error) { + // Handle errors and return an appropriate error respons + this.logger.error('Error while retrieving GitHub installation repository issues', error.message); + throw new HttpException(`Error while retrieving GitHub installation repository issues: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); + } + } } diff --git a/packages/plugins/integration-github/src/octokit.service.ts b/packages/plugins/integration-github/src/octokit.service.ts index f832ae39609..b0cd9fb384d 100644 --- a/packages/plugins/integration-github/src/octokit.service.ts +++ b/packages/plugins/integration-github/src/octokit.service.ts @@ -79,6 +79,41 @@ export class OctokitService { } } + /** + * Fetch GitHub repository issues for a given installation, owner, and repository. + * + * @param {number} installation_id - The installation ID for the GitHub app. + * @param {Object} options - Options object with 'owner' and 'repo' properties. + * @param {string} options.owner - The owner (username or organization) of the repository. + * @param {string} options.repo - The name of the repository. + * @returns {Promise>} A promise that resolves to the response from the GitHub API. + * @throws {Error} If the request to the GitHub API fails. + */ + public async getGithubRepositoryIssues(installation_id: number, { + owner, + repo + }: { + owner: string; + repo: string; + }): Promise> { + try { + // Get an Octokit instance for the installation + const octokit = await this.app.getInstallationOctokit(installation_id); + + // Send a request to the GitHub API to get repository issues + return await octokit.request('GET /repos/{owner}/{repo}/issues', { + owner: owner, + repo: repo, + headers: { + 'X-GitHub-Api-Version': GITHUB_API_VERSION + } + }); + } catch (error) { + this.logger.error('Failed to fetch GitHub installation repository issues', error.message); + throw new Error('Failed to fetch GitHub installation repository issues'); + } + } + async openIssue( title: string, body: string, From 91eba4f5edfbfda41a3c9015c38dcbf1b242a90e Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 25 Sep 2023 19:18:11 +0530 Subject: [PATCH 088/104] fix: #6734 API routes conflicting with middleware --- .../@core/services/github/github.service.ts | 3 +- .../components/wizard/wizard.component.ts | 28 ++++++++++++----- .../github/github-routing.module.ts | 21 +++++-------- .../integrations/github/github.component.ts | 12 +++++--- .../github/github-authorization.controller.ts | 4 +-- .../github/github-integration.controller.ts | 16 +++++----- .../integration/github/github.controller.ts | 4 +-- ...ntroller.ts => github.hooks.controller.ts} | 2 +- ...nts.service.ts => github.hooks.service.ts} | 0 .../integration/github/github.middleware.ts | 3 +- .../src/integration/github/github.module.ts | 30 +++++++++++++------ 11 files changed, 74 insertions(+), 49 deletions(-) rename packages/core/src/integration/github/{github.events.controller.ts => github.hooks.controller.ts} (91%) rename packages/core/src/integration/github/{github.events.service.ts => github.hooks.service.ts} (100%) diff --git a/apps/gauzy/src/app/@core/services/github/github.service.ts b/apps/gauzy/src/app/@core/services/github/github.service.ts index 1f9044b24cb..aa939cdb073 100644 --- a/apps/gauzy/src/app/@core/services/github/github.service.ts +++ b/apps/gauzy/src/app/@core/services/github/github.service.ts @@ -1,10 +1,9 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { IBasePerTenantAndOrganizationEntityModel, IGithubAppInstallInput, IGithubRepositoryResponse, IIntegrationTenant } from '@gauzy/contracts'; +import { IBasePerTenantAndOrganizationEntityModel, IGithubAppInstallInput, IGithubIssue, IGithubRepositoryResponse, IIntegrationTenant } from '@gauzy/contracts'; import { Observable, firstValueFrom } from 'rxjs'; import { toParams } from '@gauzy/common-angular'; import { API_PREFIX } from '../../constants'; -import { IGithubIssue } from 'packages/contracts/dist'; @Injectable({ providedIn: 'root', diff --git a/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts index 270580d0226..8005cc4cc95 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts @@ -1,9 +1,9 @@ -import { Component, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; +import { AfterViewInit, Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Data, Router } from '@angular/router'; import { debounceTime, filter, tap } from 'rxjs/operators'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { environment } from '@env/environment'; -import { IOrganization, IntegrationEnum } from '@gauzy/contracts'; +import { IOrganization } from '@gauzy/contracts'; import { distinctUntilChange, toParams } from '@gauzy/common-angular'; import { Store } from '../../../../../@core/services'; import { GITHUB_AUTHORIZATION_URL } from '../../github.config'; @@ -12,7 +12,7 @@ import { GITHUB_AUTHORIZATION_URL } from '../../github.config'; @Component({ templateUrl: './wizard.component.html' }) -export class GithubWizardComponent implements OnInit { +export class GithubWizardComponent implements AfterViewInit, OnInit { public organization: IOrganization; public isLoading: boolean = true; @@ -20,11 +20,25 @@ export class GithubWizardComponent implements OnInit { private window = null; constructor( - private readonly router: Router, + private readonly _activatedRoute: ActivatedRoute, + private readonly _router: Router, private readonly store: Store, ) { } - ngOnInit() { + ngOnInit(): void { + this._activatedRoute.data + .pipe( + tap(({ integration }: Data) => { + if (integration) { + this._router.navigate(['/pages/integrations/github', integration.id]); + } + }), + untilDestroyed(this) + ) + .subscribe(); + } + + ngAfterViewInit(): void { this.store.selectedOrganization$ .pipe( debounceTime(200), @@ -147,7 +161,7 @@ export class GithubWizardComponent implements OnInit { // Delay navigation by 5 seconds before redirecting setTimeout(() => { - this.router.navigate(['/pages/integrations/new']); + this._router.navigate(['/pages/integrations/github']); }, ms); // 5000 milliseconds = 5 seconds } } diff --git a/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts b/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts index 385c85a02be..d044a3a86a7 100644 --- a/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts +++ b/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts @@ -17,27 +17,22 @@ const routes: Routes = [ resolve: { integration: IntergrationResolver }, + runGuardsAndResolvers: 'always', children: [ { path: ':integrationId', component: GithubViewComponent, }, { - path: 'setup', - component: GithubViewComponent, - children: [ - { - path: 'wizard', - component: GithubWizardComponent, - }, - { - path: 'installation', - component: GithubInstallationComponent - } - ] - }, + path: 'setup/wizard', + component: GithubWizardComponent, + } ] }, + { + path: 'setup/installation', + component: GithubInstallationComponent + }, ]; @NgModule({ diff --git a/apps/gauzy/src/app/pages/integrations/github/github.component.ts b/apps/gauzy/src/app/pages/integrations/github/github.component.ts index b23718d685d..757d4d79219 100644 --- a/apps/gauzy/src/app/pages/integrations/github/github.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/github.component.ts @@ -1,9 +1,13 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Data, Router } from '@angular/router'; -import { debounceTime, tap } from 'rxjs/operators'; +import { tap } from 'rxjs/operators'; +import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; +@UntilDestroy({ checkProperties: true }) @Component({ - template: `` + template: ` + + ` }) export class GithubComponent implements OnInit { @@ -18,14 +22,14 @@ export class GithubComponent implements OnInit { ngOnInit() { this._activatedRoute.data .pipe( - debounceTime(100), tap(({ integration }: Data) => { if (integration) { this._router.navigate(['/pages/integrations/github', integration.id]); } else { this._router.navigate(['/pages/integrations/github/setup/wizard']); } - }) + }), + untilDestroyed(this) ) .subscribe(); } diff --git a/packages/core/src/integration/github/github-authorization.controller.ts b/packages/core/src/integration/github/github-authorization.controller.ts index fb38d92b429..3afc8c8e3df 100644 --- a/packages/core/src/integration/github/github-authorization.controller.ts +++ b/packages/core/src/integration/github/github-authorization.controller.ts @@ -4,7 +4,6 @@ import { ConfigService } from '@gauzy/config'; import { IGithubAppInstallInput } from '@gauzy/contracts'; import { IGithubConfig, Public } from '@gauzy/common'; -@Public() @Controller() export class GitHubAuthorizationController { constructor( @@ -16,7 +15,8 @@ export class GitHubAuthorizationController { * @param query * @param response */ - @Get('callback') + @Public() + @Get('/callback') async githubIntegrationPostInstallCallback( @Query() query: IGithubAppInstallInput, @Res() response: Response diff --git a/packages/core/src/integration/github/github-integration.controller.ts b/packages/core/src/integration/github/github-integration.controller.ts index 9cf408b28a6..db4bf57db88 100644 --- a/packages/core/src/integration/github/github-integration.controller.ts +++ b/packages/core/src/integration/github/github-integration.controller.ts @@ -17,14 +17,14 @@ export class GitHubIntegrationController { ) { } /** - * Retrieve installation metadata for a GitHub App installation associated with a specific organization. + * Get GitHub installation metadata for a specific integration. * - * This endpoint allows you to fetch metadata related to a GitHub App installation within a specific organization. + * This endpoint allows you to retrieve metadata associated with a GitHub installation for a given integration. * - * @param {number} installation_id - The installation ID for the GitHub App. - * @param {TenantOrganizationBaseDTO} query - Query parameters containing organization information. - * @returns {Promise>} A promise that resolves with the installation metadata. - * @throws {HttpException} If the query parameters are invalid or an error occurs during retrieval. + * @param {Request} request - The HTTP request object. + * @param {TenantOrganizationBaseDTO} query - Query parameters, including organizationId. + * @returns {Promise | void>} A promise that resolves with the GitHub installation metadata. + * @throws {HttpException} If the query parameters are invalid or if there's an error retrieving the metadata. */ @Get('/metadata') @UsePipes(new ValidationPipe({ transform: true })) @@ -64,7 +64,7 @@ export class GitHubIntegrationController { * * This endpoint allows you to retrieve a list of GitHub repositories associated with a GitHub App installation within a specific organization. * - * @param {number} installation_id - The installation ID for the GitHub App. + * @param {Request} request - The HTTP request object. * @param {TenantOrganizationBaseDTO} query - Query parameters containing organization information. * @returns {Promise>} A promise that resolves with the GitHub repositories. * @throws {HttpException} If the query parameters are invalid or if there's an error retrieving the repositories. @@ -107,7 +107,7 @@ export class GitHubIntegrationController { * * This endpoint allows you to retrieve issues associated with a GitHub repository for a GitHub App installation within a specific organization. * - * @param {number} installation_id - The installation ID for the GitHub App. + * @param {Request} request - The HTTP request object. * @param {TenantOrganizationBaseDTO} query - Query parameters containing organization information. * @param {string} owner - The owner (username or organization) of the repository. * @param {string} repo - The name of the repository. diff --git a/packages/core/src/integration/github/github.controller.ts b/packages/core/src/integration/github/github.controller.ts index 0b79bbdf747..ebf901880a7 100644 --- a/packages/core/src/integration/github/github.controller.ts +++ b/packages/core/src/integration/github/github.controller.ts @@ -18,7 +18,7 @@ export class GitHubController { * @param body * @returns */ - @Post('install') + @Post('/install') @HttpCode(HttpStatus.CREATED) @UsePipes(new ValidationPipe()) async addGithubAppInstallation( @@ -43,7 +43,7 @@ export class GitHubController { * @param body * @returns */ - @Post('oauth') + @Post('/oauth') @HttpCode(HttpStatus.CREATED) // ToDo - Create Class Validation DTO to validate request async oAuthEndpointAuthorization(@Body() input: IGithubAppInstallInput) { diff --git a/packages/core/src/integration/github/github.events.controller.ts b/packages/core/src/integration/github/github.hooks.controller.ts similarity index 91% rename from packages/core/src/integration/github/github.events.controller.ts rename to packages/core/src/integration/github/github.hooks.controller.ts index 19af413c1b2..ffb25ddfd32 100644 --- a/packages/core/src/integration/github/github.events.controller.ts +++ b/packages/core/src/integration/github/github.hooks.controller.ts @@ -2,7 +2,7 @@ import { Controller } from '@nestjs/common'; import { Context } from 'probot'; import { Public } from '@gauzy/common'; import { Hook } from '@gauzy/integration-github'; -import { GithubHooksService } from './github.events.service'; +import { GithubHooksService } from './github.hooks.service'; @Public() @Controller('webhook') diff --git a/packages/core/src/integration/github/github.events.service.ts b/packages/core/src/integration/github/github.hooks.service.ts similarity index 100% rename from packages/core/src/integration/github/github.events.service.ts rename to packages/core/src/integration/github/github.hooks.service.ts diff --git a/packages/core/src/integration/github/github.middleware.ts b/packages/core/src/integration/github/github.middleware.ts index b34d18c4b1f..0e1412964a1 100644 --- a/packages/core/src/integration/github/github.middleware.ts +++ b/packages/core/src/integration/github/github.middleware.ts @@ -44,7 +44,8 @@ export class GithubMiddleware implements NestMiddleware { }); } } catch (error) { - console.log(`Error while getting integration (${IntegrationEnum.GITHUB}) tenant: %s`, error?.message); + console.log(`Error while getting integration (${IntegrationEnum.GITHUB}) tenant inside middleware: %s`, error?.message); + console.log(request.path, request.url); } // Continue to the next middleware or route handler diff --git a/packages/core/src/integration/github/github.module.ts b/packages/core/src/integration/github/github.module.ts index 861f61778a8..d7e235b9d18 100644 --- a/packages/core/src/integration/github/github.module.ts +++ b/packages/core/src/integration/github/github.module.ts @@ -1,18 +1,17 @@ -import { MiddlewareConsumer, Module, NestModule, forwardRef } from '@nestjs/common'; +import { MiddlewareConsumer, Module, NestModule, RequestMethod, forwardRef } from '@nestjs/common'; import { HttpModule } from '@nestjs/axios'; import { CqrsModule } from '@nestjs/cqrs'; -import { RouterModule } from 'nest-router'; import { TenantModule } from 'tenant/tenant.module'; import { UserModule } from 'user/user.module'; import { IntegrationModule } from 'integration/integration.module'; import { IntegrationTenantModule } from 'integration-tenant/integration-tenant.module'; import { GitHubAuthorizationController } from './github-authorization.controller'; -import { GitHubController } from './github.controller'; import { GitHubIntegrationController } from './github-integration.controller'; -import { GitHubHooksController } from './github.events.controller'; +import { GitHubController } from './github.controller'; import { GithubService } from './github.service'; -import { GithubHooksService } from './github.events.service'; import { GithubMiddleware } from './github.middleware'; +import { GitHubHooksController } from './github.hooks.controller'; +import { GithubHooksService } from './github.hooks.service'; @Module({ imports: [ @@ -26,8 +25,8 @@ import { GithubMiddleware } from './github.middleware'; controllers: [ GitHubAuthorizationController, GitHubController, + GitHubHooksController, GitHubIntegrationController, - GitHubHooksController ], providers: [ GithubService, @@ -44,8 +43,21 @@ export class GithubModule implements NestModule { */ configure(consumer: MiddlewareConsumer) { // Apply middlewares to specific controllers - consumer.apply(GithubMiddleware).forRoutes( - RouterModule.resolvePath(GitHubIntegrationController) // Apply to GitHubIntegrationController - ) + consumer + .apply(GithubMiddleware) + .forRoutes( + { + path: '/integration/github/:integrationId/metadata', + method: RequestMethod.GET + }, + { + path: '/integration/github/:integrationId/repositories', + method: RequestMethod.GET + }, + { + path: '/integration/github/:integrationId/:owner/:repo/issues', + method: RequestMethod.GET + } + ); // Apply to specific routes and methods } } From e65df10e82ae707f61fb919d809452f0bee402e1 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 25 Sep 2023 19:28:50 +0530 Subject: [PATCH 089/104] fix: cspell spelling ;-) --- .../app/pages/integrations/github/github-routing.module.ts | 4 ++-- apps/gauzy/src/app/pages/integrations/integration.resolver.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts b/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts index d044a3a86a7..bab42e03c32 100644 --- a/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts +++ b/apps/gauzy/src/app/pages/integrations/github/github-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { IntegrationEnum } from '@gauzy/contracts'; -import { IntergrationResolver } from './../integration.resolver'; +import { IntegrationResolver } from './../integration.resolver'; import { GithubWizardComponent } from './components/wizard/wizard.component'; import { GithubInstallationComponent } from './components/installation/installation.component'; import { GithubComponent } from './github.component'; @@ -15,7 +15,7 @@ const routes: Routes = [ integration: IntegrationEnum.GITHUB }, resolve: { - integration: IntergrationResolver + integration: IntegrationResolver }, runGuardsAndResolvers: 'always', children: [ diff --git a/apps/gauzy/src/app/pages/integrations/integration.resolver.ts b/apps/gauzy/src/app/pages/integrations/integration.resolver.ts index d68b0c3e4b6..f6c5e32bfb0 100644 --- a/apps/gauzy/src/app/pages/integrations/integration.resolver.ts +++ b/apps/gauzy/src/app/pages/integrations/integration.resolver.ts @@ -7,7 +7,7 @@ import { IntegrationsService, Store } from './../../@core/services'; @Injectable({ providedIn: 'root' }) -export class IntergrationResolver implements Resolve> { +export class IntegrationResolver implements Resolve> { constructor( private readonly _integrationsService: IntegrationsService, private readonly _store: Store, From 68069531b2d94e74e39c4acace462e0e1922088b Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 25 Sep 2023 19:31:33 +0530 Subject: [PATCH 090/104] fix: cspell spelling ;-) --- .../src/integration/github/github-integration.controller.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/integration/github/github-integration.controller.ts b/packages/core/src/integration/github/github-integration.controller.ts index db4bf57db88..0dcad371be0 100644 --- a/packages/core/src/integration/github/github-integration.controller.ts +++ b/packages/core/src/integration/github/github-integration.controller.ts @@ -53,7 +53,7 @@ export class GitHubIntegrationController { throw new HttpException('Invalid query parameter', HttpStatus.BAD_REQUEST); } catch (error) { - // Handle errors and return an appropriate error respons + // Handle errors and return an appropriate error response this.logger.error('Error while retrieve github installation metadata', error.message); throw new HttpException(`Error while retrieve github installation metadata: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); } @@ -96,7 +96,7 @@ export class GitHubIntegrationController { throw new HttpException('Invalid request parameter', HttpStatus.UNAUTHORIZED); } catch (error) { - // Handle errors and return an appropriate error respons + // Handle errors and return an appropriate error response this.logger.error('Error while retrieving GitHub installation repositories', error.message); throw new HttpException(`Error while retrieving GitHub installation repositories: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); } @@ -143,7 +143,7 @@ export class GitHubIntegrationController { throw new HttpException('Invalid request parameter', HttpStatus.UNAUTHORIZED); } catch (error) { - // Handle errors and return an appropriate error respons + // Handle errors and return an appropriate error response this.logger.error('Error while retrieving GitHub installation repository issues', error.message); throw new HttpException(`Error while retrieving GitHub installation repository issues: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); } From 1f77a3da6cdf740adfff1d874bcd350f1aa9f824 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 26 Sep 2023 00:47:28 +0530 Subject: [PATCH 091/104] chore(envs): added sample of envs --- .env.compose | 28 +++++++++++++++++++++++++++- .env.docker | 30 +++++++++++++++++++++++++++++- .env.local | 30 +++++++++++++++++++++++++++++- .env.sample | 28 +++++++++++++++++++++++++++- 4 files changed, 112 insertions(+), 4 deletions(-) diff --git a/.env.compose b/.env.compose index 5e91f67019c..08127bb865e 100644 --- a/.env.compose +++ b/.env.compose @@ -75,10 +75,25 @@ FACEBOOK_CLIENT_SECRET=XXXXXXX FACEBOOK_CALLBACK_URL=http://localhost:3000/api/auth/facebook/callback FACEBOOK_GRAPH_VERSION=v3.0 +# Github OAuth Integration GITHUB_CLIENT_ID=XXXXXXX GITHUB_CLIENT_SECRET=XXXXXXX GITHUB_CALLBACK_URL=http://localhost:3000/api/auth/github/callback +# Github App Install Integration +GITHUB_APP_NAME= +GITHUB_APP_ID=XXXXXXX +GITHUB_APP_PRIVATE_KEY= + +# Github Webhook Configuration +GITHUB_WEBHOOK_URL=http://localhost:3000/api/auth/github/webhook +GITHUB_WEBHOOK_SECRET=XXXXXXX + +# Github Redirect URL +GITHUB_REDIRECT_URL=http://localhost:3000/api/integration/github/callback +GITHUB_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/github/setup/installation" +GITHUB_API_VERSION="2022-11-28" + LINKEDIN_CLIENT_ID=XXXXXXX LINKEDIN_CLIENT_SECRET=XXXXXXX LINKEDIN_CALLBACK_URL=http://localhost:3000/api/auth/linked/callback @@ -103,7 +118,18 @@ KEYCLOAK_AUTH_SERVER_URL=XXXXXXX KEYCLOAK_COOKIE_KEY=XXXXXXX INTEGRATED_HUBSTAFF_USER_PASS=hubstaffPassword -UPWORK_REDIRECT_URL=http://localhost:3000/api/integrations/upwork + +# Upwork Integration Config +UPWORK_API_KEY=XXXXXXX +UPWORK_API_SECRET=XXXXXXX +UPWORK_REDIRECT_URL="http://localhost:3000/api/integrations/upwork/callback" +UPWORK_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/upwork" + +# Hubstaff Integration Configuration +HUBSTAFF_CLIENT_ID=XXXXXXX +HUBSTAFF_CLIENT_SECRET=XXXXXXX +HUBSTAFF_REDIRECT_URL="http://localhost:3000/api/integration/hubstaff/callback" +HUBSTAFF_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/hubstaff" # File System: LOCAL | S3 | WASABI | CLOUDINARY FILE_PROVIDER=LOCAL diff --git a/.env.docker b/.env.docker index 9db6e7eb007..446d6a2b237 100644 --- a/.env.docker +++ b/.env.docker @@ -76,11 +76,39 @@ FACEBOOK_CLIENT_SECRET= FACEBOOK_GRAPH_VERSION=v3.0 FACEBOOK_CALLBACK_URL=http://localhost:3000/api/auth/facebook/callback +# Github OAuth Integration +GITHUB_CLIENT_ID=XXXXXXX +GITHUB_CLIENT_SECRET=XXXXXXX +GITHUB_CALLBACK_URL=http://localhost:3000/api/auth/github/callback + +# Github App Install Integration +GITHUB_APP_NAME= +GITHUB_APP_ID=XXXXXXX +GITHUB_APP_PRIVATE_KEY= + +# Github Webhook Configuration +GITHUB_WEBHOOK_URL=http://localhost:3000/api/auth/github/webhook +GITHUB_WEBHOOK_SECRET=XXXXXXX + +# Github Redirect URL +GITHUB_REDIRECT_URL=http://localhost:3000/api/integration/github/callback +GITHUB_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/github/setup/installation" +GITHUB_API_VERSION="2022-11-28" + # Third Party Integration Config INTEGRATED_USER_DEFAULT_PASS= # Upwork Integration Config -UPWORK_REDIRECT_URL=http://localhost:3000/api/integrations/upwork/callback +UPWORK_API_KEY=XXXXXXX +UPWORK_API_SECRET=XXXXXXX +UPWORK_REDIRECT_URL="http://localhost:3000/api/integrations/upwork/callback" +UPWORK_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/upwork" + +# Hubstaff Integration Configuration +HUBSTAFF_CLIENT_ID=XXXXXXX +HUBSTAFF_CLIENT_SECRET=XXXXXXX +HUBSTAFF_REDIRECT_URL="http://localhost:3000/api/integration/hubstaff/callback" +HUBSTAFF_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/hubstaff" # File System: LOCAL | S3 | WASABI | CLOUDINARY FILE_PROVIDER=LOCAL diff --git a/.env.local b/.env.local index fe0989f8da8..140565a54f6 100644 --- a/.env.local +++ b/.env.local @@ -76,11 +76,39 @@ FACEBOOK_CLIENT_SECRET= FACEBOOK_GRAPH_VERSION=v3.0 FACEBOOK_CALLBACK_URL=http://localhost:3000/api/auth/facebook/callback +# Github OAuth Integration +GITHUB_CLIENT_ID=XXXXXXX +GITHUB_CLIENT_SECRET=XXXXXXX +GITHUB_CALLBACK_URL=http://localhost:3000/api/auth/github/callback + +# Github App Install Integration +GITHUB_APP_NAME= +GITHUB_APP_ID=XXXXXXX +GITHUB_APP_PRIVATE_KEY= + +# Github Webhook Configuration +GITHUB_WEBHOOK_URL=http://localhost:3000/api/auth/github/webhook +GITHUB_WEBHOOK_SECRET=XXXXXXX + +# Github Redirect URL +GITHUB_REDIRECT_URL=http://localhost:3000/api/integration/github/callback +GITHUB_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/github/setup/installation" +GITHUB_API_VERSION="2022-11-28" + # Third Party Integration Config INTEGRATED_USER_DEFAULT_PASS= # Upwork Integration Config -UPWORK_REDIRECT_URL=http://localhost:3000/api/integrations/upwork/callback +UPWORK_API_KEY=XXXXXXX +UPWORK_API_SECRET=XXXXXXX +UPWORK_REDIRECT_URL="http://localhost:3000/api/integrations/upwork/callback" +UPWORK_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/upwork" + +# Hubstaff Integration Configuration +HUBSTAFF_CLIENT_ID=XXXXXXX +HUBSTAFF_CLIENT_SECRET=XXXXXXX +HUBSTAFF_REDIRECT_URL="http://localhost:3000/api/integration/hubstaff/callback" +HUBSTAFF_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/hubstaff" # File System: LOCAL | S3 | WASABI | CLOUDINARY FILE_PROVIDER=LOCAL diff --git a/.env.sample b/.env.sample index 6c2bae5c093..a1b783bff82 100644 --- a/.env.sample +++ b/.env.sample @@ -62,10 +62,25 @@ FACEBOOK_CLIENT_SECRET=XXXXXXX FACEBOOK_CALLBACK_URL=http://localhost:3000/api/auth/facebook/callback FACEBOOK_GRAPH_VERSION=v3.0 +# Github OAuth Integration GITHUB_CLIENT_ID=XXXXXXX GITHUB_CLIENT_SECRET=XXXXXXX GITHUB_CALLBACK_URL=http://localhost:3000/api/auth/github/callback +# Github App Install Integration +GITHUB_APP_NAME= +GITHUB_APP_ID=XXXXXXX +GITHUB_APP_PRIVATE_KEY= + +# Github Webhook Configuration +GITHUB_WEBHOOK_URL=http://localhost:3000/api/auth/github/webhook +GITHUB_WEBHOOK_SECRET=XXXXXXX + +# Github Redirect URL +GITHUB_REDIRECT_URL=http://localhost:3000/api/integration/github/callback +GITHUB_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/github/setup/installation" +GITHUB_API_VERSION="2022-11-28" + LINKEDIN_CLIENT_ID=XXXXXXX LINKEDIN_CLIENT_SECRET=XXXXXXX LINKEDIN_CALLBACK_URL=http://localhost:3000/api/auth/linked/callback @@ -90,7 +105,18 @@ KEYCLOAK_AUTH_SERVER_URL=XXXXXXX KEYCLOAK_COOKIE_KEY=XXXXXXX INTEGRATED_HUBSTAFF_USER_PASS=hubstaffPassword -UPWORK_REDIRECT_URL=http://localhost:3000/api/integrations/upwork + +# Upwork Integration Config +UPWORK_API_KEY=XXXXXXX +UPWORK_API_SECRET=XXXXXXX +UPWORK_REDIRECT_URL="http://localhost:3000/api/integrations/upwork/callback" +UPWORK_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/upwork" + +# Hubstaff Integration Configuration +HUBSTAFF_CLIENT_ID=XXXXXXX +HUBSTAFF_CLIENT_SECRET=XXXXXXX +HUBSTAFF_REDIRECT_URL="http://localhost:3000/api/integration/hubstaff/callback" +HUBSTAFF_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/hubstaff" # File System: LOCAL | S3 | WASABI | CLOUDINARY FILE_PROVIDER=LOCAL From 304c7eb9fa8855522b2749b946fdc1cd34667473 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 26 Sep 2023 01:23:38 +0530 Subject: [PATCH 092/104] fix: rename env variables configurations as per standard format --- .env.compose | 16 +++---- .env.docker | 16 +++---- .env.local | 16 +++---- .env.sample | 16 +++---- .scripts/configure.ts | 16 +++---- .scripts/env.ts | 16 +++---- .../components/wizard/wizard.component.ts | 6 +-- apps/gauzy/src/environments/model.ts | 8 ++-- packages/auth/src/github/github.strategy.ts | 6 +-- .../common/src/interfaces/IGithubConfig.ts | 46 +++++++++++++------ .../common/src/interfaces/IHubstaffConfig.ts | 14 ++++-- .../common/src/interfaces/IUpworkConfig.ts | 18 ++++++-- .../src/environments/environment.prod.ts | 34 +++++++------- .../config/src/environments/environment.ts | 34 +++++++------- packages/core/src/app.module.ts | 12 ++--- .../github/github-authorization.controller.ts | 2 +- .../src/integration/github/github.service.ts | 5 +- .../hubstaff-authorization.controller.ts | 2 +- .../upwork/upwork-authorization.controller.ts | 2 +- packages/core/src/upwork/upwork.service.ts | 2 +- .../src/lib/settings/settings.component.ts | 12 ++--- .../integration-github/src/octokit.service.ts | 8 ++-- 22 files changed, 170 insertions(+), 137 deletions(-) diff --git a/.env.compose b/.env.compose index 08127bb865e..82e3b28711e 100644 --- a/.env.compose +++ b/.env.compose @@ -81,18 +81,18 @@ GITHUB_CLIENT_SECRET=XXXXXXX GITHUB_CALLBACK_URL=http://localhost:3000/api/auth/github/callback # Github App Install Integration -GITHUB_APP_NAME= -GITHUB_APP_ID=XXXXXXX -GITHUB_APP_PRIVATE_KEY= +GAUZY_GITHUB_APP_NAME= +GAUZY_GITHUB_APP_ID=XXXXXXX +GAUZY_GITHUB_APP_PRIVATE_KEY= # Github Webhook Configuration -GITHUB_WEBHOOK_URL=http://localhost:3000/api/auth/github/webhook -GITHUB_WEBHOOK_SECRET=XXXXXXX +GAUZY_GITHUB_WEBHOOK_URL=http://localhost:3000/api/auth/github/webhook +GAUZY_GITHUB_WEBHOOK_SECRET=XXXXXXX # Github Redirect URL -GITHUB_REDIRECT_URL=http://localhost:3000/api/integration/github/callback -GITHUB_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/github/setup/installation" -GITHUB_API_VERSION="2022-11-28" +GAUZY_GITHUB_REDIRECT_URL=http://localhost:3000/api/integration/github/callback +GAUZY_GITHUB_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/github/setup/installation" +GAUZY_GITHUB_API_VERSION="2022-11-28" LINKEDIN_CLIENT_ID=XXXXXXX LINKEDIN_CLIENT_SECRET=XXXXXXX diff --git a/.env.docker b/.env.docker index 446d6a2b237..b5c51d679d9 100644 --- a/.env.docker +++ b/.env.docker @@ -82,18 +82,18 @@ GITHUB_CLIENT_SECRET=XXXXXXX GITHUB_CALLBACK_URL=http://localhost:3000/api/auth/github/callback # Github App Install Integration -GITHUB_APP_NAME= -GITHUB_APP_ID=XXXXXXX -GITHUB_APP_PRIVATE_KEY= +GAUZY_GITHUB_APP_NAME= +GAUZY_GITHUB_APP_ID=XXXXXXX +GAUZY_GITHUB_APP_PRIVATE_KEY= # Github Webhook Configuration -GITHUB_WEBHOOK_URL=http://localhost:3000/api/auth/github/webhook -GITHUB_WEBHOOK_SECRET=XXXXXXX +GAUZY_GITHUB_WEBHOOK_URL=http://localhost:3000/api/auth/github/webhook +GAUZY_GITHUB_WEBHOOK_SECRET=XXXXXXX # Github Redirect URL -GITHUB_REDIRECT_URL=http://localhost:3000/api/integration/github/callback -GITHUB_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/github/setup/installation" -GITHUB_API_VERSION="2022-11-28" +GAUZY_GITHUB_REDIRECT_URL=http://localhost:3000/api/integration/github/callback +GAUZY_GITHUB_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/github/setup/installation" +GAUZY_GITHUB_API_VERSION="2022-11-28" # Third Party Integration Config INTEGRATED_USER_DEFAULT_PASS= diff --git a/.env.local b/.env.local index 140565a54f6..dffb5862a64 100644 --- a/.env.local +++ b/.env.local @@ -82,18 +82,18 @@ GITHUB_CLIENT_SECRET=XXXXXXX GITHUB_CALLBACK_URL=http://localhost:3000/api/auth/github/callback # Github App Install Integration -GITHUB_APP_NAME= -GITHUB_APP_ID=XXXXXXX -GITHUB_APP_PRIVATE_KEY= +GAUZY_GITHUB_APP_NAME= +GAUZY_GITHUB_APP_ID=XXXXXXX +GAUZY_GITHUB_APP_PRIVATE_KEY= # Github Webhook Configuration -GITHUB_WEBHOOK_URL=http://localhost:3000/api/auth/github/webhook -GITHUB_WEBHOOK_SECRET=XXXXXXX +GAUZY_GITHUB_WEBHOOK_URL=http://localhost:3000/api/auth/github/webhook +GAUZY_GITHUB_WEBHOOK_SECRET=XXXXXXX # Github Redirect URL -GITHUB_REDIRECT_URL=http://localhost:3000/api/integration/github/callback -GITHUB_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/github/setup/installation" -GITHUB_API_VERSION="2022-11-28" +GAUZY_GITHUB_REDIRECT_URL=http://localhost:3000/api/integration/github/callback +GAUZY_GITHUB_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/github/setup/installation" +GAUZY_GITHUB_API_VERSION="2022-11-28" # Third Party Integration Config INTEGRATED_USER_DEFAULT_PASS= diff --git a/.env.sample b/.env.sample index a1b783bff82..a1437450581 100644 --- a/.env.sample +++ b/.env.sample @@ -68,18 +68,18 @@ GITHUB_CLIENT_SECRET=XXXXXXX GITHUB_CALLBACK_URL=http://localhost:3000/api/auth/github/callback # Github App Install Integration -GITHUB_APP_NAME= -GITHUB_APP_ID=XXXXXXX -GITHUB_APP_PRIVATE_KEY= +GAUZY_GITHUB_APP_NAME= +GAUZY_GITHUB_APP_ID=XXXXXXX +GAUZY_GITHUB_APP_PRIVATE_KEY= # Github Webhook Configuration -GITHUB_WEBHOOK_URL=http://localhost:3000/api/auth/github/webhook -GITHUB_WEBHOOK_SECRET=XXXXXXX +GAUZY_GITHUB_WEBHOOK_URL=http://localhost:3000/api/auth/github/webhook +GAUZY_GITHUB_WEBHOOK_SECRET=XXXXXXX # Github Redirect URL -GITHUB_REDIRECT_URL=http://localhost:3000/api/integration/github/callback -GITHUB_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/github/setup/installation" -GITHUB_API_VERSION="2022-11-28" +GAUZY_GITHUB_REDIRECT_URL=http://localhost:3000/api/integration/github/callback +GAUZY_GITHUB_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/github/setup/installation" +GAUZY_GITHUB_API_VERSION="2022-11-28" LINKEDIN_CLIENT_ID=XXXXXXX LINKEDIN_CLIENT_SECRET=XXXXXXX diff --git a/.scripts/configure.ts b/.scripts/configure.ts index 15ccbb7abc5..c95889e8c11 100644 --- a/.scripts/configure.ts +++ b/.scripts/configure.ts @@ -143,10 +143,10 @@ if (!env.IS_DOCKER) { FILE_PROVIDER: '${env.FILE_PROVIDER}', - GITHUB_APP_NAME: '${env.GITHUB_APP_NAME}', - GITHUB_APP_ID: '${env.GITHUB_APP_ID}', - GITHUB_CLIENT_ID: '${env.GITHUB_CLIENT_ID}', - GITHUB_REDIRECT_URL: '${env.GITHUB_REDIRECT_URL}', + GAUZY_GITHUB_APP_NAME: '${env.GAUZY_GITHUB_APP_NAME}', + GAUZY_GITHUB_APP_ID: '${env.GAUZY_GITHUB_APP_ID}', + GAUZY_GITHUB_CLIENT_ID: '${env.GAUZY_GITHUB_CLIENT_ID}', + GAUZY_GITHUB_REDIRECT_URL: '${env.GAUZY_GITHUB_REDIRECT_URL}', }; `; } else { @@ -246,10 +246,10 @@ if (!env.IS_DOCKER) { FILE_PROVIDER: '${env.FILE_PROVIDER}', - GITHUB_APP_NAME: '${env.GITHUB_APP_NAME}', - GITHUB_APP_ID: '${env.GITHUB_APP_ID}', - GITHUB_CLIENT_ID: '${env.GITHUB_CLIENT_ID}', - GITHUB_REDIRECT_URL: '${env.GITHUB_REDIRECT_URL}', + GAUZY_GITHUB_APP_NAME: '${env.GAUZY_GITHUB_APP_NAME}', + GAUZY_GITHUB_APP_ID: '${env.GAUZY_GITHUB_APP_ID}', + GAUZY_GITHUB_CLIENT_ID: '${env.GAUZY_GITHUB_CLIENT_ID}', + GAUZY_GITHUB_REDIRECT_URL: '${env.GAUZY_GITHUB_REDIRECT_URL}', }; `; } diff --git a/.scripts/env.ts b/.scripts/env.ts index 46485b6db8f..fdc1240e1ce 100644 --- a/.scripts/env.ts +++ b/.scripts/env.ts @@ -66,10 +66,10 @@ export type Env = Readonly<{ FILE_PROVIDER: string; - GITHUB_APP_NAME: string; - GITHUB_APP_ID: string; - GITHUB_CLIENT_ID: string; - GITHUB_REDIRECT_URL: string; + GAUZY_GITHUB_APP_NAME: string; + GAUZY_GITHUB_APP_ID: string; + GAUZY_GITHUB_CLIENT_ID: string; + GAUZY_GITHUB_REDIRECT_URL: string; }>; export const env: Env = cleanEnv( @@ -124,10 +124,10 @@ export const env: Env = cleanEnv( FILE_PROVIDER: str({ default: 'LOCAL' }), - GITHUB_APP_NAME: str({ default: '' }), - GITHUB_APP_ID: str({ default: '' }), - GITHUB_CLIENT_ID: str({ default: '' }), - GITHUB_REDIRECT_URL: str({ default: '' }), + GAUZY_GITHUB_APP_NAME: str({ default: '' }), + GAUZY_GITHUB_APP_ID: str({ default: '' }), + GAUZY_GITHUB_CLIENT_ID: str({ default: '' }), + GAUZY_GITHUB_REDIRECT_URL: str({ default: '' }), }, { strict: true, dotEnvPath: __dirname + '/../.env' } ); diff --git a/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts index 8005cc4cc95..710da78b6e8 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/wizard/wizard.component.ts @@ -56,8 +56,8 @@ export class GithubWizardComponent implements AfterViewInit, OnInit { * Redirect the user to GitHub for authorization. */ private async oAuthAppAuthorization() { - const redirect_uri = environment.GITHUB_REDIRECT_URL; - const client_id = environment.GITHUB_CLIENT_ID; + const redirect_uri = environment.GAUZY_GITHUB_REDIRECT_URL; + const client_id = environment.GAUZY_GITHUB_CLIENT_ID; // Define your query parameters const queryParams = toParams({ @@ -114,7 +114,7 @@ export class GithubWizardComponent implements AfterViewInit, OnInit { window.frames[windowName].focus(); } else { /** Navigate to the target external URL */ - const url = `https://github.com/apps/${environment.GITHUB_APP_NAME}/installations/new?state=${state.toString()}`; + const url = `https://github.com/apps/${environment.GAUZY_GITHUB_APP_NAME}/installations/new?state=${state.toString()}`; /** Navigate to the external URL with query parameters */ this.window = window.open( diff --git a/apps/gauzy/src/environments/model.ts b/apps/gauzy/src/environments/model.ts index 742b70ab14f..50f426c11be 100644 --- a/apps/gauzy/src/environments/model.ts +++ b/apps/gauzy/src/environments/model.ts @@ -71,8 +71,8 @@ export interface Environment { FILE_PROVIDER: string; /** Github Integration */ - GITHUB_APP_NAME: string; - GITHUB_APP_ID: string; - GITHUB_CLIENT_ID: string; - GITHUB_REDIRECT_URL: string; + GAUZY_GITHUB_APP_NAME: string; + GAUZY_GITHUB_APP_ID: string; + GAUZY_GITHUB_CLIENT_ID: string; + GAUZY_GITHUB_REDIRECT_URL: string; } diff --git a/packages/auth/src/github/github.strategy.ts b/packages/auth/src/github/github.strategy.ts index 533e0c8b037..70c5a8af92f 100644 --- a/packages/auth/src/github/github.strategy.ts +++ b/packages/auth/src/github/github.strategy.ts @@ -40,9 +40,9 @@ export const config = (configService: ConfigService) => { const { baseUrl } = configService.apiConfigOptions as IApiServerOptions; return { - clientID: github.CLIENT_ID || 'disabled', - clientSecret: github.CLIENT_SECRET || 'disabled', - callbackURL: github.CALLBACK_URL || `${baseUrl}/api/auth/github/callback`, + clientID: github.clientId || 'disabled', + clientSecret: github.clientSecret || 'disabled', + callbackURL: github.callbackUrl || `${baseUrl}/api/auth/github/callback`, passReqToCallback: true, scope: ['user:email'] }; diff --git a/packages/common/src/interfaces/IGithubConfig.ts b/packages/common/src/interfaces/IGithubConfig.ts index d698be86075..1b6576b6c9b 100644 --- a/packages/common/src/interfaces/IGithubConfig.ts +++ b/packages/common/src/interfaces/IGithubConfig.ts @@ -1,23 +1,39 @@ +/** + * Configuration options for GitHub integration. + */ export interface IGithubConfig extends Partial { - /** */ - readonly CLIENT_ID: string; - readonly CLIENT_SECRET: string; - readonly CALLBACK_URL: string; + /** The GitHub OAuth App Client ID. */ + readonly clientId: string; + + /** The GitHub OAuth App Client Secret. */ + readonly clientSecret: string; + + /** The callback URL for GitHub OAuth authentication. */ + readonly callbackUrl: string; } +/** + * Configuration options for a GitHub Integration. + */ export interface IGithubIntegrationConfig { - /** */ - readonly APP_ID: string; - readonly APP_NAME: string; - readonly APP_PRIVATE_KEY: string; + /** The GitHub App ID. */ + readonly appId: string; + + /** The name of the GitHub App. */ + readonly appName: string; + + /** The private key associated with the GitHub App. */ + readonly appPrivateKey: string; + + /** The URL to redirect to after GitHub App installation. */ + readonly postInstallUrl: string; - /** */ - readonly POST_INSTALL_URL: string; + /** The URL for receiving GitHub webhooks. */ + readonly webhookUrl: string; - /** */ - readonly WEBHOOK_URL: string; - readonly WEBHOOK_SECRET: string; + /** The secret used to secure GitHub webhooks. */ + readonly webhookSecret: string; - /** */ - readonly API_VERSION: string; + /** The GitHub API version to use. */ + readonly apiVersion: string; } diff --git a/packages/common/src/interfaces/IHubstaffConfig.ts b/packages/common/src/interfaces/IHubstaffConfig.ts index 38c4f15fb76..925f722f7d9 100644 --- a/packages/common/src/interfaces/IHubstaffConfig.ts +++ b/packages/common/src/interfaces/IHubstaffConfig.ts @@ -1,5 +1,13 @@ +/** + * Configuration options for Hubstaff integration. + */ export interface IHubstaffConfig { - readonly CLIENT_ID: string; - readonly CLIENT_SECRET: string; - readonly POST_INSTALL_URL: string; + /** The Hubstaff OAuth App Client ID. */ + readonly clientId: string; + + /** The Hubstaff OAuth App Client Secret. */ + readonly clientSecret: string; + + /** The URL to redirect to after Hubstaff App installation. */ + readonly postInstallUrl: string; } diff --git a/packages/common/src/interfaces/IUpworkConfig.ts b/packages/common/src/interfaces/IUpworkConfig.ts index a34094db082..e4a9f8838c9 100644 --- a/packages/common/src/interfaces/IUpworkConfig.ts +++ b/packages/common/src/interfaces/IUpworkConfig.ts @@ -1,6 +1,16 @@ +/** + * Configuration options for Upwork integration. + */ export interface IUpworkConfig { - readonly API_KEY: string; - readonly API_SECRET: string; - readonly CALLBACK_URL: string; - readonly POST_INSTALL_URL: string; + /** The Upwork API Key. */ + readonly apiKey: string; + + /** The Upwork API Secret. */ + readonly apiSecret: string; + + /** The callback URL for Upwork OAuth authentication. */ + readonly callbackUrl: string; + + /** The URL to redirect to after Upwork App installation. */ + readonly postInstallUrl: string; } diff --git a/packages/config/src/environments/environment.prod.ts b/packages/config/src/environments/environment.prod.ts index 75dafb655e7..3e744af5e70 100644 --- a/packages/config/src/environments/environment.prod.ts +++ b/packages/config/src/environments/environment.prod.ts @@ -109,22 +109,22 @@ export const environment: IEnvironment = { github: { /**Github OAuth Configuration */ - CLIENT_ID: process.env.GITHUB_CLIENT_ID, - CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET, - CALLBACK_URL: process.env.GITHUB_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/github/callback`, + clientId: process.env.GAUZY_GITHUB_CLIENT_ID, + clientSecret: process.env.GAUZY_GITHUB_CLIENT_SECRET, + callbackUrl: process.env.GAUZY_GITHUB_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/github/callback`, /** Github App Install Configuration */ - APP_ID: process.env.GITHUB_APP_ID, - APP_NAME: process.env.GITHUB_APP_NAME, - APP_PRIVATE_KEY: process.env.GITHUB_APP_PRIVATE_KEY.replace(/\\n/g, '\n'), + appId: process.env.GAUZY_GITHUB_APP_ID, + appName: process.env.GAUZY_GITHUB_APP_NAME, + appPrivateKey: process.env.GAUZY_GITHUB_APP_PRIVATE_KEY.replace(/\\n/g, '\n'), /** Github App Post Install Configuration */ - POST_INSTALL_URL: process.env.GITHUB_POST_INSTALL_URL, + postInstallUrl: process.env.GAUZY_GITHUB_POST_INSTALL_URL, - WEBHOOK_SECRET: process.env.GITHUB_WEBHOOK_SECRET, - WEBHOOK_URL: process.env.GITHUB_WEBHOOK_URL, + webhookSecret: process.env.GAUZY_GITHUB_WEBHOOK_SECRET, + webhookUrl: process.env.GAUZY_GITHUB_WEBHOOK_URL, - API_VERSION: process.env.GITHUB_API_VERSION + apiVersion: process.env.GAUZY_GITHUB_API_VERSION }, microsoftConfig: { @@ -173,18 +173,18 @@ export const environment: IEnvironment = { defaultIntegratedUserPass: process.env.INTEGRATED_USER_DEFAULT_PASS || '123456', upwork: { - API_KEY: process.env.UPWORK_API_KEY, - API_SECRET: process.env.UPWORK_API_SECRET, - CALLBACK_URL: process.env.UPWORK_REDIRECT_URL || `${process.env.API_BASE_URL}/api/integrations/upwork/callback`, - POST_INSTALL_URL: process.env.UPWORK_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/upwork`, + apiKey: process.env.UPWORK_API_KEY, + apiSecret: process.env.UPWORK_API_SECRET, + callbackUrl: process.env.UPWORK_REDIRECT_URL || `${process.env.API_BASE_URL}/api/integrations/upwork/callback`, + postInstallUrl: process.env.UPWORK_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/upwork`, }, hubstaff: { /** Hubstaff Integration Configuration */ - CLIENT_ID: process.env.HUBSTAFF_CLIENT_ID, - CLIENT_SECRET: process.env.HUBSTAFF_CLIENT_SECRET, + clientId: process.env.HUBSTAFF_CLIENT_ID, + clientSecret: process.env.HUBSTAFF_CLIENT_SECRET, /** Hubstaff Integration Post Install URL */ - POST_INSTALL_URL: process.env.HUBSTAFF_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/hubstaff`, + postInstallUrl: process.env.HUBSTAFF_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/hubstaff`, }, isElectron: process.env.IS_ELECTRON === 'true' ? true : false, diff --git a/packages/config/src/environments/environment.ts b/packages/config/src/environments/environment.ts index 3da9c9c0fd7..40bef9afb87 100644 --- a/packages/config/src/environments/environment.ts +++ b/packages/config/src/environments/environment.ts @@ -112,22 +112,22 @@ export const environment: IEnvironment = { github: { /**Github OAuth Configuration */ - CLIENT_ID: process.env.GITHUB_CLIENT_ID, - CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET, - CALLBACK_URL: process.env.GITHUB_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/github/callback`, + clientId: process.env.GAUZY_GITHUB_CLIENT_ID, + clientSecret: process.env.GAUZY_GITHUB_CLIENT_SECRET, + callbackUrl: process.env.GAUZY_GITHUB_CALLBACK_URL || `${process.env.API_BASE_URL}/api/auth/github/callback`, /** Github App Install Configuration */ - APP_ID: process.env.GITHUB_APP_ID, - APP_NAME: process.env.GITHUB_APP_NAME, - APP_PRIVATE_KEY: process.env.GITHUB_APP_PRIVATE_KEY.replace(/\\n/g, '\n'), + appId: process.env.GAUZY_GITHUB_APP_ID, + appName: process.env.GAUZY_GITHUB_APP_NAME, + appPrivateKey: process.env.GAUZY_GITHUB_APP_PRIVATE_KEY.replace(/\\n/g, '\n'), /** Github App Post Install Configuration */ - POST_INSTALL_URL: process.env.GITHUB_POST_INSTALL_URL, + postInstallUrl: process.env.GAUZY_GITHUB_POST_INSTALL_URL, - WEBHOOK_SECRET: process.env.GITHUB_WEBHOOK_SECRET, - WEBHOOK_URL: process.env.GITHUB_WEBHOOK_URL, + webhookSecret: process.env.GAUZY_GITHUB_WEBHOOK_SECRET, + webhookUrl: process.env.GAUZY_GITHUB_WEBHOOK_URL, - API_VERSION: process.env.GITHUB_API_VERSION + apiVersion: process.env.GAUZY_GITHUB_API_VERSION }, microsoftConfig: { @@ -176,18 +176,18 @@ export const environment: IEnvironment = { defaultIntegratedUserPass: process.env.INTEGRATED_USER_DEFAULT_PASS || '123456', upwork: { - API_KEY: process.env.UPWORK_API_KEY, - API_SECRET: process.env.UPWORK_API_SECRET, - CALLBACK_URL: process.env.UPWORK_REDIRECT_URL || `${process.env.API_BASE_URL}/api/integrations/upwork/callback`, - POST_INSTALL_URL: process.env.UPWORK_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/upwork`, + apiKey: process.env.UPWORK_API_KEY, + apiSecret: process.env.UPWORK_API_SECRET, + callbackUrl: process.env.UPWORK_REDIRECT_URL || `${process.env.API_BASE_URL}/api/integrations/upwork/callback`, + postInstallUrl: process.env.UPWORK_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/upwork`, }, hubstaff: { /** Hubstaff Integration Configuration */ - CLIENT_ID: process.env.HUBSTAFF_CLIENT_ID, - CLIENT_SECRET: process.env.HUBSTAFF_CLIENT_SECRET, + clientId: process.env.HUBSTAFF_CLIENT_ID, + clientSecret: process.env.HUBSTAFF_CLIENT_SECRET, /** Hubstaff Integration Post Install URL */ - POST_INSTALL_URL: process.env.HUBSTAFF_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/hubstaff`, + postInstallUrl: process.env.HUBSTAFF_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/hubstaff`, }, isElectron: process.env.IS_ELECTRON === 'true' ? true : false, diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index c1fe03f8b7a..63a1b6406d7 100644 --- a/packages/core/src/app.module.ts +++ b/packages/core/src/app.module.ts @@ -252,18 +252,18 @@ if (environment.sentry && environment.sentry.dsn) { : []), // Probot - ...(github && environment.github.APP_ID + ...(github && environment.github.appId ? [ ProbotModule.forRoot({ isGlobal: true, path: 'integration/github/webhook', // Webhook URL in GitHub will be: https://api.gauzy.co/api/integration/github/webhook config: { /** Client Configuration */ - clientId: environment.github.CLIENT_ID, - clientSecret: environment.github.CLIENT_SECRET, - appId: environment.github.APP_ID, - privateKey: environment.github.APP_PRIVATE_KEY, - webhookSecret: environment.github.WEBHOOK_SECRET + clientId: environment.github.clientId, + clientSecret: environment.github.clientSecret, + appId: environment.github.appId, + privateKey: environment.github.appPrivateKey, + webhookSecret: environment.github.webhookSecret }, }), ] diff --git a/packages/core/src/integration/github/github-authorization.controller.ts b/packages/core/src/integration/github/github-authorization.controller.ts index 3afc8c8e3df..929ba09927f 100644 --- a/packages/core/src/integration/github/github-authorization.controller.ts +++ b/packages/core/src/integration/github/github-authorization.controller.ts @@ -37,7 +37,7 @@ export class GitHubAuthorizationController { urlParams.append('state', query.state); /** Redirect to the URL */ - return response.redirect(`${github.POST_INSTALL_URL}?${urlParams.toString()}`); + return response.redirect(`${github.postInstallUrl}?${urlParams.toString()}`); } catch (error) { // Handle errors and return an appropriate error response throw new HttpException(`Failed to add GitHub installation: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); diff --git a/packages/core/src/integration/github/github.service.ts b/packages/core/src/integration/github/github.service.ts index 7665d3144ba..a6bbd32af1f 100644 --- a/packages/core/src/integration/github/github.service.ts +++ b/packages/core/src/integration/github/github.service.ts @@ -9,7 +9,6 @@ import { IntegrationTenantFirstOrCreateCommand } from 'integration-tenant/comman import { IntegrationService } from 'integration/integration.service'; import { RequestContext } from '../../core/context'; import { GITHUB_ACCESS_TOKEN_URL } from './github.config'; - const { github } = environment; @Injectable() @@ -111,8 +110,8 @@ export class GithubService { }); const urlParams = new URLSearchParams(); - urlParams.append('client_id', github.CLIENT_ID); - urlParams.append('client_secret', github.CLIENT_SECRET); + urlParams.append('client_id', github.clientId); + urlParams.append('client_secret', github.clientSecret); urlParams.append('code', code); const tokens$ = this._httpService.post(GITHUB_ACCESS_TOKEN_URL, urlParams, { diff --git a/packages/core/src/integration/hubstaff/hubstaff-authorization.controller.ts b/packages/core/src/integration/hubstaff/hubstaff-authorization.controller.ts index ddbe1fe1bea..bc8d43cde6f 100644 --- a/packages/core/src/integration/hubstaff/hubstaff-authorization.controller.ts +++ b/packages/core/src/integration/hubstaff/hubstaff-authorization.controller.ts @@ -39,7 +39,7 @@ export class HubstaffAuthorizationController { urlParams.append('state', query.state); /** Redirect to the URL */ - return response.redirect(`${hubstaff.POST_INSTALL_URL}?${urlParams.toString()}`); + return response.redirect(`${hubstaff.postInstallUrl}?${urlParams.toString()}`); } catch (error) { // Handle errors and return an appropriate error response throw new HttpException(`Failed to add ${IntegrationEnum.HUBSTAFF} integration: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); diff --git a/packages/core/src/upwork/upwork-authorization.controller.ts b/packages/core/src/upwork/upwork-authorization.controller.ts index 0561c178d5f..32b5e30f10d 100644 --- a/packages/core/src/upwork/upwork-authorization.controller.ts +++ b/packages/core/src/upwork/upwork-authorization.controller.ts @@ -40,7 +40,7 @@ export class UpworkAuthorizationController { urlParams.append('oauth_verifier', query.oauth_verifier); /** Redirect to the URL */ - return response.redirect(`${upwork.POST_INSTALL_URL}?${urlParams.toString()}`); + return response.redirect(`${upwork.postInstallUrl}?${urlParams.toString()}`); } catch (error) { // Handle errors and return an appropriate error response throw new HttpException(`Failed to add ${IntegrationEnum.UPWORK} integration: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); diff --git a/packages/core/src/upwork/upwork.service.ts b/packages/core/src/upwork/upwork.service.ts index 6919ab4e1d7..737a7cf4dbf 100644 --- a/packages/core/src/upwork/upwork.service.ts +++ b/packages/core/src/upwork/upwork.service.ts @@ -165,7 +165,7 @@ export class UpworkService { this._upworkApi = new UpworkApi(config); - const authUrl = environment.upwork.CALLBACK_URL; + const authUrl = environment.upwork.callbackUrl; console.log(`Upwork callback URL: ${authUrl}`); diff --git a/packages/desktop-ui-lib/src/lib/settings/settings.component.ts b/packages/desktop-ui-lib/src/lib/settings/settings.component.ts index 216ae405362..bb9bda33aee 100644 --- a/packages/desktop-ui-lib/src/lib/settings/settings.component.ts +++ b/packages/desktop-ui-lib/src/lib/settings/settings.component.ts @@ -187,18 +187,18 @@ export class SettingsComponent implements OnInit, AfterViewInit { title: 'Github', fields: [ { - name: 'GITHUB_CLIENT_ID', - field: 'GITHUB_CLIENT_ID', + name: 'GAUZY_GITHUB_CLIENT_ID', + field: 'GAUZY_GITHUB_CLIENT_ID', value: '' }, { - name: 'GITHUB_CLIENT_SECRET', - field: 'GITHUB_CLIENT_SECRET', + name: 'GAUZY_GITHUB_CLIENT_SECRET', + field: 'GAUZY_GITHUB_CLIENT_SECRET', value: '' }, { - name: 'GITHUB_CALLBACK_URL', - field: 'GITHUB_CALLBACK_URL', + name: 'GAUZY_GITHUB_CALLBACK_URL', + field: 'GAUZY_GITHUB_CALLBACK_URL', value: '' } ] diff --git a/packages/plugins/integration-github/src/octokit.service.ts b/packages/plugins/integration-github/src/octokit.service.ts index b0cd9fb384d..e6b61c1e961 100644 --- a/packages/plugins/integration-github/src/octokit.service.ts +++ b/packages/plugins/integration-github/src/octokit.service.ts @@ -3,7 +3,7 @@ import { App } from 'octokit'; import { ModuleProviders, ProbotConfig } from './probot.types'; import { ResponseHeaders as OctokitResponseHeaders } from "@octokit/types"; -const GITHUB_API_VERSION = process.env.GITHUB_API_VERSION; +const GAUZY_GITHUB_API_VERSION = process.env.GAUZY_GITHUB_API_VERSION; export interface OctokitResponse { data: T; // The response data received from the GitHub API. @@ -45,7 +45,7 @@ export class OctokitService { return await octokit.request('GET /app/installations/{installation_id}', { installation_id, headers: { - 'X-GitHub-Api-Version': GITHUB_API_VERSION + 'X-GitHub-Api-Version': GAUZY_GITHUB_API_VERSION } }); } catch (error) { @@ -70,7 +70,7 @@ export class OctokitService { return await octokit.request('GET /installation/repositories', { installation_id, headers: { - 'X-GitHub-Api-Version': GITHUB_API_VERSION + 'X-GitHub-Api-Version': GAUZY_GITHUB_API_VERSION } }); } catch (error) { @@ -105,7 +105,7 @@ export class OctokitService { owner: owner, repo: repo, headers: { - 'X-GitHub-Api-Version': GITHUB_API_VERSION + 'X-GitHub-Api-Version': GAUZY_GITHUB_API_VERSION } }); } catch (error) { From d33b3488f759c8576355c5dd3810b66d58e65573 Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Tue, 26 Sep 2023 00:11:24 +0200 Subject: [PATCH 093/104] feat: add entity relationship support in query --- packages/core/src/tasks/task.service.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/core/src/tasks/task.service.ts b/packages/core/src/tasks/task.service.ts index ff45c78c65c..18c9bbdec67 100644 --- a/packages/core/src/tasks/task.service.ts +++ b/packages/core/src/tasks/task.service.ts @@ -180,11 +180,14 @@ export class TaskService extends TenantAwareCrudService { * If additional options found */ query.setFindOptions({ - ...(isNotEmpty(options) && isNotEmpty(options.where) - ? { + ...(isNotEmpty(options) && + isNotEmpty(options.where) && { where: options.where, - } - : {}), + }), + ...(isNotEmpty(options) && + isNotEmpty(options.relations) && { + relations: options.relations, + }), }); query.andWhere( new Brackets((qb: WhereExpressionBuilder) => { From 0087a37f7ed2e7fcf26a6cf4f0efa916fd88106b Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Tue, 26 Sep 2023 00:12:43 +0200 Subject: [PATCH 094/104] feat: create a util class for color adapter --- .../src/lib/tasks/tasks.component.ts | 48 +++--------------- .../src/lib/utils/color-adapter.ts | 49 +++++++++++++++++++ 2 files changed, 56 insertions(+), 41 deletions(-) create mode 100644 packages/desktop-ui-lib/src/lib/utils/color-adapter.ts diff --git a/packages/desktop-ui-lib/src/lib/tasks/tasks.component.ts b/packages/desktop-ui-lib/src/lib/tasks/tasks.component.ts index 959dc64c7a8..db66e12197b 100644 --- a/packages/desktop-ui-lib/src/lib/tasks/tasks.component.ts +++ b/packages/desktop-ui-lib/src/lib/tasks/tasks.component.ts @@ -9,9 +9,9 @@ import { TaskStatusEnum, } from '@gauzy/contracts'; import { NbToastrService } from '@nebular/theme'; -import { Color, rgbString } from '@kurkle/color'; import * as moment from 'moment'; import { TranslateService } from '@ngx-translate/core'; +import { ColorAdapter } from '../utils'; @Component({ selector: 'ngx-tasks', @@ -77,9 +77,7 @@ export class TasksComponent implements OnInit { })(); this.form = new FormGroup({ description: new FormControl(null), - dueDate: new FormControl( - moment().add(1, 'day').utc().toDate() - ), + dueDate: new FormControl(moment().add(1, 'day').utc().toDate()), estimate: new FormControl(null), estimateDays: new FormControl(null, [Validators.min(0)]), estimateHours: new FormControl(null, [ @@ -106,7 +104,7 @@ export class TasksComponent implements OnInit { try { this.projects = await this.timeTrackerService.getProjects({ organizationContactId: null, - ...user + ...user, }); } catch (error) { console.error( @@ -161,7 +159,7 @@ export class TasksComponent implements OnInit { this.isAddTask.emit(false); this.newTaskCallback.emit({ isSuccess: true, - message: this.translate.instant('TOASTR.MESSAGE.CREATED') + message: this.translate.instant('TOASTR.MESSAGE.CREATED'), }); } catch (error) { console.log(error); @@ -207,43 +205,11 @@ export class TasksComponent implements OnInit { }; public background(bgColor: string) { - const color = new Color(bgColor); - return color.valid ? bgColor : this._test(bgColor); + return ColorAdapter.background(bgColor); } - public backgroundContrast(bgColor) { - let color = new Color(bgColor); - color = color.valid ? color : new Color(this._hex2rgb(bgColor)); - const MIN_THRESHOLD = 128; - const MAX_THRESHOLD = 186; - const contrast = color.rgb - ? color.rgb.r * 0.299 + color.rgb.g * 0.587 + color.rgb.b * 0.114 - : null; - if (contrast < MIN_THRESHOLD) { - return '#ffffff'; - } else if (contrast > MAX_THRESHOLD) { - return '#000000'; - } - } - - private _hex2rgb(hex: string) { - hex = this._test(hex); - return rgbString({ - r: parseInt(hex.slice(1, 3), 16), - g: parseInt(hex.slice(3, 5), 16), - b: parseInt(hex.slice(5, 7), 16), - a: 1, - }); - } - - private _test(hex: string): string { - const regex = /^#[0-9A-F]{6}$/i; - if (regex.test(hex)) { - return hex; - } else { - hex = '#' + hex; - return regex.test(hex) ? hex : '#000000'; - } + public backgroundContrast(bgColor: string) { + return ColorAdapter.contrast(bgColor); } private _formatStatus(name: string): string { diff --git a/packages/desktop-ui-lib/src/lib/utils/color-adapter.ts b/packages/desktop-ui-lib/src/lib/utils/color-adapter.ts new file mode 100644 index 00000000000..6abcfa9ffae --- /dev/null +++ b/packages/desktop-ui-lib/src/lib/utils/color-adapter.ts @@ -0,0 +1,49 @@ +import { Color, rgbString } from '@kurkle/color'; + +export class ColorAdapter { + public static hex2Rgb(hex: string) { + hex = this.normalize(hex); + return rgbString({ + r: parseInt(hex.slice(1, 3), 16), + g: parseInt(hex.slice(3, 5), 16), + b: parseInt(hex.slice(5, 7), 16), + a: 1, + }); + } + + public static normalize(hex: string): string { + const regex = /^#[0-9A-F]{6}$/i; + if (regex.test(hex)) { + return hex; + } else { + hex = '#' + hex; + return regex.test(hex) ? hex : '#000000'; + } + } + + public static contrast(bgColor: string) { + let color = new Color(bgColor); + color = color.valid ? color : new Color(this.hex2Rgb(bgColor)); + const MIN_THRESHOLD = 128; + const MAX_THRESHOLD = 186; + const contrast = color.rgb + ? color.rgb.r * 0.299 + color.rgb.g * 0.587 + color.rgb.b * 0.114 + : null; + if (contrast < MIN_THRESHOLD) { + return '#ffffff'; + } else if (contrast > MAX_THRESHOLD) { + return '#000000'; + } + } + + public static background(bgColor: string) { + const color = new Color(bgColor); + return color.valid ? bgColor : this.normalize(bgColor); + } + + public static hexToHsl(hexColor: string): string { + let color = new Color(hexColor); + color = color.valid ? color : new Color(this.hex2Rgb(hexColor)); + return color.hslString(); + } +} From 8744f9347e87deaeb3d2dc5a72379735b937db7f Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Tue, 26 Sep 2023 00:14:03 +0200 Subject: [PATCH 095/104] feat: save statususes in store service --- .../src/lib/services/store.service.ts | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/packages/desktop-ui-lib/src/lib/services/store.service.ts b/packages/desktop-ui-lib/src/lib/services/store.service.ts index 7e2a10b0947..d827565974c 100644 --- a/packages/desktop-ui-lib/src/lib/services/store.service.ts +++ b/packages/desktop-ui-lib/src/lib/services/store.service.ts @@ -11,16 +11,17 @@ import { IFeatureToggle, IFeatureOrganization, FeatureEnum, - ComponentLayoutStyleEnum + ComponentLayoutStyleEnum, + ITaskStatus, } from '@gauzy/contracts'; import { Injectable } from '@angular/core'; import { StoreConfig, Store as AkitaStore, Query } from '@datorama/akita'; import { ComponentEnum, - SYSTEM_DEFAULT_LAYOUT + SYSTEM_DEFAULT_LAYOUT, } from '../constants/layout.constants'; import { map } from 'rxjs/operators'; -import { merge, Subject } from 'rxjs'; +import { merge, Observable, Subject } from 'rxjs'; import * as _ from 'underscore'; export interface AppState { @@ -35,6 +36,7 @@ export interface AppState { featureOrganizations: IFeatureOrganization[]; featureTenant: IFeatureOrganization[]; isOffline: boolean; + statuses: ITaskStatus[]; } export interface PersistState { @@ -58,6 +60,7 @@ export function createInitialAppState(): AppState { featureOrganizations: [], featureTenant: [], isOffline: false, + statuses: [], } as AppState; } @@ -80,7 +83,7 @@ export function createInitialPersistState(): PersistState { preferredLanguage, componentLayout, tenantId, - host + host, } as PersistState; } @@ -121,7 +124,7 @@ export class Store { protected appQuery: AppQuery, protected persistStore: PersistStore, protected persistQuery: PersistQuery - ) { } + ) {} user$ = this.appQuery.select((state) => state.user); selectedOrganization$ = this.appQuery.select( @@ -133,9 +136,15 @@ export class Store { (state) => state.userRolePermissions ); featureToggles$ = this.appQuery.select((state) => state.featureToggles); + featureOrganizations$ = this.appQuery.select( (state) => state.featureOrganizations ); + + statuses$: Observable = this.appQuery.select( + (state) => state.statuses + ); + featureTenant$ = this.appQuery.select((state) => state.featureTenant); preferredLanguage$ = this.persistQuery.select( (state) => state.preferredLanguage @@ -315,6 +324,17 @@ export class Store { }); } + get statuses(): ITaskStatus[] { + const { statuses } = this.appQuery.getValue(); + return statuses; + } + + set statuses(statuses: ITaskStatus[]) { + this.appStore.update({ + statuses, + }); + } + get featureOrganizations(): IFeatureOrganization[] { const { featureOrganizations } = this.appQuery.getValue(); return featureOrganizations; @@ -385,8 +405,8 @@ export class Store { getDateFromOrganizationSettings() { const dateObj = this.selectedDate; switch ( - this.selectedOrganization && - this.selectedOrganization.defaultValueDateType + this.selectedOrganization && + this.selectedOrganization.defaultValueDateType ) { case DefaultValueDateTypeEnum.TODAY: { return new Date(Date.now()); @@ -483,7 +503,7 @@ export class Store { set tenantId(value: string) { this.persistStore.update({ - tenantId: value + tenantId: value, }); } @@ -494,7 +514,7 @@ export class Store { set host(value: string) { this.persistStore.update({ - host: value - }) + host: value, + }); } } From 7acae2d50f6b6d4de91109dbdba1e9cf86bda752 Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Tue, 26 Sep 2023 00:14:57 +0200 Subject: [PATCH 096/104] feat: create replace pipe --- .../lib/time-tracker/pipes/replace.pipe.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 packages/desktop-ui-lib/src/lib/time-tracker/pipes/replace.pipe.ts diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/pipes/replace.pipe.ts b/packages/desktop-ui-lib/src/lib/time-tracker/pipes/replace.pipe.ts new file mode 100644 index 00000000000..b81c3866999 --- /dev/null +++ b/packages/desktop-ui-lib/src/lib/time-tracker/pipes/replace.pipe.ts @@ -0,0 +1,26 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { isRegExp, isString, isUndefined } from 'underscore'; + +@Pipe({ + name: 'replace', +}) +export class ReplacePipe implements PipeTransform { + transform(input: any, pattern: any, replacement: any): any { + if ( + !isString(input) || + isUndefined(pattern) || + isUndefined(replacement) + ) { + return input; + } + if (isRegExp(pattern)) { + return input.replace(pattern, replacement); + } else { + return this.replaceAll(input, pattern, replacement); + } + } + + replaceAll(string, search, replace) { + return string.split(search).join(replace); + } +} From 4933fb026db6678d61c978a98c612a23740b6ed0 Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Tue, 26 Sep 2023 00:16:55 +0200 Subject: [PATCH 097/104] feat: create a badge component that renderer complex object --- .../task-badge-default.component.html | 6 +++ .../task-badge-default.component.scss | 16 +++++++ .../task-badge-default.component.ts | 39 +++++++++++++++ .../task-badge-view.component.html | 15 ++++++ .../task-badge-view.component.scss | 18 +++++++ .../task-badge-view.component.ts | 47 +++++++++++++++++++ 6 files changed, 141 insertions(+) create mode 100644 packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-default/task-badge-default.component.html create mode 100644 packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-default/task-badge-default.component.scss create mode 100644 packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-default/task-badge-default.component.ts create mode 100644 packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-view/task-badge-view.component.html create mode 100644 packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-view/task-badge-view.component.scss create mode 100644 packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-view/task-badge-view.component.ts diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-default/task-badge-default.component.html b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-default/task-badge-default.component.html new file mode 100644 index 00000000000..df13c27b7b1 --- /dev/null +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-default/task-badge-default.component.html @@ -0,0 +1,6 @@ + diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-default/task-badge-default.component.scss b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-default/task-badge-default.component.scss new file mode 100644 index 00000000000..7747a396967 --- /dev/null +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-default/task-badge-default.component.scss @@ -0,0 +1,16 @@ +.badge { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + position: relative; + width: fit-content; + height: 1.5rem; + font-size: 12px; + font-weight: 600; + letter-spacing: 0; + text-align: left; + padding: 2px 4px; + line-height: 1; + border-radius: calc(var(--border-radius) / 3); +} diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-default/task-badge-default.component.ts b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-default/task-badge-default.component.ts new file mode 100644 index 00000000000..046be699ad1 --- /dev/null +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-default/task-badge-default.component.ts @@ -0,0 +1,39 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { TaskStatusEnum } from '@gauzy/contracts'; +import { NbComponentStatus } from '@nebular/theme'; + +@Component({ + selector: 'gauzy-task-badge-default', + templateUrl: './task-badge-default.component.html', + styleUrls: ['./task-badge-default.component.scss'], +}) +export class TaskBadgeDefaultComponent implements OnInit { + @Input() taskBadge: string; + public status: NbComponentStatus; + + ngOnInit(): void { + switch (this.taskBadge) { + case TaskStatusEnum.OPEN: + this.status = 'basic'; + break; + case TaskStatusEnum.IN_PROGRESS: + this.status = 'info'; + break; + case TaskStatusEnum.READY_FOR_REVIEW: + this.status = 'warning'; + break; + case TaskStatusEnum.IN_REVIEW: + this.status = 'info'; + break; + case TaskStatusEnum.COMPLETED: + this.status = 'success'; + break; + case TaskStatusEnum.BLOCKED: + this.status = 'danger'; + break; + default: + this.status = 'basic'; + break; + } + } +} diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-view/task-badge-view.component.html b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-view/task-badge-view.component.html new file mode 100644 index 00000000000..b84283760f3 --- /dev/null +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-view/task-badge-view.component.html @@ -0,0 +1,15 @@ +
+
+
+
+ badge +
+
{{ name | replace : '-' : ' ' | titlecase }}
+
+
+
diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-view/task-badge-view.component.scss b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-view/task-badge-view.component.scss new file mode 100644 index 00000000000..a76cfac5321 --- /dev/null +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-view/task-badge-view.component.scss @@ -0,0 +1,18 @@ +.badge-color { + display: flex; + padding: 4px; + width: fit-content; + line-height: 1; + border-radius: var(--border-radius); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + gap: 4px; + align-items: center; + font-weight: 600; + + .badge-img { + width: 18px; + height: 100%; + } +} diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-view/task-badge-view.component.ts b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-view/task-badge-view.component.ts new file mode 100644 index 00000000000..099d034cc41 --- /dev/null +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-badge-view/task-badge-view.component.ts @@ -0,0 +1,47 @@ +import { Component, Input } from '@angular/core'; +import { ITaskPriority, ITaskSize, ITaskStatus } from '@gauzy/contracts'; +import { ColorAdapter } from '../../../utils'; + +export type ITaskBadge = ITaskStatus | ITaskSize | ITaskPriority; + +@Component({ + selector: 'gauzy-task-badge-view', + templateUrl: './task-badge-view.component.html', + styleUrls: ['./task-badge-view.component.scss'], +}) +export class TaskBadgeViewComponent { + constructor() { + this._taskBadge = null; + } + + private _taskBadge: ITaskBadge; + + public get taskBadge(): ITaskBadge { + return this._taskBadge; + } + + @Input() + public set taskBadge(value: ITaskBadge) { + this._taskBadge = value; + } + + public get textColor() { + return ColorAdapter.contrast(this.taskBadge.color); + } + + public get backgroundColor() { + return ColorAdapter.background(this.taskBadge.color); + } + + public get icon() { + return this.taskBadge.fullIconUrl; + } + + public get name() { + return this.taskBadge.name; + } + + public get imageFilter() { + return ColorAdapter.hexToHsl(this.taskBadge.color); + } +} From 7c1d0f1338107cd7bbe1092cbdf4b308dba87d77 Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Tue, 26 Sep 2023 00:17:32 +0200 Subject: [PATCH 098/104] feat: create a status component that renderer status in table --- .../task-render/task-render.module.ts | 12 +++++ .../task-status/task-status.component.html | 27 ++++++++++ .../task-status/task-status.component.scss | 14 ++++++ .../task-status/task-status.component.ts | 50 +++++++++++++++++++ .../desktop-ui-lib/src/lib/utils/index.ts | 1 + 5 files changed, 104 insertions(+) create mode 100644 packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-status/task-status.component.html create mode 100644 packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-status/task-status.component.scss create mode 100644 packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-status/task-status.component.ts create mode 100644 packages/desktop-ui-lib/src/lib/utils/index.ts diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render.module.ts b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render.module.ts index a25a9d0ae7b..345566433d3 100644 --- a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render.module.ts +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render.module.ts @@ -1,8 +1,10 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { + NbBadgeModule, NbButtonModule, NbIconModule, + NbPopoverModule, NbProgressBarModule, NbTooltipModule, } from '@nebular/theme'; @@ -16,6 +18,10 @@ import { TaskEstimateInputComponent } from './task-estimate/task-estimate-input/ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { TaskDueDateComponent } from './task-due-date/task-due-date.component'; import { TranslateModule } from '@ngx-translate/core'; +import { TaskBadgeViewComponent } from './task-badge-view/task-badge-view.component'; +import { TaskStatusComponent } from './task-status/task-status.component'; +import { TaskBadgeDefaultComponent } from './task-badge-default/task-badge-default.component'; +import { ReplacePipe } from '../pipes/replace.pipe'; @NgModule({ declarations: [ @@ -26,6 +32,10 @@ import { TranslateModule } from '@ngx-translate/core'; TaskRenderCellComponent, TaskEstimateInputComponent, TaskDueDateComponent, + TaskBadgeViewComponent, + ReplacePipe, + TaskStatusComponent, + TaskBadgeDefaultComponent, ], imports: [ CommonModule, @@ -37,6 +47,8 @@ import { TranslateModule } from '@ngx-translate/core'; DesktopDirectiveModule, NbButtonModule, TranslateModule, + NbPopoverModule, + NbBadgeModule, ], }) export class TaskRenderModule {} diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-status/task-status.component.html b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-status/task-status.component.html new file mode 100644 index 00000000000..d3c677c3c19 --- /dev/null +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-status/task-status.component.html @@ -0,0 +1,27 @@ + + + + + +
+ + + +
+
+ + + + diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-status/task-status.component.scss b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-status/task-status.component.scss new file mode 100644 index 00000000000..5636fa78186 --- /dev/null +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-status/task-status.component.scss @@ -0,0 +1,14 @@ +.view { + display: flex; + flex-direction: column; + gap: 4px; + padding: 4px; +} + +.status-view { + cursor: pointer; +} + +::ng-deep nb-popover > span.arrow { + display: none; +} diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-status/task-status.component.ts b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-status/task-status.component.ts new file mode 100644 index 00000000000..697d9c2b573 --- /dev/null +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-status/task-status.component.ts @@ -0,0 +1,50 @@ +import { Component, EventEmitter, OnInit } from '@angular/core'; +import { ITaskRender, TaskRenderComponent } from '../task-render.component'; +import { ITaskStatus, TaskStatusEnum } from '@gauzy/contracts'; +import { Store } from '../../../services'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; + +@UntilDestroy({ checkProperties: true }) +@Component({ + selector: 'gauzy-task-status', + templateUrl: './task-status.component.html', + styleUrls: ['./task-status.component.scss'], +}) +export class TaskStatusComponent extends TaskRenderComponent implements OnInit { + public statuses$: Observable; + public updated: EventEmitter; + + constructor(private readonly store: Store) { + super(); + this.updated = new EventEmitter(); + } + + public get taskStatus$(): Observable { + return this.task$.pipe( + map((task: ITaskRender) => task?.taskStatus), + untilDestroyed(this) + ); + } + + public get status$(): Observable { + return this.task$.pipe( + map((task: ITaskRender) => String(task?.status)), + untilDestroyed(this) + ); + } + + public updateStatus(taskStatus: ITaskStatus) { + this.rowData = { + ...this.task, + status: taskStatus.name as TaskStatusEnum, + taskStatus, + }; + this.updated.emit(taskStatus); + } + + public ngOnInit(): void { + this.statuses$ = this.store.statuses$; + } +} diff --git a/packages/desktop-ui-lib/src/lib/utils/index.ts b/packages/desktop-ui-lib/src/lib/utils/index.ts new file mode 100644 index 00000000000..935f5bfda47 --- /dev/null +++ b/packages/desktop-ui-lib/src/lib/utils/index.ts @@ -0,0 +1 @@ +export * from './color-adapter'; From 466b1daa3dc5c3e0f3d892c10d40f99152110563 Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Tue, 26 Sep 2023 00:18:04 +0200 Subject: [PATCH 099/104] feat: improve task estimate --- .../task-estimate/task-estimate.component.html | 17 +++++++++++++++-- .../task-estimate/task-estimate.component.ts | 10 ++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-estimate/task-estimate.component.html b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-estimate/task-estimate.component.html index 2d0498dee5d..454a5e1bffb 100644 --- a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-estimate/task-estimate.component.html +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-estimate/task-estimate.component.html @@ -1,11 +1,24 @@
-
{{ isEdit ? 'Edit:' : 'Estimate:' }}
+
+ {{ + ((isEditDisabled$ | async) + ? 'TASKS_PAGE.COMPLETED' + : isEdit + ? 'TIMESHEET.EDIT' + : 'TASKS_PAGE.ESTIMATE' + ) | translate + }}: +
{{ (estimate$ | async) || 0 | durationFormat : 'HH:mm' }}
- +
diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-estimate/task-estimate.component.ts b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-estimate/task-estimate.component.ts index 41a4ebbb005..5382f2737d3 100644 --- a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-estimate/task-estimate.component.ts +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-estimate/task-estimate.component.ts @@ -3,6 +3,7 @@ import { ITaskRender } from '../task-render.component'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; +import { TaskStatusEnum } from '@gauzy/contracts'; @UntilDestroy({ checkProperties: true }) @Component({ @@ -27,6 +28,15 @@ export class TaskEstimateComponent { ); } + public get isEditDisabled$(): Observable { + return this.task$.pipe( + map( + (task: ITaskRender) => task?.status === TaskStatusEnum.COMPLETED + ), + untilDestroyed(this) + ); + } + public update(event: number): void { this.isEdit = false; if (isNaN(Number(event))) return; From 6401777ce281eba7b14bed8fa7bcb262f78b7cb7 Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Tue, 26 Sep 2023 00:18:22 +0200 Subject: [PATCH 100/104] feat: improve task render cell component --- .../task-render-cell.component.html | 11 ++++++++--- .../task-render-cell/task-render-cell.component.ts | 13 +++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render-cell/task-render-cell.component.html b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render-cell/task-render-cell.component.html index 8b1df88359a..07758fdca9e 100644 --- a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render-cell/task-render-cell.component.html +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render-cell/task-render-cell.component.html @@ -11,11 +11,16 @@
{{ title }}
-
{{ size }}
-
{{ status }}
-
{{ priority }}
+
+ +
+
+ +
diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render-cell/task-render-cell.component.ts b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render-cell/task-render-cell.component.ts index c4a9a157ac7..96e330b55f7 100644 --- a/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render-cell/task-render-cell.component.ts +++ b/packages/desktop-ui-lib/src/lib/time-tracker/task-render/task-render-cell/task-render-cell.component.ts @@ -3,6 +3,7 @@ import { TaskRenderComponent } from '../task-render.component'; import { map } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; +import { ITaskPriority, ITaskSize } from '@gauzy/contracts'; @UntilDestroy({ checkProperties: true }) @Component({ @@ -19,16 +20,12 @@ export class TaskRenderCellComponent extends TaskRenderComponent { return `#${this.task.taskNumber || this.buildTaskNumber()}`; } - public get status(): string { - return this.task.status; + public get size(): ITaskSize { + return this.task.taskSize; } - public get size(): string { - return this.task.size; - } - - public get priority(): string { - return this.task.priority; + public get priority(): ITaskPriority { + return this.task.taskPriority; } public get isSelected$(): Observable { From c9339f29d56c7c5abac81326be1d89b77ab317af Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Tue, 26 Sep 2023 00:18:51 +0200 Subject: [PATCH 101/104] feat: caching task status to increase performance --- .../desktop-ui-lib/src/lib/services/index.ts | 1 + .../lib/services/task-status-cache.service.ts | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 packages/desktop-ui-lib/src/lib/services/task-status-cache.service.ts diff --git a/packages/desktop-ui-lib/src/lib/services/index.ts b/packages/desktop-ui-lib/src/lib/services/index.ts index e08837c85f1..197b3c63a4d 100644 --- a/packages/desktop-ui-lib/src/lib/services/index.ts +++ b/packages/desktop-ui-lib/src/lib/services/index.ts @@ -21,3 +21,4 @@ export * from './image-cache.service'; export * from './language-cache.service'; export * from './time-tracker-date.manager'; export * from './time-zone-manager'; +export * from './task-status-cache.service'; diff --git a/packages/desktop-ui-lib/src/lib/services/task-status-cache.service.ts b/packages/desktop-ui-lib/src/lib/services/task-status-cache.service.ts new file mode 100644 index 00000000000..6b934d2203a --- /dev/null +++ b/packages/desktop-ui-lib/src/lib/services/task-status-cache.service.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@angular/core'; +import { ITaskStatus } from '@gauzy/contracts'; +import { AbstractCacheService } from './abstract-cache.service'; +import { StorageService } from './storage.service'; +import { Store } from './store.service'; + +@Injectable({ + providedIn: 'root', +}) +export class TaskStatusCacheService extends AbstractCacheService< + ITaskStatus[] +> { + constructor( + protected _storageService: StorageService, + protected _store: Store + ) { + super(_storageService, _store); + this.prefix = TaskStatusCacheService.name.toString(); + this.duration = 24 * 3600 * 1000 * 7; // 1 week + } +} From 2e3911737849d91b09cb0b318e0963d1fe75baab Mon Sep 17 00:00:00 2001 From: Adolphe Kifungo <45813955+adkif@users.noreply.github.com> Date: Tue, 26 Sep 2023 00:20:15 +0200 Subject: [PATCH 102/104] feat: integrate task status, size and priority to ng2smarttable --- .../time-tracker/time-tracker.component.html | 328 ++++++++++-------- .../time-tracker/time-tracker.component.ts | 104 +++++- .../lib/time-tracker/time-tracker.service.ts | 98 +++++- 3 files changed, 358 insertions(+), 172 deletions(-) diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/time-tracker.component.html b/packages/desktop-ui-lib/src/lib/time-tracker/time-tracker.component.html index 1ff642f2884..cbd1e92dadc 100644 --- a/packages/desktop-ui-lib/src/lib/time-tracker/time-tracker.component.html +++ b/packages/desktop-ui-lib/src/lib/time-tracker/time-tracker.component.html @@ -1,11 +1,11 @@ @@ -22,10 +22,10 @@
@@ -55,15 +55,15 @@
@@ -104,42 +104,42 @@ >
@@ -533,15 +555,17 @@
-
- +
+
@@ -572,28 +594,28 @@
@@ -629,25 +651,25 @@
- +
- - + + {{ 'TIMER_TRACKER.DIALOG.WARNING' | translate }} @@ -741,15 +763,15 @@
-
diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/time-tracker.component.ts b/packages/desktop-ui-lib/src/lib/time-tracker/time-tracker.component.ts index fcabea734d8..7d9da5183e8 100644 --- a/packages/desktop-ui-lib/src/lib/time-tracker/time-tracker.component.ts +++ b/packages/desktop-ui-lib/src/lib/time-tracker/time-tracker.component.ts @@ -44,6 +44,7 @@ import { IOrganizationContact, ITask, ITasksStatistics, + ITaskStatus, ITaskUpdateInput, LanguagesEnum, PermissionsEnum, @@ -79,6 +80,7 @@ import { } from '../always-on/always-on.service'; import { TaskDurationComponent, TaskProgressComponent } from './task-render'; import { TaskRenderCellComponent } from './task-render/task-render-cell/task-render-cell.component'; +import { TaskStatusComponent } from './task-render/task-status/task-status.component'; enum TimerStartMode { MANUAL = 'manual', @@ -432,11 +434,20 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit { statistics: ITasksStatistics[] ): (ITask & ITasksStatistics)[] { let arr: (ITask & ITasksStatistics)[] = []; - arr = arr.concat(tasks, statistics); + arr = arr.concat(statistics, tasks); return arr.reduce((result, current) => { const existing = result.find((item: any) => item.id === current.id); if (existing) { - Object.assign(existing, current); + const updatedAtMoment = moment(existing?.updatedAt).utc(true); + Object.assign( + existing, + current, + updatedAtMoment.isAfter(current?.updatedAt) + ? { + updatedAt: updatedAtMoment.toISOString(), + } + : {} + ); } else { result.push(current); } @@ -752,6 +763,45 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit { }); }, }, + taskStatus: { + title: this._translateService.instant('SM_TABLE.STATUS'), + type: 'custom', + renderComponent: TaskStatusComponent, + onComponentInitFunction: ( + instance: TaskStatusComponent + ) => { + instance.updated.subscribe({ + next: async (taskStatus: ITaskStatus) => { + const { tenantId, organizationId } = + this._store; + const id = instance.task.id; + const title = instance.task.title; + const status = + taskStatus.name as TaskStatusEnum; + const taskUpdateInput: ITaskUpdateInput = { + organizationId, + tenantId, + status, + title, + taskStatus, + }; + await this.timeTrackerService.updateTask( + id, + taskUpdateInput + ); + this.toastrService.success( + this._translateService.instant( + 'TOASTR.MESSAGE.UPDATED' + ) + ); + this.refreshTimer(); + }, + error: (err: any) => { + console.warn(err); + }, + }); + }, + }, }, hideSubHeader: true, actions: false, @@ -766,6 +816,22 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit { }; } + private async loadStatuses(): Promise { + if (!this._store.organizationId && !this._store.tenantId) { + return; + } + const { organizationId, tenantId } = this._store; + this._store.statuses = await this.timeTrackerService.statuses({ + tenantId, + organizationId, + ...(this.projectSelect + ? { + projectId: this.projectSelect, + } + : {}), + }); + } + ngOnInit(): void { this._sourceData$ = new BehaviorSubject( new LocalDataSource(this.tableData) @@ -955,6 +1021,7 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit { this.electronService.ipcRenderer.send('show_ao'); } const parallelizedTasks: Promise[] = [ + this.loadStatuses(), this.getClient(arg), this.getProjects(arg), this.getTask(arg), @@ -1609,6 +1676,7 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit { console.log('stop tracking'); await this.stopTimer(onClick); } + this.refreshTimer(); } else { this.loading = false; console.log('Error', 'validation failed'); @@ -1672,6 +1740,7 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit { }); } await this._toggle(timer, onClick); + await this.updateOrganizationTeamEmployee(); this.electronService.ipcRenderer.send('request_permission'); } catch (error) { this._startMode = TimerStartMode.STOP; @@ -2685,4 +2754,35 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit { this._isLockSyncProcess = false; } } + + public async updateOrganizationTeamEmployee(): Promise { + try { + if (!this.taskSelect) { + return; + } + const [task] = this._tasks$ + .getValue() + .filter(({ id }) => id === this.taskSelect); + // TODO: Add task teams selector + const organizationTeamId = task?.teams[0]?.id || null; + if (!organizationTeamId) { + return; + } + const { tenantId, organizationId } = this._store; + const { employeeId } = this._store.user; + const activeTaskId = this.taskSelect; + const payload = { + activeTaskId, + tenantId, + organizationId, + organizationTeamId, + }; + await this.timeTrackerService.updateOrganizationTeamEmployee( + employeeId, + payload + ); + } catch (error) { + console.error(error); + } + } } diff --git a/packages/desktop-ui-lib/src/lib/time-tracker/time-tracker.service.ts b/packages/desktop-ui-lib/src/lib/time-tracker/time-tracker.service.ts index b1c37849b9c..fe31530aeb1 100644 --- a/packages/desktop-ui-lib/src/lib/time-tracker/time-tracker.service.ts +++ b/packages/desktop-ui-lib/src/lib/time-tracker/time-tracker.service.ts @@ -6,14 +6,18 @@ import { catchError, map, shareReplay, tap } from 'rxjs/operators'; import { firstValueFrom, throwError } from 'rxjs'; import { toParams } from '@gauzy/common-angular'; import { - TimeLogSourceEnum, - TimeLogType, - IOrganizationProjectCreateInput, - IOrganizationProject, - IOrganizationContactCreateInput, - IOrganizationContact, IGetTasksStatistics, + IOrganizationContact, + IOrganizationContactCreateInput, + IOrganizationProject, + IOrganizationProjectCreateInput, + IOrganizationTeamEmployee, + IPagination, + ITaskStatus, + ITaskStatusFindInput, ITaskUpdateInput, + TimeLogSourceEnum, + TimeLogType, } from '@gauzy/contracts'; import { ClientCacheService } from '../services/client-cache.service'; import { TaskCacheService } from '../services/task-cache.service'; @@ -25,12 +29,22 @@ import { TagCacheService } from '../services/tag-cache.service'; import { TimeLogCacheService } from '../services/time-log-cache.service'; import { LoggerService } from '../electron/services'; import { API_PREFIX } from '../constants/app.constants'; -import { Store, TimeTrackerDateManager } from '../services'; +import { + Store, + TaskStatusCacheService, + TimeTrackerDateManager, +} from '../services'; @Injectable({ providedIn: 'root', }) export class TimeTrackerService { + AW_HOST = 'http://localhost:5600'; + token = ''; + userId = ''; + employeeId = ''; + buckets: any = {}; + constructor( private readonly http: HttpClient, private readonly _clientCacheService: ClientCacheService, @@ -42,15 +56,10 @@ export class TimeTrackerService { private readonly _userOrganizationService: UserOrganizationService, private readonly _timeLogService: TimeLogCacheService, private readonly _loggerService: LoggerService, - private readonly _store: Store + private readonly _store: Store, + private readonly _taskStatusCacheService: TaskStatusCacheService ) {} - AW_HOST = 'http://localhost:5600'; - token = ''; - userId = ''; - employeeId = ''; - buckets: any = {}; - createAuthorizationHeader(headers: Headers) { headers.append('Authorization', 'Basic ' + btoa('username:password')); } @@ -66,14 +75,32 @@ export class TimeTrackerService { } : {}), }, + relations: [ + 'project', + 'tags', + 'teams', + 'teams.members', + 'teams.members.employee', + 'teams.members.employee.user', + 'creator', + 'organizationSprint', + 'taskStatus', + 'taskSize', + 'taskPriority', + ], + join: { + alias: 'task', + leftJoinAndSelect: { + members: 'task.members', + user: 'members.user', + }, + }, }; let tasks$ = this._taskCacheService.getValue(request); if (!tasks$) { tasks$ = this.http .get(`${API_PREFIX}/tasks/employee/${values.employeeId}`, { - params: toParams({ - ...request, - }), + params: toParams(request), }) .pipe( map((response: any) => response), @@ -649,6 +676,7 @@ export class TimeTrackerService { ) .pipe( tap(() => this._taskCacheService.clear()), + tap(() => this._taskStatusCacheService.clear()), catchError((error) => { error.error = { ...error.error, @@ -658,4 +686,40 @@ export class TimeTrackerService { ) ); } + + public async statuses( + params: ITaskStatusFindInput + ): Promise { + let taskStatuses$ = this._taskStatusCacheService.getValue(params); + if (!taskStatuses$) { + taskStatuses$ = this.http + .get>(`${API_PREFIX}/task-statuses`, { + params: toParams({ ...params }), + }) + .pipe( + map((res) => res.items), + shareReplay(1) + ); + this._taskStatusCacheService.setValue(taskStatuses$, params); + } + return firstValueFrom(taskStatuses$); + } + + public async updateOrganizationTeamEmployee( + employeeId: string, + values: Partial + ): Promise { + const params = { + organizationId: values.organizationId, + activeTaskId: values.activeTaskId, + organizationTeamId: values.organizationTeamId, + tenantId: values.tenantId, + }; + return firstValueFrom( + this.http.put( + `${API_PREFIX}/organization-team-employee/${employeeId}`, + params + ) + ); + } } From 7d5402a05b4e63ac4377f242bbb9aff619bdb9d5 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 26 Sep 2023 11:09:53 +0530 Subject: [PATCH 103/104] fix: if config available then create app using Octokit --- packages/core/src/app.module.ts | 4 ++-- .../integration-github/src/octokit.service.ts | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index 63a1b6406d7..0b4a25270fe 100644 --- a/packages/core/src/app.module.ts +++ b/packages/core/src/app.module.ts @@ -251,8 +251,8 @@ if (environment.sentry && environment.sentry.dsn) { ] : []), - // Probot - ...(github && environment.github.appId + // Probot Configuration + ...(github && github.appId ? [ ProbotModule.forRoot({ isGlobal: true, diff --git a/packages/plugins/integration-github/src/octokit.service.ts b/packages/plugins/integration-github/src/octokit.service.ts index e6b61c1e961..534d45b4017 100644 --- a/packages/plugins/integration-github/src/octokit.service.ts +++ b/packages/plugins/integration-github/src/octokit.service.ts @@ -3,7 +3,7 @@ import { App } from 'octokit'; import { ModuleProviders, ProbotConfig } from './probot.types'; import { ResponseHeaders as OctokitResponseHeaders } from "@octokit/types"; -const GAUZY_GITHUB_API_VERSION = process.env.GAUZY_GITHUB_API_VERSION; +const GITHUB_API_VERSION = process.env.GAUZY_GITHUB_API_VERSION; export interface OctokitResponse { data: T; // The response data received from the GitHub API. @@ -23,10 +23,12 @@ export class OctokitService { @Inject(ModuleProviders.ProbotConfig) private readonly config: ProbotConfig ) { - this.app = new App({ - appId: this.config.appId, - privateKey: this.config.privateKey, - }); + if (this.config) { + this.app = new App({ + appId: this.config.appId, + privateKey: this.config.privateKey, + }); + } } /** @@ -45,7 +47,7 @@ export class OctokitService { return await octokit.request('GET /app/installations/{installation_id}', { installation_id, headers: { - 'X-GitHub-Api-Version': GAUZY_GITHUB_API_VERSION + 'X-GitHub-Api-Version': GITHUB_API_VERSION } }); } catch (error) { @@ -70,7 +72,7 @@ export class OctokitService { return await octokit.request('GET /installation/repositories', { installation_id, headers: { - 'X-GitHub-Api-Version': GAUZY_GITHUB_API_VERSION + 'X-GitHub-Api-Version': GITHUB_API_VERSION } }); } catch (error) { @@ -105,7 +107,7 @@ export class OctokitService { owner: owner, repo: repo, headers: { - 'X-GitHub-Api-Version': GAUZY_GITHUB_API_VERSION + 'X-GitHub-Api-Version': GITHUB_API_VERSION } }); } catch (error) { From 8b27c837a5c93863b9fe583f0e60da408fc0b5e7 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 26 Sep 2023 13:40:35 +0530 Subject: [PATCH 104/104] fix: github environment variables not available --- .../common/src/interfaces/IGithubConfig.ts | 3 - .../src/environments/environment.prod.ts | 9 +- .../config/src/environments/environment.ts | 9 +- packages/core/src/app.module.ts | 28 ++--- .../plugins/integration-github/package.json | 5 +- .../integration-github/src/octokit.service.ts | 107 ++++++------------ .../src/probot.discovery.ts | 20 +++- .../integration-github/src/probot.helpers.ts | 6 +- 8 files changed, 80 insertions(+), 107 deletions(-) diff --git a/packages/common/src/interfaces/IGithubConfig.ts b/packages/common/src/interfaces/IGithubConfig.ts index 1b6576b6c9b..6eb7b725f0f 100644 --- a/packages/common/src/interfaces/IGithubConfig.ts +++ b/packages/common/src/interfaces/IGithubConfig.ts @@ -33,7 +33,4 @@ export interface IGithubIntegrationConfig { /** The secret used to secure GitHub webhooks. */ readonly webhookSecret: string; - - /** The GitHub API version to use. */ - readonly apiVersion: string; } diff --git a/packages/config/src/environments/environment.prod.ts b/packages/config/src/environments/environment.prod.ts index 3e744af5e70..87c44de2afe 100644 --- a/packages/config/src/environments/environment.prod.ts +++ b/packages/config/src/environments/environment.prod.ts @@ -116,15 +116,14 @@ export const environment: IEnvironment = { /** Github App Install Configuration */ appId: process.env.GAUZY_GITHUB_APP_ID, appName: process.env.GAUZY_GITHUB_APP_NAME, - appPrivateKey: process.env.GAUZY_GITHUB_APP_PRIVATE_KEY.replace(/\\n/g, '\n'), + appPrivateKey: process.env.GAUZY_GITHUB_APP_PRIVATE_KEY, /** Github App Post Install Configuration */ - postInstallUrl: process.env.GAUZY_GITHUB_POST_INSTALL_URL, + postInstallUrl: process.env.GAUZY_GITHUB_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/github/setup/installation`, + /** Github Webhook Configuration */ webhookSecret: process.env.GAUZY_GITHUB_WEBHOOK_SECRET, - webhookUrl: process.env.GAUZY_GITHUB_WEBHOOK_URL, - - apiVersion: process.env.GAUZY_GITHUB_API_VERSION + webhookUrl: process.env.GAUZY_GITHUB_WEBHOOK_URL || `${process.env.API_BASE_URL}/api/integration/github/webhook` }, microsoftConfig: { diff --git a/packages/config/src/environments/environment.ts b/packages/config/src/environments/environment.ts index 40bef9afb87..d5fcc9e85b8 100644 --- a/packages/config/src/environments/environment.ts +++ b/packages/config/src/environments/environment.ts @@ -119,15 +119,14 @@ export const environment: IEnvironment = { /** Github App Install Configuration */ appId: process.env.GAUZY_GITHUB_APP_ID, appName: process.env.GAUZY_GITHUB_APP_NAME, - appPrivateKey: process.env.GAUZY_GITHUB_APP_PRIVATE_KEY.replace(/\\n/g, '\n'), + appPrivateKey: process.env.GAUZY_GITHUB_APP_PRIVATE_KEY, /** Github App Post Install Configuration */ - postInstallUrl: process.env.GAUZY_GITHUB_POST_INSTALL_URL, + postInstallUrl: process.env.GAUZY_GITHUB_POST_INSTALL_URL || `${process.env.CLIENT_BASE_URL}/#/pages/integrations/github/setup/installation`, + /** Github Webhook Configuration */ webhookSecret: process.env.GAUZY_GITHUB_WEBHOOK_SECRET, - webhookUrl: process.env.GAUZY_GITHUB_WEBHOOK_URL, - - apiVersion: process.env.GAUZY_GITHUB_API_VERSION + webhookUrl: process.env.GAUZY_GITHUB_WEBHOOK_URL || `${process.env.API_BASE_URL}/api/integration/github/webhook` }, microsoftConfig: { diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index 0b4a25270fe..41fefc21c79 100644 --- a/packages/core/src/app.module.ts +++ b/packages/core/src/app.module.ts @@ -252,22 +252,18 @@ if (environment.sentry && environment.sentry.dsn) { : []), // Probot Configuration - ...(github && github.appId - ? [ - ProbotModule.forRoot({ - isGlobal: true, - path: 'integration/github/webhook', // Webhook URL in GitHub will be: https://api.gauzy.co/api/integration/github/webhook - config: { - /** Client Configuration */ - clientId: environment.github.clientId, - clientSecret: environment.github.clientSecret, - appId: environment.github.appId, - privateKey: environment.github.appPrivateKey, - webhookSecret: environment.github.webhookSecret - }, - }), - ] - : []), + ProbotModule.forRoot({ + isGlobal: true, + path: 'integration/github/webhook', // Webhook URL in GitHub will be: https://api.gauzy.co/api/integration/github/webhook + config: { + /** Client Configuration */ + clientId: github.clientId, + clientSecret: github.clientSecret, + appId: github.appId, + privateKey: github.appPrivateKey, + webhookSecret: github.webhookSecret + }, + }), ThrottlerModule.forRootAsync({ inject: [ConfigService], diff --git a/packages/plugins/integration-github/package.json b/packages/plugins/integration-github/package.json index 09964c588b1..78c183a3c68 100644 --- a/packages/plugins/integration-github/package.json +++ b/packages/plugins/integration-github/package.json @@ -31,12 +31,13 @@ "@nestjs/common": "^9.2.1", "@nestjs/core": "^9.2.1", "@octokit/rest": "^18.0.0", + "chalk": "4.1.2", + "express": "^4.17.2", "octokit": "2.1.0", "pino-std-serializers": "^6.2.2", "probot": "^12.3.1", "smee-client": "^1.2.3", - "underscore": "^1.13.3", - "express": "^4.17.2" + "underscore": "^1.13.3" }, "devDependencies": { "@types/node": "^17.0.33", diff --git a/packages/plugins/integration-github/src/octokit.service.ts b/packages/plugins/integration-github/src/octokit.service.ts index 534d45b4017..8b2888f5dd2 100644 --- a/packages/plugins/integration-github/src/octokit.service.ts +++ b/packages/plugins/integration-github/src/octokit.service.ts @@ -1,9 +1,10 @@ import { Inject, Injectable, Logger } from '@nestjs/common'; +import * as chalk from 'chalk'; import { App } from 'octokit'; -import { ModuleProviders, ProbotConfig } from './probot.types'; import { ResponseHeaders as OctokitResponseHeaders } from "@octokit/types"; +import { ModuleProviders, ProbotConfig } from './probot.types'; -const GITHUB_API_VERSION = process.env.GAUZY_GITHUB_API_VERSION; +const GITHUB_API_VERSION = process.env.GAUZY_GITHUB_API_VERSION || '2022-11-28'; // Define a default version export interface OctokitResponse { data: T; // The response data received from the GitHub API. @@ -14,23 +15,38 @@ export interface OctokitResponse { @Injectable() export class OctokitService { - private readonly logger = new Logger('OctokitService'); - /** */ - private readonly app: App; + private readonly logger = new Logger('OctokitService'); + private readonly app: InstanceType | undefined; constructor( @Inject(ModuleProviders.ProbotConfig) private readonly config: ProbotConfig ) { - if (this.config) { - this.app = new App({ - appId: this.config.appId, - privateKey: this.config.privateKey, - }); + /** */ + try { + if (this.config.appId && this.config.privateKey) { + this.app = new App({ + appId: this.config.appId, + privateKey: this.config.privateKey, + }); + console.log(chalk.green(`Octokit App successfully initialized.`)); + } else { + console.error(chalk.red(`Octokit App initialization failed: Missing appId or privateKey.`)); + } + } catch (error) { + console.error(chalk.red(`Octokit App initialization failed: ${error.message}`)); } } + /** + * + * @returns + */ + getApp(): InstanceType | undefined { + return this.app; + } + /** * Get GitHub metadata for a specific installation. * @@ -39,6 +55,9 @@ export class OctokitService { * @throws {Error} If the request to fetch metadata fails. */ public async getGithubInstallationMetadata(installation_id: number): Promise> { + if (!this.app) { + throw new Error('Octokit instance is not available.'); + } try { // Get an Octokit instance for the installation const octokit = await this.app.getInstallationOctokit(installation_id); @@ -64,6 +83,9 @@ export class OctokitService { * @throws {Error} If the request to fetch repositories fails. */ public async getGithubRepositories(installation_id: number): Promise> { + if (!this.app) { + throw new Error('Octokit instance is not available.'); + } try { // Get an Octokit instance for the installation const octokit = await this.app.getInstallationOctokit(installation_id); @@ -98,6 +120,9 @@ export class OctokitService { owner: string; repo: string; }): Promise> { + if (!this.app) { + throw new Error('Octokit instance is not available.'); + } try { // Get an Octokit instance for the installation const octokit = await this.app.getInstallationOctokit(installation_id); @@ -115,66 +140,4 @@ export class OctokitService { throw new Error('Failed to fetch GitHub installation repository issues'); } } - - async openIssue( - title: string, - body: string, - owner: string, - repo: string, - installationId: number - ) { - const octokit = await this.app.getInstallationOctokit(installationId); - octokit - .request('POST /repos/{owner}/{repo}/issues', { - owner, - repo, - title, - body, - headers: { - 'X-GitHub-Api-Version': '2022-11-28', - }, - }) - .then((data) => { - console.log('data', data); - }) - .catch((error) => { - console.log('error', error); - }); - } - - async editIssue( - issueNumber: number, - title: string, - body: string, - repo: string, - owner: string, - installationId: number - ) { - const octokit = await this.app.getInstallationOctokit(installationId); - octokit - .request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', { - owner, - repo, - title, - body, - - issue_number: issueNumber, - - // TODO: - // pass dynamic values as required - // Add all the fields that we have - // Ex. - // labels: ['bug', 'GauzyAPI'], - - headers: { - 'X-GitHub-Api-Version': '2022-11-28', - }, - }) - .then((data) => { - console.log('data', data); - }) - .catch((error) => { - console.log('error', error); - }); - } } diff --git a/packages/plugins/integration-github/src/probot.discovery.ts b/packages/plugins/integration-github/src/probot.discovery.ts index eb29f1e9e42..9477168c7ac 100644 --- a/packages/plugins/integration-github/src/probot.discovery.ts +++ b/packages/plugins/integration-github/src/probot.discovery.ts @@ -11,6 +11,7 @@ import { import { Probot } from 'probot'; import SmeeClient from 'smee-client'; import * as _ from 'underscore'; +import * as chalk from 'chalk'; import { v4 } from 'uuid'; import { ModuleProviders, ProbotConfig } from './probot.types'; import { createProbot, createSmee } from './probot.helpers'; @@ -32,7 +33,17 @@ export class ProbotDiscovery implements OnModuleInit, OnApplicationBootstrap, On private readonly config: ProbotConfig ) { this.hooks = new Map(); - this.probot = createProbot(this.config); + /** */ + try { + if (this.config.appId && this.config.privateKey) { + this.probot = createProbot(this.config); + console.log(chalk.green(`Probot App successfully initialized.`)); + } else { + console.error(chalk.red(`Probot App initialization failed: Missing appId or privateKey.`)); + } + } catch (error) { + console.error(chalk.red(`Probot App initialization failed: ${error.message}`)); + } } /** @@ -73,6 +84,9 @@ export class ProbotDiscovery implements OnModuleInit, OnApplicationBootstrap, On * Initialize and mount event listeners for Probot hooks. */ mountHooks() { + if (!this.probot) { + return; + } this.probot .load((app: { on: (eventName: any, callback: (context: any) => Promise) => any; @@ -103,7 +117,6 @@ export class ProbotDiscovery implements OnModuleInit, OnApplicationBootstrap, On }; } - /** * Explore and analyze methods of instance wrappers (controllers and providers). */ @@ -195,6 +208,9 @@ export class ProbotDiscovery implements OnModuleInit, OnApplicationBootstrap, On * @returns A promise that resolves when the webhook is processed. */ public receiveHook(request: any) { + if (!this.probot) { + return; + } // Extract relevant information from the request const id = request.headers['x-github-delivery'] as string; const event = request.headers['x-github-event']; diff --git a/packages/plugins/integration-github/src/probot.helpers.ts b/packages/plugins/integration-github/src/probot.helpers.ts index 5cbd4b58a6b..da3dde942db 100644 --- a/packages/plugins/integration-github/src/probot.helpers.ts +++ b/packages/plugins/integration-github/src/probot.helpers.ts @@ -5,6 +5,8 @@ import { Octokit } from '@octokit/rest'; import { createAppAuth } from '@octokit/auth-app'; import { OctokitConfig, ProbotConfig } from './probot.types'; +const GITHUB_API_URL = 'https://api.github.com'; + /** * Parse and restructure Probot configuration into a more organized format. * @param config - Probot configuration. @@ -12,9 +14,9 @@ import { OctokitConfig, ProbotConfig } from './probot.types'; */ export const parseConfig = (config: ProbotConfig): Record => ({ appId: config.appId, - privateKey: getPrivateKey({ env: { PRIVATE_KEY: config.privateKey } }) as string, + privateKey: getPrivateKey({ env: { PRIVATE_KEY: config.privateKey ? config.privateKey.replace(/\\n/g, '\n') : '' } }) as string, webhookSecret: config.webhookSecret, - ghUrl: config.ghUrl || 'https://api.github.com', + ghUrl: config.ghUrl || GITHUB_API_URL, webhookProxy: config.webhookProxy, webhookPath: config.webhookPath, clientId: config.clientId,