Skip to content

Commit

Permalink
Merge pull request #612 from ganimtron-10/ticketing-linear
Browse files Browse the repository at this point in the history
Feat: Linear Integration (Ticketing)
  • Loading branch information
naelob authored Sep 3, 2024
2 parents dca151a + fe2a4f2 commit 5aa5b58
Show file tree
Hide file tree
Showing 31 changed files with 1,202 additions and 24 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ GITLAB_TICKETING_CLOUD_CLIENT_SECRET=
# Github
GITHUB_TICKETING_CLOUD_CLIENT_ID=
GITHUB_TICKETING_CLOUD_CLIENT_SECRET=
# Linear
LINEAR_TICKETING_CLOUD_CLIENT_ID=
LINEAR_TICKETING_CLOUD_CLIENT_SECRET=

# ================================================
# File Storage
# ================================================
Expand Down
1 change: 1 addition & 0 deletions packages/api/scripts/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,7 @@ CREATE TABLE connector_sets
ecom_shopify boolean NULL,
ecom_amazon boolean NULL,
ecom_squarespace boolean NULL,
tcg_linear boolean NULL,
ats_ashby boolean NULL,
ecom_webflow boolean NULL,
crm_microsoftdynamicssales boolean NULL,
Expand Down
8 changes: 4 additions & 4 deletions packages/api/scripts/seed.sql
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
INSERT INTO users (id_user, identification_strategy, email, password_hash, first_name, last_name) VALUES
('0ce39030-2901-4c56-8db0-5e326182ec6b', 'b2c','[email protected]', '$2b$10$Y7Q8TWGyGuc5ecdIASbBsuXMo3q/Rs3/cnY.mLZP4tUgfGUOCUBlG', 'local', 'Panora');

INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, fs_box, tcg_github, hris_deel, hris_sage, ats_ashby, crm_microsoftdynamicssales, ecom_webflow) VALUES
('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE),
('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE),
('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, fs_box, tcg_github, hris_deel, hris_sage, ats_ashby, crm_microsoftdynamicssales, ecom_webflow, tcg_linear) VALUES
('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE),
('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE),
('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);

INSERT INTO projects (id_project, name, sync_mode, id_user, id_connector_set) VALUES
('1e468c15-aa57-4448-aa2b-7fed640d1e3d', 'Project 1', 'pull', '0ce39030-2901-4c56-8db0-5e326182ec6b', '1709da40-17f7-4d3a-93a0-96dc5da6ddd7'),
Expand Down
37 changes: 25 additions & 12 deletions packages/api/src/@core/utils/types/original/original.ticketing.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
import { LinearTicketInput, LinearTicketOutput } from '@ticketing/ticket/services/linear/types';

import { LinearCommentInput, LinearCommentOutput } from '@ticketing/comment/services/linear/types';

import { LinearCollectionInput, LinearCollectionOutput } from '@ticketing/collection/services/linear/types';

import { LinearTagInput, LinearTagOutput } from '@ticketing/tag/services/linear/types';

import { LinearTeamInput, LinearTeamOutput } from '@ticketing/team/services/linear/types';

import { LinearUserInput, LinearUserOutput } from '@ticketing/user/services/linear/types';

import { GithubCollectionInput, GithubCollectionOutput } from '@ticketing/collection/services/github/types';

import { GithubCommentInput, GithubCommentOutput } from '@ticketing/comment/services/github/types';
Expand All @@ -9,6 +21,7 @@ import { GithubTeamInput, GithubTeamOutput } from '@ticketing/team/services/gith
import { GithubTicketInput, GithubTicketOutput } from '@ticketing/ticket/services/github/types';

import { GithubUserInput, GithubUserOutput } from '@ticketing/user/services/github/types';

import { GitlabUserInput, GitlabUserOutput } from '@ticketing/user/services/gitlab/types';

import {
Expand Down Expand Up @@ -144,7 +157,7 @@ export type OriginalTicketInput =
| FrontTicketInput
| GorgiasTicketInput
| JiraTicketInput
| GitlabTicketInput | GithubTicketInput;
| GitlabTicketInput | GithubTicketInput | LinearTicketInput;
//| JiraServiceMgmtTicketInput;

/* comment */
Expand All @@ -153,14 +166,14 @@ export type OriginalCommentInput =
| FrontCommentInput
| GorgiasCommentInput
| JiraCommentInput
| GitlabCommentInput | GithubCommentInput;
| GitlabCommentInput | GithubCommentInput | LinearCommentInput;
//| JiraCommentServiceMgmtInput;
/* user */
export type OriginalUserInput =
| ZendeskUserInput
| FrontUserInput
| GorgiasUserInput
| JiraUserInput | GithubUserInput | GitlabUserInput;
| JiraUserInput | GithubUserInput | GitlabUserInput | LinearUserInput;
//| JiraServiceMgmtUserInput;
/* account */
export type OriginalAccountInput = ZendeskAccountInput | FrontAccountInput;
Expand All @@ -176,20 +189,20 @@ export type OriginalTagInput =
| FrontTagInput
| GorgiasTagInput
| JiraTagInput
| GitlabTagInput | GithubTagInput;
| GitlabTagInput | GithubTagInput | LinearTagInput;

/* team */
export type OriginalTeamInput =
| ZendeskTeamInput
| FrontTeamInput
| GorgiasTeamInput
| JiraTeamInput | GithubTeamInput;
| JiraTeamInput | GithubTeamInput | LinearTeamInput;

/* attachment */
export type OriginalAttachmentInput = null;
export type OriginalCollectionInput =
| JiraCollectionInput
| GitlabCollectionInput | GithubCollectionInput;
| GitlabCollectionInput | GithubCollectionInput | LinearCollectionInput;

export type TicketingObjectInput =
| OriginalTicketInput
Expand All @@ -210,21 +223,21 @@ export type OriginalTicketOutput =
| FrontTicketOutput
| GorgiasTicketOutput
| JiraTicketOutput
| GitlabTicketOutput | GithubTicketOutput;
| GitlabTicketOutput | GithubTicketOutput | LinearTicketOutput;

/* comment */
export type OriginalCommentOutput =
| ZendeskCommentOutput
| FrontCommentOutput
| GorgiasCommentOutput
| JiraCommentOutput
| GitlabCommentOutput | GithubCommentOutput;
| GitlabCommentOutput | GithubCommentOutput | LinearCommentOutput;
/* user */
export type OriginalUserOutput =
| ZendeskUserOutput
| FrontUserOutput
| GorgiasUserOutput
| JiraUserOutput | GithubUserOutput | GitlabUserOutput;
| JiraUserOutput | GithubUserOutput | GitlabUserOutput | LinearUserOutput;
/* account */
export type OriginalAccountOutput = ZendeskAccountOutput | FrontAccountOutput;
/* contact */
Expand All @@ -239,14 +252,14 @@ export type OriginalTagOutput =
| FrontTagOutput
| GorgiasTagOutput
| JiraTagOutput
| GitlabTagOutput | GithubTagOutput;
| GitlabTagOutput | GithubTagOutput | LinearTagOutput;

/* team */
export type OriginalTeamOutput =
| ZendeskTeamOutput
| FrontTeamOutput
| GorgiasTeamOutput
| JiraTeamOutput | GithubTeamOutput;
| JiraTeamOutput | GithubTeamOutput | LinearTeamOutput;

/* attachment */
export type OriginalAttachmentOutput =
Expand All @@ -259,7 +272,7 @@ export type OriginalAttachmentOutput =

export type OriginalCollectionOutput =
| JiraCollectionOutput
| GitlabCollectionOutput | GithubCollectionOutput;
| GitlabCollectionOutput | GithubCollectionOutput | LinearCollectionOutput;

export type TicketingObjectOutput =
| OriginalTicketOutput
Expand Down
16 changes: 15 additions & 1 deletion packages/api/src/ticketing/@lib/@utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as fs from 'fs';

@Injectable()
export class Utils {
constructor(private readonly prisma: PrismaService) {}
constructor(private readonly prisma: PrismaService) { }

async fetchFileStreamFromURL(file_url: string) {
return fs.createReadStream(file_url);
Expand All @@ -27,6 +27,20 @@ export class Utils {
}
}

async getTeamRemoteIdFromUuid(uuid: string) {
try {
const res = await this.prisma.tcg_teams.findFirst({
where: {
id_tcg_team: uuid,
},
});
if (!res) return undefined;
return res.remote_id;
} catch (error) {
throw error;
}
}

async getRemoteIdFromTagName(name: string, connection_id: string) {
try {
const res = await this.prisma.tcg_tags.findFirst({
Expand Down
6 changes: 5 additions & 1 deletion packages/api/src/ticketing/collection/collection.module.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { LinearCollectionMapper } from './services/linear/mappers';
import { LinearService } from './services/linear';
import { GithubCollectionMapper } from './services/github/mappers';
import { GithubService } from './services/github';
import { EncryptionService } from '@@core/@core-services/encryption/encryption.service';
Expand Down Expand Up @@ -39,7 +41,9 @@ import { IngestDataService } from '@@core/@core-services/unification/ingest-data
GitlabCollectionMapper,
GithubService,
GithubCollectionMapper,
LinearService,
LinearCollectionMapper,
],
exports: [SyncService, ServiceRegistry, WebhookService],
})
export class CollectionModule {}
export class CollectionModule { }
65 changes: 65 additions & 0 deletions packages/api/src/ticketing/collection/services/linear/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Injectable } from '@nestjs/common';
import { LoggerService } from '@@core/@core-services/logger/logger.service';
import { PrismaService } from '@@core/@core-services/prisma/prisma.service';
import { EncryptionService } from '@@core/@core-services/encryption/encryption.service';
import { TicketingObject } from '@ticketing/@lib/@types';
import { ApiResponse } from '@@core/utils/types';
import axios from 'axios';
import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors';
import { ServiceRegistry } from '../registry.service';
import { ICollectionService } from '@ticketing/collection/types';
import { LinearCollectionOutput, LinearCollectionInput } from './types';
import { SyncParam } from '@@core/utils/types/interface';

@Injectable()
export class LinearService implements ICollectionService {
constructor(
private prisma: PrismaService,
private logger: LoggerService,
private cryptoService: EncryptionService,
private registry: ServiceRegistry,
) {
this.logger.setContext(
TicketingObject.collection.toUpperCase() + ':' + LinearService.name,
);
this.registry.registerService('linear', this);
}

async sync(data: SyncParam): Promise<ApiResponse<LinearCollectionOutput[]>> {
try {
const { linkedUserId } = data;

const connection = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
provider_slug: 'linear',
vertical: 'ticketing',
},
});

const projectQuery = {
"query": "query { projects { nodes { id, name, description } }}"
};

let resp = await axios.post(
`${connection.account_url}`,
projectQuery, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.cryptoService.decrypt(
connection.access_token,
)}`,
},
});
this.logger.log(`Synced linear collections !`);

return {
data: resp.data.data.projects.nodes,
message: 'Linear collections retrieved',
statusCode: 200,
};
} catch (error) {
throw error;
}
}
}
69 changes: 69 additions & 0 deletions packages/api/src/ticketing/collection/services/linear/mappers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { ICollectionMapper } from '@ticketing/collection/types';
import { LinearCollectionInput, LinearCollectionOutput } from './types';
import {
UnifiedTicketingCollectionInput,
UnifiedTicketingCollectionOutput,
} from '@ticketing/collection/types/model.unified';
import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry';
import { Injectable } from '@nestjs/common';
import { Utils } from '@ticketing/@lib/@utils';

@Injectable()
export class LinearCollectionMapper implements ICollectionMapper {
constructor(private mappersRegistry: MappersRegistry, private utils: Utils) {
this.mappersRegistry.registerService(
'ticketing',
'collection',
'linear',
this,
);
}
desunify(
source: UnifiedTicketingCollectionInput,
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): LinearCollectionInput {
return;
}

unify(
source: LinearCollectionOutput | LinearCollectionOutput[],
connectionId: string,
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): UnifiedTicketingCollectionOutput | UnifiedTicketingCollectionOutput[] {
// If the source is not an array, convert it to an array for mapping
const sourcesArray = Array.isArray(source) ? source : [source];

return sourcesArray.map((collection) =>
this.mapSingleCollectionToUnified(
collection,
connectionId,
customFieldMappings,
),
);
}

private mapSingleCollectionToUnified(
collection: LinearCollectionOutput,
connectionId: string,
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): UnifiedTicketingCollectionOutput {
const unifiedCollection: UnifiedTicketingCollectionOutput = {
remote_id: collection.id,
remote_data: collection,
name: collection.name,
description: collection.description,
collection_type: 'PROJECT',
};

return unifiedCollection;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
interface LinearCollection {
id: string
name: string
description: string
}

export type LinearCollectionInput = Partial<LinearCollection>;
export type LinearCollectionOutput = LinearCollectionInput;
6 changes: 5 additions & 1 deletion packages/api/src/ticketing/comment/comment.module.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { LinearCommentMapper } from './services/linear/mappers';
import { LinearService } from './services/linear';
import { GithubCommentMapper } from './services/github/mappers';
import { GithubService } from './services/github';
import { LoggerService } from '@@core/@core-services/logger/logger.service';
Expand Down Expand Up @@ -45,7 +47,9 @@ import { SyncService } from './sync/sync.service';
GitlabCommentMapper,
GithubService,
GithubCommentMapper,
LinearService,
LinearCommentMapper,
],
exports: [SyncService, ServiceRegistry, WebhookService],
})
export class CommentModule {}
export class CommentModule { }
Loading

0 comments on commit 5aa5b58

Please sign in to comment.