From dbf61c46fe19ec7f2ef468543e56ace0bdbee263 Mon Sep 17 00:00:00 2001 From: Irwansyah Date: Sat, 31 Aug 2024 17:51:34 +0700 Subject: [PATCH 1/2] feat: add bamboohr user/services --- .../src/ats/user/services/bamboohr/index.ts | 58 ++++++++++++ .../src/ats/user/services/bamboohr/mappers.ts | 94 +++++++++++++++++++ .../src/ats/user/services/bamboohr/types.ts | 11 +++ 3 files changed, 163 insertions(+) create mode 100644 packages/api/src/ats/user/services/bamboohr/index.ts create mode 100644 packages/api/src/ats/user/services/bamboohr/mappers.ts create mode 100644 packages/api/src/ats/user/services/bamboohr/types.ts diff --git a/packages/api/src/ats/user/services/bamboohr/index.ts b/packages/api/src/ats/user/services/bamboohr/index.ts new file mode 100644 index 000000000..ef4d773cc --- /dev/null +++ b/packages/api/src/ats/user/services/bamboohr/index.ts @@ -0,0 +1,58 @@ +import { Injectable } from '@nestjs/common'; +import { IUserService } from '@ats/user/types'; +import { AtsObject } from '@ats/@lib/@types'; +import axios from 'axios'; +import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; +import { LoggerService } from '@@core/@core-services/logger/logger.service'; +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; +import { ApiResponse } from '@@core/utils/types'; +import { ServiceRegistry } from '../registry.service'; +import { SyncParam } from '@@core/utils/types/interface'; +import { BamboohrUserOutput } from './types'; + +@Injectable() +export class BamboohrService implements IUserService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + AtsObject.user.toUpperCase() + ':' + BamboohrService.name, + ); + this.registry.registerService('bamboohr', this); + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'bamboohr', + vertical: 'ats', + }, + }); + const resp = await axios.post(`${connection.account_url}/user.list`, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${Buffer.from( + `${this.cryptoService.decrypt(connection.access_token)}:`, + ).toString('base64')}`, + }, + }); + const users: BamboohrUserOutput[] = resp.data.results; + this.logger.log(`Synced bamboohr users !`); + + return { + data: users, + message: 'Bamboohr users retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/ats/user/services/bamboohr/mappers.ts b/packages/api/src/ats/user/services/bamboohr/mappers.ts new file mode 100644 index 000000000..88b8c3f4a --- /dev/null +++ b/packages/api/src/ats/user/services/bamboohr/mappers.ts @@ -0,0 +1,94 @@ +import { + UnifiedAtsUserInput, + UnifiedAtsUserOutput, + UserAccessRole, +} from '@ats/user/types/model.unified'; +import { IUserMapper } from '@ats/user/types'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { Injectable } from '@nestjs/common'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { Utils } from '@ats/@lib/@utils'; +import { BamboohrUserInput, BamboohrUserOutput } from './types'; + +@Injectable() +export class BamboohrUserMapper implements IUserMapper { + constructor( + private mappersRegistry: MappersRegistry, + private utils: Utils, + private coreUnificationService: CoreUnification, + ) { + this.mappersRegistry.registerService('ats', 'user', 'bamboohr', this); + } + + mapToUserAccessRole( + data: + | 'Organization Admin' + | 'Elevated Access' + | 'Limited Access' + | 'External Recruiter', + ): UserAccessRole | string { + switch (data) { + case 'Organization Admin': + return 'SUPER_ADMIN'; + case 'Elevated Access': + return 'ADMIN'; + case 'Limited Access': + return 'LIMITED_TEAM_MEMBER'; + case 'External Recruiter': + return 'INTERVIEWER'; + } + } + + async desunify( + source: UnifiedAtsUserInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + return; + } + + async unify( + source: BamboohrUserOutput | BamboohrUserOutput[], + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return await this.mapSingleUserToUnified( + source, + connectionId, + customFieldMappings, + ); + } + // Handling array of BamboohrUserOutput + return Promise.all( + source.map((user) => + this.mapSingleUserToUnified(user, connectionId, customFieldMappings), + ), + ); + } + + private async mapSingleUserToUnified( + user: BamboohrUserOutput, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + return { + remote_id: user.id, + remote_data: user, + first_name: user.firstName || null, + last_name: user.lastName || null, + email: user.email || null, + disabled: user.isEnabled || null, + access_role: this.mapToUserAccessRole(user.globalRole as any), + remote_modified_at: user.updatedAt || null, + }; + } +} diff --git a/packages/api/src/ats/user/services/bamboohr/types.ts b/packages/api/src/ats/user/services/bamboohr/types.ts new file mode 100644 index 000000000..ac8257681 --- /dev/null +++ b/packages/api/src/ats/user/services/bamboohr/types.ts @@ -0,0 +1,11 @@ +export interface BamboohrUserInput { + id: string; + firstName: string; + lastName: string; + email: string; + globalRole: string; + isEnabled: boolean; + updatedAt: string; +} + +export type BamboohrUserOutput = Partial; From 57b5a5daed04cd155fb7ce9ec5bed9ea94515ec0 Mon Sep 17 00:00:00 2001 From: Irwansyah Date: Mon, 2 Sep 2024 12:42:47 +0700 Subject: [PATCH 2/2] fix: failed to create ats user --- .../api/src/ats/user/sync/sync.service.ts | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/api/src/ats/user/sync/sync.service.ts b/packages/api/src/ats/user/sync/sync.service.ts index 8d2061c85..16a83946c 100644 --- a/packages/api/src/ats/user/sync/sync.service.ts +++ b/packages/api/src/ats/user/sync/sync.service.ts @@ -7,13 +7,13 @@ import { FieldMappingService } from '@@core/field-mapping/field-mapping.service' import { ServiceRegistry } from '../services/registry.service'; import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { ApiResponse } from '@@core/utils/types'; + import { IUserService } from '../types'; import { OriginalUserOutput } from '@@core/utils/types/original/original.ats'; import { UnifiedAtsUserOutput } from '../types/model.unified'; import { ats_users as AtsUser } from '@prisma/client'; import { ATS_PROVIDERS } from '@panora/shared'; -import { AtsObject } from '@ats/@lib/@types'; + import { BullQueueService } from '@@core/@core-services/queues/shared.service'; import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; @@ -50,12 +50,12 @@ export class SyncService implements OnModuleInit, IBaseSync { this.logger.log('Syncing users...'); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -102,7 +102,9 @@ export class SyncService implements OnModuleInit, IBaseSync { const service: IUserService = this.serviceRegistry.getService(integrationId); if (!service) { - this.logger.log(`No service found in {vertical:ats, commonObject: user} for integration ID: ${integrationId}`); + this.logger.log( + `No service found in {vertical:ats, commonObject: user} for integration ID: ${integrationId}`, + ); return; } @@ -133,7 +135,6 @@ export class SyncService implements OnModuleInit, IBaseSync { const existingUser = await this.prisma.ats_users.findFirst({ where: { remote_id: originId, - }, }); @@ -162,7 +163,7 @@ export class SyncService implements OnModuleInit, IBaseSync { id_ats_user: uuidv4(), created_at: new Date(), remote_id: originId, - + id_connection: connection_id, }, }); }