diff --git a/.env.example b/.env.example index 85cc0f535..8760d042c 100644 --- a/.env.example +++ b/.env.example @@ -128,19 +128,6 @@ SHAREPOINT_FILESTORAGE_CLOUD_CLIENT_SECRET= DROPBOX_FILESTORAGE_CLOUD_CLIENT_ID= DROPBOX_FILESTORAGE_CLOUD_CLIENT_SECRET= -# ================================================ -# HRIS -# ================================================ -# Deel -DEEL_HRIS_CLOUD_CLIENT_ID= -DEEL_HRIS_CLOUD_CLIENT_SECRET= -# Sage -SAGE_HRIS_CLOUD_CLIENT_ID= -SAGE_HRIS_CLOUD_CLIENT_SECRET= -# Gusto -GUSTO_HRIS_CLOUD_CLIENT_ID= -GUSTO_HRIS_CLOUD_CLIENT_SECRET= - # ================================================ # ECOMMERCE diff --git a/.github/workflows/codesee-arch-diagram.yml b/.github/workflows/codesee-arch-diagram.yml deleted file mode 100644 index 806d41d12..000000000 --- a/.github/workflows/codesee-arch-diagram.yml +++ /dev/null @@ -1,23 +0,0 @@ -# This workflow was added by CodeSee. Learn more at https://codesee.io/ -# This is v2.0 of this workflow file -on: - push: - branches: - - main - pull_request_target: - types: [opened, synchronize, reopened] - -name: CodeSee - -permissions: read-all - -jobs: - codesee: - runs-on: ubuntu-latest - continue-on-error: true - name: Analyze the repo with CodeSee - steps: - - uses: Codesee-io/codesee-action@v2 - with: - codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }} - codesee-url: https://app.codesee.io diff --git a/README.md b/README.md index 8d38bc80a..c7ad1b930 100644 --- a/README.md +++ b/README.md @@ -83,18 +83,6 @@ Panora supports integration with the following objects across multiple platforms | Gitlab | ✔ | ✔ | ✔ | | | | | ✔| | Github | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔| | -### ATS Unified API (New!) - -| | Activities | Applications | Candidates | Departments | Interviews | Jobs | Offers | Offices | Scorecard | Users | Eeocs | Job Interview Stage | Tags | Reject Reasons | -|-------------|:----------:|:------------:|:----------:|:-----------:|:----------:|:----:|:------:|:-------:|:---------:|:-----:|:-------:|:-------:|:-------:|:-------:| -| Ashby | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | ✔ | | ✔| ✔| | - -### HRIS Unified API (New!) - -| | Bankinfos | Benefits | Companies | Dependents | Employee | Employee Payroll Runs | Employer Benefits | Employments | Groups | Locations | Paygroups | Payrollrun | Timeoff | Timeoff Balances | Timesheet Entries | -|-------------|:----------:|:------------:|:----------:|:-----------:|:----------:|:----:|:------:|:-------:|:---------:|:-----:|:-----:|:-----:|:-----:|:-----:| :-----:| -| Gusto | | ✔ | ✔ | | ✔ | | ✔ | ✔ | ✔ | ✔ | | | | | | - ### File Storage Unified API | | Drives | Files | Folders | Groups | Users | Permissions | Shared Links | @@ -118,102 +106,7 @@ Your favourite software is missing? [Ask the community to build a connector!](ht # 🚢 Roadmap -## 🧠 Retrieval Engine for RAG - -- [x] Access and manage data from any source, including documents, chunk & vectors -- [x] Semantic, keyword and hybrid search against a vector database - -## 🪄 Integrations Coming Soon - -#### CRM - -- [x] Microsoft Dynamics 365 -- [x] Linear -- [x] Redtail CRM -- [x] Wealthbox -- [x] Leadsquared -- [x] Salesforce -- [ ] Affinity CRM -- [ ] Odoo -- [ ] Intelliflow -- [ ] Xplan -- [ ] Plannr -- [ ] ACT! -- [ ] Jungo -- [ ] Surefire -- [ ] Velocity - -#### Ticketing - -- [ ] Service Now -- [ ] Wrike -- [ ] Dixa -- [ ] Asana -- [ ] Aha -- [ ] Clickup - -#### Accounting - -- [ ] Wave Financial -- [ ] Xero -- [ ] Quickbooks - -#### File Storage - -- [x] Google Drive -- [x] Dropbox -- [x] Sharepoint -- [x] One Drive - -#### Productivity - -- [ ] Slack -- [ ] Notion - -#### HRIS - -- [ ] Workday -- [ ] ADP Workforce -- [x] Sage -- [x] Deel -- [ ] BambooHR -- [ ] Rippling - -#### Ecommerce - -- [ ] Ebay -- [ ] Faire -- [x] Webflow -- [ ] Mercado Libre -- [ ] Prestashop -- [ ] Magento -- [ ] BigCommerce - -#### ATS - -- [ ] Greenhouse -- [ ] Lever -- [ ] Avature - -#### Cybersecurity - -- [ ] Snyk -- [ ] Qualys -- [ ] Crowdstrike -- [ ] Semgrep -- [ ] Rapids7InsightVm -- [ ] Tenable -- [ ] SentinelOne -- [ ] Microsoft Defender - -#### Legacy Softwares - -- [ ] Netsuite (Accounting) -- [ ] SAP (ERP) -- [ ] Ariba -- [ ] Concur -- [ ] Magaya (TMS) -- [ ] Cargowise (TMS) +See our [project roadmap here !](https://github.com/orgs/panoratech/projects/5) # 👾 Join the community diff --git a/apps/magic-link/src/lib/ProviderModal.tsx b/apps/magic-link/src/lib/ProviderModal.tsx index 54d03011b..c7696011f 100644 --- a/apps/magic-link/src/lib/ProviderModal.tsx +++ b/apps/magic-link/src/lib/ProviderModal.tsx @@ -269,10 +269,6 @@ const ProviderModal = () => { return "File Storage"; case "crm": return "CRM"; - case "ats": - return "ATS"; - case "hris": - return "HRIS"; default: return vertical.substring(0,1).toUpperCase() + vertical.substring(1) } diff --git a/apps/webapp/src/app/(Dashboard)/configuration/page.tsx b/apps/webapp/src/app/(Dashboard)/configuration/page.tsx index c5b24c01c..106c1d1e6 100644 --- a/apps/webapp/src/app/(Dashboard)/configuration/page.tsx +++ b/apps/webapp/src/app/(Dashboard)/configuration/page.tsx @@ -109,7 +109,7 @@ export default function Page() { data_type: mapping.data_type, })) - const VERTICALS = verticals.filter((vertical) => !["marketingautomation", "cybersecurity", "productivity"].includes(vertical)); + const VERTICALS = verticals.filter((vertical) => !["marketingautomation", "productivity"].includes(vertical)); useEffect(() => { if (pullFrequencies) { diff --git a/apps/webapp/src/components/Configuration/Connector/ConnectorLayout.tsx b/apps/webapp/src/components/Configuration/Connector/ConnectorLayout.tsx index 44553fa35..5fbfaa7dd 100644 --- a/apps/webapp/src/components/Configuration/Connector/ConnectorLayout.tsx +++ b/apps/webapp/src/components/Configuration/Connector/ConnectorLayout.tsx @@ -46,7 +46,7 @@ export function ConnectorLayout({

Connectors

-

By default, all connectors use Panora managed credentials. You are free to edit them by creating your custom developer apps inside your favorite softwares !

+

By default, all connectors use Panora managed credentials. You can edit settings to use your own credentials on this screen.

diff --git a/apps/webapp/src/components/Configuration/Connector/VerticalSelector.tsx b/apps/webapp/src/components/Configuration/Connector/VerticalSelector.tsx index b709fbd59..9cb36b6f1 100644 --- a/apps/webapp/src/components/Configuration/Connector/VerticalSelector.tsx +++ b/apps/webapp/src/components/Configuration/Connector/VerticalSelector.tsx @@ -33,17 +33,17 @@ export function VerticalSelector({ onSelectVertical }: { onSelectVertical: (vert - + No verticals found. { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers['Authorization'] = `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`; - - config.headers = { - ...config.headers, - ...headers, - }; - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'ats.ashby.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: BasicAuthCallbackParams) { - try { - const { linkedUserId, projectId, body } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - - let db_res; - const connection_token = uuidv4(); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(body.api_key), - account_url: CONNECTORS_METADATA['ats']['ashby'].urls - .apiUrl as string, - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'ashby', - vertical: 'ats', - token_type: 'basic', - account_url: CONNECTORS_METADATA['ats']['ashby'].urls - .apiUrl as string, - access_token: this.cryptoService.encrypt(body.api_key), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - // Provide a blank implementation for handleTokenRefresh - async handleTokenRefresh(params: RefreshParams): Promise { - return Promise.resolve(); - } -} diff --git a/packages/api/src/@core/connections/ats/services/ats.connection.service.ts b/packages/api/src/@core/connections/ats/services/ats.connection.service.ts deleted file mode 100644 index 946f44ff9..000000000 --- a/packages/api/src/@core/connections/ats/services/ats.connection.service.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { - CallbackParams, - IConnectionCategory, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { Injectable } from '@nestjs/common'; -import { connections as Connection } from '@prisma/client'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from './registry.service'; -import { CategoryConnectionRegistry } from '@@core/@core-services/registries/connections-categories.registry'; -import { PassthroughResponse } from '@@core/passthrough/types'; - -@Injectable() -export class AtsConnectionsService implements IConnectionCategory { - constructor( - private serviceRegistry: ServiceRegistry, - private connectionCategoryRegistry: CategoryConnectionRegistry, - private webhook: WebhookService, - private logger: LoggerService, - private prisma: PrismaService, - ) { - this.logger.setContext(AtsConnectionsService.name); - this.connectionCategoryRegistry.registerService('ats', this); - } - //STEP 1:[FRONTEND STEP] - //create a frontend SDK snippet in which an authorization embedded link is set up so when users click - // on it to grant access => they grant US the access and then when confirmed - /*const authUrl = - 'https://app.hubspot.com/oauth/authorize' + - `?client_id=${encodeURIComponent(CLIENT_ID)}` + - `&scope=${encodeURIComponent(SCOPES)}` + - `&redirect_uri=${encodeURIComponent(REDIRECT_URI)}`;*/ //oauth/callback - - // oauth server calls this redirect callback - // WE WOULD HAVE CREATED A DEV ACCOUNT IN THE 5 CRMs (Panora dev account) - // we catch the tmp token and swap it against oauth2 server for access/refresh tokens - // to perform actions on his behalf - // this call pass 1. integrationID 2. CustomerId 3. Panora Api Key - async handleCallBack( - providerName: string, - callbackOpts: CallbackParams, - type_strategy: 'oauth2' | 'apikey' | 'basic', - ) { - try { - const serviceName = providerName.toLowerCase(); - - const service = this.serviceRegistry.getService(serviceName); - - if (!service) { - throw new ReferenceError(`Unknown provider, found ${providerName}`); - } - const data: Connection = await service.handleCallback(callbackOpts); - const event = await this.prisma.events.create({ - data: { - id_connection: data.id_connection, - id_project: data.id_project, - id_event: uuidv4(), - status: 'success', - type: 'connection.created', - method: 'GET', - url: `/${type_strategy}/callback`, - provider: providerName.toLowerCase(), - direction: '0', - timestamp: new Date(), - id_linked_user: callbackOpts.linkedUserId, - }, - }); - //directly send the webhook - await this.webhook.dispatchWebhook( - data, - 'connection.created', - callbackOpts.projectId, - event.id_event, - ); - } catch (error) { - throw error; - } - } - - async handleTokensRefresh( - connectionId: string, - providerName: string, - refresh_token: string, - id_project: string, - account_url?: string, - ) { - try { - const serviceName = providerName.toLowerCase(); - const service = this.serviceRegistry.getService(serviceName); - if (!service) { - throw new ReferenceError(`Unknown provider, found ${providerName}`); - } - const refreshOpts: RefreshParams = { - connectionId: connectionId, - refreshToken: refresh_token, - account_url: account_url, - projectId: id_project, - }; - await service.handleTokenRefresh(refreshOpts); - } catch (error) { - throw error; - } - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - const serviceName = connection.provider_slug.toLowerCase(); - const service = this.serviceRegistry.getService(serviceName); - if (!service) { - throw new ReferenceError(`Unknown provider, found ${serviceName}`); - } - return await service.passthrough(input, connectionId); - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/ats/services/bamboohr/bamboohr.service.ts b/packages/api/src/@core/connections/ats/services/bamboohr/bamboohr.service.ts deleted file mode 100644 index 3f0df3d4e..000000000 --- a/packages/api/src/@core/connections/ats/services/bamboohr/bamboohr.service.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - DynamicApiUrl, - providerToType, -} from '@panora/shared'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -export type BamboohrOAuthResponse = { - access_token: string; - token_type: string; - expires_in: number; - scope: string; - id_token: string; -}; - -@Injectable() -export class BamboohrConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - private env: EnvironmentService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private cService: ConnectionsStrategiesService, - private connectionUtils: ConnectionUtils, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(BamboohrConnectionService.name); - this.registry.registerService('bamboohr', this); - this.type = providerToType('bamboohr', 'ats', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers['Authorization'] = `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`; - - config.headers = { - ...config.headers, - ...headers, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'ats.bamboohr.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, body } = opts; - const { api_key, subdomain } = body; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'bamboohr', - vertical: 'ats', - }, - }); - - let db_res; - const connection_token = uuidv4(); - const BASE_API_URL = ( - CONNECTORS_METADATA['ats']['bamboohr'].urls.apiUrl as DynamicApiUrl - )(subdomain); - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(api_key), - account_url: BASE_API_URL, - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'bamboohr', - vertical: 'ats', - token_type: 'basic', - account_url: BASE_API_URL, - access_token: this.cryptoService.encrypt(api_key), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - - handleTokenRefresh?(opts: RefreshParams): Promise { - return Promise.resolve(); - } -} diff --git a/packages/api/src/@core/connections/ats/services/greenhouse/greenhouse.service.ts b/packages/api/src/@core/connections/ats/services/greenhouse/greenhouse.service.ts deleted file mode 100644 index 5db40c157..000000000 --- a/packages/api/src/@core/connections/ats/services/greenhouse/greenhouse.service.ts +++ /dev/null @@ -1,228 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - OAuth2AuthData, - providerToType, -} from '@panora/shared'; -import axios from 'axios'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -export type GreenhouseOAuthResponse = { - access_token: string; - refresh_token: string; - expires_in: number; - token_type: string; -}; - -@Injectable() -export class GreenhouseConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - private env: EnvironmentService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private cService: ConnectionsStrategiesService, - private connectionUtils: ConnectionUtils, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(GreenhouseConnectionService.name); - this.registry.registerService('greenhouse', this); - this.type = providerToType('greenhouse', 'ats', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers['Authorization'] = `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`; - - config.headers = { - ...config.headers, - ...headers, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'ats.greenhouse.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, code } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'greenhouse', - vertical: 'ats', - }, - }); - - const REDIRECT_URI = `${this.env.getPanoraBaseUrl()}/connections/oauth/callback`; - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - redirect_uri: REDIRECT_URI, - code: code, - grant_type: 'authorization_code', - }); - const res = await axios.post( - 'https://api.greenhouse.io/oauth/token', - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: GreenhouseOAuthResponse = res.data; - this.logger.log( - 'OAuth credentials : greenhouse ats ' + JSON.stringify(data), - ); - - let db_res; - const connection_token = uuidv4(); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - account_url: CONNECTORS_METADATA['ats']['greenhouse'].urls - .apiUrl as string, - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'greenhouse', - vertical: 'ats', - token_type: 'oauth2', - account_url: CONNECTORS_METADATA['ats']['greenhouse'].urls - .apiUrl as string, - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - - async handleTokenRefresh(opts: RefreshParams) { - try { - const { connectionId, refreshToken, projectId } = opts; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - grant_type: 'refresh_token', - refresh_token: this.cryptoService.decrypt(refreshToken), - }); - const res = await axios.post( - 'https://app.greenhouse.com/oauth/token', - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: GreenhouseOAuthResponse = res.data; - await this.prisma.connections.update({ - where: { - id_connection: connectionId, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - }, - }); - this.logger.log('OAuth credentials updated : greenhouse '); - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/ats/services/jobadder/jobadder.service.ts b/packages/api/src/@core/connections/ats/services/jobadder/jobadder.service.ts deleted file mode 100644 index 02e86d9c0..000000000 --- a/packages/api/src/@core/connections/ats/services/jobadder/jobadder.service.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { AuthStrategy, OAuth2AuthData, providerToType } from '@panora/shared'; -import axios from 'axios'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -export type JobadderOAuthResponse = { - access_token: string; - refresh_token: string; - expires_in: number; - token_type: string; - api: string; -}; - -@Injectable() -export class JobadderConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - private env: EnvironmentService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private cService: ConnectionsStrategiesService, - private connectionUtils: ConnectionUtils, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(JobadderConnectionService.name); - this.registry.registerService('jobadder', this); - this.type = providerToType('jobadder', 'ats', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers['Authorization'] = `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`; - - config.headers = { - ...config.headers, - ...headers, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'ats.jobadder.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, code } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'jobadder', - vertical: 'ats', - }, - }); - - const REDIRECT_URI = `${ - this.env.getPanoraBaseUrl() - }/connections/oauth/callback`; - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - redirect_uri: REDIRECT_URI, - code: code, - grant_type: 'authorization_code', - }); - const res = await axios.post( - 'https://id.jobadder.com/connect/token', - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: JobadderOAuthResponse = res.data; - this.logger.log( - 'OAuth credentials : jobadder ats ' + JSON.stringify(data), - ); - - let db_res; - const connection_token = uuidv4(); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - account_url: data.api, - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'jobadder', - vertical: 'ats', - token_type: 'oauth2', - account_url: data.api, - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - - async handleTokenRefresh(opts: RefreshParams) { - try { - const { connectionId, refreshToken, projectId } = opts; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - grant_type: 'refresh_token', - refresh_token: this.cryptoService.decrypt(refreshToken), - }); - const res = await axios.post( - 'https://id.jobadder.com/connect/token', - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: JobadderOAuthResponse = res.data; - await this.prisma.connections.update({ - where: { - id_connection: connectionId, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - }, - }); - this.logger.log('OAuth credentials updated : jobadder '); - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/ats/services/lever/lever.service.ts b/packages/api/src/@core/connections/ats/services/lever/lever.service.ts deleted file mode 100644 index 05fec6ba6..000000000 --- a/packages/api/src/@core/connections/ats/services/lever/lever.service.ts +++ /dev/null @@ -1,227 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - OAuth2AuthData, - providerToType, -} from '@panora/shared'; -import axios from 'axios'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -export type LeverOAuthResponse = { - access_token: string; - refresh_token: string; - expires_in: number; - token_type: string; -}; - -@Injectable() -export class LeverConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - private env: EnvironmentService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private cService: ConnectionsStrategiesService, - private connectionUtils: ConnectionUtils, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(LeverConnectionService.name); - this.registry.registerService('lever', this); - this.type = providerToType('lever', 'ats', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers['Authorization'] = `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`; - - config.headers = { - ...config.headers, - ...headers, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'ats.lever.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, code } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'lever', - vertical: 'ats', - }, - }); - - const REDIRECT_URI = `${ - this.env.getPanoraBaseUrl() - }/connections/oauth/callback`; - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - redirect_uri: REDIRECT_URI, - code: code, - grant_type: 'authorization_code', - }); - const res = await axios.post( - 'https://sandbox-lever.auth0.com/oauth/token', - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: LeverOAuthResponse = res.data; - this.logger.log('OAuth credentials : lever ats ' + JSON.stringify(data)); - - let db_res; - const connection_token = uuidv4(); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - account_url: CONNECTORS_METADATA['ats']['lever'].urls - .apiUrl as string, - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'lever', - vertical: 'ats', - token_type: 'oauth2', - account_url: CONNECTORS_METADATA['ats']['lever'].urls - .apiUrl as string, - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - - async handleTokenRefresh(opts: RefreshParams) { - try { - const { connectionId, refreshToken, projectId } = opts; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - grant_type: 'refresh_token', - refresh_token: this.cryptoService.decrypt(refreshToken), - }); - const res = await axios.post( - 'https://sandbox-lever.auth0.com/oauth/token', - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: LeverOAuthResponse = res.data; - await this.prisma.connections.update({ - where: { - id_connection: connectionId, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - }, - }); - this.logger.log('OAuth credentials updated : lever '); - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/ats/services/registry.service.ts b/packages/api/src/@core/connections/ats/services/registry.service.ts deleted file mode 100644 index de82a1fcb..000000000 --- a/packages/api/src/@core/connections/ats/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { IConnectionService } from '@@core/connections/@utils/types'; -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IConnectionService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IConnectionService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/@core/connections/ats/services/workday/workday.service.ts b/packages/api/src/@core/connections/ats/services/workday/workday.service.ts deleted file mode 100644 index 31ead4988..000000000 --- a/packages/api/src/@core/connections/ats/services/workday/workday.service.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - APIKeyCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { CONNECTORS_METADATA } from '@panora/shared'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -@Injectable() -export class WorkdayConnectionService extends AbstractBaseConnectionService { - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private connectionUtils: ConnectionUtils, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(WorkdayConnectionService.name); - this.registry.registerService('workday', this); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers['Authorization'] = `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`; - - config.headers = { - ...config.headers, - ...headers, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'ats.workday.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - handleTokenRefresh?(opts: RefreshParams): Promise { - return Promise.resolve(); - } - - async handleCallback(opts: APIKeyCallbackParams) { - try { - const { linkedUserId, projectId } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'workday', - vertical: 'ats', - }, - }); - - let db_res; - const connection_token = uuidv4(); - // construct a custom key as workday asks for 3 params (X-Api-Key, X-User-Token, X-User-Email) - // we simply use the string panoradelimiter to separate and encode easily - const data_to_encode = this.connectionUtils.applyPanoraDelimiter([ - opts.body.api_key, - opts.body.userToken, - opts.body.userEmail, - ]); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data_to_encode), - account_url: CONNECTORS_METADATA['ats']['workday'].urls - .apiUrl as string, - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'workday', - vertical: 'ats', - token_type: 'api_key', - account_url: CONNECTORS_METADATA['ats']['workday'].urls - .apiUrl as string, - access_token: this.cryptoService.encrypt(data_to_encode), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/connections.module.ts b/packages/api/src/@core/connections/connections.module.ts index 188d25c6c..6d0472af9 100644 --- a/packages/api/src/@core/connections/connections.module.ts +++ b/packages/api/src/@core/connections/connections.module.ts @@ -3,16 +3,13 @@ import { ValidateUserService } from '@@core/utils/services/validate-user.service import { Module } from '@nestjs/common'; import { OAuthTokenRefreshService } from './@token-refresh/refresh.service'; import { AccountingConnectionModule } from './accounting/accounting.connection.module'; -import { AtsConnectionModule } from './ats/ats.connection.module'; import { ConnectionsController } from './connections.controller'; import { CrmConnectionModule } from './crm/crm.connection.module'; import { FilestorageConnectionModule } from './filestorage/filestorage.connection.module'; -import { HrisConnectionModule } from './hris/hris.connection.module'; import { ProductivityConnectionsModule } from './productivity/productivity.connection.module'; import { MarketingAutomationConnectionsModule } from './marketingautomation/marketingautomation.connection.module'; import { TicketingConnectionModule } from './ticketing/ticketing.connection.module'; import { EcommerceConnectionModule } from './ecommerce/ecommerce.connection.module'; -import { CybersecurityConnectionsModule } from './cybersecurity/cybersecurity.connection.module'; @Module({ controllers: [ConnectionsController], @@ -21,12 +18,9 @@ import { CybersecurityConnectionsModule } from './cybersecurity/cybersecurity.co ProductivityConnectionsModule, TicketingConnectionModule, AccountingConnectionModule, - AtsConnectionModule, MarketingAutomationConnectionsModule, FilestorageConnectionModule, - HrisConnectionModule, EcommerceConnectionModule, - CybersecurityConnectionsModule, SyncModule, ], providers: [ValidateUserService, OAuthTokenRefreshService], @@ -36,13 +30,10 @@ import { CybersecurityConnectionsModule } from './cybersecurity/cybersecurity.co CrmConnectionModule, TicketingConnectionModule, AccountingConnectionModule, - AtsConnectionModule, MarketingAutomationConnectionsModule, FilestorageConnectionModule, EcommerceConnectionModule, - HrisConnectionModule, ProductivityConnectionsModule, - CybersecurityConnectionsModule, ], }) export class ConnectionsModule {} diff --git a/packages/api/src/@core/connections/cybersecurity/cybersecurity.connection.module.ts b/packages/api/src/@core/connections/cybersecurity/cybersecurity.connection.module.ts deleted file mode 100644 index 65508349f..000000000 --- a/packages/api/src/@core/connections/cybersecurity/cybersecurity.connection.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; -import { WebhookModule } from '@@core/@core-services/webhooks/panora-webhooks/webhook.module'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { Module } from '@nestjs/common'; -import { CybersecurityConnectionsService } from './services/cybersecurity.connection.service'; -import { ServiceRegistry } from './services/registry.service'; -import { TenableConnectionService } from './services/tenable/tenable.service'; -import { QualysConnectionService } from './services/qualys/qualys.service'; -import { SemgrepConnectionService } from './services/semgrep/semgrep.service'; -import { SentineloneConnectionService } from './services/sentinelone/sentinelone.service'; -import { Rapid7ConnectionService } from './services/rapid7insightvm/rapid7.service'; -import { SnykConnectionService } from './services/snyk/snyk.service'; -import { CrowdstrikeConnectionService } from './services/crowdstrike/crowdstrike.service'; -import { MicrosoftdefenderConnectionService } from './services/microsoftdefender/microsoftdefender.service'; - -@Module({ - imports: [WebhookModule, BullQueueModule], - providers: [ - CybersecurityConnectionsService, - WebhookService, - EnvironmentService, - ServiceRegistry, - ConnectionsStrategiesService, - //PROVIDERS SERVICES - SemgrepConnectionService, - TenableConnectionService, - QualysConnectionService, - SentineloneConnectionService, - Rapid7ConnectionService, - SnykConnectionService, - CrowdstrikeConnectionService, - MicrosoftdefenderConnectionService, - ], - exports: [CybersecurityConnectionsService], -}) -export class CybersecurityConnectionsModule {} diff --git a/packages/api/src/@core/connections/cybersecurity/services/crowdstrike/crowdstrike.service.ts b/packages/api/src/@core/connections/cybersecurity/services/crowdstrike/crowdstrike.service.ts deleted file mode 100644 index 4fcff71f9..000000000 --- a/packages/api/src/@core/connections/cybersecurity/services/crowdstrike/crowdstrike.service.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - OAuth2AuthData, - providerToType, -} from '@panora/shared'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import axios from 'axios'; - -export interface CrowdstrikeOAuthResponse { - access_token: string; - token_type: string; - scope: string; -} - -@Injectable() -export class CrowdstrikeConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - protected cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - private connectionUtils: ConnectionUtils, - private cService: ConnectionsStrategiesService, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(CrowdstrikeConnectionService.name); - this.registry.registerService('crowdstrike', this); - this.type = providerToType( - 'crowdstrike', - 'cybersecurity', - AuthStrategy.oauth2, - ); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - const access_token = JSON.parse( - this.cryptoService.decrypt(connection.access_token), - ); - config.headers = { - ...config.headers, - ...headers, - Authorization: `Bearer ${access_token}`, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'cybersecurity.crowdstrike.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, code } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'crowdstrike', - vertical: 'cybersecurity', - }, - }); - //reconstruct the redirect URI that was passed in the frontend it must be the same - const REDIRECT_URI = `${ - this.env.getDistributionMode() == 'selfhost' - ? this.env.getTunnelIngress() - : this.env.getPanoraBaseUrl() - }/connections/oauth/callback`; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - redirect_uri: REDIRECT_URI, - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - code: code, - grant_type: 'authorization_code', - }); - const res = await axios.post( - 'https://api.crowdstrike.com/oauth/access_token', - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: CrowdstrikeOAuthResponse = res.data; - // save tokens for this customer inside our db - let db_res; - const connection_token = uuidv4(); - const BASE_API_URL = CONNECTORS_METADATA['cybersecurity']['crowdstrike'] - .urls.apiUrl as string; - // get the site id for the token - const site = await axios.get('https://api.crowdstrike.com/v2/sites', { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - Authorization: `Bearer ${data.access_token}`, - }, - }); - const site_id = site.data.sites[0].id; - if (isNotUnique) { - // Update existing connection - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - status: 'valid', - created_at: new Date(), - }, - }); - } else { - // Create new connection - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'crowdstrike', - vertical: 'cybersecurity', - token_type: 'oauth2', - account_url: `${BASE_API_URL}/sites/${site_id}`, - access_token: this.cryptoService.encrypt(data.access_token), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - this.logger.log('Successfully added tokens inside DB ' + db_res); - return db_res; - } catch (error) { - throw error; - } - } - - async handleTokenRefresh(opts: RefreshParams) { - return; - } -} diff --git a/packages/api/src/@core/connections/cybersecurity/services/cybersecurity.connection.service.ts b/packages/api/src/@core/connections/cybersecurity/services/cybersecurity.connection.service.ts deleted file mode 100644 index 586f81b2e..000000000 --- a/packages/api/src/@core/connections/cybersecurity/services/cybersecurity.connection.service.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { - CallbackParams, - IConnectionCategory, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { Injectable } from '@nestjs/common'; -import { connections as Connection } from '@prisma/client'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from './registry.service'; -import { CategoryConnectionRegistry } from '@@core/@core-services/registries/connections-categories.registry'; -import { PassthroughResponse } from '@@core/passthrough/types'; - -@Injectable() -export class CybersecurityConnectionsService implements IConnectionCategory { - constructor( - private serviceRegistry: ServiceRegistry, - private connectionCategoryRegistry: CategoryConnectionRegistry, - private webhook: WebhookService, - private logger: LoggerService, - private prisma: PrismaService, - ) { - this.logger.setContext(CybersecurityConnectionsService.name); - this.connectionCategoryRegistry.registerService('cybersecurity', this); - } - //STEP 1:[FRONTEND STEP] - //create a frontend SDK snippet in which an authorization embedded link is set up so when users click - // on it to grant access => they grant US the access and then when confirmed - /*const authUrl = - 'https://app.hubspot.com/oauth/authorize' + - `?client_id=${encodeURIComponent(CLIENT_ID)}` + - `&scope=${encodeURIComponent(SCOPES)}` + - `&redirect_uri=${encodeURIComponent(REDIRECT_URI)}`;*/ //oauth/callback - - // oauth server calls this redirect callback - // WE WOULD HAVE CREATED A DEV ACCOUNT IN THE 5 CRMs (Panora dev account) - // we catch the tmp token and swap it against oauth2 server for access/refresh tokens - // to perform actions on his behalf - // this call pass 1. integrationID 2. CustomerId 3. Panora Api Key - async handleCallBack( - providerName: string, - callbackOpts: CallbackParams, - type_strategy: 'oauth2' | 'apikey' | 'basic', - ) { - try { - const serviceName = providerName.toLowerCase(); - - const service = this.serviceRegistry.getService(serviceName); - - if (!service) { - throw new ReferenceError(`Unknown provider, found ${providerName}`); - } - const data: Connection = await service.handleCallback(callbackOpts); - const event = await this.prisma.events.create({ - data: { - id_connection: data.id_connection, - id_project: data.id_project, - id_event: uuidv4(), - status: 'success', - type: 'connection.created', - method: 'GET', - url: `/${type_strategy}/callback`, - provider: providerName.toLowerCase(), - direction: '0', - timestamp: new Date(), - id_linked_user: callbackOpts.linkedUserId, - }, - }); - //directly send the webhook - await this.webhook.dispatchWebhook( - data, - 'connection.created', - callbackOpts.projectId, - event.id_event, - ); - } catch (error) { - throw error; - } - } - - async handleTokensRefresh( - connectionId: string, - providerName: string, - refresh_token: string, - id_project: string, - account_url?: string, - ) { - try { - const serviceName = providerName.toLowerCase(); - const service = this.serviceRegistry.getService(serviceName); - if (!service) { - throw new ReferenceError(`Unknown provider, found ${providerName}`); - } - const refreshOpts: RefreshParams = { - connectionId: connectionId, - refreshToken: refresh_token, - account_url: account_url, - projectId: id_project, - }; - await service.handleTokenRefresh(refreshOpts); - } catch (error) { - throw error; - } - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - const serviceName = connection.provider_slug.toLowerCase(); - const service = this.serviceRegistry.getService(serviceName); - if (!service) { - throw new ReferenceError(`Unknown provider, found ${serviceName}`); - } - return await service.passthrough(input, connectionId); - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/cybersecurity/services/microsoftdefender/microsoftdefender.service.ts b/packages/api/src/@core/connections/cybersecurity/services/microsoftdefender/microsoftdefender.service.ts deleted file mode 100644 index c874a92ba..000000000 --- a/packages/api/src/@core/connections/cybersecurity/services/microsoftdefender/microsoftdefender.service.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - OAuth2AuthData, - providerToType, -} from '@panora/shared'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import axios from 'axios'; - -export interface MicrosoftdefenderOAuthResponse { - access_token: string; - token_type: string; - scope: string; -} - -@Injectable() -export class MicrosoftdefenderConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - protected cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - private connectionUtils: ConnectionUtils, - private cService: ConnectionsStrategiesService, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(MicrosoftdefenderConnectionService.name); - this.registry.registerService('microsoftdefender', this); - this.type = providerToType( - 'microsoftdefender', - 'cybersecurity', - AuthStrategy.oauth2, - ); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - const access_token = JSON.parse( - this.cryptoService.decrypt(connection.access_token), - ); - config.headers = { - ...config.headers, - ...headers, - Authorization: `Bearer ${access_token}`, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'cybersecurity.microsoftdefender.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, code } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'microsoftdefender', - vertical: 'cybersecurity', - }, - }); - //reconstruct the redirect URI that was passed in the frontend it must be the same - const REDIRECT_URI = `${ - this.env.getDistributionMode() == 'selfhost' - ? this.env.getTunnelIngress() - : this.env.getPanoraBaseUrl() - }/connections/oauth/callback`; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - redirect_uri: REDIRECT_URI, - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - code: code, - grant_type: 'authorization_code', - }); - const res = await axios.post( - 'https://api.microsoftdefender.com/oauth/access_token', - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: MicrosoftdefenderOAuthResponse = res.data; - // save tokens for this customer inside our db - let db_res; - const connection_token = uuidv4(); - const BASE_API_URL = CONNECTORS_METADATA['cybersecurity'][ - 'microsoftdefender' - ].urls.apiUrl as string; - // get the site id for the token - const site = await axios.get( - 'https://api.microsoftdefender.com/v2/sites', - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - Authorization: `Bearer ${data.access_token}`, - }, - }, - ); - const site_id = site.data.sites[0].id; - if (isNotUnique) { - // Update existing connection - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - status: 'valid', - created_at: new Date(), - }, - }); - } else { - // Create new connection - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'microsoftdefender', - vertical: 'cybersecurity', - token_type: 'oauth2', - account_url: `${BASE_API_URL}/sites/${site_id}`, - access_token: this.cryptoService.encrypt(data.access_token), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - this.logger.log('Successfully added tokens inside DB ' + db_res); - return db_res; - } catch (error) { - throw error; - } - } - - async handleTokenRefresh(opts: RefreshParams) { - return; - } -} diff --git a/packages/api/src/@core/connections/cybersecurity/services/qualys/qualys.service.ts b/packages/api/src/@core/connections/cybersecurity/services/qualys/qualys.service.ts deleted file mode 100644 index 7840147b9..000000000 --- a/packages/api/src/@core/connections/cybersecurity/services/qualys/qualys.service.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - DynamicApiUrl, - providerToType, -} from '@panora/shared'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -@Injectable() -export class QualysConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private connectionUtils: ConnectionUtils, - private cService: ConnectionsStrategiesService, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(QualysConnectionService.name); - this.registry.registerService('qualys', this); - this.type = providerToType('qualys', 'cybersecurity', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - const decryptedData = JSON.parse( - this.cryptoService.decrypt(connection.access_token), - ); - - const { username, password } = decryptedData; - - config.headers = { - ...config.headers, - ...headers, - 'X-Requested-With': 'Curl Sample', - Authorization: `Basic ${Buffer.from(`${username}:${password}`).toString( - 'base64', - )}`, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'cybersecurity.qualys.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, body } = opts; - const { username, password, api_url } = body; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'qualys', - vertical: 'cybersecurity', - }, - }); - - let db_res; - const connection_token = uuidv4(); - const BASE_API_URL = ( - CONNECTORS_METADATA['cybersecurity']['qualys'].urls - .apiUrl as DynamicApiUrl - )(api_url); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt( - JSON.stringify({ username, password }), - ), - account_url: BASE_API_URL, - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'qualys', - vertical: 'cybersecurity', - token_type: 'basic', - account_url: BASE_API_URL, - access_token: this.cryptoService.encrypt( - JSON.stringify({ username, password }), - ), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - - handleTokenRefresh?(opts: RefreshParams): Promise { - throw new Error('Method not implemented.'); - } -} diff --git a/packages/api/src/@core/connections/cybersecurity/services/rapid7insightvm/rapid7.service.ts b/packages/api/src/@core/connections/cybersecurity/services/rapid7insightvm/rapid7.service.ts deleted file mode 100644 index 12e5cd767..000000000 --- a/packages/api/src/@core/connections/cybersecurity/services/rapid7insightvm/rapid7.service.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - DynamicApiUrl, - providerToType, -} from '@panora/shared'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -@Injectable() -export class Rapid7ConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private connectionUtils: ConnectionUtils, - private cService: ConnectionsStrategiesService, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(Rapid7ConnectionService.name); - this.registry.registerService('rapid7', this); - this.type = providerToType('rapid7', 'cybersecurity', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - config.headers = { - ...config.headers, - ...headers, - Authorization: `Basic ${Buffer.from( - `:${connection.access_token}`, - ).toString('base64')}`, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'cybersecurity.rapid7.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, body } = opts; - const { api_key, region } = body; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'rapid7', - vertical: 'cybersecurity', - }, - }); - - let db_res; - const connection_token = uuidv4(); - const BASE_API_URL = ( - CONNECTORS_METADATA['cybersecurity']['rapid7'].urls - .apiUrl as DynamicApiUrl - )(region); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(api_key), - account_url: BASE_API_URL, - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'rapid7', - vertical: 'cybersecurity', - token_type: 'basic', - account_url: BASE_API_URL, - access_token: this.cryptoService.encrypt(api_key), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - - handleTokenRefresh?(opts: RefreshParams): Promise { - throw new Error('Method not implemented.'); - } -} diff --git a/packages/api/src/@core/connections/cybersecurity/services/registry.service.ts b/packages/api/src/@core/connections/cybersecurity/services/registry.service.ts deleted file mode 100644 index de82a1fcb..000000000 --- a/packages/api/src/@core/connections/cybersecurity/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { IConnectionService } from '@@core/connections/@utils/types'; -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IConnectionService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IConnectionService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/@core/connections/cybersecurity/services/semgrep/semgrep.service.ts b/packages/api/src/@core/connections/cybersecurity/services/semgrep/semgrep.service.ts deleted file mode 100644 index 7bd36d5d0..000000000 --- a/packages/api/src/@core/connections/cybersecurity/services/semgrep/semgrep.service.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - DynamicApiUrl, - providerToType, -} from '@panora/shared'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -@Injectable() -export class SemgrepConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private connectionUtils: ConnectionUtils, - private cService: ConnectionsStrategiesService, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(SemgrepConnectionService.name); - this.registry.registerService('semgrep', this); - this.type = providerToType('semgrep', 'cybersecurity', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - config.headers = { - ...config.headers, - ...headers, - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'cybersecurity.semgrep.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, body } = opts; - const { api_key } = body; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'semgrep', - vertical: 'cybersecurity', - }, - }); - - let db_res; - const connection_token = uuidv4(); - const BASE_API_URL = CONNECTORS_METADATA['cybersecurity']['semgrep'].urls - .apiUrl as string; - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(api_key), - account_url: BASE_API_URL, - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'semgrep', - vertical: 'cybersecurity', - token_type: 'basic', - account_url: BASE_API_URL, - access_token: this.cryptoService.encrypt(api_key), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - - handleTokenRefresh?(opts: RefreshParams): Promise { - throw new Error('Method not implemented.'); - } -} diff --git a/packages/api/src/@core/connections/cybersecurity/services/sentinelone/sentinelone.service.ts b/packages/api/src/@core/connections/cybersecurity/services/sentinelone/sentinelone.service.ts deleted file mode 100644 index 7d6f02fb5..000000000 --- a/packages/api/src/@core/connections/cybersecurity/services/sentinelone/sentinelone.service.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - DynamicApiUrl, - providerToType, -} from '@panora/shared'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -@Injectable() -export class SentineloneConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private connectionUtils: ConnectionUtils, - private cService: ConnectionsStrategiesService, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(SentineloneConnectionService.name); - this.registry.registerService('sentinelone', this); - this.type = providerToType( - 'sentinelone', - 'cybersecurity', - AuthStrategy.oauth2, - ); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - config.headers = { - ...config.headers, - ...headers, - Authorization: `APIToken ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'cybersecurity.sentinelone.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, body } = opts; - const { api_key, host } = body; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'sentinelone', - vertical: 'cybersecurity', - }, - }); - - let db_res; - const connection_token = uuidv4(); - const BASE_API_URL = ( - CONNECTORS_METADATA['cybersecurity']['sentinelone'].urls - .apiUrl as DynamicApiUrl - )(host); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(api_key), - account_url: BASE_API_URL, - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'sentinelone', - vertical: 'cybersecurity', - token_type: 'basic', - account_url: BASE_API_URL, - access_token: this.cryptoService.encrypt(api_key), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - - handleTokenRefresh?(opts: RefreshParams): Promise { - throw new Error('Method not implemented.'); - } -} diff --git a/packages/api/src/@core/connections/cybersecurity/services/snyk/snyk.service.ts b/packages/api/src/@core/connections/cybersecurity/services/snyk/snyk.service.ts deleted file mode 100644 index f5cb498c0..000000000 --- a/packages/api/src/@core/connections/cybersecurity/services/snyk/snyk.service.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - OAuth2AuthData, - providerToType, -} from '@panora/shared'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import axios from 'axios'; - -export interface SnykOAuthResponse { - access_token: string; - refresh_token: string; - token_type: string; - scope: string; - expires_in: string; -} - -@Injectable() -export class SnykConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - protected cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - private connectionUtils: ConnectionUtils, - private cService: ConnectionsStrategiesService, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(SnykConnectionService.name); - this.registry.registerService('snyk', this); - this.type = providerToType('snyk', 'cybersecurity', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - const access_token = JSON.parse( - this.cryptoService.decrypt(connection.access_token), - ); - config.headers = { - ...config.headers, - ...headers, - Authorization: `token ${access_token}`, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'cybersecurity.snyk.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, code, code_verifier } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'snyk', - vertical: 'cybersecurity', - }, - }); - //reconstruct the redirect URI that was passed in the frontend it must be the same - const REDIRECT_URI = `${ - this.env.getDistributionMode() == 'selfhost' - ? this.env.getTunnelIngress() - : this.env.getPanoraBaseUrl() - }/connections/oauth/callback`; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - redirect_uri: REDIRECT_URI, - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - code: code, - grant_type: 'authorization_code', - code_verifier: code_verifier, - }); - const res = await axios.post( - 'https://api.snyk.io/oauth2/token', - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: SnykOAuthResponse = res.data; - console.log('result is ' + JSON.stringify(data)); - // save tokens for this customer inside our db - let db_res; - const connection_token = uuidv4(); - const BASE_API_URL = CONNECTORS_METADATA['cybersecurity']['snyk'].urls - .apiUrl as string; - if (isNotUnique) { - // Update existing connection - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - }, - }); - } else { - // Create new connection - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'snyk', - vertical: 'cybersecurity', - token_type: 'oauth2', - account_url: BASE_API_URL, - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - this.logger.log('Successfully added tokens inside DB ' + db_res); - return db_res; - } catch (error) { - throw error; - } - } - - async handleTokenRefresh(opts: RefreshParams) { - try { - const { connectionId, refreshToken, projectId } = opts; - const REDIRECT_URI = `${ - this.env.getDistributionMode() == 'selfhost' - ? this.env.getTunnelIngress() - : this.env.getPanoraBaseUrl() - }/connections/oauth/callback`; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - grant_type: 'refresh_token', - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - refresh_token: this.cryptoService.decrypt(refreshToken), - redirect_uri: REDIRECT_URI, - }); - - const res = await axios.post( - `https://app.deel.com/oauth2/tokens`, - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: SnykOAuthResponse = res.data; - await this.prisma.connections.update({ - where: { - id_connection: connectionId, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - }, - }); - this.logger.log('OAuth credentials updated : deel '); - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/cybersecurity/services/tenable/tenable.service.ts b/packages/api/src/@core/connections/cybersecurity/services/tenable/tenable.service.ts deleted file mode 100644 index 7c125d3d8..000000000 --- a/packages/api/src/@core/connections/cybersecurity/services/tenable/tenable.service.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - DynamicApiUrl, - providerToType, -} from '@panora/shared'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -@Injectable() -export class TenableConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private connectionUtils: ConnectionUtils, - private cService: ConnectionsStrategiesService, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(TenableConnectionService.name); - this.registry.registerService('tenable', this); - this.type = providerToType('tenable', 'cybersecurity', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - const decryptedData = JSON.parse( - this.cryptoService.decrypt(connection.access_token), - ); - - const { access_key, secret_key } = decryptedData; // todo - - config.headers = { - ...config.headers, - ...headers, - 'X-ApiKeys': `${secret_key}`, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'cybersecurity.tenable.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, body } = opts; - const { access_key, secret_key } = body; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'tenable', - vertical: 'cybersecurity', - }, - }); - - let db_res; - const connection_token = uuidv4(); - const BASE_API_URL = CONNECTORS_METADATA['cybersecurity']['tenable'].urls - .apiUrl as string; - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(secret_key), - account_url: BASE_API_URL, - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'tenable', - vertical: 'cybersecurity', - token_type: 'basic', - account_url: BASE_API_URL, - access_token: this.cryptoService.encrypt(secret_key), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - - handleTokenRefresh?(opts: RefreshParams): Promise { - throw new Error('Method not implemented.'); - } -} diff --git a/packages/api/src/@core/connections/hris/hris.connection.module.ts b/packages/api/src/@core/connections/hris/hris.connection.module.ts deleted file mode 100644 index da5e50d4a..000000000 --- a/packages/api/src/@core/connections/hris/hris.connection.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; -import { WebhookModule } from '@@core/@core-services/webhooks/panora-webhooks/webhook.module'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { Module } from '@nestjs/common'; -import { BamboohrConnectionService } from './services/bamboohr/bamboohr.service'; -import { DeelConnectionService } from './services/deel/deel.service'; -import { FactorialConnectionService } from './services/factorial/factorial.service'; -import { GustoConnectionService } from './services/gusto/gusto.service'; -import { HrisConnectionsService } from './services/hris.connection.service'; -import { NamelyConnectionService } from './services/namely/namely.service'; -import { PayfitConnectionService } from './services/payfit/payfit.service'; -import { ServiceRegistry } from './services/registry.service'; -import { RipplingConnectionService } from './services/rippling/rippling.service'; -import { SageConnectionService } from './services/sage/sage.service'; - -@Module({ - imports: [WebhookModule, BullQueueModule], - providers: [ - HrisConnectionsService, - ServiceRegistry, - WebhookService, - EnvironmentService, - ConnectionsStrategiesService, - // PROVIDERS SERVICES - RipplingConnectionService, - DeelConnectionService, - GustoConnectionService, - PayfitConnectionService, - FactorialConnectionService, - NamelyConnectionService, - BamboohrConnectionService, - SageConnectionService, - ], - exports: [HrisConnectionsService], -}) -export class HrisConnectionModule {} diff --git a/packages/api/src/@core/connections/hris/services/bamboohr/bamboohr.service.ts b/packages/api/src/@core/connections/hris/services/bamboohr/bamboohr.service.ts deleted file mode 100644 index f5d59db0d..000000000 --- a/packages/api/src/@core/connections/hris/services/bamboohr/bamboohr.service.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - OAuth2AuthData, - providerToType, -} from '@panora/shared'; -import axios from 'axios'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -export type BamboohrOAuthResponse = { - access_token: string; - token_type: string; - expires_in: number; - scope: string; - id_token: string; -}; - -@Injectable() -export class BamboohrConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - private env: EnvironmentService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private cService: ConnectionsStrategiesService, - private connectionUtils: ConnectionUtils, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(BamboohrConnectionService.name); - this.registry.registerService('bamboohr', this); - this.type = providerToType('bamboohr', 'hris', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers['Authorization'] = `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`; - - config.headers = { - ...config.headers, - ...headers, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'hris.bamboohr.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - handleTokenRefresh?(opts: RefreshParams): Promise { - return Promise.resolve(); - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, code } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'bamboohr', - vertical: 'hris', - }, - }); - - //reconstruct the redirect URI that was passed in the githubend it must be the same - const REDIRECT_URI = `${ - this.env.getPanoraBaseUrl() - }/connections/oauth/callback`; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - redirect_uri: REDIRECT_URI, - code: code, - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - grant_type: 'authorization_code', - scope: CONNECTORS_METADATA['hris']['bamboohr'].scopes, - }); - const res = await axios.post( - `https://.bamboohr.com/token.php?request=token`, - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: BamboohrOAuthResponse = res.data; - this.logger.log( - 'OAuth credentials : bamboohr hris ' + JSON.stringify(data), - ); - - const formData_ = new URLSearchParams({ - id_token: data.id_token, - applicationKey: '', // TODO - }); - //fetch the api key of the user - const res_ = await axios.post( - `https://api.bamboohr.com/api/gateway.php/{company}/v1/oidcLogin`, - formData_.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data_: { - success: boolean; - userId: number; - employeeId: number; - key: string; - } = res_.data; - this.logger.log( - 'OAuth credentials : bamboohr hris apikey res ' + JSON.stringify(data_), - ); - - let db_res; - const connection_token = uuidv4(); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data_.key), - account_url: `https://api.bamboohr.com/api/gateway.php/{company}`, - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'bamboohr', - vertical: 'hris', - token_type: 'oauth2', - account_url: `https://api.bamboohr.com/api/gateway.php/{company}`, - access_token: this.cryptoService.encrypt(data_.key), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/hris/services/deel/deel.service.ts b/packages/api/src/@core/connections/hris/services/deel/deel.service.ts deleted file mode 100644 index c80edf18c..000000000 --- a/packages/api/src/@core/connections/hris/services/deel/deel.service.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - OAuth2AuthData, - providerToType, -} from '@panora/shared'; -import axios from 'axios'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -export type DeelOAuthResponse = { - access_token: string; - refresh_token: string; - token_type: string; - expires_in: number; - scope: string; -}; - -@Injectable() -export class DeelConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - private env: EnvironmentService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private cService: ConnectionsStrategiesService, - private connectionUtils: ConnectionUtils, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(DeelConnectionService.name); - this.registry.registerService('deel', this); - this.type = providerToType('deel', 'hris', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers['Authorization'] = `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`; - - config.headers = { - ...config.headers, - ...headers, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'hris.deel.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, code } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'deel', - vertical: 'hris', - }, - }); - - //reconstruct the redirect URI that was passed in the githubend it must be the same - const REDIRECT_URI = `${ - this.env.getDistributionMode() == 'selfhost' - ? this.env.getTunnelIngress() - : this.env.getPanoraBaseUrl() - }/connections/oauth/callback`; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - redirect_uri: REDIRECT_URI, - code: code, - grant_type: 'authorization_code', - }); - const res = await axios.post( - `https://app.deel.com/oauth2/tokens`, - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - Authorization: `Basic ${Buffer.from( - `${CREDENTIALS.CLIENT_ID}:${CREDENTIALS.CLIENT_SECRET}`, - ).toString('base64')}`, - }, - }, - ); - const data: DeelOAuthResponse = res.data; - this.logger.log('OAuth credentials : deel hris ' + JSON.stringify(data)); - - let db_res; - const connection_token = uuidv4(); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - account_url: CONNECTORS_METADATA['hris']['deel'].urls - .apiUrl as string, - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'deel', - vertical: 'hris', - token_type: 'oauth2', - account_url: CONNECTORS_METADATA['hris']['deel'].urls - .apiUrl as string, - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - async handleTokenRefresh(opts: RefreshParams) { - try { - const { connectionId, refreshToken, projectId } = opts; - const REDIRECT_URI = `${ - this.env.getDistributionMode() == 'selfhost' - ? this.env.getTunnelIngress() - : this.env.getPanoraBaseUrl() - }/connections/oauth/callback`; - - const formData = new URLSearchParams({ - grant_type: 'refresh_token', - refresh_token: this.cryptoService.decrypt(refreshToken), - redirect_uri: REDIRECT_URI, - }); - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const res = await axios.post( - `https://app.deel.com/oauth2/tokens`, - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - Authorization: `Basic ${Buffer.from( - `${CREDENTIALS.CLIENT_ID}:${CREDENTIALS.CLIENT_SECRET}`, - ).toString('base64')}`, - }, - }, - ); - const data: DeelOAuthResponse = res.data; - await this.prisma.connections.update({ - where: { - id_connection: connectionId, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - }, - }); - this.logger.log('OAuth credentials updated : deel '); - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/hris/services/factorial/factorial.service.ts b/packages/api/src/@core/connections/hris/services/factorial/factorial.service.ts deleted file mode 100644 index 54418031b..000000000 --- a/packages/api/src/@core/connections/hris/services/factorial/factorial.service.ts +++ /dev/null @@ -1,234 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - OAuth2AuthData, - providerToType, -} from '@panora/shared'; -import axios from 'axios'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -export type FactorialOAuthResponse = { - access_token: string; - refresh_token: string; - token_type: string; - expires_in: number; - scope: string; -}; - -@Injectable() -export class FactorialConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - private env: EnvironmentService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private cService: ConnectionsStrategiesService, - private connectionUtils: ConnectionUtils, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(FactorialConnectionService.name); - this.registry.registerService('factorial', this); - this.type = providerToType('factorial', 'hris', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers['Authorization'] = `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`; - - config.headers = { - ...config.headers, - ...headers, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'hris.factorial.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, code } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'factorial', - vertical: 'hris', - }, - }); - - //reconstruct the redirect URI that was passed in the githubend it must be the same - const REDIRECT_URI = `${ - this.env.getPanoraBaseUrl() - }/connections/oauth/callback`; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - redirect_uri: REDIRECT_URI, - code: code, - grant_type: 'authorization_code', - }); - const res = await axios.post( - `https://api.factorialhr.com/oauth/token`, - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: FactorialOAuthResponse = res.data; - this.logger.log( - 'OAuth credentials : factorial hris ' + JSON.stringify(data), - ); - - let db_res; - const connection_token = uuidv4(); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - account_url: CONNECTORS_METADATA['hris']['factorial'].urls - .apiUrl as string, - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'factorial', - vertical: 'hris', - token_type: 'oauth2', - account_url: CONNECTORS_METADATA['hris']['factorial'].urls - .apiUrl as string, - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - async handleTokenRefresh(opts: RefreshParams) { - try { - const { connectionId, refreshToken, projectId } = opts; - const REDIRECT_URI = `${ - this.env.getPanoraBaseUrl() - }/connections/oauth/callback`; - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - const formData = new URLSearchParams({ - grant_type: 'refresh_token', - refresh_token: this.cryptoService.decrypt(refreshToken), - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - }); - - const res = await axios.post( - `https://api.factorialhr.com/oauth/token`, - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: FactorialOAuthResponse = res.data; - await this.prisma.connections.update({ - where: { - id_connection: connectionId, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - }, - }); - this.logger.log('OAuth credentials updated : factorial '); - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/hris/services/gusto/gusto.service.ts b/packages/api/src/@core/connections/hris/services/gusto/gusto.service.ts deleted file mode 100644 index 101a85a67..000000000 --- a/packages/api/src/@core/connections/hris/services/gusto/gusto.service.ts +++ /dev/null @@ -1,230 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - OAuth2AuthData, - providerToType, -} from '@panora/shared'; -import axios from 'axios'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -export type GustoOAuthResponse = { - access_token: string; - refresh_token: string; - token_type: string; - expires_in: number; - scope: string; -}; - -@Injectable() -export class GustoConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - private env: EnvironmentService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private cService: ConnectionsStrategiesService, - private connectionUtils: ConnectionUtils, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(GustoConnectionService.name); - this.registry.registerService('gusto', this); - this.type = providerToType('gusto', 'hris', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers['Authorization'] = `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`; - - config.headers = { - ...config.headers, - ...headers, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'hris.gusto.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, code } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gusto', - vertical: 'hris', - }, - }); - - const REDIRECT_URI = `${this.env.getPanoraBaseUrl()}/connections/oauth/callback`; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - redirect_uri: REDIRECT_URI, - code: code, - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - grant_type: 'authorization_code', - }); - const res = await axios.post( - `https://api.gusto-demo.com/oauth/token`, - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: GustoOAuthResponse = res.data; - this.logger.log('OAuth credentials : gusto hris ' + JSON.stringify(data)); - - let db_res; - const connection_token = uuidv4(); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - account_url: CONNECTORS_METADATA['hris']['gusto'].urls - .apiUrl as string, - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'gusto', - vertical: 'hris', - token_type: 'oauth2', - account_url: CONNECTORS_METADATA['hris']['gusto'].urls - .apiUrl as string, - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - async handleTokenRefresh(opts: RefreshParams) { - try { - const { connectionId, refreshToken, projectId } = opts; - const REDIRECT_URI = `${this.env.getPanoraBaseUrl()}/connections/oauth/callback`; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - grant_type: 'refresh_token', - refresh_token: this.cryptoService.decrypt(refreshToken), - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - redirect_uri: REDIRECT_URI, - }); - - const res = await axios.post( - `https://api.gusto-demo.com/oauth/token`, - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: GustoOAuthResponse = res.data; - await this.prisma.connections.update({ - where: { - id_connection: connectionId, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - }, - }); - this.logger.log('OAuth credentials updated : gusto '); - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/hris/services/hris.connection.service.ts b/packages/api/src/@core/connections/hris/services/hris.connection.service.ts deleted file mode 100644 index e039480d3..000000000 --- a/packages/api/src/@core/connections/hris/services/hris.connection.service.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { - CallbackParams, - IConnectionCategory, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { Injectable } from '@nestjs/common'; -import { connections as Connection } from '@prisma/client'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from './registry.service'; -import { CategoryConnectionRegistry } from '@@core/@core-services/registries/connections-categories.registry'; -import { PassthroughResponse } from '@@core/passthrough/types'; - -@Injectable() -export class HrisConnectionsService implements IConnectionCategory { - constructor( - private serviceRegistry: ServiceRegistry, - private connectionCategoryRegistry: CategoryConnectionRegistry, - private webhook: WebhookService, - private logger: LoggerService, - private prisma: PrismaService, - ) { - this.logger.setContext(HrisConnectionsService.name); - this.connectionCategoryRegistry.registerService('hris', this); - } - - //STEP 1:[FRONTEND STEP] - //create a frontend SDK snippet in which an authorization embedded link is set up so when users click - // on it to grant access => they grant US the access and then when confirmed - /*const authUrl = - 'https://app.hubspot.com/oauth/authorize' + - `?client_id=${encodeURIComponent(CLIENT_ID)}` + - `&scope=${encodeURIComponent(SCOPES)}` + - `&redirect_uri=${encodeURIComponent(REDIRECT_URI)}`;*/ //oauth/callback - - // oauth server calls this redirect callback - // WE WOULD HAVE CREATED A DEV ACCOUNT IN THE 5 Hriss (Panora dev account) - // we catch the tmp token and swap it against oauth2 server for access/refresh tokens - // to perform actions on his behalf - // this call pass 1. integrationID 2. CustomerId 3. Panora Api Key - async handleCallBack( - providerName: string, - callbackOpts: CallbackParams, - type_strategy: 'oauth2' | 'apikey' | 'basic', - ) { - try { - const serviceName = providerName.toLowerCase(); - - const service = this.serviceRegistry.getService(serviceName); - - if (!service) { - throw new ReferenceError(`Unknown provider, found ${providerName}`); - } - const data: Connection = await service.handleCallback(callbackOpts); - const event = await this.prisma.events.create({ - data: { - id_connection: data.id_connection, - id_project: data.id_project, - id_event: uuidv4(), - status: 'success', - type: 'connection.created', - method: 'GET', - url: `/${type_strategy}/callback`, - provider: providerName.toLowerCase(), - direction: '0', - timestamp: new Date(), - id_linked_user: callbackOpts.linkedUserId, - }, - }); - //directly send the webhook - await this.webhook.dispatchWebhook( - data, - 'connection.created', - callbackOpts.projectId, - event.id_event, - ); - } catch (error) { - throw error; - } - } - - async handleTokensRefresh( - connectionId: string, - providerName: string, - refresh_token: string, - id_project: string, - account_url?: string, - ) { - try { - const serviceName = providerName.toLowerCase(); - const service = this.serviceRegistry.getService(serviceName); - if (!service) { - throw new ReferenceError(`Unknown provider, found ${providerName}`); - } - const refreshOpts: RefreshParams = { - connectionId: connectionId, - refreshToken: refresh_token, - account_url: account_url, - projectId: id_project, - }; - await service.handleTokenRefresh(refreshOpts); - } catch (error) { - throw error; - } - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - const serviceName = connection.provider_slug.toLowerCase(); - const service = this.serviceRegistry.getService(serviceName); - if (!service) { - throw new ReferenceError(`Unknown provider, found ${serviceName}`); - } - return await service.passthrough(input, connectionId); - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/hris/services/namely/namely.service.ts b/packages/api/src/@core/connections/hris/services/namely/namely.service.ts deleted file mode 100644 index 693df3c6e..000000000 --- a/packages/api/src/@core/connections/hris/services/namely/namely.service.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - OAuth2AuthData, - providerToType, -} from '@panora/shared'; -import axios from 'axios'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -export type NamelyOAuthResponse = { - access_token: string; - refresh_token: string; - token_type: string; - expires_in: number; - scope: string; -}; - -@Injectable() -export class NamelyConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - private env: EnvironmentService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private cService: ConnectionsStrategiesService, - private connectionUtils: ConnectionUtils, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(NamelyConnectionService.name); - this.registry.registerService('namely', this); - this.type = providerToType('namely', 'hris', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers['Authorization'] = `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`; - - config.headers = { - ...config.headers, - ...headers, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'hris.namely.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, code } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'namely', - vertical: 'hris', - }, - }); - - const REDIRECT_URI = `${this.env.getPanoraBaseUrl()}/connections/oauth/callback`; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - // redirect_uri: REDIRECT_URI, - code: code, - grant_type: 'authorization_code', - }); - const res = await axios.post( - `${CREDENTIALS.SUBDOMAIN}/api/v1/oauth2/token`, - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: NamelyOAuthResponse = res.data; - this.logger.log( - 'OAuth credentials : namely hris ' + JSON.stringify(data), - ); - - let db_res; - const connection_token = uuidv4(); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - account_url: CONNECTORS_METADATA['hris']['namely'].urls - .apiUrl as string, - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'namely', - vertical: 'hris', - token_type: 'oauth2', - account_url: CONNECTORS_METADATA['hris']['namely'].urls - .apiUrl as string, - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - async handleTokenRefresh(opts: RefreshParams) { - try { - const { connectionId, refreshToken, projectId } = opts; - const REDIRECT_URI = `${this.env.getPanoraBaseUrl()}/connections/oauth/callback`; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - grant_type: 'refresh_token', - refresh_token: this.cryptoService.decrypt(refreshToken), - redirect_uri: REDIRECT_URI, - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - }); - - const res = await axios.post( - `${CREDENTIALS.SUBDOMAIN}/api/v1/oauth2/token`, - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: NamelyOAuthResponse = res.data; - await this.prisma.connections.update({ - where: { - id_connection: connectionId, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - }, - }); - this.logger.log('OAuth credentials updated : namely '); - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/hris/services/payfit/payfit.service.ts b/packages/api/src/@core/connections/hris/services/payfit/payfit.service.ts deleted file mode 100644 index 6af86a2a2..000000000 --- a/packages/api/src/@core/connections/hris/services/payfit/payfit.service.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - OAuth2AuthData, - providerToType, -} from '@panora/shared'; -import axios from 'axios'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -export type PayfitOAuthResponse = { - access_token: string; - refresh_token: string; - token_type: string; - expires_in: number; - scope: string; -}; - -@Injectable() -export class PayfitConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - private env: EnvironmentService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private cService: ConnectionsStrategiesService, - private connectionUtils: ConnectionUtils, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(PayfitConnectionService.name); - this.registry.registerService('payfit', this); - this.type = providerToType('payfit', 'hris', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers['Authorization'] = `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`; - - config.headers = { - ...config.headers, - ...headers, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'hris.payfit.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, code } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'payfit', - vertical: 'hris', - }, - }); - - //reconstruct the redirect URI that was passed in the githubend it must be the same - const REDIRECT_URI = `${ - this.env.getPanoraBaseUrl() - }/connections/oauth/callback`; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - redirect_uri: REDIRECT_URI, - code: code, - client_id: CREDENTIALS.CLIENT_ID, - client_secret: CREDENTIALS.CLIENT_SECRET, - grant_type: 'authorization_code', - }); - const res = await axios.post( - `https://oauth.payfit.com/token`, - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - }, - }, - ); - const data: PayfitOAuthResponse = res.data; - this.logger.log( - 'OAuth credentials : payfit hris ' + JSON.stringify(data), - ); - - let db_res; - const connection_token = uuidv4(); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - account_url: CONNECTORS_METADATA['hris']['payfit'].urls - .apiUrl as string, - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'payfit', - vertical: 'hris', - token_type: 'oauth2', - account_url: CONNECTORS_METADATA['hris']['payfit'].urls - .apiUrl as string, - access_token: this.cryptoService.encrypt(data.access_token), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - async handleTokenRefresh(opts: RefreshParams) { - return; - } -} diff --git a/packages/api/src/@core/connections/hris/services/registry.service.ts b/packages/api/src/@core/connections/hris/services/registry.service.ts deleted file mode 100644 index 37bbe6a07..000000000 --- a/packages/api/src/@core/connections/hris/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IConnectionService } from '@@core/connections/@utils/types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IConnectionService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IConnectionService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/@core/connections/hris/services/rippling/rippling.service.ts b/packages/api/src/@core/connections/hris/services/rippling/rippling.service.ts deleted file mode 100644 index d55527826..000000000 --- a/packages/api/src/@core/connections/hris/services/rippling/rippling.service.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - OAuth2AuthData, - providerToType, -} from '@panora/shared'; -import axios from 'axios'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -export type RipplingOAuthResponse = { - access_token: string; - refresh_token: string; - token_type: string; - expires_in: number; - scope: string; -}; - -@Injectable() -export class RipplingConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - private env: EnvironmentService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private cService: ConnectionsStrategiesService, - private connectionUtils: ConnectionUtils, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(RipplingConnectionService.name); - this.registry.registerService('rippling', this); - this.type = providerToType('rippling', 'hris', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers['Authorization'] = `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`; - - config.headers = { - ...config.headers, - ...headers, - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'hris.rippling.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, code } = opts; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'rippling', - vertical: 'hris', - }, - }); - - //reconstruct the redirect URI that was passed in the githubend it must be the same - const REDIRECT_URI = `${ - this.env.getDistributionMode() == 'selfhost' - ? this.env.getTunnelIngress() - : this.env.getPanoraBaseUrl() - }/connections/oauth/callback`; - - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const formData = new URLSearchParams({ - redirect_uri: REDIRECT_URI, - code: code, - grant_type: 'authorization_code', - }); - const res = await axios.post( - `https://api.rippling.com/api/o/token`, - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - Authorization: `Basic ${Buffer.from( - `${CREDENTIALS.CLIENT_ID}:${CREDENTIALS.CLIENT_SECRET}`, - ).toString('base64')}`, - }, - }, - ); - const data: RipplingOAuthResponse = res.data; - this.logger.log( - 'OAuth credentials : rippling hris ' + JSON.stringify(data), - ); - - let db_res; - const connection_token = uuidv4(); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - account_url: CONNECTORS_METADATA['hris']['rippling'].urls - .apiUrl as string, - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'rippling', - vertical: 'hris', - token_type: 'oauth2', - account_url: CONNECTORS_METADATA['hris']['rippling'].urls - .apiUrl as string, - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - async handleTokenRefresh(opts: RefreshParams) { - try { - const { connectionId, refreshToken, projectId } = opts; - - const formData = new URLSearchParams({ - grant_type: 'refresh_token', - refresh_token: this.cryptoService.decrypt(refreshToken), - }); - const CREDENTIALS = (await this.cService.getCredentials( - projectId, - this.type, - )) as OAuth2AuthData; - - const res = await axios.post( - `https://api.rippling.com/api/o/token/`, - formData.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - Authorization: `Basic ${Buffer.from( - `${CREDENTIALS.CLIENT_ID}:${CREDENTIALS.CLIENT_SECRET}`, - ).toString('base64')}`, - }, - }, - ); - const data: RipplingOAuthResponse = res.data; - await this.prisma.connections.update({ - where: { - id_connection: connectionId, - }, - data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), - expiration_timestamp: new Date( - new Date().getTime() + Number(data.expires_in) * 1000, - ), - }, - }); - this.logger.log('OAuth credentials updated : rippling '); - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/@core/connections/hris/services/sage/sage.service.ts b/packages/api/src/@core/connections/hris/services/sage/sage.service.ts deleted file mode 100644 index cbcdf1199..000000000 --- a/packages/api/src/@core/connections/hris/services/sage/sage.service.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - AbstractBaseConnectionService, - OAuthCallbackParams, - PassthroughInput, - RefreshParams, -} from '@@core/connections/@utils/types'; -import { PassthroughResponse } from '@@core/passthrough/types'; -import { Injectable } from '@nestjs/common'; -import { - AuthStrategy, - CONNECTORS_METADATA, - DynamicApiUrl, - providerToType, -} from '@panora/shared'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../registry.service'; - -@Injectable() -export class SageConnectionService extends AbstractBaseConnectionService { - private readonly type: string; - - constructor( - protected prisma: PrismaService, - private logger: LoggerService, - protected cryptoService: EncryptionService, - private registry: ServiceRegistry, - private connectionUtils: ConnectionUtils, - private cService: ConnectionsStrategiesService, - private retryService: RetryHandler, - ) { - super(prisma, cryptoService); - this.logger.setContext(SageConnectionService.name); - this.registry.registerService('sage', this); - this.type = providerToType('sage', 'hris', AuthStrategy.oauth2); - } - - async passthrough( - input: PassthroughInput, - connectionId: string, - ): Promise { - try { - const { headers } = input; - const config = await this.constructPassthrough(input, connectionId); - - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - - config.headers = { - ...config.headers, - ...headers, - 'X-Auth-Token': this.cryptoService.decrypt(connection.access_token), - }; - - return await this.retryService.makeRequest( - { - method: config.method, - url: config.url, - data: config.data, - headers: config.headers, - }, - 'hris.sage.passthrough', - config.linkedUserId, - ); - } catch (error) { - throw error; - } - } - - async handleCallback(opts: OAuthCallbackParams) { - try { - const { linkedUserId, projectId, body } = opts; - const { api_key, subdomain } = body; - const isNotUnique = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'sage', - vertical: 'hris', - }, - }); - - let db_res; - const connection_token = uuidv4(); - const BASE_API_URL = ( - CONNECTORS_METADATA['hris']['sage'].urls.apiUrl as DynamicApiUrl - )(subdomain); - - if (isNotUnique) { - db_res = await this.prisma.connections.update({ - where: { - id_connection: isNotUnique.id_connection, - }, - data: { - access_token: this.cryptoService.encrypt(api_key), - account_url: BASE_API_URL, - status: 'valid', - created_at: new Date(), - }, - }); - } else { - db_res = await this.prisma.connections.create({ - data: { - id_connection: uuidv4(), - connection_token: connection_token, - provider_slug: 'sage', - vertical: 'hris', - token_type: 'basic', - account_url: BASE_API_URL, - access_token: this.cryptoService.encrypt(api_key), - status: 'valid', - created_at: new Date(), - projects: { - connect: { id_project: projectId }, - }, - linked_users: { - connect: { - id_linked_user: await this.connectionUtils.getLinkedUserId( - projectId, - linkedUserId, - ), - }, - }, - }, - }); - } - return db_res; - } catch (error) { - throw error; - } - } - - handleTokenRefresh?(opts: RefreshParams): Promise { - throw new Error('Method not implemented.'); - } -} diff --git a/packages/api/src/@core/sync/sync.controller.ts b/packages/api/src/@core/sync/sync.controller.ts index 36814b768..7bd20b490 100644 --- a/packages/api/src/@core/sync/sync.controller.ts +++ b/packages/api/src/@core/sync/sync.controller.ts @@ -29,20 +29,6 @@ export class UpdatePullFrequencyDto { }) crm?: number; - @ApiProperty({ - type: Number, - example: 3600, - description: 'Frequency in seconds', - }) - ats?: number; - - @ApiProperty({ - type: Number, - example: 7200, - description: 'Frequency in seconds', - }) - hris?: number; - @ApiProperty({ type: Number, example: 14400, @@ -80,9 +66,7 @@ export class ResyncStatusDto { example: 'ticketing', enum: [ 'ticketing', - 'ats', 'accounting', - 'hris', 'crm', 'filestorage', 'ecommerce', @@ -127,8 +111,6 @@ export class SyncController { 'marketingautomation', 'crm', 'filestorage', - 'ats', - 'hris', 'accounting', 'ecommerce', ], diff --git a/packages/api/src/@core/sync/sync.service.ts b/packages/api/src/@core/sync/sync.service.ts index 2233df697..c86ebe5c0 100644 --- a/packages/api/src/@core/sync/sync.service.ts +++ b/packages/api/src/@core/sync/sync.service.ts @@ -52,8 +52,6 @@ export class CoreSyncService { if (projectSyncConfig) { const syncIntervals = { crm: projectSyncConfig.crm, - ats: projectSyncConfig.ats, - hris: projectSyncConfig.hris, accounting: projectSyncConfig.accounting, filestorage: projectSyncConfig.filestorage, ecommerce: projectSyncConfig.ecommerce, @@ -210,12 +208,6 @@ export class CoreSyncService { case ConnectorCategory.FileStorage: await this.handleFileStorageSync(provider, linkedUserId); break; - case ConnectorCategory.Ats: - await this.handleAtsSync(provider, linkedUserId); - break; - case ConnectorCategory.Hris: - await this.handleHrisSync(provider, linkedUserId); - break; case ConnectorCategory.Accounting: await this.handleAccountingSync(provider, linkedUserId); break; @@ -236,144 +228,6 @@ export class CoreSyncService { return; } - async handleHrisSync(provider: string, linkedUserId: string) { - // add other objects when i have info on the order - //todo: define here the topological order PER provider - const tasks = [ - () => - this.registry.getService('hris', 'company').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - ]; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: provider.toLowerCase(), - }, - }); - - for (const task of tasks) { - try { - await task(); - } catch (error) { - this.logger.error(`Task failed: ${error.message}`, error); - } - } - const companies = await this.prisma.hris_companies.findMany({ - where: { - id_connection: connection.id_connection, - }, - }); - - const companiesEmployeeTasks = companies.map( - (company) => async () => - this.registry.getService('hris', 'employee').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - id_company: company.id_hris_company, - }), - ); - - const companiesEmployerBenefitsTasks = companies.map( - (company) => async () => - this.registry.getService('hris', 'employerbenefit').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - id_company: company.id_hris_company, - }), - ); - - const companiesGroupsTasks = companies.map( - (company) => async () => - this.registry.getService('hris', 'group').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - id_company: company.id_hris_company, - }), - ); - - const employees = await this.prisma.hris_employees.findMany({ - where: { - id_connection: connection.id_connection, - }, - }); - - const employeesBenefitsTasks = employees.map( - (employee) => async () => - this.registry.getService('hris', 'benefit').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - id_employee: employee.id_hris_employee, - }), - ); - - const employeesLocationsTasks = employees.map( - (employee) => async () => - this.registry.getService('hris', 'location').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - id_employee: employee.id_hris_employee, - }), - ); - - for (const task of companiesEmployeeTasks) { - try { - await task(); - } catch (error) { - this.logger.error( - `Companies Employee task failed: ${error.message}`, - error, - ); - } - } - - for (const task of employeesLocationsTasks) { - try { - await task(); - } catch (error) { - this.logger.error( - `Companies Location task failed: ${error.message}`, - error, - ); - } - } - - for (const task of companiesEmployerBenefitsTasks) { - try { - await task(); - } catch (error) { - this.logger.error( - `Companies Employer Benefits task failed: ${error.message}`, - error, - ); - } - } - - for (const task of companiesGroupsTasks) { - try { - await task(); - } catch (error) { - this.logger.error( - `Companies Groups task failed: ${error.message}`, - error, - ); - } - } - - for (const task of employeesBenefitsTasks) { - try { - await task(); - } catch (error) { - this.logger.error( - `Employees Benefits task failed: ${error.message}`, - error, - ); - } - } - } - async handleCrmSync(provider: string, linkedUserId: string) { const tasks = [ () => @@ -685,92 +539,6 @@ export class CoreSyncService { } } - async handleAtsSync(provider: string, linkedUserId: string) { - const tasks = [ - () => - this.registry.getService('ats', 'office').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'department').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'rejectreason').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'user').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'jobs').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'jobinterviewstage').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'candidate').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'tag').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'eeocs').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'attachment').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'activity').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'application').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'offer').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'interview').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - () => - this.registry.getService('ats', 'scorecard').syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUserId, - }), - ]; - for (const task of tasks) { - try { - await task(); - } catch (error) { - this.logger.error(`File Storage Task failed: ${error.message}`, error); - } - } - } // we must have a sync_jobs table with 7 (verticals) rows, one of each is syncing details async getSyncStatus(vertical: string) { diff --git a/packages/api/src/@core/utils/decorators/utils.ts b/packages/api/src/@core/utils/decorators/utils.ts index 3f0fc49e3..bec576eca 100644 --- a/packages/api/src/@core/utils/decorators/utils.ts +++ b/packages/api/src/@core/utils/decorators/utils.ts @@ -1,7 +1,5 @@ import { CRM_PROVIDERS, - HRIS_PROVIDERS, - ATS_PROVIDERS, ACCOUNTING_PROVIDERS, TICKETING_PROVIDERS, MARKETINGAUTOMATION_PROVIDERS, @@ -11,10 +9,8 @@ import { CrmObject, FileStorageObject, TicketingObject, - HrisObject, AccountingObject, MarketingAutomationObject, - AtsObject, } from '@panora/shared'; import * as fs from 'fs'; import * as path from 'path'; @@ -27,8 +23,6 @@ interface ProviderMetadata { export async function generatePanoraParamsSpec(spec: any) { const verticals = { crm: [CRM_PROVIDERS, CrmObject], - hris: [HRIS_PROVIDERS, HrisObject], - ats: [ATS_PROVIDERS, AtsObject], accounting: [ACCOUNTING_PROVIDERS, AccountingObject], ticketing: [TICKETING_PROVIDERS, TicketingObject], marketingautomation: [ diff --git a/packages/api/src/@core/utils/errors.ts b/packages/api/src/@core/utils/errors.ts index b8b778cf9..d447565ea 100644 --- a/packages/api/src/@core/utils/errors.ts +++ b/packages/api/src/@core/utils/errors.ts @@ -106,15 +106,11 @@ export class ConnectionsError extends ErrorBase< | 'HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION' | 'HANDLE_OAUTH_CALLBACK_FILESTORAGE' | 'HANDLE_OAUTH_CALLBACK_ACCOUNTING' - | 'HANDLE_OAUTH_CALLBACK_ATS' - | 'HANDLE_OAUTH_CALLBACK_HRIS' | 'HANDLE_OAUTH_REFRESH_TICKETING' | 'HANDLE_OAUTH_REFRESH_CRM' | 'HANDLE_OAUTH_REFRESH_MARKETINGAUTOMATION' | 'HANDLE_OAUTH_REFRESH_FILESTORAGE' | 'HANDLE_OAUTH_REFRESH_ACCOUNTING' - | 'HANDLE_OAUTH_REFRESH_ATS' - | 'HANDLE_OAUTH_REFRESH_HRIS' > {} export class ManagedWebhooksError extends ErrorBase< @@ -185,8 +181,6 @@ export class CoreSyncError extends ErrorBase< | 'MARKETINGAUTOMATION_INITIAL_SYNC_ERROR' | 'FILESTORAGE_INITIAL_SYNC_ERROR' | 'ACCOUNTING_INITIAL_SYNC_ERROR' - | 'HRIS_INITIAL_SYNC_ERROR' - | 'ATS_INITIAL_SYNC_ERROR' | 'MARKETINGAUTOMATION_INITIAL_SYNC_ERROR' > {} diff --git a/packages/api/src/@core/utils/types/desunify.input.ts b/packages/api/src/@core/utils/types/desunify.input.ts index f98eba7a0..bd425e913 100644 --- a/packages/api/src/@core/utils/types/desunify.input.ts +++ b/packages/api/src/@core/utils/types/desunify.input.ts @@ -1,18 +1,14 @@ import { AccountingObjectInput } from './original/original.accounting'; -import { AtsObjectInput } from './original/original.ats'; import { CrmObjectInput } from './original/original.crm'; import { EcommerceObjectInput } from './original/original.ecommerce'; import { FileStorageObjectInput } from './original/original.file-storage'; -import { HrisObjectInput } from './original/original.hris'; import { MarketingAutomationObjectInput } from './original/original.marketing-automation'; import { TicketingObjectInput } from './original/original.ticketing'; export type DesunifyReturnType = | CrmObjectInput | TicketingObjectInput - | AtsObjectInput | MarketingAutomationObjectInput | AccountingObjectInput | FileStorageObjectInput - | HrisObjectInput | EcommerceObjectInput; diff --git a/packages/api/src/@core/utils/types/index.ts b/packages/api/src/@core/utils/types/index.ts index c1ec43c0b..e52f4a3e3 100644 --- a/packages/api/src/@core/utils/types/index.ts +++ b/packages/api/src/@core/utils/types/index.ts @@ -1,12 +1,10 @@ import { AccountingObject, UnifiedAccounting } from '@accounting/@lib/@types'; -import { AtsObject, UnifiedAts } from '@ats/@lib/@types'; import { CrmObject, UnifiedCrm } from '@crm/@lib/@types'; import { EcommerceObject, UnifiedEcommerce } from '@ecommerce/@lib/@types'; import { FileStorageObject, UnifiedFileStorage, } from '@filestorage/@lib/@types'; -import { HrisObject, UnifiedHris } from '@hris/@lib/@types'; import { MarketingAutomationObject, UnifiedMarketingAutomation, @@ -19,8 +17,6 @@ export type Unified = | UnifiedTicketing | UnifiedFileStorage | UnifiedMarketingAutomation - | UnifiedAts - | UnifiedHris | UnifiedAccounting | UnifiedEcommerce; @@ -28,8 +24,6 @@ export type UnifyReturnType = Unified | Unified[]; export type TargetObject = | CrmObject - | HrisObject - | AtsObject | AccountingObject | FileStorageObject | MarketingAutomationObject diff --git a/packages/api/src/@core/utils/types/original/original.ats.ts b/packages/api/src/@core/utils/types/original/original.ats.ts deleted file mode 100644 index 0cf06bb8e..000000000 --- a/packages/api/src/@core/utils/types/original/original.ats.ts +++ /dev/null @@ -1,174 +0,0 @@ -/* INPUT */ - -import { - AshbyActivityInput, - AshbyActivityOutput, -} from '@ats/activity/services/ashby/types'; -import { - AshbyApplicationInput, - AshbyApplicationOutput, -} from '@ats/application/services/ashby/types'; -import { - AshbyAttachmentInput, - AshbyAttachmentOutput, -} from '@ats/attachment/services/ashby/types'; -import { - AshbyCandidateInput, - AshbyCandidateOutput, -} from '@ats/candidate/services/ashby/types'; -import { - AshbyDepartmentInput, - AshbyDepartmentOutput, -} from '@ats/department/services/ashby/types'; -import { - AshbyInterviewInput, - AshbyInterviewOutput, -} from '@ats/interview/services/ashby/types'; -import { AshbyJobInput, AshbyJobOutput } from '@ats/job/services/ashby/types'; -import { - AshbyJobInterviewStageInput, - AshbyJobInterviewStageOutput, -} from '@ats/jobinterviewstage/services/ashby/types'; -import { - AshbyOfferInput, - AshbyOfferOutput, -} from '@ats/offer/services/ashby/types'; -import { - AshbyOfficeInput, - AshbyOfficeOutput, -} from '@ats/office/services/ashby/types'; -import { - AshbyRejectReasonInput, - AshbyRejectReasonOutput, -} from '@ats/rejectreason/services/ashby/types'; -import { AshbyTagInput, AshbyTagOutput } from '@ats/tag/services/ashby/types'; -import { - AshbyUserInput, - AshbyUserOutput, -} from '@ats/user/services/ashby/types'; - -/* activity */ -export type OriginalActivityInput = AshbyActivityInput; - -/* application */ -export type OriginalApplicationInput = AshbyApplicationInput; - -/* attachment */ -export type OriginalAttachmentInput = AshbyAttachmentInput; - -/* candidate */ -export type OriginalCandidateInput = AshbyCandidateInput; - -/* department */ -export type OriginalDepartmentInput = AshbyDepartmentInput; - -/* interview */ -export type OriginalInterviewInput = AshbyInterviewInput; - -/* jobinterviewstage */ -export type OriginalJobInterviewStageInput = AshbyJobInterviewStageInput; - -/* job */ -export type OriginalJobInput = AshbyJobInput; - -/* offer */ -export type OriginalOfferInput = AshbyOfferInput; - -/* office */ -export type OriginalOfficeInput = AshbyOfficeInput; - -/* rejectreason */ -export type OriginalRejectReasonInput = AshbyRejectReasonInput; - -/* scorecard */ -export type OriginalScoreCardInput = any; - -/* tag */ -export type OriginalTagInput = AshbyTagInput; - -/* user */ -export type OriginalUserInput = AshbyUserInput; - -/* eeocs */ -export type OriginalEeocsInput = any; - -export type AtsObjectInput = - | OriginalActivityInput - | OriginalApplicationInput - | OriginalAttachmentInput - | OriginalCandidateInput - | OriginalDepartmentInput - | OriginalInterviewInput - | OriginalJobInterviewStageInput - | OriginalJobInput - | OriginalOfferInput - | OriginalOfficeInput - | OriginalRejectReasonInput - | OriginalScoreCardInput - | OriginalTagInput - | OriginalUserInput - | OriginalEeocsInput; - -/* OUTPUT */ - -/* activity */ -export type OriginalActivityOutput = AshbyActivityOutput; - -/* application */ -export type OriginalApplicationOutput = AshbyApplicationOutput; - -/* attachment */ -export type OriginalAttachmentOutput = AshbyAttachmentOutput; - -/* candidate */ -export type OriginalCandidateOutput = AshbyCandidateOutput; - -/* department */ -export type OriginalDepartmentOutput = AshbyDepartmentOutput; - -/* interview */ -export type OriginalInterviewOutput = AshbyInterviewOutput; - -/* jobinterviewstage */ -export type OriginalJobInterviewStageOutput = AshbyJobInterviewStageOutput; - -/* job */ -export type OriginalJobOutput = AshbyJobOutput; - -/* offer */ -export type OriginalOfferOutput = AshbyOfferOutput; - -/* office */ -export type OriginalOfficeOutput = AshbyOfficeOutput; - -/* rejectreason */ -export type OriginalRejectReasonOutput = AshbyRejectReasonOutput; - -/* scorecard */ -export type OriginalScoreCardOutput = any; - -/* tag */ -export type OriginalTagOutput = AshbyTagOutput; - -/* user */ -export type OriginalUserOutput = AshbyUserOutput; - -/* eeocs */ -export type OriginalEeocsOutput = any; - -export type AtsObjectOutput = - | OriginalActivityOutput - | OriginalApplicationOutput - | OriginalAttachmentOutput - | OriginalCandidateOutput - | OriginalDepartmentOutput - | OriginalInterviewOutput - | OriginalJobInterviewStageOutput - | OriginalJobOutput - | OriginalOfferOutput - | OriginalOfficeOutput - | OriginalRejectReasonOutput - | OriginalScoreCardOutput - | OriginalTagOutput - | OriginalUserOutput - | OriginalEeocsOutput; diff --git a/packages/api/src/@core/utils/types/original/original.hris.ts b/packages/api/src/@core/utils/types/original/original.hris.ts deleted file mode 100644 index df980dafa..000000000 --- a/packages/api/src/@core/utils/types/original/original.hris.ts +++ /dev/null @@ -1,152 +0,0 @@ -/* INPUT */ - -import { GustoBenefitOutput } from '@hris/benefit/services/gusto/types'; -import { DeelCompanyOutput } from '@hris/company/services/deel/types'; -import { GustoCompanyOutput } from '@hris/company/services/gusto/types'; -import { DeelEmployeeOutput } from '@hris/employee/services/deel/types'; -import { GustoEmployeeOutput } from '@hris/employee/services/gusto/types'; -import { SageEmployeeOutput } from '@hris/employee/services/sage/types'; -import { GustoEmployerbenefitOutput } from '@hris/employerbenefit/services/gusto/types'; -import { DeelEmploymentOutput } from '@hris/employment/services/deel/types'; -import { GustoEmploymentOutput } from '@hris/employment/services/gusto/types'; -import { DeelGroupOutput } from '@hris/group/services/deel/types'; -import { GustoGroupOutput } from '@hris/group/services/gusto/types'; -import { SageGroupOutput } from '@hris/group/services/sage/types'; -import { DeelLocationOutput } from '@hris/location/services/deel/types'; -import { GustoLocationOutput } from '@hris/location/services/gusto/types'; -import { SageTimeoffOutput } from '@hris/timeoff/services/sage/types'; -import { SageTimeoffbalanceOutput } from '@hris/timeoffbalance/services/sage/types'; - -/* bankinfo */ -export type OriginalBankInfoInput = any; - -/* benefit */ -export type OriginalBenefitInput = any; - -/* company */ -export type OriginalCompanyInput = any; - -/* dependent */ -export type OriginalDependentInput = any; - -/* employee */ -export type OriginalEmployeeInput = any; - -/* employeepayrollrun */ -export type OriginalEmployeePayrollRunInput = any; - -/* employerbenefit */ -export type OriginalEmployerBenefitInput = any; - -/* employment */ -export type OriginalEmploymentInput = any; - -/* group */ -export type OriginalGroupInput = any; - -/* location */ -export type OriginalLocationInput = any; - -/* paygroup */ -export type OriginalPayGroupInput = any; - -/* payrollrun */ -export type OriginalPayrollRunInput = any; - -/* timeoff */ -export type OriginalTimeoffInput = any; - -/* timeoffbalance */ -export type OriginalTimeoffBalanceInput = any; - -/* timesheetentry */ -export type OriginalTimesheetentryInput = any; - -export type HrisObjectInput = - | OriginalBankInfoInput - | OriginalBenefitInput - | OriginalCompanyInput - | OriginalDependentInput - | OriginalEmployeeInput - | OriginalEmployeePayrollRunInput - | OriginalEmployerBenefitInput - | OriginalEmploymentInput - | OriginalGroupInput - | OriginalLocationInput - | OriginalPayGroupInput - | OriginalPayrollRunInput - | OriginalTimeoffInput - | OriginalTimeoffBalanceInput - | OriginalTimesheetentryInput; - -/* OUTPUT */ - -/* bankinfo */ -export type OriginalBankInfoOutput = any; - -/* benefit */ -export type OriginalBenefitOutput = GustoBenefitOutput; - -/* company */ -export type OriginalCompanyOutput = GustoCompanyOutput | DeelCompanyOutput; - -/* dependent */ -export type OriginalDependentOutput = any; - -/* employee */ -export type OriginalEmployeeOutput = - | GustoEmployeeOutput - | SageEmployeeOutput - | DeelEmployeeOutput; - -/* employeepayrollrun */ -export type OriginalEmployeePayrollRunOutput = any; - -/* employerbenefit */ -export type OriginalEmployerBenefitOutput = GustoEmployerbenefitOutput; - -/* employment */ -export type OriginalEmploymentOutput = - | GustoEmploymentOutput - | DeelEmploymentOutput; - -/* group */ -export type OriginalGroupOutput = - | GustoGroupOutput - | DeelGroupOutput - | SageGroupOutput; - -/* location */ -export type OriginalLocationOutput = GustoLocationOutput | DeelLocationOutput; - -/* paygroup */ -export type OriginalPayGroupOutput = any; - -/* payrollrun */ -export type OriginalPayrollRunOutput = any; - -/* timeoff */ -export type OriginalTimeoffOutput = SageTimeoffOutput; - -/* timeoffbalance */ -export type OriginalTimeoffBalanceOutput = SageTimeoffbalanceOutput; - -/* timesheetentry */ -export type OriginalTimesheetentryOutput = any; - -export type HrisObjectOutput = - | OriginalBankInfoOutput - | OriginalBenefitOutput - | OriginalCompanyOutput - | OriginalDependentOutput - | OriginalEmployeeOutput - | OriginalEmployeePayrollRunOutput - | OriginalEmployerBenefitOutput - | OriginalEmploymentOutput - | OriginalGroupOutput - | OriginalLocationOutput - | OriginalPayGroupOutput - | OriginalPayrollRunOutput - | OriginalTimeoffOutput - | OriginalTimeoffBalanceOutput - | OriginalTimesheetentryOutput; diff --git a/packages/api/src/@core/utils/types/unify.output.ts b/packages/api/src/@core/utils/types/unify.output.ts index e0725b161..a909ae197 100644 --- a/packages/api/src/@core/utils/types/unify.output.ts +++ b/packages/api/src/@core/utils/types/unify.output.ts @@ -1,18 +1,14 @@ import { AccountingObjectOutput } from './original/original.accounting'; -import { AtsObjectOutput } from './original/original.ats'; import { CrmObjectOutput } from './original/original.crm'; import { EcommerceObjectOutput } from './original/original.ecommerce'; import { FileStorageObjectOutput } from './original/original.file-storage'; -import { HrisObjectOutput } from './original/original.hris'; import { MarketingAutomationObjectOutput } from './original/original.marketing-automation'; import { TicketingObjectOutput } from './original/original.ticketing'; export type UnifySourceType = | CrmObjectOutput | TicketingObjectOutput - | AtsObjectOutput | MarketingAutomationObjectOutput | AccountingObjectOutput | FileStorageObjectOutput - | HrisObjectOutput | EcommerceObjectOutput; diff --git a/packages/api/src/app.module.ts b/packages/api/src/app.module.ts index c00dff5d1..5d0e8ab96 100644 --- a/packages/api/src/app.module.ts +++ b/packages/api/src/app.module.ts @@ -10,10 +10,8 @@ import { LoggerModule } from 'nestjs-pino'; import { AccountingModule } from './accounting/accounting.module'; import { AppController } from './app.controller'; import { AppService } from './app.service'; -import { AtsModule } from './ats/ats.module'; import { CrmModule } from './crm/crm.module'; import { FileStorageModule } from './filestorage/filestorage.module'; -import { HrisModule } from './hris/hris.module'; import { MarketingAutomationModule } from './marketingautomation/marketingautomation.module'; import { CoreSharedModule } from '@@core/@core-services/module'; import { EcommerceModule } from '@ecommerce/ecommerce.module'; @@ -22,9 +20,7 @@ import { EcommerceModule } from '@ecommerce/ecommerce.module'; imports: [ CoreSharedModule, CoreModule, - HrisModule, MarketingAutomationModule, - AtsModule, AccountingModule, FileStorageModule, EcommerceModule, diff --git a/packages/api/src/ats/@lib/@types/index.ts b/packages/api/src/ats/@lib/@types/index.ts deleted file mode 100644 index 2d2df5f49..000000000 --- a/packages/api/src/ats/@lib/@types/index.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { IActivityService } from '@ats/activity/types'; -import { - UnifiedAtsActivityInput, - UnifiedAtsActivityOutput, -} from '@ats/activity/types/model.unified'; -import { IApplicationService } from '@ats/application/types'; -import { - UnifiedAtsApplicationInput, - UnifiedAtsApplicationOutput, -} from '@ats/application/types/model.unified'; -import { IAttachmentService } from '@ats/attachment/types'; -import { - UnifiedAtsAttachmentInput, - UnifiedAtsAttachmentOutput, -} from '@ats/attachment/types/model.unified'; -import { ICandidateService } from '@ats/candidate/types'; -import { - UnifiedAtsCandidateInput, - UnifiedAtsCandidateOutput, -} from '@ats/candidate/types/model.unified'; -import { IDepartmentService } from '@ats/department/types'; -import { - UnifiedAtsDepartmentInput, - UnifiedAtsDepartmentOutput, -} from '@ats/department/types/model.unified'; -import { IEeocsService } from '@ats/eeocs/types'; -import { - UnifiedAtsEeocsInput, - UnifiedAtsEeocsOutput, -} from '@ats/eeocs/types/model.unified'; -import { IInterviewService } from '@ats/interview/types'; -import { - UnifiedAtsInterviewInput, - UnifiedAtsInterviewOutput, -} from '@ats/interview/types/model.unified'; -import { IJobService } from '@ats/job/types'; -import { - UnifiedAtsJobInput, - UnifiedAtsJobOutput, -} from '@ats/job/types/model.unified'; -import { IJobInterviewStageService } from '@ats/jobinterviewstage/types'; -import { - UnifiedAtsJobinterviewstageInput, - UnifiedAtsJobinterviewstageOutput, -} from '@ats/jobinterviewstage/types/model.unified'; -import { IOfferService } from '@ats/offer/types'; -import { - UnifiedAtsOfferInput, - UnifiedAtsOfferOutput, -} from '@ats/offer/types/model.unified'; -import { IOfficeService } from '@ats/office/types'; -import { - UnifiedAtsOfficeInput, - UnifiedAtsOfficeOutput, -} from '@ats/office/types/model.unified'; -import { IRejectReasonService } from '@ats/rejectreason/types'; -import { - UnifiedAtsRejectreasonInput, - UnifiedAtsRejectreasonOutput, -} from '@ats/rejectreason/types/model.unified'; -import { IScoreCardService } from '@ats/scorecard/types'; -import { - UnifiedAtsScorecardInput, - UnifiedAtsScorecardOutput, -} from '@ats/scorecard/types/model.unified'; -import { ITagService } from '@ats/tag/types'; -import { - UnifiedAtsTagInput, - UnifiedAtsTagOutput, -} from '@ats/tag/types/model.unified'; -import { IUserService } from '@ats/user/types'; -import { - UnifiedAtsUserInput, - UnifiedAtsUserOutput, -} from '@ats/user/types/model.unified'; -import { ApiProperty } from '@nestjs/swagger'; -import { IsIn, IsString } from 'class-validator'; - -export enum AtsObject { - activity = 'activity', - application = 'application', - attachment = 'attachment', - candidate = 'candidate', - department = 'department', - interview = 'interview', - jobinterviewstage = 'jobinterviewstage', - job = 'job', - offer = 'offer', - office = 'office', - rejectreason = 'rejectreason', - scorecard = 'scorecard', - tag = 'tag', - user = 'user', - eeocs = 'eeocs', -} - -export type UnifiedAts = - | UnifiedAtsActivityInput - | UnifiedAtsActivityOutput - | UnifiedAtsApplicationInput - | UnifiedAtsApplicationOutput - | UnifiedAtsAttachmentInput - | UnifiedAtsAttachmentOutput - | UnifiedAtsCandidateInput - | UnifiedAtsCandidateOutput - | UnifiedAtsDepartmentInput - | UnifiedAtsDepartmentOutput - | UnifiedAtsInterviewInput - | UnifiedAtsInterviewOutput - | UnifiedAtsJobinterviewstageInput - | UnifiedAtsJobinterviewstageOutput - | UnifiedAtsJobInput - | UnifiedAtsJobOutput - | UnifiedAtsOfferInput - | UnifiedAtsOfferOutput - | UnifiedAtsOfficeInput - | UnifiedAtsOfficeOutput - | UnifiedAtsRejectreasonInput - | UnifiedAtsRejectreasonOutput - | UnifiedAtsScorecardInput - | UnifiedAtsScorecardOutput - | UnifiedAtsTagInput - | UnifiedAtsTagOutput - | UnifiedAtsUserInput - | UnifiedAtsUserOutput - | UnifiedAtsEeocsInput - | UnifiedAtsEeocsOutput; - -export type IAtsService = - | IActivityService - | IApplicationService - | IAttachmentService - | ICandidateService - | IDepartmentService - | IInterviewService - | IJobInterviewStageService - | IJobService - | IOfferService - | IOfficeService - | IRejectReasonService - | IScoreCardService - | ITagService - | IUserService - | IEeocsService; - -export class Email { - @ApiProperty({ - type: String, - nullable: true, - description: 'The email address', - }) - @IsString() - email_address: string; - - @ApiProperty({ - type: String, - //enum: ['PERSONAL', 'WORK'], - nullable: true, - description: - 'The email address type. Authorized values are either PERSONAL or WORK.', - }) - ////@IsIn(['PERSONAL', 'WORK']) - @IsString() - email_address_type: string; -} - -export class Phone { - @ApiProperty({ - type: String, - nullable: true, - description: - 'The phone number starting with a plus (+) followed by the country code (e.g +336676778890 for France)', - }) - @IsString() - phone_number: string; - - @ApiProperty({ - type: String, - //enum: ['MOBILE', 'WORK'], - nullable: true, - description: 'The phone type. Authorized values are either MOBILE or WORK', - }) - ////@IsIn(['MOBILE', 'WORK']) - @IsString() - phone_type: string; -} - -export class Url { - @ApiProperty({ - type: String, - nullable: true, - description: 'The url.', - }) - @IsString() - url: string; - - @ApiProperty({ - type: String, - nullable: true, - description: - 'The url type. It takes [WEBSITE | BLOG | LINKEDIN | GITHUB | OTHER]', - }) - @IsString() - url_type: 'WEBSITE' | 'BLOG' | 'LINKEDIN' | 'GITHUB' | 'OTHER' | string; -} diff --git a/packages/api/src/ats/@lib/@unification/index.ts b/packages/api/src/ats/@lib/@unification/index.ts deleted file mode 100644 index f890a35ea..000000000 --- a/packages/api/src/ats/@lib/@unification/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { AtsObject } from '@ats/@lib/@types'; -import { BullQueueService } from '@@core/@core-services/queues/shared.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Unified, UnifyReturnType } from '@@core/utils/types'; -import { UnifySourceType } from '@@core/utils/types/unify.output'; -import { AtsObjectInput } from '@@core/utils/types/original/original.ats'; -import { IUnification } from '@@core/utils/types/interface'; -import { UnificationRegistry } from '@@core/@core-services/registries/unification.registry'; -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class AtsUnificationService implements IUnification { - constructor( - private registry: UnificationRegistry, - private mappersRegistry: MappersRegistry, - ) { - this.registry.registerService('ats', this); - } - async desunify({ - sourceObject, - targetType_, - providerName, - customFieldMappings, - }: { - sourceObject: T; - targetType_: AtsObject; - providerName: string; - customFieldMappings?: { - slug: string; - remote_id: string; - }[]; - }): Promise { - const mapping = this.mappersRegistry.getService( - 'ats', - targetType_, - providerName, - ); - if (mapping) { - return mapping.desunify(sourceObject, customFieldMappings); - } - - throw new Error( - `Unsupported target type for ${providerName}: ${targetType_}`, - ); - } - - async unify({ - sourceObject, - targetType_, - providerName, - connectionId, - customFieldMappings, - }: { - sourceObject: T; - targetType_: AtsObject; - providerName: string; - connectionId: string; - customFieldMappings?: { - slug: string; - remote_id: string; - }[]; - }): Promise { - const mapping = this.mappersRegistry.getService( - 'ats', - targetType_, - providerName, - ); - if (mapping) { - return mapping.unify(sourceObject, connectionId, customFieldMappings); - } - - throw new Error( - `Unsupported target type for ${providerName}: ${targetType_}`, - ); - } -} diff --git a/packages/api/src/ats/@lib/@utils/index.ts b/packages/api/src/ats/@lib/@utils/index.ts deleted file mode 100644 index 2f676c809..000000000 --- a/packages/api/src/ats/@lib/@utils/index.ts +++ /dev/null @@ -1,261 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Email, Phone, Url } from '../@types'; - -@Injectable() -export class Utils { - constructor(private readonly prisma: PrismaService) {} - - async getJobUuidFromRemoteId(remote_id: string, connection_id: string) { - try { - const res = await this.prisma.ats_jobs.findFirst({ - where: { - remote_id: remote_id, - id_connection: connection_id, - }, - }); - if (!res) { - return undefined; - } - return res.id_ats_job; - } catch (error) { - throw error; - } - } - - async getCandidateUuidFromRemoteId(remote_id: string, connection_id: string) { - try { - const res = await this.prisma.ats_candidates.findFirst({ - where: { - remote_id: remote_id, - id_connection: connection_id, - }, - }); - if (!res) { - return undefined; - } - return res.id_ats_candidate; - } catch (error) { - throw error; - } - } - - async getCandidateRemoteIdFromUuid(uuid: string) { - try { - const res = await this.prisma.ats_candidates.findUnique({ - where: { - id_ats_candidate: uuid, - }, - }); - if (!res) { - return undefined; - } - return res.remote_id; - } catch (error) { - throw error; - } - } - async getUserRemoteIdFromUuid(uuid: string) { - try { - const res = await this.prisma.ats_users.findUnique({ - where: { - id_ats_user: uuid, - }, - }); - if (!res) { - return undefined; - } - return res.remote_id; - } catch (error) { - throw error; - } - } - async getInterviewStageRemoteIdFromUuid(uuid: string) { - try { - const res = await this.prisma.ats_job_interview_stages.findUnique({ - where: { - id_ats_job_interview_stage: uuid, - }, - }); - if (!res) { - return undefined; - } - return res.remote_id; - } catch (error) { - throw error; - } - } - async getJobRemoteIdFromUuid(uuid: string) { - try { - const res = await this.prisma.ats_jobs.findUnique({ - where: { - id_ats_job: uuid, - }, - }); - if (!res) { - return undefined; - } - return res.remote_id; - } catch (error) { - throw error; - } - } - - async getRejectReasonUuidFromRemoteId( - remote_id: string, - connection_id: string, - ) { - try { - const res = await this.prisma.ats_reject_reasons.findFirst({ - where: { - remote_id: remote_id, - id_connection: connection_id, - }, - }); - if (!res) { - return undefined; - } - return res.id_ats_reject_reason; - } catch (error) { - throw error; - } - } - - async getUserUuidFromRemoteId(remote_id: string, connection_id: string) { - try { - const res = await this.prisma.ats_users.findFirst({ - where: { - remote_id: remote_id, - id_connection: connection_id, - }, - }); - if (!res) { - return undefined; - } - return res.id_ats_user; - } catch (error) { - throw error; - } - } - - async getStageUuidFromRemoteId(remote_id: string, connection_id: string) { - try { - const res = await this.prisma.ats_job_interview_stages.findFirst({ - where: { - remote_id: remote_id, - id_connection: connection_id, - }, - }); - if (!res) { - return undefined; - } - return res.id_ats_job_interview_stage; - } catch (error) { - throw error; - } - } - async getApplicationUuidFromRemoteId( - remote_id: string, - connection_id: string, - ) { - try { - const res = await this.prisma.ats_applications.findFirst({ - where: { - remote_id: remote_id, - id_connection: connection_id, - }, - }); - if (!res) { - return undefined; - } - return res.id_ats_application; - } catch (error) { - throw error; - } - } - async getDepartmentUuidFromRemoteId( - remote_id: string, - connection_id: string, - ) { - try { - const res = await this.prisma.ats_departments.findFirst({ - where: { - remote_id: remote_id, - id_connection: connection_id, - }, - }); - if (!res) { - return undefined; - } - return res.id_ats_department; - } catch (error) { - throw error; - } - } - - async getOfficeUuidFromRemoteId(remote_id: string, connection_id: string) { - try { - const res = await this.prisma.ats_offices.findFirst({ - where: { - remote_id: remote_id, - id_connection: connection_id, - }, - }); - if (!res) { - return undefined; - } - return res.id_ats_office; - } catch (error) { - throw error; - } - } - - normalizeEmailsAndNumbers(email_addresses: Email[], phone_numbers: Phone[]) { - let normalizedEmails = []; - const normalizedPhones = []; - - if (email_addresses) { - normalizedEmails = email_addresses.map((email) => ({ - value: email.email_address, - created_at: new Date(), - modified_at: new Date(), - id_ats_candidate_email_address: uuidv4(), - type: - email.email_address_type === '' ? 'work' : email.email_address_type, - })); - } - if (phone_numbers) { - phone_numbers.forEach((phone) => { - if (!phone.phone_number) return; - normalizedPhones.push({ - created_at: new Date(), - modified_at: new Date(), - id_ats_candidate_phone_number: uuidv4(), - type: phone.phone_type === '' ? 'work' : phone.phone_type, - value: phone.phone_number, - }); - }); - } - return { - normalizedEmails, - normalizedPhones, - }; - } - normalizeUrls(urls: Url[]) { - const normalizedUrls = []; - if (urls) { - urls.forEach((url) => { - if (!url.url) return; - normalizedUrls.push({ - created_at: new Date(), - modified_at: new Date(), - id_ats_candidate_url: uuidv4(), - type: url.url_type || null, - value: url.url, - }); - }); - } - return normalizedUrls; - } -} diff --git a/packages/api/src/ats/README.md b/packages/api/src/ats/README.md deleted file mode 100644 index 08ddd6b86..000000000 --- a/packages/api/src/ats/README.md +++ /dev/null @@ -1,8 +0,0 @@ -### ATS -Panora supports integration with the following objects across multiple platforms: - -| | Activities | Applications | Candidates | Departments | Interviews | Jobs | Offers | Offices | Scorecard | Users | -|-------------|:----------:|:------------:|:----------:|:-----------:|:----------:|:----:|:------:|:-------:|:---------:|:-----:| -| Ashby | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | - -Your favourite software is missing? [Ask the community to build a connector!](https://github.com/panoratech/Panora/issues/new) \ No newline at end of file diff --git a/packages/api/src/ats/activity/activity.controller.ts b/packages/api/src/ats/activity/activity.controller.ts deleted file mode 100644 index fb43427c3..000000000 --- a/packages/api/src/ats/activity/activity.controller.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { - UnifiedAtsActivityInput, - UnifiedAtsActivityOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { ActivityService } from './services/activity.service'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, - ApiPostCustomResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('ats/activities') -@Controller('ats/activities') -export class ActivityController { - constructor( - private readonly activityService: ActivityService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(ActivityController.name); - } - - @ApiOperation({ - operationId: 'listAtsActivity', - summary: 'List Activities', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsActivityOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getActivitys( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.activityService.getActivities( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsActivity', - summary: 'Retrieve Activities', - description: 'Retrieve Activities from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the activity you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsActivityOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.activityService.getActivity( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } - - @ApiOperation({ - operationId: 'createAtsActivity', - summary: 'Create Activities', - description: 'Create Activities in any supported Ats software', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiBody({ type: UnifiedAtsActivityInput }) - @ApiPostCustomResponse(UnifiedAtsActivityOutput) - @UseGuards(ApiKeyAuthGuard) - @Post() - async addActivity( - @Body() unifiedActivityData: UnifiedAtsActivityInput, - @Headers('x-connection-token') connection_token: string, - @Query('remote_data') remote_data?: boolean, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.activityService.addActivity( - unifiedActivityData, - connectionId, - projectId, - remoteSource, - linkedUserId, - remote_data, - ); - } catch (error) { - throw new Error(error); - } - } -} diff --git a/packages/api/src/ats/activity/activity.module.ts b/packages/api/src/ats/activity/activity.module.ts deleted file mode 100644 index 7d3ab5c01..000000000 --- a/packages/api/src/ats/activity/activity.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { Module } from '@nestjs/common'; -import { ActivityController } from './activity.controller'; -import { ActivityService } from './services/activity.service'; -import { AshbyService } from './services/ashby'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Utils } from '@ats/@lib/@utils'; -import { AshbyActivityMapper } from './services/ashby/mappers'; - -@Module({ - controllers: [ActivityController], - providers: [ - ActivityService, - CoreUnification, - SyncService, - WebhookService, - ServiceRegistry, - IngestDataService, - AshbyActivityMapper, - Utils, - /* PROVIDERS SERVICES */ - AshbyService, - ], - exports: [SyncService], -}) -export class ActivityModule {} diff --git a/packages/api/src/ats/activity/services/activity.service.ts b/packages/api/src/ats/activity/services/activity.service.ts deleted file mode 100644 index f1c622152..000000000 --- a/packages/api/src/ats/activity/services/activity.service.ts +++ /dev/null @@ -1,371 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ApiResponse } from '@@core/utils/types'; -import { OriginalActivityOutput } from '@@core/utils/types/original/original.ats'; -import { AtsObject } from '@ats/@lib/@types'; -import { Injectable } from '@nestjs/common'; -import { v4 as uuidv4 } from 'uuid'; -import { IActivityService } from '../types'; -import { - ActivityType, - ActivityVisibility, - UnifiedAtsActivityInput, - UnifiedAtsActivityOutput, -} from '../types/model.unified'; -import { ServiceRegistry } from './registry.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -@Injectable() -export class ActivityService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private ingestService: IngestDataService, - ) { - this.logger.setContext(ActivityService.name); - } - - async addActivity( - unifiedActivityData: UnifiedAtsActivityInput, - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - remote_data?: boolean, - ): Promise { - try { - const linkedUser = await this.validateLinkedUser(linkedUserId); - - const customFieldMappings = - await this.fieldMappingService.getCustomFieldMappings( - integrationId, - linkedUserId, - 'ats.activity', - ); - - const desunifiedObject = - await this.coreUnification.desunify({ - sourceObject: unifiedActivityData, - targetType: AtsObject.activity, - providerName: integrationId, - vertical: 'ats', - customFieldMappings: unifiedActivityData.field_mappings - ? customFieldMappings - : [], - }); - - const service = this.serviceRegistry.getService( - integrationId, - ) as IActivityService; - const resp: ApiResponse = - await service.addActivity(desunifiedObject, linkedUserId); - - const unifiedObject = (await this.coreUnification.unify< - OriginalActivityOutput[] - >({ - sourceObject: [resp.data], - targetType: AtsObject.activity, - providerName: integrationId, - vertical: 'ats', - connectionId: connection_id, - customFieldMappings: customFieldMappings, - })) as UnifiedAtsActivityOutput[]; - - const source_activity = resp.data; - const target_activity = unifiedObject[0]; - - const unique_ats_activity_id = await this.saveOrUpdateActivity( - target_activity, - connection_id, - ); - - await this.ingestService.processFieldMappings( - target_activity.field_mappings, - unique_ats_activity_id, - integrationId, - linkedUserId, - ); - - await this.ingestService.processRemoteData( - unique_ats_activity_id, - source_activity, - ); - - const result_activity = await this.getActivity( - unique_ats_activity_id, - undefined, - undefined, - connection_id, - project_id, - remote_data, - ); - - const status_resp = resp.statusCode === 201 ? 'success' : 'fail'; - const event = await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: status_resp, - type: 'ats.activity.created', - method: 'POST', - url: '/ats/activities', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - await this.webhook.dispatchWebhook( - result_activity, - 'ats.activity.created', - linkedUser.id_project, - event.id_event, - ); - return result_activity; - } catch (error) { - throw error; - } - } - - async validateLinkedUser(linkedUserId: string) { - const linkedUser = await this.prisma.linked_users.findUnique({ - where: { id_linked_user: linkedUserId }, - }); - if (!linkedUser) throw new ReferenceError('Linked User Not Found'); - return linkedUser; - } - - async saveOrUpdateActivity( - activity: UnifiedAtsActivityOutput, - connection_id: string, - ): Promise { - const existingActivity = await this.prisma.ats_activities.findFirst({ - where: { remote_id: activity.remote_id, id_connection: connection_id }, - }); - - const data: any = { - activity_type: activity.activity_type, - subject: activity.subject, - body: activity.body, - visibility: activity.visibility, - remote_created_at: activity.remote_created_at, - modified_at: new Date(), - }; - - if (existingActivity) { - const res = await this.prisma.ats_activities.update({ - where: { id_ats_activity: existingActivity.id_ats_activity }, - data: data, - }); - return res.id_ats_activity; - } else { - data.created_at = new Date(); - data.remote_id = activity.remote_id; - data.id_connection = connection_id; - data.id_ats_activity = uuidv4(); - - const newActivity = await this.prisma.ats_activities.create({ - data: data, - }); - return newActivity.id_ats_activity; - } - } - - async getActivity( - id_ats_activity: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const activity = await this.prisma.ats_activities.findUnique({ - where: { id_ats_activity: id_ats_activity }, - }); - - const values = await this.prisma.value.findMany({ - where: { entity: { ressource_owner_id: activity.id_ats_activity } }, - include: { attribute: true }, - }); - - const fieldMappingsMap = new Map(); - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - const field_mappings = Object.fromEntries(fieldMappingsMap); - - const unifiedActivity: UnifiedAtsActivityOutput = { - id: activity.id_ats_activity, - activity_type: activity.activity_type, - subject: activity.subject, - body: activity.body, - visibility: activity.visibility, - candidate_id: activity.id_ats_candidate, - remote_created_at: String(activity.remote_created_at), - field_mappings: field_mappings, - remote_id: activity.remote_id, - created_at: activity.created_at, - modified_at: activity.modified_at, - }; - - let res: UnifiedAtsActivityOutput = unifiedActivity; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: activity.id_ats_activity }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - if (linkedUserId && integrationId) { - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.activity.pull', - method: 'GET', - url: '/ats/activity', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - } - - return res; - } catch (error) { - throw error; - } - } - - async getActivities( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsActivityOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = await this.prisma.ats_activities.findFirst({ - where: { - id_connection: connection_id, - id_ats_activity: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const activities = await this.prisma.ats_activities.findMany({ - take: limit + 1, - cursor: cursor ? { id_ats_activity: cursor } : undefined, - orderBy: { created_at: 'asc' }, - where: {}, - }); - - if (activities.length === limit + 1) { - next_cursor = Buffer.from( - activities[activities.length - 1].id_ats_activity, - ).toString('base64'); - activities.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - - const unifiedActivities: UnifiedAtsActivityOutput[] = await Promise.all( - activities.map(async (activity) => { - const values = await this.prisma.value.findMany({ - where: { entity: { ressource_owner_id: activity.id_ats_activity } }, - include: { attribute: true }, - }); - - const fieldMappingsMap = new Map(); - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an object -const field_mappings = Object.fromEntries(fieldMappingsMap); - - return { - id: activity.id_ats_activity, - activity_type: activity.activity_type, - subject: activity.subject, - body: activity.body, - visibility: activity.visibility, - candidate_id: activity.id_ats_candidate, - remote_created_at: String(activity.remote_created_at), - field_mappings: field_mappings, - remote_id: activity.remote_id, - created_at: activity.created_at, - modified_at: activity.modified_at, - }; - }), - ); - - let res: UnifiedAtsActivityOutput[] = unifiedActivities; - - if (remote_data) { - const remote_array_data: UnifiedAtsActivityOutput[] = await Promise.all( - res.map(async (activity) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: activity.id }, - }); - const remote_data = JSON.parse(resp.data); - return { ...activity, remote_data }; - }), - ); - - res = remote_array_data; - } - - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.activity.pull', - method: 'GET', - url: '/ats/activities', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { data: res, prev_cursor, next_cursor }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/activity/services/ashby/index.ts b/packages/api/src/ats/activity/services/ashby/index.ts deleted file mode 100644 index 664065f3c..000000000 --- a/packages/api/src/ats/activity/services/ashby/index.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IActivityService } from '@ats/activity/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 { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { ApiResponse } from '@@core/utils/types'; -import { ServiceRegistry } from '../registry.service'; -import { AshbyActivityInput, AshbyActivityOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalActivityOutput } from '@@core/utils/types/original/original.ats'; -import { SyncParam } from '@@core/utils/types/interface'; - -@Injectable() -export class AshbyService implements IActivityService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - AtsObject.activity.toUpperCase() + ':' + AshbyService.name, - ); - this.registry.registerService('ashby', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId, candidate_id } = data; - if (!candidate_id) return; - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - const candidate = await this.prisma.ats_candidates.findUnique({ - where: { - id_ats_candidate: candidate_id as string, - }, - }); - const resp = await axios.post( - `${connection.account_url}/candidate.listNotes`, - JSON.stringify({ - candidateId: candidate.remote_id, - }), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }, - ); - const activitys: AshbyActivityOutput[] = resp.data.results; - this.logger.log(`Synced ashby activitys !`); - - return { - data: activitys, - message: 'Ashby activitys retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/activity/services/ashby/mappers.ts b/packages/api/src/ats/activity/services/ashby/mappers.ts deleted file mode 100644 index d2647a81f..000000000 --- a/packages/api/src/ats/activity/services/ashby/mappers.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { AshbyActivityInput, AshbyActivityOutput } from './types'; -import { - UnifiedAtsActivityInput, - UnifiedAtsActivityOutput, -} from '@ats/activity/types/model.unified'; -import { IActivityMapper } from '@ats/activity/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'; - -@Injectable() -export class AshbyActivityMapper implements IActivityMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('ats', 'activity', 'ashby', this); - } - - async desunify( - source: UnifiedAtsActivityInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return; - } - - async unify( - source: AshbyActivityOutput | AshbyActivityOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - if (!Array.isArray(source)) { - return await this.mapSingleActivityToUnified( - source, - connectionId, - customFieldMappings, - ); - } - // Handling array of AshbyActivityOutput - return Promise.all( - source.map((activity) => - this.mapSingleActivityToUnified( - activity, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleActivityToUnified( - activity: AshbyActivityOutput, - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return { - remote_id: activity.id, - remote_data: activity, - subject: null, - body: activity.content, - visibility: null, - remote_created_at: activity.createdAt, - //remote_created_at: activity.created_at || null, - //remote_modified_at: activity.modified_at || null, - }; - } -} diff --git a/packages/api/src/ats/activity/services/ashby/types.ts b/packages/api/src/ats/activity/services/ashby/types.ts deleted file mode 100644 index 0eec92e6f..000000000 --- a/packages/api/src/ats/activity/services/ashby/types.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface AshbyActivityInput { - id: string; - createdAt: string; - content: string; - author: { - id: string; - firstName: string; - lastName: string; - email: string; - }; -} - -export type AshbyActivityOutput = Partial; diff --git a/packages/api/src/ats/activity/services/registry.service.ts b/packages/api/src/ats/activity/services/registry.service.ts deleted file mode 100644 index 37ed7c2f5..000000000 --- a/packages/api/src/ats/activity/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IActivityService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IActivityService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IActivityService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/activity/sync/sync.service.ts b/packages/api/src/ats/activity/sync/sync.service.ts deleted file mode 100644 index 917345ad3..000000000 --- a/packages/api/src/ats/activity/sync/sync.service.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -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'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ApiResponse } from '@@core/utils/types'; -import { OriginalActivityOutput } from '@@core/utils/types/original/original.ats'; -import { AtsObject } from '@ats/@lib/@types'; -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; -import { ATS_PROVIDERS } from '@panora/shared'; -import { ats_activities as AtsActivity } from '@prisma/client'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../services/registry.service'; -import { IActivityService } from '../types'; -import { UnifiedAtsActivityOutput } from '../types/model.unified'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'activity', this); - } - onModuleInit() { -// - } - - //function used by sync worker which populate our ats_activities table - //its role is to fetch all activities from providers 3rd parties and save the info inside our db - // @Cron('*/2 * * * *') // every 2 minutes (for testing) - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - //todo: HANDLE DATA REMOVED FROM PROVIDER - async syncForLinkedUser(data: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId, id_candidate } = data; - const service: IActivityService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: activity} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsActivityOutput, - OriginalActivityOutput, - IActivityService - >(integrationId, linkedUserId, 'ats', 'activity', service, [ - { - paramName: 'id_candidate', - param: id_candidate, - shouldPassToIngest: true, - shouldPassToService: true, - }, - ]); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - activities: UnifiedAtsActivityOutput[], - originSource: string, - remote_data: Record[], - id_candidate?: string, - ): Promise { - try { - const activities_results: AtsActivity[] = []; - - const updateOrCreateActivity = async ( - activity: UnifiedAtsActivityOutput, - originId: string, - ) => { - let existingActivity; - if (!originId) { - existingActivity = await this.prisma.ats_activities.findFirst({ - where: { - subject: activity.subject, - id_ats_candidate: activity.candidate_id, - }, - }); - } else { - existingActivity = await this.prisma.ats_activities.findFirst({ - where: { - remote_id: originId, - }, - }); - } - - const baseData: any = { - id_candidate: id_candidate ?? null, - activity_type: activity.activity_type ?? null, - body: activity.body ?? null, - remote_created_at: activity.remote_created_at ?? null, - subject: activity.subject ?? null, - visibility: activity.visibility ?? null, - modified_at: new Date(), - }; - - let res; - if (existingActivity) { - res = await this.prisma.ats_activities.update({ - where: { - id_ats_activity: existingActivity.id_ats_activity, - }, - data: baseData, - }); - } else { - res = await this.prisma.ats_activities.create({ - data: { - ...baseData, - id_ats_activity: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - - return res; - }; - - for (let i = 0; i < activities.length; i++) { - const activity = activities[i]; - const originId = activity.remote_id; - - const res = await updateOrCreateActivity(activity, originId); - const activity_id = res.id_ats_activity; - activities_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - activity.field_mappings, - activity_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData(activity_id, remote_data[i]); - } - - return activities_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/activity/types/index.ts b/packages/api/src/ats/activity/types/index.ts deleted file mode 100644 index bf2da2d90..000000000 --- a/packages/api/src/ats/activity/types/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { UnifiedAtsActivityInput, UnifiedAtsActivityOutput } from './model.unified'; -import { OriginalActivityOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface IActivityService extends IBaseObjectService { - addActivity?( - activityData: DesunifyReturnType, - linkedUserId: string, - ): Promise>; - - sync(data: SyncParam): Promise>; -} - -export interface IActivityMapper { - desunify( - source: UnifiedAtsActivityInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalActivityOutput | OriginalActivityOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/activity/types/model.unified.ts b/packages/api/src/ats/activity/types/model.unified.ts deleted file mode 100644 index 2d21f0bf4..000000000 --- a/packages/api/src/ats/activity/types/model.unified.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsIn, -} from 'class-validator'; - -export type ActivityType = 'NOTE' | 'EMAIL' | 'OTHER'; -export type ActivityVisibility = 'ADMIN_ONLY' | 'PUBLIC' | 'PRIVATE'; - -export class UnifiedAtsActivityInput { - @ApiPropertyOptional({ - type: String, - //// enum: ['NOTE', 'EMAIL', 'OTHER'], - example: 'NOTE', - nullable: true, - description: 'The type of activity. NOTE, EMAIL or OTHER', - }) - ////@IsIn(['NOTE', 'EMAIL', 'OTHER']) - @IsOptional() - activity_type?: ActivityType | string; - - @ApiPropertyOptional({ - type: String, - example: 'Email subject', - nullable: true, - description: 'The subject of the activity', - }) - @IsString() - @IsOptional() - subject?: string; - - @ApiPropertyOptional({ - type: String, - example: 'Dear Diana, I love you', - nullable: true, - description: 'The body of the activity', - }) - @IsString() - @IsOptional() - body?: string; - - @ApiPropertyOptional({ - type: String, - // enum: ['ADMIN_ONLY', 'PUBLIC', 'PRIVATE'], - example: 'PUBLIC', - nullable: true, - description: - 'The visibility of the activity. ADMIN_ONLY, PUBLIC or PRIVATE', - }) - ////@IsIn(['ADMIN_ONLY', 'PUBLIC', 'PRIVATE']) - @IsOptional() - visibility?: ActivityVisibility | string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the candidate', - }) - @IsUUID() - @IsOptional() - candidate_id?: string; - - @ApiPropertyOptional({ - type: String, - format: 'date-time', - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The remote creation date of the activity', - }) - @IsDateString() - @IsOptional() - remote_created_at?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - additionalProperties: true, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsActivityOutput extends UnifiedAtsActivityInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the activity', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: - 'The remote ID of the activity in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: - 'The remote data of the activity in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The created date of the object', - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The modified date of the object', - }) - @IsOptional() - modified_at?: Date; -} diff --git a/packages/api/src/ats/activity/utils/index.ts b/packages/api/src/ats/activity/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/activity/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/application/application.controller.ts b/packages/api/src/ats/application/application.controller.ts deleted file mode 100644 index 4476e5b40..000000000 --- a/packages/api/src/ats/application/application.controller.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { - UnifiedAtsApplicationInput, - UnifiedAtsApplicationOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { ApplicationService } from './services/application.service'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, - ApiPostCustomResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('ats/applications') -@Controller('ats/applications') -export class ApplicationController { - constructor( - private readonly applicationService: ApplicationService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(ApplicationController.name); - } - - @ApiOperation({ - operationId: 'listAtsApplication', - summary: 'List Applications', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsApplicationOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getApplications( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.applicationService.getApplications( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsApplication', - summary: 'Retrieve Applications', - description: 'Retrieve Applications from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the application you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsApplicationOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.applicationService.getApplication( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } - - @ApiOperation({ - operationId: 'createAtsApplication', - summary: 'Create Applications', - description: 'Create Applications in any supported Ats software', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiBody({ type: UnifiedAtsApplicationInput }) - @ApiPostCustomResponse(UnifiedAtsApplicationOutput) - @UseGuards(ApiKeyAuthGuard) - @Post() - async addApplication( - @Body() unifiedApplicationData: UnifiedAtsApplicationInput, - @Headers('x-connection-token') connection_token: string, - @Query('remote_data') remote_data?: boolean, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.applicationService.addApplication( - unifiedApplicationData, - connectionId, - projectId, - remoteSource, - linkedUserId, - remote_data, - ); - } catch (error) { - throw new Error(error); - } - } -} diff --git a/packages/api/src/ats/application/application.module.ts b/packages/api/src/ats/application/application.module.ts deleted file mode 100644 index 1bbfc24a2..000000000 --- a/packages/api/src/ats/application/application.module.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ApplicationController } from './application.controller'; -import { SyncService } from './sync/sync.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { ApplicationService } from './services/application.service'; -import { ServiceRegistry } from './services/registry.service'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { AshbyService } from './services/ashby'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { AshbyApplicationMapper } from './services/ashby/mappers'; -import { Utils } from '@ats/@lib/@utils'; - -@Module({ - controllers: [ApplicationController], - providers: [ - ApplicationService, - - CoreUnification, - - SyncService, - WebhookService, - - ServiceRegistry, - - IngestDataService, - - AshbyApplicationMapper, - Utils, - /* PROVIDERS SERVICES */ - AshbyService, - ], - exports: [SyncService], -}) -export class ApplicationModule {} diff --git a/packages/api/src/ats/application/services/application.service.ts b/packages/api/src/ats/application/services/application.service.ts deleted file mode 100644 index 3a0607618..000000000 --- a/packages/api/src/ats/application/services/application.service.ts +++ /dev/null @@ -1,424 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ApiResponse } from '@@core/utils/types'; -import { OriginalApplicationOutput } from '@@core/utils/types/original/original.ats'; -import { AtsObject } from '@ats/@lib/@types'; -import { Injectable } from '@nestjs/common'; -import { v4 as uuidv4 } from 'uuid'; -import { IApplicationService } from '../types'; -import { - UnifiedAtsApplicationInput, - UnifiedAtsApplicationOutput, -} from '../types/model.unified'; -import { ServiceRegistry } from './registry.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -@Injectable() -export class ApplicationService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private ingestService: IngestDataService, - ) { - this.logger.setContext(ApplicationService.name); - } - - async addApplication( - unifiedApplicationData: UnifiedAtsApplicationInput, - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - remote_data?: boolean, - ): Promise { - try { - const linkedUser = await this.validateLinkedUser(linkedUserId); - - const customFieldMappings = - await this.fieldMappingService.getCustomFieldMappings( - integrationId, - linkedUserId, - 'ats.application', - ); - - const desunifiedObject = - await this.coreUnification.desunify({ - sourceObject: unifiedApplicationData, - targetType: AtsObject.application, - providerName: integrationId, - vertical: 'ats', - customFieldMappings: unifiedApplicationData.field_mappings - ? customFieldMappings - : [], - }); - - const service = this.serviceRegistry.getService( - integrationId, - ) as IApplicationService; - const resp: ApiResponse = - await service.addApplication(desunifiedObject, linkedUserId); - - const unifiedObject = (await this.coreUnification.unify< - OriginalApplicationOutput[] - >({ - sourceObject: [resp.data], - targetType: AtsObject.application, - providerName: integrationId, - vertical: 'ats', - connectionId: connection_id, - customFieldMappings: customFieldMappings, - })) as UnifiedAtsApplicationOutput[]; - - const source_application = resp.data; - const target_application = unifiedObject[0]; - - const unique_ats_application_id = await this.saveOrUpdateApplication( - target_application, - connection_id, - ); - - if (target_application.candidate_id) { - await this.prisma.ats_applications.update({ - where: { - id_ats_application: unique_ats_application_id, - }, - data: { - id_ats_candidate: target_application.candidate_id, - }, - }); - } - - await this.ingestService.processFieldMappings( - target_application.field_mappings, - unique_ats_application_id, - integrationId, - linkedUserId, - ); - - await this.ingestService.processRemoteData( - unique_ats_application_id, - source_application, - ); - - const result_application = await this.getApplication( - unique_ats_application_id, - undefined, - undefined, - connection_id, - project_id, - remote_data, - ); - - const status_resp = resp.statusCode === 201 ? 'success' : 'fail'; - const event = await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: status_resp, - type: 'ats.application.created', - method: 'POST', - url: '/ats/applications', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - await this.webhook.dispatchWebhook( - result_application, - 'ats.application.created', - linkedUser.id_project, - event.id_event, - ); - return result_application; - } catch (error) { - throw error; - } - } - - async validateLinkedUser(linkedUserId: string) { - const linkedUser = await this.prisma.linked_users.findUnique({ - where: { id_linked_user: linkedUserId }, - }); - if (!linkedUser) throw new ReferenceError('Linked User Not Found'); - return linkedUser; - } - - async saveOrUpdateApplication( - application: UnifiedAtsApplicationOutput, - connection_id: string, - ): Promise { - const existingApplication = await this.prisma.ats_applications.findFirst({ - where: { remote_id: application.remote_id, id_connection: connection_id }, - }); - - const data: any = { - applied_at: application.applied_at, - rejected_at: application.rejected_at, - offers: application.offers, - source: application.source, - credited_to: application.credited_to, - current_stage: application.current_stage, - reject_reason: application.reject_reason, - id_ats_job: application.job_id, - modified_at: new Date(), - }; - - if (existingApplication) { - const res = await this.prisma.ats_applications.update({ - where: { id_ats_application: existingApplication.id_ats_application }, - data: data, - }); - return res.id_ats_application; - } else { - data.created_at = new Date(); - data.remote_id = application.remote_id; - data.id_connection = connection_id; - data.id_ats_application = uuidv4(); - - const newApplication = await this.prisma.ats_applications.create({ - data: data, - }); - return newApplication.id_ats_application; - } - } - - async getApplication( - id_ats_application: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const application = await this.prisma.ats_applications.findUnique({ - where: { id_ats_application: id_ats_application }, - }); - - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: application.id_ats_application }, - }, - include: { attribute: true }, - }); - - const fieldMappingsMap = new Map(); - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - const field_mappings = Object.fromEntries(fieldMappingsMap); - - const resOffers = await this.prisma.ats_offers.findMany({ - where: { - id_ats_application: application.id_ats_application, - }, - }); - let offers; - if (resOffers && resOffers.length > 0) { - offers = resOffers.map((off) => { - return off.id_ats_offer; - }); - } - const unifiedApplication: UnifiedAtsApplicationOutput = { - id: application.id_ats_application, - applied_at: String(application.applied_at) || null, - rejected_at: String(application.rejected_at) || null, - offers: offers || null, - source: application.source || null, - credited_to: application.credited_to || null, - current_stage: application.current_stage || null, - reject_reason: application.reject_reason || null, - candidate_id: application.id_ats_candidate || null, - job_id: application.id_ats_job || null, - field_mappings: field_mappings, - remote_id: application.remote_id || null, - created_at: application.created_at || null, - modified_at: application.modified_at || null, - remote_created_at: null, - remote_modified_at: null, - }; - - let res: UnifiedAtsApplicationOutput = unifiedApplication; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: application.id_ats_application }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - if (linkedUserId && integrationId) { - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.application.pull', - method: 'GET', - url: '/ats/application', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - } - - return res; - } catch (error) { - throw error; - } - } - - async getApplications( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsApplicationOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = await this.prisma.ats_applications.findFirst({ - where: { - id_connection: connection_id, - id_ats_application: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const applications = await this.prisma.ats_applications.findMany({ - take: limit + 1, - cursor: cursor ? { id_ats_application: cursor } : undefined, - orderBy: { created_at: 'asc' }, - where: {}, - }); - - if (applications.length === limit + 1) { - next_cursor = Buffer.from( - applications[applications.length - 1].id_ats_application, - ).toString('base64'); - applications.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - - const unifiedApplications: UnifiedAtsApplicationOutput[] = - await Promise.all( - applications.map(async (application) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: application.id_ats_application }, - }, - include: { attribute: true }, - }); - - const fieldMappingsMap = new Map(); - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - const field_mappings = Array.from( - fieldMappingsMap, - ([key, value]) => ({ [key]: value }), - ); - - const resOffers = await this.prisma.ats_offers.findMany({ - where: { - id_ats_application: application.id_ats_application, - }, - }); - let offers; - if (resOffers && resOffers.length > 0) { - offers = resOffers.map((off) => { - return off.id_ats_offer; - }); - } - - return { - id: application.id_ats_application, - applied_at: String(application.applied_at) || null, - rejected_at: String(application.rejected_at) || null, - offers: offers || null, - source: application.source || null, - credited_to: application.credited_to || null, - current_stage: application.current_stage || null, - reject_reason: application.reject_reason || null, - candidate_id: application.id_ats_candidate || null, - job_id: application.id_ats_job || null, - field_mappings: field_mappings, - remote_id: application.remote_id || null, - created_at: application.created_at || null, - modified_at: application.modified_at || null, - remote_created_at: null, - remote_modified_at: null, - }; - }), - ); - - let res: UnifiedAtsApplicationOutput[] = unifiedApplications; - - if (remote_data) { - const remote_array_data: UnifiedAtsApplicationOutput[] = - await Promise.all( - res.map(async (application) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: application.id }, - }); - const remote_data = JSON.parse(resp.data); - return { ...application, remote_data }; - }), - ); - - res = remote_array_data; - } - - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.application.pull', - method: 'GET', - url: '/ats/applications', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { data: res, prev_cursor, next_cursor }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/application/services/ashby/index.ts b/packages/api/src/ats/application/services/ashby/index.ts deleted file mode 100644 index 4e9438cd4..000000000 --- a/packages/api/src/ats/application/services/ashby/index.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IApplicationService } from '@ats/application/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 { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { ApiResponse } from '@@core/utils/types'; -import { ServiceRegistry } from '../registry.service'; -import { AshbyApplicationInput, AshbyApplicationOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalApplicationOutput } from '@@core/utils/types/original/original.ats'; -import { SyncParam } from '@@core/utils/types/interface'; - -@Injectable() -export class AshbyService implements IApplicationService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - AtsObject.application.toUpperCase() + ':' + AshbyService.name, - ); - this.registry.registerService('ashby', this); - } - - async addApplication( - applicationData: AshbyApplicationInput, - linkedUserId: string, - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - const resp = await axios.post( - `${connection.account_url}/application.create`, - JSON.stringify(applicationData), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }, - ); - - return { - data: resp.data.results, - message: 'Ashby application created', - statusCode: 201, - }; - } catch (error) { - throw error; - } - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - const resp = await axios.post( - `${connection.account_url}/application.list`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }, - ); - const applications: AshbyApplicationOutput[] = resp.data.results; - this.logger.log(`Synced ashby applications !`); - - return { - data: applications, - message: 'Ashby applications retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/application/services/ashby/mappers.ts b/packages/api/src/ats/application/services/ashby/mappers.ts deleted file mode 100644 index 28e95cb02..000000000 --- a/packages/api/src/ats/application/services/ashby/mappers.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { AshbyApplicationInput, AshbyApplicationOutput } from './types'; -import { - UnifiedAtsApplicationInput, - UnifiedAtsApplicationOutput, -} from '@ats/application/types/model.unified'; -import { IApplicationMapper } from '@ats/application/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 { PrismaService } from '@@core/@core-services/prisma/prisma.service'; - -@Injectable() -export class AshbyApplicationMapper implements IApplicationMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('ats', 'application', 'ashby', this); - } - - async desunify( - source: UnifiedAtsApplicationInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - if (!source.candidate_id) - throw new ReferenceError( - 'You must provide a candidate id to insert an application', - ); - if (!source.job_id) - throw new ReferenceError( - 'You must provide a job id to insert an application', - ); - - const candidateId = await this.utils.getCandidateRemoteIdFromUuid( - source.candidate_id, - ); - const jobId = await this.utils.getJobRemoteIdFromUuid(source.job_id); - const res: any = { - canidateId: candidateId, - jobId: jobId, - }; - if (source.credited_to) { - res.creditedToUserId = await this.utils.getUserRemoteIdFromUuid( - source.credited_to, - ); - } - if (source.current_stage) { - res.interviewStageId = await this.utils.getInterviewStageRemoteIdFromUuid( - source.current_stage, - ); - } - - return res; - } - - async unify( - source: AshbyApplicationOutput | AshbyApplicationOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - if (!Array.isArray(source)) { - return await this.mapSingleApplicationToUnified( - source, - connectionId, - customFieldMappings, - ); - } - // Handling array of AshbyApplicationOutput - return Promise.all( - source.map((application) => - this.mapSingleApplicationToUnified( - application, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleApplicationToUnified( - application: AshbyApplicationOutput, - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return { - remote_id: application.id, - remote_data: application, - applied_at: null, - rejected_at: application.archivedAt, - offers: null, // would be synced after and retrieve when user do a GET request - source: application.source.title || null, - credited_to: - (await this.utils.getUserUuidFromRemoteId( - application.creditedToUser.id, - connectionId, - )) || null, - current_stage: - (await this.utils.getStageUuidFromRemoteId( - application.currentInterviewStage.id, - connectionId, - )) || null, - reject_reason: - (await this.utils.getRejectReasonUuidFromRemoteId( - application.archiveReason.id, - connectionId, - )) || null, - candidate_id: - (await this.utils.getCandidateUuidFromRemoteId( - application.candidate.id, - connectionId, - )) || null, - job_id: - (await this.utils.getJobUuidFromRemoteId( - application.job.id, - connectionId, - )) || null, - remote_created_at: application.createdAt, - remote_modified_at: application.updatedAt, - }; - } -} diff --git a/packages/api/src/ats/application/services/ashby/types.ts b/packages/api/src/ats/application/services/ashby/types.ts deleted file mode 100644 index f343aa5d7..000000000 --- a/packages/api/src/ats/application/services/ashby/types.ts +++ /dev/null @@ -1,117 +0,0 @@ -export type AshbyApplicationInput = Partial<{ - canidateId: string; - jobId: string; - interviewPlanId: string; - interviewStageId: string; - sourceId: string; - creditedToUserId: string; - applicationHistory: { - stageId: string; - stageNumber: number; - enteredStageAt: Date; - archiveReasonId: string; - }[]; -}>; - -export type AshbyApplicationOutput = { - id: string; - createdAt: string; - updatedAt: string; - status: string; - customFields: Array<{ - id: string; - title: string; - value: string; - }>; - candidate: { - id: string; - name: string; - primaryEmailAddress: { - value: string; - type: string; - isPrimary: boolean; - }; - primaryPhoneNumber: { - value: string; - type: string; - isPrimary: boolean; - }; - }; - currentInterviewStage: { - id: string; - title: string; - type: string; - orderInInterviewPlan: number; - interviewStageGroupId: string; - interviewPlanId: string; - }; - source: { - id: string; - title: string; - isArchived: boolean; - sourceType: { - id: string; - title: string; - isArchived: boolean; - }; - }; - archiveReason: { - id: string; - text: string; - reasonType: string; - isArchived: boolean; - }; - archivedAt: string; - job: { - id: string; - title: string; - locationId: string; - departmentId: string; - }; - creditedToUser: { - id: string; - firstName: string; - lastName: string; - email: string; - globalRole: string; - isEnabled: boolean; - updatedAt: string; - }; - hiringTeam: Array<{ - email: string; - firstName: string; - lastName: string; - role: string; - userId: string; - }>; - appliedViaJobPostingId: string; - openings: Array<{ - id: string; - openedAt: string; - closedAt: string; - isArchived: boolean; - archivedAt: string; - openingState: string; - latestVersion: { - id: string; - identifier: string; - description: string; - authorId: string; - createdAt: string; - teamId: string; - jobIds: string[]; - targetHireDate: string; - targetStartDate: string; - isBackfill: boolean; - employmentType: string; - locationIds: string[]; - hiringTeam: Array<{ - email: string; - firstName: string; - lastName: string; - role: string; - userId: string; - }>; - }; - }>; -}; diff --git a/packages/api/src/ats/application/services/registry.service.ts b/packages/api/src/ats/application/services/registry.service.ts deleted file mode 100644 index eaee5866c..000000000 --- a/packages/api/src/ats/application/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IApplicationService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IApplicationService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IApplicationService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/application/sync/sync.service.ts b/packages/api/src/ats/application/sync/sync.service.ts deleted file mode 100644 index 3d538f775..000000000 --- a/packages/api/src/ats/application/sync/sync.service.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { BullQueueService } from '@@core/@core-services/queues/shared.service'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { OriginalApplicationOutput } from '@@core/utils/types/original/original.ats'; -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; -import { ATS_PROVIDERS } from '@panora/shared'; -import { ats_applications as AtsApplication } from '@prisma/client'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../services/registry.service'; -import { IApplicationService } from '../types'; -import { UnifiedAtsApplicationOutput } from '../types/model.unified'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'application', this); - } - onModuleInit() { -// - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IApplicationService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: application} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsApplicationOutput, - OriginalApplicationOutput, - IApplicationService - >(integrationId, linkedUserId, 'ats', 'application', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - applications: UnifiedAtsApplicationOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const applications_results: AtsApplication[] = []; - - const updateOrCreateApplication = async ( - application: UnifiedAtsApplicationOutput, - originId: string, - ) => { - const existingApplication = - await this.prisma.ats_applications.findFirst({ - where: { - remote_id: originId, - }, - }); - - const baseData: any = { - applied_at: application.applied_at ?? null, - rejected_at: application.rejected_at ?? null, - offers: application.offers ?? null, - source: application.source ?? null, - credited_to: application.credited_to ?? null, - current_stage: application.current_stage ?? null, - reject_reason: application.reject_reason ?? null, - candidate_id: application.candidate_id ?? null, - job_id: application.job_id ?? null, - modified_at: new Date(), - }; - - let res; - if (existingApplication) { - res = await this.prisma.ats_applications.update({ - where: { - id_ats_application: existingApplication.id_ats_application, - }, - data: baseData, - }); - } else { - res = await this.prisma.ats_applications.create({ - data: { - ...baseData, - id_ats_application: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - - return res; - }; - - for (let i = 0; i < applications.length; i++) { - const application = applications[i]; - const originId = application.remote_id; - - if (!originId || originId === '') { - throw new ReferenceError(`Origin id not there, found ${originId}`); - } - - const res = await updateOrCreateApplication(application, originId); - const application_id = res.id_ats_application; - applications_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - application.field_mappings, - application_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - application_id, - remote_data[i], - ); - } - - return applications_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/application/types/index.ts b/packages/api/src/ats/application/types/index.ts deleted file mode 100644 index 76f40aa86..000000000 --- a/packages/api/src/ats/application/types/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedAtsApplicationInput, - UnifiedAtsApplicationOutput, -} from './model.unified'; -import { OriginalApplicationOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface IApplicationService extends IBaseObjectService { - addApplication( - applicationData: DesunifyReturnType, - linkedUserId: string, - ): Promise>; - - sync(data: SyncParam): Promise>; -} - -export interface IApplicationMapper { - desunify( - source: UnifiedAtsApplicationInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalApplicationOutput | OriginalApplicationOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/application/types/model.unified.ts b/packages/api/src/ats/application/types/model.unified.ts deleted file mode 100644 index 2483c9c03..000000000 --- a/packages/api/src/ats/application/types/model.unified.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsArray, -} from 'class-validator'; - -export class UnifiedAtsApplicationInput { - @ApiPropertyOptional({ - type: Date, - nullable: true, - description: 'The application date', - example: '2024-10-01T12:00:00Z', - }) - @IsDateString() - @IsOptional() - applied_at?: string; - - @ApiPropertyOptional({ - type: Date, - nullable: true, - description: 'The rejection date', - example: '2024-10-01T12:00:00Z', - }) - @IsDateString() - @IsOptional() - rejected_at?: string; - - @ApiPropertyOptional({ - type: [String], - nullable: true, - description: 'The offers UUIDs for the application', - example: [ - '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - '12345678-1234-1234-1234-123456789012', - ], - }) - @IsArray() - @IsOptional() - offers?: string[]; - - @ApiPropertyOptional({ - type: String, - nullable: true, - description: 'The source of the application', - example: 'Source Name', - }) - @IsString() - @IsOptional() - source?: string; - - @ApiPropertyOptional({ - type: String, - nullable: true, - description: 'The UUID of the person credited for the application', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @IsUUID() - @IsOptional() - credited_to?: string; - - @ApiPropertyOptional({ - type: String, - nullable: true, - description: 'The UUID of the current stage of the application', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @IsUUID() - @IsOptional() - current_stage?: string; - - @ApiPropertyOptional({ - type: String, - nullable: true, - description: 'The rejection reason for the application', - example: 'Candidate not experienced enough', - }) - @IsString() - @IsOptional() - reject_reason?: string; - - @ApiPropertyOptional({ - type: String, - nullable: true, - description: 'The UUID of the candidate', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @IsUUID() - @IsOptional() - candidate_id?: string; - - @ApiPropertyOptional({ - type: String, - description: 'The UUID of the job', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @IsUUID() - @IsOptional() - job_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - additionalProperties: true, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsApplicationOutput extends UnifiedAtsApplicationInput { - @ApiPropertyOptional({ - type: String, - nullable: true, - description: 'The UUID of the application', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - nullable: true, - description: - 'The remote ID of the application in the context of the 3rd Party', - example: 'id_1', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: - 'The remote data of the application in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The created date of the object', - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The modified date of the object', - }) - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Date, - nullable: true, - description: 'The remote created date of the object', - }) - @IsOptional() - remote_created_at?: string; - - @ApiPropertyOptional({ - type: Date, - nullable: true, - description: 'The remote modified date of the object', - }) - @IsOptional() - remote_modified_at?: string; -} diff --git a/packages/api/src/ats/application/utils/index.ts b/packages/api/src/ats/application/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/application/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/ats.module.ts b/packages/api/src/ats/ats.module.ts deleted file mode 100644 index b554144ba..000000000 --- a/packages/api/src/ats/ats.module.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ActivityModule } from './activity/activity.module'; -import { ApplicationModule } from './application/application.module'; -import { AttachmentModule } from './attachment/attachment.module'; -import { CandidateModule } from './candidate/candidate.module'; -import { DepartmentModule } from './department/department.module'; -import { InterviewModule } from './interview/interview.module'; -import { JobInterviewStageModule } from './jobinterviewstage/jobinterviewstage.module'; -import { JobModule } from './job/job.module'; -import { OfferModule } from './offer/offer.module'; -import { OfficeModule } from './office/office.module'; -import { RejectReasonModule } from './rejectreason/rejectreason.module'; -import { ScoreCardModule } from './scorecard/scorecard.module'; -import { TagModule } from './tag/tag.module'; -import { UserModule } from './user/user.module'; -import { EeocsModule } from './eeocs/eeocs.module'; -import { AtsUnificationService } from './@lib/@unification'; - -@Module({ - exports: [ - ActivityModule, - ApplicationModule, - AttachmentModule, - CandidateModule, - DepartmentModule, - InterviewModule, - JobInterviewStageModule, - JobModule, - OfferModule, - OfficeModule, - RejectReasonModule, - ScoreCardModule, - TagModule, - UserModule, - EeocsModule, - ], - providers: [AtsUnificationService], - imports: [ - ActivityModule, - ApplicationModule, - AttachmentModule, - CandidateModule, - DepartmentModule, - InterviewModule, - JobInterviewStageModule, - JobModule, - OfferModule, - OfficeModule, - RejectReasonModule, - ScoreCardModule, - TagModule, - UserModule, - EeocsModule, - ], -}) -export class AtsModule {} diff --git a/packages/api/src/ats/attachment/attachment.controller.ts b/packages/api/src/ats/attachment/attachment.controller.ts deleted file mode 100644 index d12bf7793..000000000 --- a/packages/api/src/ats/attachment/attachment.controller.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { - UnifiedAtsAttachmentInput, - UnifiedAtsAttachmentOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { AttachmentService } from './services/attachment.service'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, - ApiPostCustomResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - - -@ApiTags('ats/attachments') -@Controller('ats/attachments') -export class AttachmentController { - constructor( - private readonly attachmentService: AttachmentService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(AttachmentController.name); - } - - @ApiOperation({ - operationId: 'listAtsAttachment', - summary: 'List Attachments', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsAttachmentOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getAttachments( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.attachmentService.getAttachments( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsAttachment', - summary: 'Retrieve Attachments', - description: 'Retrieve Attachments from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the attachment you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsAttachmentOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.attachmentService.getAttachment( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } - - @ApiOperation({ - operationId: 'createAtsAttachment', - summary: 'Create Attachments', - description: 'Create Attachments in any supported ATS software', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiBody({ type: UnifiedAtsAttachmentInput }) - @ApiPostCustomResponse(UnifiedAtsAttachmentOutput) - @UseGuards(ApiKeyAuthGuard) - @Post() - async addAttachment( - @Body() unifiedAttachmentData: UnifiedAtsAttachmentInput, - @Headers('x-connection-token') connection_token: string, - @Query('remote_data') remote_data?: boolean, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.attachmentService.addAttachment( - unifiedAttachmentData, - connectionId, - projectId, - remoteSource, - linkedUserId, - remote_data, - ); - } catch (error) { - throw new Error(error); - } - } -} diff --git a/packages/api/src/ats/attachment/attachment.module.ts b/packages/api/src/ats/attachment/attachment.module.ts deleted file mode 100644 index ff74efb5b..000000000 --- a/packages/api/src/ats/attachment/attachment.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Module } from '@nestjs/common'; -import { AttachmentController } from './attachment.controller'; -import { SyncService } from './sync/sync.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { AttachmentService } from './services/attachment.service'; -import { ServiceRegistry } from './services/registry.service'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { AshbyAttachmentMapper } from './services/ashby/mappers'; -import { Utils } from '@ats/@lib/@utils'; - -@Module({ - controllers: [AttachmentController], - providers: [ - AttachmentService, - - SyncService, - WebhookService, - CoreUnification, - - ServiceRegistry, - - IngestDataService, - - AshbyAttachmentMapper, - Utils, - /* PROVIDERS SERVICES */ - ], - exports: [SyncService], -}) -export class AttachmentModule {} diff --git a/packages/api/src/ats/attachment/services/ashby/index.ts b/packages/api/src/ats/attachment/services/ashby/index.ts deleted file mode 100644 index a1b90137f..000000000 --- a/packages/api/src/ats/attachment/services/ashby/index.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Injectable } from '@nestjs/common'; -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 { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { ApiResponse } from '@@core/utils/types'; -import { ServiceRegistry } from '../registry.service'; -import { AshbyAttachmentInput, AshbyAttachmentOutput } from './types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { IAttachmentService } from '@ats/attachment/types'; -import { AttachmentType } from '@ats/attachment/types/model.unified'; -import * as fs from 'fs'; -import * as FormData from 'form-data'; - -@Injectable() -export class AshbyService implements IAttachmentService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - AtsObject.attachment.toUpperCase() + ':' + AshbyService.name, - ); - this.registry.registerService('ashby', this); - } - async addAttachment( - attachmentData: AshbyAttachmentInput, - linkedUserId: string, - attachment_type: AttachmentType, - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - let resp; - let result: AshbyAttachmentOutput[] = []; - const formData = new FormData(); - if (attachment_type === 'RESUME') { - formData.append('resume', fs.createReadStream(attachmentData.file)); - resp = await axios.post( - `${connection.account_url}/candidate.uploadResume`, - JSON.stringify(formData), - { - headers: { - 'Content-Type': 'multipart/form-data', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }, - ); - result = [resp.data.results.resumeFileHandle]; - } else { - formData.append('file', fs.createReadStream(attachmentData.file)); - resp = await axios.post( - `${connection.account_url}/candidate.uploadFile`, - JSON.stringify(formData), - { - headers: { - 'Content-Type': 'multipart/form-data', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }, - ); - result = resp.data.results.fileHandles; - } - - return { - data: result, - message: 'Ashby attachment created', - statusCode: 201, - }; - } catch (error) { - throw error; - } - } - - async sync(data: SyncParam): Promise> { - return; - } -} diff --git a/packages/api/src/ats/attachment/services/ashby/mappers.ts b/packages/api/src/ats/attachment/services/ashby/mappers.ts deleted file mode 100644 index 2dc28286f..000000000 --- a/packages/api/src/ats/attachment/services/ashby/mappers.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { AshbyAttachmentInput, AshbyAttachmentOutput } from './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 { - OriginalAttachmentOutput, - OriginalTagOutput, -} from '@@core/utils/types/original/original.ats'; -import { AtsObject } from '@ats/@lib/@types'; -import { UnifiedAtsTagOutput } from '@ats/tag/types/model.unified'; -import { url } from 'inspector'; -import { IAttachmentMapper } from '@ats/attachment/types'; -import { - UnifiedAtsAttachmentInput, - UnifiedAtsAttachmentOutput, -} from '@ats/attachment/types/model.unified'; -import axios from 'axios'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; - -@Injectable() -export class AshbyAttachmentMapper implements IAttachmentMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private coreUnificationService: CoreUnification, - private prisma: PrismaService, - private cryptoService: EncryptionService, - ) { - this.mappersRegistry.registerService('ats', 'attachment', 'ashby', this); - } - - async desunify( - source: UnifiedAtsAttachmentInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return { - candidateId: await this.utils.getCandidateRemoteIdFromUuid( - source.candidate_id, - ), - file: source.file_name, - }; - } - - async unify( - source: AshbyAttachmentOutput | AshbyAttachmentOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - if (!Array.isArray(source)) { - return await this.mapSingleAttachmentToUnified( - source, - connectionId, - customFieldMappings, - ); - } - // Handling array of AshbyAttachmentOutput - return Promise.all( - source.map((attachment) => - this.mapSingleAttachmentToUnified( - attachment, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleAttachmentToUnified( - attachment: AshbyAttachmentOutput, - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - let url; - if (attachment.handle) { - // fetch the url given the handle - const connection = await this.prisma.connections.findUnique({ - where: { - id_connection: connectionId, - }, - }); - const resp = await axios.post( - `${connection.account_url}/file.info`, - JSON.stringify({ fileHandle: attachment.handle }), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }, - ); - url = resp.data.results.url; - } - - return { - remote_id: attachment.id, - remote_data: attachment, - file_url: url || null, - attachment_type: attachment.resume == true ? 'RESUME' : 'OTHER', - file_name: attachment.name || null, - }; - } -} diff --git a/packages/api/src/ats/attachment/services/ashby/types.ts b/packages/api/src/ats/attachment/services/ashby/types.ts deleted file mode 100644 index a2006753d..000000000 --- a/packages/api/src/ats/attachment/services/ashby/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type AshbyAttachmentInput = { - candidateId: string; - file: string; -}; - -export type AshbyAttachmentOutput = Partial<{ - id: string; - name: string; - handle: string; - [key: string]: any; -}>; diff --git a/packages/api/src/ats/attachment/services/attachment.service.ts b/packages/api/src/ats/attachment/services/attachment.service.ts deleted file mode 100644 index 8739ed9fe..000000000 --- a/packages/api/src/ats/attachment/services/attachment.service.ts +++ /dev/null @@ -1,410 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { - AttachmentType, - UnifiedAtsAttachmentInput, - UnifiedAtsAttachmentOutput, -} from '../types/model.unified'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ServiceRegistry } from './registry.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ApiResponse } from '@@core/utils/types'; -import { OriginalAttachmentOutput } from '@@core/utils/types/original/original.ats'; -import { IAttachmentService } from '../types'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { AtsObject } from '@ats/@lib/@types'; -import { BullQueueService } from '@@core/@core-services/queues/shared.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class AttachmentService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private ingestService: IngestDataService, - ) { - this.logger.setContext(AttachmentService.name); - } - - async addAttachment( - unifiedAttachmentData: UnifiedAtsAttachmentInput, - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - remote_data?: boolean, - ): Promise { - try { - const linkedUser = await this.validateLinkedUser(linkedUserId); - - const customFieldMappings = - await this.fieldMappingService.getCustomFieldMappings( - integrationId, - linkedUserId, - 'ats.attachment', - ); - - const desunifiedObject = - await this.coreUnification.desunify({ - sourceObject: unifiedAttachmentData, - targetType: AtsObject.attachment, - providerName: integrationId, - vertical: 'ats', - customFieldMappings: unifiedAttachmentData.field_mappings - ? customFieldMappings - : [], - }); - - const service = this.serviceRegistry.getService( - integrationId, - ) as IAttachmentService; - const resp: ApiResponse = - await service.addAttachment( - desunifiedObject, - linkedUserId, - unifiedAttachmentData.attachment_type, - ); - - const unifiedObject = (await this.coreUnification.unify< - OriginalAttachmentOutput[] - >({ - sourceObject: [resp.data], - targetType: AtsObject.attachment, - providerName: integrationId, - vertical: 'ats', - connectionId: connection_id, - customFieldMappings: customFieldMappings, - })) as UnifiedAtsAttachmentOutput[]; - - const source_attachment = resp.data; - const target_attachment = unifiedObject[0]; - - const unique_ats_attachment_id = await this.saveOrUpdateAttachment( - target_attachment, - connection_id, - ); - - if (target_attachment.candidate_id) { - await this.prisma.ats_candidate_attachments.update({ - where: { - id_ats_candidate_attachment: unique_ats_attachment_id, - }, - data: { - id_ats_candidate: target_attachment.candidate_id, - }, - }); - } - - await this.ingestService.processFieldMappings( - target_attachment.field_mappings, - unique_ats_attachment_id, - integrationId, - linkedUserId, - ); - - await this.ingestService.processRemoteData( - unique_ats_attachment_id, - source_attachment, - ); - - const result_attachment = await this.getAttachment( - unique_ats_attachment_id, - undefined, - undefined, - connection_id, - project_id, - remote_data, - ); - - const status_resp = resp.statusCode === 201 ? 'success' : 'fail'; - const event = await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: status_resp, - type: 'ats.attachment.created', - method: 'POST', - url: '/ats/attachments', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - await this.webhook.dispatchWebhook( - result_attachment, - 'ats.attachment.created', - linkedUser.id_project, - event.id_event, - ); - return result_attachment; - } catch (error) { - throw error; - } - } - - async validateLinkedUser(linkedUserId: string) { - const linkedUser = await this.prisma.linked_users.findUnique({ - where: { id_linked_user: linkedUserId }, - }); - if (!linkedUser) throw new ReferenceError('Linked User Not Found'); - return linkedUser; - } - - async saveOrUpdateAttachment( - attachment: UnifiedAtsAttachmentOutput, - connection_id: string, - ): Promise { - const existingAttachment = - await this.prisma.ats_candidate_attachments.findFirst({ - where: { - remote_id: attachment.remote_id, - id_connection: connection_id, - }, - }); - - const data: any = { - file_url: attachment.file_url, - file_name: attachment.file_name, - file_type: attachment.attachment_type, - remote_created_at: attachment.remote_created_at, - remote_modified_at: attachment.remote_modified_at, - modified_at: new Date(), - }; - - if (existingAttachment) { - const res = await this.prisma.ats_candidate_attachments.update({ - where: { - id_ats_candidate_attachment: - existingAttachment.id_ats_candidate_attachment, - }, - data: data, - }); - return res.id_ats_candidate_attachment; - } else { - data.created_at = new Date(); - data.remote_id = attachment.remote_id; - data.id_connection = connection_id; - data.id_ats_candidate_attachment = uuidv4(); - - const newAttachment = await this.prisma.ats_candidate_attachments.create({ - data: data, - }); - return newAttachment.id_ats_candidate_attachment; - } - } - - async getAttachment( - id_ats_attachment: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const attachment = await this.prisma.ats_candidate_attachments.findUnique( - { - where: { id_ats_candidate_attachment: id_ats_attachment }, - }, - ); - - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: attachment.id_ats_candidate_attachment, - }, - }, - include: { attribute: true }, - }); - - const fieldMappingsMap = new Map(); - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - const field_mappings = Object.fromEntries(fieldMappingsMap); - - const unifiedAttachment: UnifiedAtsAttachmentOutput = { - id: attachment.id_ats_candidate_attachment, - file_url: attachment.file_url, - file_name: attachment.file_name, - attachment_type: attachment.file_type, - remote_created_at: String(attachment.remote_created_at), - remote_modified_at: String(attachment.remote_modified_at), - candidate_id: attachment.id_ats_candidate, - field_mappings: field_mappings, - remote_id: attachment.remote_id, - created_at: attachment.created_at, - modified_at: attachment.modified_at, - }; - - let res: UnifiedAtsAttachmentOutput = unifiedAttachment; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: attachment.id_ats_candidate_attachment }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - if (linkedUserId && integrationId) { - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.attachment.pull', - method: 'GET', - url: '/ats/attachment', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - } - - return res; - } catch (error) { - throw error; - } - } - - async getAttachments( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsAttachmentOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = - await this.prisma.ats_candidate_attachments.findFirst({ - where: { - id_connection: connection_id, - id_ats_candidate_attachment: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const attachments = await this.prisma.ats_candidate_attachments.findMany({ - take: limit + 1, - cursor: cursor ? { id_ats_candidate_attachment: cursor } : undefined, - orderBy: { created_at: 'asc' }, - where: {}, - }); - - if (attachments.length === limit + 1) { - next_cursor = Buffer.from( - attachments[attachments.length - 1].id_ats_candidate_attachment, - ).toString('base64'); - attachments.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - - const unifiedAttachments: UnifiedAtsAttachmentOutput[] = - await Promise.all( - attachments.map(async (attachment) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: attachment.id_ats_candidate_attachment, - }, - }, - include: { attribute: true }, - }); - - const fieldMappingsMap = new Map(); - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - const field_mappings = Array.from( - fieldMappingsMap, - ([key, value]) => ({ [key]: value }), - ); - - return { - id: attachment.id_ats_candidate_attachment, - file_url: attachment.file_url, - file_name: attachment.file_name, - attachment_type: attachment.file_type, - remote_created_at: String(attachment.remote_created_at), - remote_modified_at: String(attachment.remote_modified_at), - candidate_id: attachment.id_ats_candidate, - field_mappings: field_mappings, - remote_id: attachment.remote_id, - created_at: attachment.created_at, - modified_at: attachment.modified_at, - }; - }), - ); - - let res: UnifiedAtsAttachmentOutput[] = unifiedAttachments; - - if (remote_data) { - const remote_array_data: UnifiedAtsAttachmentOutput[] = - await Promise.all( - res.map(async (attachment) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: attachment.id }, - }); - const remote_data = JSON.parse(resp.data); - return { ...attachment, remote_data }; - }), - ); - - res = remote_array_data; - } - - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.attachment.pull', - method: 'GET', - url: '/ats/attachments', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { data: res, prev_cursor, next_cursor }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/attachment/services/registry.service.ts b/packages/api/src/ats/attachment/services/registry.service.ts deleted file mode 100644 index 467340b4b..000000000 --- a/packages/api/src/ats/attachment/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IAttachmentService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IAttachmentService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IAttachmentService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/attachment/sync/sync.service.ts b/packages/api/src/ats/attachment/sync/sync.service.ts deleted file mode 100644 index ec0f47d7a..000000000 --- a/packages/api/src/ats/attachment/sync/sync.service.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { BullQueueService } from '@@core/@core-services/queues/shared.service'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { IBaseSync } from '@@core/utils/types/interface'; -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { ats_candidate_attachments as AtsAttachment } from '@prisma/client'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../services/registry.service'; -import { UnifiedAtsAttachmentOutput } from '../types/model.unified'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'attachment', this); - } - onModuleInit() { - return; - } - async kickstartSync(id_project?: string) { - return; - } - - // it is synced within candidate sync - - async saveToDb( - connection_id: string, - linkedUserId: string, - attachments: UnifiedAtsAttachmentOutput[], - originSource: string, - remote_data: Record[], - candidate_id?: string, - ): Promise { - try { - const attachments_results: AtsAttachment[] = []; - - const updateOrCreateAttachment = async ( - attachment: UnifiedAtsAttachmentOutput, - originId: string, - ) => { - let existingAttachment; - if (!originId) { - existingAttachment = - await this.prisma.ats_candidate_attachments.findFirst({ - where: { - file_name: attachment.file_name ?? null, - file_url: attachment.file_url ?? null, - }, - }); - } else { - existingAttachment = - await this.prisma.ats_candidate_attachments.findFirst({ - where: { - remote_id: originId, - }, - }); - } - - const baseData: any = { - file_url: attachment.file_url ?? null, - file_name: attachment.file_name ?? null, - file_type: attachment.attachment_type ?? null, - remote_created_at: attachment.remote_created_at ?? null, - remote_modified_at: attachment.remote_modified_at ?? null, - candidate_id: candidate_id ?? null, - modified_at: new Date(), - }; - - let res; - if (existingAttachment) { - res = await this.prisma.ats_candidate_attachments.update({ - where: { - id_ats_candidate_attachment: - existingAttachment.id_ats_candidate_attachment, - }, - data: baseData, - }); - } else { - res = await this.prisma.ats_candidate_attachments.create({ - data: { - ...baseData, - id_ats_attachment: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - - return res; - }; - - for (let i = 0; i < attachments.length; i++) { - const attachment = attachments[i]; - const originId = attachment.remote_id; - - if (!originId || originId === '') { - throw new ReferenceError(`Origin id not there, found ${originId}`); - } - - const res = await updateOrCreateAttachment(attachment, originId); - const attachment_id = res.id_ats_attachment; - attachments_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - attachment.field_mappings, - attachment_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - attachment_id, - remote_data[i], - ); - } - - return attachments_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/attachment/types/index.ts b/packages/api/src/ats/attachment/types/index.ts deleted file mode 100644 index e312a8f18..000000000 --- a/packages/api/src/ats/attachment/types/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - AttachmentType, - UnifiedAtsAttachmentInput, - UnifiedAtsAttachmentOutput, -} from './model.unified'; -import { OriginalAttachmentOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface IAttachmentService extends IBaseObjectService { - addAttachment?( - attachmentData: DesunifyReturnType, - linkedUserId: string, - attachment_type?: AttachmentType | string, - ): Promise>; - - sync(data: SyncParam): Promise>; -} - -export interface IAttachmentMapper { - desunify( - source: UnifiedAtsAttachmentInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalAttachmentOutput | OriginalAttachmentOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/attachment/types/model.unified.ts b/packages/api/src/ats/attachment/types/model.unified.ts deleted file mode 100644 index 3843665c6..000000000 --- a/packages/api/src/ats/attachment/types/model.unified.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsIn, -} from 'class-validator'; - -export type AttachmentType = - | 'RESUME' - | 'COVER_LETTER' - | 'OFFER_LETTER' - | 'OTHER'; -export class UnifiedAtsAttachmentInput { - @ApiPropertyOptional({ - type: String, - example: 'https://example.com/file.pdf', - nullable: true, - description: 'The URL of the file', - }) - @IsString() - @IsOptional() - file_url?: string; - - @ApiPropertyOptional({ - type: String, - example: 'file.pdf', - nullable: true, - description: 'The name of the file', - }) - @IsString() - @IsOptional() - file_name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'RESUME', - //// enum: ['RESUME', 'COVER_LETTER', 'OFFER_LETTER', 'OTHER'], - nullable: true, - description: 'The type of the file', - }) - ////@IsIn(['RESUME', 'COVER_LETTER', 'OFFER_LETTER', 'OTHER']) - @IsOptional() - attachment_type?: AttachmentType | string; - - @ApiPropertyOptional({ - type: String, - example: '2024-10-01T12:00:00Z', - format: 'date-time', - nullable: true, - description: 'The remote creation date of the attachment', - }) - @IsDateString() - @IsOptional() - remote_created_at?: string; - - @ApiPropertyOptional({ - type: String, - example: '2024-10-01T12:00:00Z', - format: 'date-time', - nullable: true, - description: 'The remote modification date of the attachment', - }) - @IsDateString() - @IsOptional() - remote_modified_at?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the candidate', - }) - @IsUUID() - @IsOptional() - candidate_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - additionalProperties: true, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsAttachmentOutput extends UnifiedAtsAttachmentInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the attachment', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The remote ID of the attachment', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: - 'The remote data of the attachment in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The created date of the object', - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The modified date of the object', - }) - @IsOptional() - modified_at?: Date; -} diff --git a/packages/api/src/ats/attachment/utils/index.ts b/packages/api/src/ats/attachment/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/attachment/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/candidate/candidate.controller.ts b/packages/api/src/ats/candidate/candidate.controller.ts deleted file mode 100644 index ffcc8f5f3..000000000 --- a/packages/api/src/ats/candidate/candidate.controller.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { - UnifiedAtsCandidateInput, - UnifiedAtsCandidateOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { CandidateService } from './services/candidate.service'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, - ApiPostCustomResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - - -@ApiTags('ats/candidates') -@Controller('ats/candidates') -export class CandidateController { - constructor( - private readonly candidateService: CandidateService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(CandidateController.name); - } - - @ApiOperation({ - operationId: 'listAtsCandidate', // Updated operationId - summary: 'List Candidates', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsCandidateOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getCandidates( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.candidateService.getCandidates( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsCandidate', // Updated operationId - summary: 'Retrieve Candidates', - description: 'Retrieve Candidates from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the candidate you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsCandidateOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.candidateService.getCandidate( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } - - @ApiOperation({ - operationId: 'createAtsCandidate', // Updated operationId - summary: 'Create Candidates', - description: 'Create Candidates in any supported Ats software', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiBody({ type: UnifiedAtsCandidateInput }) - @ApiPostCustomResponse(UnifiedAtsCandidateOutput) - @UseGuards(ApiKeyAuthGuard) - @Post() - async addCandidate( - @Body() unifiedCandidateData: UnifiedAtsCandidateInput, - @Headers('x-connection-token') connection_token: string, - @Query('remote_data') remote_data?: boolean, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.candidateService.addCandidate( - unifiedCandidateData, - connectionId, - projectId, - remoteSource, - linkedUserId, - remote_data, - ); - } catch (error) { - throw new Error(error); - } - } -} diff --git a/packages/api/src/ats/candidate/candidate.module.ts b/packages/api/src/ats/candidate/candidate.module.ts deleted file mode 100644 index 26e0fd8ed..000000000 --- a/packages/api/src/ats/candidate/candidate.module.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Module } from '@nestjs/common'; -import { CandidateController } from './candidate.controller'; -import { CandidateService } from './services/candidate.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { AshbyService } from './services/ashby'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Utils } from '@ats/@lib/@utils'; -import { ServiceRegistry as ApplicationServiceRegistry } from '@ats/application/services/registry.service'; -import { ServiceRegistry as AttachmentServiceRegistry } from '@ats/attachment/services/registry.service'; -import { AshbyCandidateMapper } from './services/ashby/mappers'; -@Module({ - controllers: [CandidateController], - providers: [ - CandidateService, - - CoreUnification, - - SyncService, - WebhookService, - - ServiceRegistry, - - IngestDataService, - - ApplicationServiceRegistry, - AttachmentServiceRegistry, - Utils, - /* PROVIDERS SERVICES */ - AshbyCandidateMapper, - AshbyService, - ], - exports: [SyncService], -}) -export class CandidateModule {} diff --git a/packages/api/src/ats/candidate/services/ashby/index.ts b/packages/api/src/ats/candidate/services/ashby/index.ts deleted file mode 100644 index 909cfc6b3..000000000 --- a/packages/api/src/ats/candidate/services/ashby/index.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ICandidateService } from '@ats/candidate/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 { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { ApiResponse } from '@@core/utils/types'; -import { ServiceRegistry } from '../registry.service'; -import { AshbyCandidateInput, AshbyCandidateOutput } from './types'; -import { SyncParam } from '@@core/utils/types/interface'; - -@Injectable() -export class AshbyService implements ICandidateService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - AtsObject.candidate.toUpperCase() + ':' + AshbyService.name, - ); - this.registry.registerService('ashby', this); - } - async addCandidate( - candidateData: AshbyCandidateInput, - linkedUserId: string, - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - const resp = await axios.post( - `${connection.account_url}/candidate.create`, - JSON.stringify(candidateData), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }, - ); - - return { - data: resp.data.results, - message: 'Ashby candidate created', - statusCode: 201, - }; - } catch (error) { - throw error; - } - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - const resp = await axios.post( - `${connection.account_url}/candidate.list`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }, - ); - const candidates: AshbyCandidateOutput[] = resp.data.results; - this.logger.log(`Synced ashby candidates !`); - - return { - data: candidates, - message: 'Ashby candidates retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/candidate/services/ashby/mappers.ts b/packages/api/src/ats/candidate/services/ashby/mappers.ts deleted file mode 100644 index c12376644..000000000 --- a/packages/api/src/ats/candidate/services/ashby/mappers.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { AshbyCandidateInput, AshbyCandidateOutput } from './types'; -import { - UnifiedAtsCandidateInput, - UnifiedAtsCandidateOutput, -} from '@ats/candidate/types/model.unified'; -import { ICandidateMapper } from '@ats/candidate/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 { - OriginalAttachmentOutput, - OriginalTagOutput, -} from '@@core/utils/types/original/original.ats'; -import { AtsObject } from '@ats/@lib/@types'; -import { UnifiedAtsAttachmentOutput } from '@ats/attachment/types/model.unified'; -import { UnifiedAtsTagOutput } from '@ats/tag/types/model.unified'; - -@Injectable() -export class AshbyCandidateMapper implements ICandidateMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('ats', 'candidate', 'ashby', this); - } - - async desunify( - source: UnifiedAtsCandidateInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - const data: any = { - name: `${source.first_name || ''} ${source.last_name || ''}`, - }; - if (source.locations) { - } - if (source.email_addresses) { - } - if (source.phone_numbers) { - const number = source.phone_numbers[0]; - data.phoneNumber = number.phone_number; - } - - if (source.urls) { - for (const url of source.urls) { - switch (url.url_type) { - case 'LINKEDIN': - data.linkedInUrl = url.url; - break; - case 'GITHUB': - data.githubUrl = url.url; - break; - default: - data.website = url.url; - break; - } - } - } - - if (source.locations) { - const tab = source.locations.split(','); - data.location = { - city: tab[0] ?? null, - region: tab[1] ?? null, - country: tab[2] ?? null, - }; - } - return data; - } - - async unify( - source: AshbyCandidateOutput | AshbyCandidateOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - if (!Array.isArray(source)) { - return await this.mapSingleCandidateToUnified( - source, - connectionId, - customFieldMappings, - ); - } - // Handling array of AshbyCandidateOutput - return Promise.all( - source.map((candidate) => - this.mapSingleCandidateToUnified( - candidate, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleCandidateToUnified( - candidate: AshbyCandidateOutput, - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - let applications; - if (candidate.applicationIds) { - for (const uuid of candidate.applicationIds) { - const app = await this.utils.getApplicationUuidFromRemoteId( - uuid, - connectionId, - ); - if (app) applications.push(app); - } - } - let opts; - if (candidate.fileHandles && candidate.fileHandles.length > 0) { - const attachments = (await this.coreUnificationService.unify< - OriginalAttachmentOutput[] - >({ - sourceObject: candidate.fileHandles, - targetType: AtsObject.attachment, - providerName: 'ashby', - vertical: 'ats', - connectionId: connectionId, - customFieldMappings: [], - })) as UnifiedAtsAttachmentOutput[]; - opts = { - attachments: attachments, - }; - } - if (candidate.resumeFileHandle) { - candidate.resumeFileHandle.resume = true; - const attachments = (await this.coreUnificationService.unify< - OriginalAttachmentOutput[] - >({ - sourceObject: [candidate.resumeFileHandle], - targetType: AtsObject.attachment, - providerName: 'ashby', - vertical: 'ats', - connectionId: connectionId, - customFieldMappings: [], - })) as UnifiedAtsAttachmentOutput[]; - opts = { - attachments: [opts.attachments, ...attachments], - }; - } - if (candidate.tags && candidate.tags.length > 0) { - const tags = (await this.coreUnificationService.unify< - OriginalTagOutput[] - >({ - sourceObject: candidate.tags, - targetType: AtsObject.tag, - providerName: 'ashby', - vertical: 'ats', - connectionId: connectionId, - customFieldMappings: [], - })) as UnifiedAtsTagOutput[]; - opts = { - tags: tags, - }; - } - - if (candidate.emailAddresses) { - let email_addresses; - for (const email of candidate.emailAddresses) { - email_addresses.push({ - email_address: email.value, - email_address_type: email.type == 'Work' ? 'work' : 'primary', - }); - } - opts = { - email_addresses: email_addresses, - }; - } - if (candidate.phoneNumbers) { - let phone_numbers; - for (const phone of candidate.phoneNumbers) { - phone_numbers.push({ - phone_number: phone.value, - phone_type: phone.type == 'Work' ? 'work' : 'primary', - }); - } - opts = { - phone_numbers: phone_numbers, - }; - } - - if (candidate.socialLinks) { - let urls; - for (const link of candidate.socialLinks) { - urls.push({ - url: link.url, - url_type: link.type, - }); - } - opts = { - urls: urls, - }; - } - - return { - remote_id: candidate.id, - remote_data: candidate, - first_name: candidate.name, - last_name: null, - last_interaction_at: null, - locations: candidate.primaryLocation.locationSummary || null, - applications: applications || null, - company: candidate.company || null, - title: candidate.position || null, - is_private: null, - email_reachable: null, - remote_created_at: candidate.createdAt, - ...opts, - }; - } -} diff --git a/packages/api/src/ats/candidate/services/ashby/types.ts b/packages/api/src/ats/candidate/services/ashby/types.ts deleted file mode 100644 index 92fa94cdb..000000000 --- a/packages/api/src/ats/candidate/services/ashby/types.ts +++ /dev/null @@ -1,119 +0,0 @@ -export type AshbyCandidateInput = { - name: string; // required - email?: string; - phoneNumber?: string; - linkedInUrl?: string; - githubUrl?: string; - website?: string; - alternateEmailAddresses?: string[]; - sourceId?: string; - creditedToUserId?: string; - location?: { - city?: string; - region?: string; - country?: string; - }; - createdAt?: string; // ISO date string -}; -export interface AshbyCandidateOutput { - id: string; - createdAt: string; - name: string; - primaryEmailAddress: PrimaryEmailAddress; - emailAddresses: EmailAddress[]; - primaryPhoneNumber: PhoneNumber; - phoneNumbers: PhoneNumber[]; - socialLinks: SocialLink[]; - tags: Tag[]; - position: string; - company: string; - school: string; - applicationIds: string[]; - resumeFileHandle: ResumeFileHandle; - fileHandles: FileHandle[]; - customFields: CustomField[]; - profileUrl: string; - source: { - id: string; - title: string; - isArchived: boolean; - sourceType: SourceType; - }; - creditedToUser: CreditedToUser; - timezone: string; - primaryLocation: PrimaryLocation; -} -interface PrimaryEmailAddress { - value: string; - type: string; - isPrimary: boolean; -} - -interface EmailAddress { - value: string; - type: string; - isPrimary: boolean; -} - -interface PhoneNumber { - value: string; - type: string; - isPrimary: boolean; -} - -interface SocialLink { - url: string; - type: string; -} - -interface Tag { - id: string; - title: string; - isArchived: boolean; -} - -interface ResumeFileHandle { - id: string; - name: string; - handle: string; - [key: string]: any; -} - -interface FileHandle { - id: string; - name: string; - handle: string; -} - -interface CustomField { - id: string; - title: string; - value: string; -} - -interface SourceType { - id: string; - title: string; - isArchived: boolean; -} - -interface CreditedToUser { - id: string; - firstName: string; - lastName: string; - email: string; - globalRole: string; - isEnabled: boolean; - updatedAt: string; -} - -interface LocationComponent { - type: string; - name: string; -} - -interface PrimaryLocation { - id: string; - locationSummary: string; - locationComponents: LocationComponent[]; -} diff --git a/packages/api/src/ats/candidate/services/candidate.service.ts b/packages/api/src/ats/candidate/services/candidate.service.ts deleted file mode 100644 index fdeac53f5..000000000 --- a/packages/api/src/ats/candidate/services/candidate.service.ts +++ /dev/null @@ -1,751 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ApiResponse } from '@@core/utils/types'; -import { - OriginalApplicationOutput, - OriginalAttachmentOutput, - OriginalCandidateOutput, -} from '@@core/utils/types/original/original.ats'; -import { AtsObject } from '@ats/@lib/@types'; -import { Injectable } from '@nestjs/common'; -import { v4 as uuidv4 } from 'uuid'; -import { ICandidateService } from '../types'; -import { - UnifiedAtsCandidateInput, - UnifiedAtsCandidateOutput, -} from '../types/model.unified'; -import { ServiceRegistry } from './registry.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { - UnifiedAtsApplicationInput, - UnifiedAtsApplicationOutput, -} from '@ats/application/types/model.unified'; -import { ServiceRegistry as ApplicationServiceRegistry } from '@ats/application/services/registry.service'; -import { ServiceRegistry as AttachmentServiceRegistry } from '@ats/attachment/services/registry.service'; -import { IApplicationService } from '@ats/application/types'; -import { - UnifiedAtsAttachmentInput, - UnifiedAtsAttachmentOutput, -} from '@ats/attachment/types/model.unified'; -import { IAttachmentService } from '@ats/attachment/types'; - -@Injectable() -export class CandidateService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private registry: CoreSyncRegistry, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private applicationServiceRegistry: ApplicationServiceRegistry, - private attachmentServiceRegistry: AttachmentServiceRegistry, - private coreUnification: CoreUnification, - private ingestService: IngestDataService, - ) { - this.logger.setContext(CandidateService.name); - } - - async addCandidate( - unifiedCandidateData: UnifiedAtsCandidateInput, - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - remote_data?: boolean, - ): Promise { - try { - const linkedUser = await this.validateLinkedUser(linkedUserId); - - const customFieldMappings = - await this.fieldMappingService.getCustomFieldMappings( - integrationId, - linkedUserId, - 'ats.candidate', - ); - - const desunifiedObject = - await this.coreUnification.desunify({ - sourceObject: unifiedCandidateData, - targetType: AtsObject.candidate, - providerName: integrationId, - vertical: 'ats', - customFieldMappings: unifiedCandidateData.field_mappings - ? customFieldMappings - : [], - }); - - const service = this.serviceRegistry.getService( - integrationId, - ) as ICandidateService; - const resp: ApiResponse = - await service.addCandidate(desunifiedObject, linkedUserId); - - const unifiedObject = (await this.coreUnification.unify< - OriginalCandidateOutput[] - >({ - sourceObject: [resp.data], - targetType: AtsObject.candidate, - providerName: integrationId, - vertical: 'ats', - connectionId: connection_id, - customFieldMappings: customFieldMappings, - })) as UnifiedAtsCandidateOutput[]; - - const source_candidate = resp.data; - const target_candidate = unifiedObject[0]; - - const unique_ats_candidate_id = await this.saveOrUpdateCandidate( - target_candidate, - connection_id, - ); - - await this.processAttachments( - unifiedCandidateData.attachments, - unique_ats_candidate_id, - connection_id, - linkedUserId, - integrationId, - ); - - // now that we have inserted our candidate, we might have an application nested that we have to insert - await this.processApplication( - unifiedCandidateData.applications[0], - unique_ats_candidate_id, - connection_id, - linkedUserId, - integrationId, - ); - - // Update related objects, if any (e.g., tags) - // Assuming there is a method to handle tags and other related objects - - await this.ingestService.processFieldMappings( - target_candidate.field_mappings, - unique_ats_candidate_id, - integrationId, - linkedUserId, - ); - - await this.ingestService.processRemoteData( - unique_ats_candidate_id, - source_candidate, - ); - - const result_candidate = await this.getCandidate( - unique_ats_candidate_id, - undefined, - undefined, - connection_id, - project_id, - remote_data, - ); - - const status_resp = resp.statusCode === 201 ? 'success' : 'fail'; - const event = await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: status_resp, - type: 'ats.candidate.created', - method: 'POST', - url: '/ats/candidates', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - await this.webhook.dispatchWebhook( - result_candidate, - 'ats.candidate.created', - linkedUser.id_project, - event.id_event, - ); - return result_candidate; - } catch (error) { - throw error; - } - } - - async validateLinkedUser(linkedUserId: string) { - const linkedUser = await this.prisma.linked_users.findUnique({ - where: { id_linked_user: linkedUserId }, - }); - if (!linkedUser) throw new ReferenceError('Linked User Not Found'); - return linkedUser; - } - - async processAttachments( - attachments: any[], - candidate_id: string, - connection_id: string, - linkedUserId: string, - integrationId: string, - ): Promise { - try { - if (attachments && attachments.length > 0) { - if (typeof attachments[0] === 'string') { - await Promise.all( - attachments.map(async (uuid: string) => { - const attachment = - await this.prisma.ats_candidate_attachments.findUnique({ - where: { id_ats_candidate_attachment: uuid }, - }); - if (!attachment) - throw new ReferenceError( - 'You inserted an ats_attachment_id which does not exist', - ); - }), - ); - return attachments; - } else { - await Promise.all( - attachments.map(async (unified_attachmt) => { - unified_attachmt.candidate_id = candidate_id; // we insert the candidate_id on the fly as it may be mandatory for some providers when creating an application - const desunifiedObject = - await this.coreUnification.desunify({ - sourceObject: unified_attachmt, - targetType: AtsObject.attachment, - providerName: integrationId, - vertical: 'ats', - customFieldMappings: [], - }); - - const service = this.attachmentServiceRegistry.getService( - integrationId, - ) as IAttachmentService; - - const attachment_type = unified_attachmt.attachment_type; - - const resp: ApiResponse = - await service.addAttachment( - desunifiedObject, - linkedUserId, - attachment_type, - ); - - const unifiedObject = (await this.coreUnification.unify< - OriginalAttachmentOutput[] - >({ - sourceObject: [resp.data], - targetType: AtsObject.attachment, - providerName: integrationId, - vertical: 'ats', - connectionId: connection_id, - customFieldMappings: [], - })) as UnifiedAtsAttachmentOutput[]; - - await this.saveOrUpdateAttachment( - unifiedObject[0], - connection_id, - ); - }), - ); - } - } - return []; - } catch (error) { - throw error; - } - } - - async saveOrUpdateAttachment( - att: UnifiedAtsAttachmentOutput, - connection_id: string, - ): Promise { - try { - let existingAtt; - - if (att.remote_id) { - existingAtt = await this.prisma.ats_candidate_attachments.findFirst({ - where: { - remote_id: att.remote_id, - id_connection: connection_id, - }, - }); - } else { - existingAtt = await this.prisma.ats_candidate_attachments.findFirst({ - where: { - file_name: att.file_name, - file_url: att.file_url, - id_connection: connection_id, - }, - }); - } - - const data: any = { - file_name: att.file_name, - file_url: att.file_url, - attachment_type: att.attachment_type, - id_ats_candidate: att.candidate_id, - modified_at: new Date(), - }; - - if (existingAtt) { - const res = await this.prisma.ats_candidate_attachments.update({ - where: { - id_ats_candidate_attachment: - existingAtt.id_ats_candidate_attachment, - }, - data: data, - }); - return res.id_ats_candidate_attachment; - } else { - data.created_at = new Date(); - data.remote_id = att.remote_id; - data.id_connection = connection_id; - data.id_ats_application = uuidv4(); - - const newApp = await this.prisma.ats_candidate_attachments.create({ - data: data, - }); - return newApp.id_ats_candidate_attachment; - } - } catch (error) { - throw error; - } - } - - async saveOrUpdateApplication( - application: UnifiedAtsApplicationOutput, - connection_id: string, - ): Promise { - try { - const existingApp = await this.prisma.ats_applications.findFirst({ - where: { - remote_id: application.remote_id, - id_connection: connection_id, - }, - }); - - const data: any = { - applied_at: application.applied_at, - rejected_at: application.rejected_at, - offers: application.offers, - source: application.source, - credited_to: application.credited_to, - current_stage: application.current_stage, - reject_reason: application.reject_reason, - id_ats_job: application.job_id, - id_ats_candidate: application.candidate_id, - modified_at: new Date(), - }; - - if (existingApp) { - const res = await this.prisma.ats_applications.update({ - where: { id_ats_application: existingApp.id_ats_candidate }, - data: data, - }); - return res.id_ats_application; - } else { - data.created_at = new Date(); - data.remote_id = application.remote_id; - data.id_connection = connection_id; - data.id_ats_application = uuidv4(); - - const newApp = await this.prisma.ats_applications.create({ - data: data, - }); - return newApp.id_ats_application; - } - } catch (error) { - throw error; - } - } - - async processApplication( - application: any, - candidate_id: string, - connection_id: string, - linkedUserId: string, - integrationId: string, - ) { - try { - if (application) { - if (typeof application === 'string') { - const app = await this.prisma.ats_applications.findUnique({ - where: { id_ats_application: application }, - }); - if (!app) - throw new ReferenceError( - 'You inserted an ats_application which does not exist', - ); - await this.prisma.ats_applications.update({ - where: { - id_ats_application: app.id_ats_application, - }, - data: { - id_ats_candidate: candidate_id, - }, - }); - } else { - application.candidate_id = candidate_id; // we insert the candidate_id on the fly as it may be mandatory for some providers when creating an application - const desunifiedObject = - await this.coreUnification.desunify({ - sourceObject: application, - targetType: AtsObject.application, - providerName: integrationId, - vertical: 'ats', - customFieldMappings: [], - }); - - const service = this.applicationServiceRegistry.getService( - integrationId, - ) as IApplicationService; - - const resp: ApiResponse = - await service.addApplication(desunifiedObject, linkedUserId); - - const unifiedObject = (await this.coreUnification.unify< - OriginalApplicationOutput[] - >({ - sourceObject: [resp.data], - targetType: AtsObject.application, - providerName: integrationId, - vertical: 'ats', - connectionId: connection_id, - customFieldMappings: [], - })) as UnifiedAtsApplicationOutput[]; - - await this.saveOrUpdateApplication(unifiedObject[0], connection_id); - } - } - } catch (error) { - throw error; - } - } - - async saveOrUpdateCandidate( - candidate: UnifiedAtsCandidateOutput, - connection_id: string, - ): Promise { - try { - const existingCandidate = await this.prisma.ats_candidates.findFirst({ - where: { remote_id: candidate.remote_id, id_connection: connection_id }, - }); - - const data: any = { - first_name: candidate.first_name, - last_name: candidate.last_name, - company: candidate.company, - title: candidate.title, - remote_created_at: candidate.remote_created_at, - remote_modified_at: candidate.remote_modified_at, - last_interaction_at: candidate.last_interaction_at, - is_private: candidate.is_private, - email_reachable: candidate.email_reachable, - locations: candidate.locations, - modified_at: new Date(), - }; - - if (existingCandidate) { - const res = await this.prisma.ats_candidates.update({ - where: { id_ats_candidate: existingCandidate.id_ats_candidate }, - data: data, - }); - return res.id_ats_candidate; - } else { - data.created_at = new Date(); - data.remote_id = candidate.remote_id; - data.id_connection = connection_id; - data.id_ats_candidate = uuidv4(); - - const newCandidate = await this.prisma.ats_candidates.create({ - data: data, - }); - return newCandidate.id_ats_candidate; - } - } catch (error) { - throw error; - } - } - - async getCandidate( - id_ats_candidate: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const candidate = await this.prisma.ats_candidates.findUnique({ - where: { id_ats_candidate: id_ats_candidate }, - }); - - const attachments = await this.prisma.ats_candidate_attachments.findMany({ - where: { id_ats_candidate: id_ats_candidate }, - }); - - const applications = await this.prisma.ats_applications.findMany({ - where: { id_ats_candidate: id_ats_candidate }, - }); - - const emailAddresses = - await this.prisma.ats_candidate_email_addresses.findMany({ - where: { id_ats_candidate: id_ats_candidate }, - }); - - const phoneNumbers = - await this.prisma.ats_candidate_phone_numbers.findMany({ - where: { id_ats_candidate: id_ats_candidate }, - }); - - const urls = await this.prisma.ats_candidate_urls.findMany({ - where: { id_ats_candidate: id_ats_candidate }, - }); - - const values = await this.prisma.value.findMany({ - where: { entity: { ressource_owner_id: candidate.id_ats_candidate } }, - include: { attribute: true }, - }); - - const fieldMappingsMap = new Map(); - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - const field_mappings = Object.fromEntries(fieldMappingsMap); - - const unifiedCandidate: UnifiedAtsCandidateOutput = { - id: candidate.id_ats_candidate, - remote_id: candidate.remote_id, - created_at: candidate.created_at, - modified_at: candidate.modified_at, - first_name: candidate.first_name, - last_name: candidate.last_name, - company: candidate.company, - title: candidate.title, - remote_created_at: String(candidate.remote_created_at), - remote_modified_at: String(candidate.remote_modified_at), - last_interaction_at: String(candidate.last_interaction_at), - is_private: candidate.is_private, - email_reachable: candidate.email_reachable, - locations: candidate.locations, - attachments: attachments.map( - (attachment) => attachment.id_ats_candidate_attachment, - ), - applications: applications.map( - (application) => application.id_ats_application, - ), - email_addresses: emailAddresses.map((email) => ({ - email_address: email.value, - email_address_type: email.type, - })), - phone_numbers: phoneNumbers.map((phone) => ({ - phone_number: phone.value, - phone_type: phone.type, - })), - tags: candidate.tags, - urls: urls.map((url) => ({ - url: url.value, - url_type: url.type, - })), - field_mappings: field_mappings, - }; - - let res: UnifiedAtsCandidateOutput = unifiedCandidate; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: candidate.id_ats_candidate }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - if (linkedUserId && integrationId) { - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.candidate.pull', - method: 'GET', - url: '/ats/candidate', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - } - return res; - } catch (error) { - throw error; - } - } - - async getCandidates( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsCandidateOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = await this.prisma.ats_candidates.findFirst({ - where: { - id_connection: connection_id, - id_ats_candidate: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const candidates = await this.prisma.ats_candidates.findMany({ - take: limit + 1, - cursor: cursor ? { id_ats_candidate: cursor } : undefined, - orderBy: { created_at: 'asc' }, - where: {}, - }); - - if (candidates.length === limit + 1) { - next_cursor = Buffer.from( - candidates[candidates.length - 1].id_ats_candidate, - ).toString('base64'); - candidates.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - - const unifiedCandidates: UnifiedAtsCandidateOutput[] = await Promise.all( - candidates.map(async (candidate) => { - const attachments = - await this.prisma.ats_candidate_attachments.findMany({ - where: { id_ats_candidate: candidate.id_ats_candidate }, - }); - - const applications = await this.prisma.ats_applications.findMany({ - where: { id_ats_candidate: candidate.id_ats_candidate }, - }); - - const emailAddresses = - await this.prisma.ats_candidate_email_addresses.findMany({ - where: { id_ats_candidate: candidate.id_ats_candidate }, - }); - - const phoneNumbers = - await this.prisma.ats_candidate_phone_numbers.findMany({ - where: { id_ats_candidate: candidate.id_ats_candidate }, - }); - const urls = await this.prisma.ats_candidate_urls.findMany({ - where: { id_ats_candidate: candidate.id_ats_candidate }, - }); - - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: candidate.id_ats_candidate }, - }, - include: { attribute: true }, - }); - - const fieldMappingsMap = new Map(); - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an object -const field_mappings = Object.fromEntries(fieldMappingsMap); - - return { - id: candidate.id_ats_candidate, - remote_id: candidate.remote_id, - created_at: candidate.created_at, - modified_at: candidate.modified_at, - first_name: candidate.first_name, - last_name: candidate.last_name, - company: candidate.company, - title: candidate.title, - remote_created_at: String(candidate.remote_created_at), - remote_modified_at: String(candidate.remote_modified_at), - last_interaction_at: String(candidate.last_interaction_at), - is_private: candidate.is_private, - email_reachable: candidate.email_reachable, - locations: candidate.locations, - attachments: attachments.map( - (attachment) => attachment.id_ats_candidate_attachment, - ), - applications: applications.map( - (application) => application.id_ats_application, - ), - email_addresses: emailAddresses.map((email) => ({ - email_address: email.value, - email_address_type: email.type, - })), - phone_numbers: phoneNumbers.map((phone) => ({ - phone_number: phone.value, - phone_type: phone.type, - })), - tags: candidate.tags, - urls: urls.map((url) => ({ - url: url.value, - url_type: url.type, - })), - field_mappings: field_mappings, - }; - }), - ); - - let res: UnifiedAtsCandidateOutput[] = unifiedCandidates; - - if (remote_data) { - const remote_array_data: UnifiedAtsCandidateOutput[] = - await Promise.all( - res.map(async (candidate) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: candidate.id }, - }); - const remote_data = JSON.parse(resp.data); - return { ...candidate, remote_data }; - }), - ); - - res = remote_array_data; - } - - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.candidate.pull', - method: 'GET', - url: '/ats/candidates', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { data: res, prev_cursor, next_cursor }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/candidate/services/registry.service.ts b/packages/api/src/ats/candidate/services/registry.service.ts deleted file mode 100644 index 11f52c9f6..000000000 --- a/packages/api/src/ats/candidate/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ICandidateService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: ICandidateService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): ICandidateService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/candidate/sync/sync.service.ts b/packages/api/src/ats/candidate/sync/sync.service.ts deleted file mode 100644 index 29b7d8692..000000000 --- a/packages/api/src/ats/candidate/sync/sync.service.ts +++ /dev/null @@ -1,341 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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 { ICandidateService } from '../types'; -import { OriginalCandidateOutput } from '@@core/utils/types/original/original.ats'; -import { UnifiedAtsCandidateOutput } from '../types/model.unified'; -import { ats_candidates as AtsCandidate } 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'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Utils } from '@ats/@lib/@utils'; -import { UnifiedAtsAttachmentOutput } from '@ats/attachment/types/model.unified'; -import { UnifiedAtsTagOutput } from '@ats/tag/types/model.unified'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - private utils: Utils, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'candidate', this); - } - onModuleInit() { - // - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: ICandidateService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: candidate} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsCandidateOutput, - OriginalCandidateOutput, - ICandidateService - >(integrationId, linkedUserId, 'ats', 'candidate', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - candidates: UnifiedAtsCandidateOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const candidates_results: AtsCandidate[] = []; - const updateOrCreateEmails = async ( - candidateId: string, - emails: any[], - ) => { - if (emails && emails.length > 0) { - await Promise.all( - emails.map(async (email) => { - const existingEmail = - await this.prisma.ats_candidate_email_addresses.findFirst({ - where: { - id_ats_candidate: candidateId, - value: email.value, - }, - }); - if (existingEmail) { - await this.prisma.ats_candidate_email_addresses.update({ - where: { - id_ats_candidate_email_address: - existingEmail.id_ats_candidate_email_address, - }, - data: { - ...email, - id_ats_candidate: candidateId, - }, - }); - } else { - await this.prisma.ats_candidate_email_addresses.create({ - data: { - ...email, - id_ats_candidate: candidateId, - }, - }); - } - }), - ); - } - }; - - const updateOrCreatePhones = async ( - candidateId: string, - phones: any[], - ) => { - if (phones && phones.length > 0) { - await Promise.all( - phones.map(async (phone) => { - const existingPhone = - await this.prisma.ats_candidate_phone_numbers.findFirst({ - where: { - id_ats_candidate: candidateId, - value: phone.value, - }, - }); - if (existingPhone) { - await this.prisma.ats_candidate_phone_numbers.update({ - where: { - id_ats_candidate_phone_number: - existingPhone.id_ats_candidate_phone_number, - }, - data: { - ...phone, - id_ats_candidate: candidateId, - }, - }); - } else { - await this.prisma.ats_candidate_phone_numbers.create({ - data: { - ...phone, - id_ats_candidate: candidateId, - }, - }); - } - }), - ); - } - }; - - const updateOrCreateUrls = async (candidateId: string, urls: any[]) => { - if (urls && urls.length > 0) { - await Promise.all( - urls.map(async (url) => { - const existingUrl = - await this.prisma.ats_candidate_urls.findFirst({ - where: { - id_ats_candidate: candidateId, - value: url.value, - }, - }); - if (existingUrl) { - await this.prisma.ats_candidate_urls.update({ - where: { - id_ats_candidate_url: existingUrl.id_ats_candidate_url, - }, - data: { - ...url, - id_ats_candidate: candidateId, - }, - }); - } else { - await this.prisma.ats_candidate_urls.create({ - data: { - ...url, - id_ats_candidate: candidateId, - }, - }); - } - }), - ); - } - }; - - const updateOrCreateCandidate = async ( - candidate: UnifiedAtsCandidateOutput, - originId: string, - ) => { - const existingCandidate = await this.prisma.ats_candidates.findFirst({ - where: { - remote_id: originId, - }, - }); - - const { normalizedEmails, normalizedPhones } = - this.utils.normalizeEmailsAndNumbers( - candidate.email_addresses, - candidate.phone_numbers, - ); - const normalizedUrls = this.utils.normalizeUrls(candidate.urls); - - const baseData: any = { - first_name: candidate.first_name ?? null, - last_name: candidate.last_name ?? null, - company: candidate.company ?? null, - title: candidate.title ?? null, - locations: candidate.locations ?? null, - is_private: candidate.is_private ?? null, - email_reachable: candidate.email_reachable ?? null, - remote_created_at: candidate.remote_created_at ?? null, - remote_modified_at: candidate.remote_modified_at ?? null, - last_interaction_at: candidate.last_interaction_at ?? null, - modified_at: new Date(), - }; - - let res; - if (existingCandidate) { - res = await this.prisma.ats_candidates.update({ - where: { - id_ats_candidate: existingCandidate.id_ats_candidate, - }, - data: baseData, - }); - } else { - res = await this.prisma.ats_candidates.create({ - data: { - ...baseData, - id_ats_candidate: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - - const candidateId = res.id_ats_candidate; - - await updateOrCreateEmails(candidateId, normalizedEmails); - await updateOrCreatePhones(candidateId, normalizedPhones); - await updateOrCreateUrls(candidateId, normalizedUrls); - - return res; - }; - - for (let i = 0; i < candidates.length; i++) { - const candidate = candidates[i]; - const originId = candidate.remote_id; - - if (!originId || originId === '') { - throw new ReferenceError(`Origin id not there, found ${originId}`); - } - - const res = await updateOrCreateCandidate(candidate, originId); - const candidate_id = res.id_ats_candidate; - candidates_results.push(res); - - // Process attachments - if (candidate.attachments) { - await this.registry.getService('ats', 'attachment').saveToDb( - connection_id, - linkedUserId, - candidate.attachments, - originSource, - candidate.attachments.map( - (att: UnifiedAtsAttachmentOutput) => att.remote_data, - ), - { candidate_id }, - ); - } - - // Process tags - if (candidate.tags) { - const tags = await this.registry.getService('ats', 'tag').saveToDb( - connection_id, - linkedUserId, - candidate.tags, - originSource, - candidate.tags.map((tag: UnifiedAtsTagOutput) => tag.remote_data), - ); - await this.prisma.ats_candidates.update({ - where: { - id_ats_candidate: candidate_id, - }, - data: { - tags: tags.map((tag) => tag.id_tcg_tag as string), - }, - }); - } - - // Process field mappings - await this.ingestService.processFieldMappings( - candidate.field_mappings, - candidate_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - candidate_id, - remote_data[i], - ); - } - - return candidates_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/candidate/types/index.ts b/packages/api/src/ats/candidate/types/index.ts deleted file mode 100644 index fe0f24198..000000000 --- a/packages/api/src/ats/candidate/types/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { UnifiedAtsCandidateInput, UnifiedAtsCandidateOutput } from './model.unified'; -import { OriginalCandidateOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface ICandidateService extends IBaseObjectService { - addCandidate( - candidateData: DesunifyReturnType, - linkedUserId: string, - ): Promise>; - - sync(data: SyncParam): Promise>; -} - -export interface ICandidateMapper { - desunify( - source: UnifiedAtsCandidateInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalCandidateOutput | OriginalCandidateOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/candidate/types/model.unified.ts b/packages/api/src/ats/candidate/types/model.unified.ts deleted file mode 100644 index 528c3a60b..000000000 --- a/packages/api/src/ats/candidate/types/model.unified.ts +++ /dev/null @@ -1,543 +0,0 @@ -import { Email, Phone, Url } from '@ats/@lib/@types'; -import { UnifiedAtsApplicationOutput } from '@ats/application/types/model.unified'; -import { UnifiedAtsAttachmentOutput } from '@ats/attachment/types/model.unified'; -import { UnifiedAtsTagOutput } from '@ats/tag/types/model.unified'; -import { - ApiProperty, - ApiPropertyOptional, - getSchemaPath, -} from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsBoolean, - IsDateString, -} from 'class-validator'; - -export class UnifiedAtsCandidateInput { - @ApiPropertyOptional({ - type: String, - example: 'Joe', - nullable: true, - description: 'The first name of the candidate', - }) - @IsString() - @IsOptional() - first_name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'Doe', - nullable: true, - description: 'The last name of the candidate', - }) - @IsString() - @IsOptional() - last_name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'Acme', - nullable: true, - description: 'The company of the candidate', - }) - @IsString() - @IsOptional() - company?: string; - - @ApiPropertyOptional({ - type: String, - example: 'Analyst', - nullable: true, - description: 'The title of the candidate', - }) - @IsString() - @IsOptional() - title?: string; - - @ApiPropertyOptional({ - type: String, - example: 'New York', - nullable: true, - description: 'The locations of the candidate', - }) - @IsString() - @IsOptional() - locations?: string; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: 'Whether the candidate is private', - }) - @IsBoolean() - @IsOptional() - is_private?: boolean; - - @ApiPropertyOptional({ - type: Boolean, - example: true, - nullable: true, - description: 'Whether the candidate is reachable by email', - }) - @IsBoolean() - @IsOptional() - email_reachable?: boolean; - - @ApiPropertyOptional({ - type: String, - example: '2024-10-01T12:00:00Z', - format: 'date-time', - nullable: true, - description: 'The remote creation date of the candidate', - }) - @IsDateString() - @IsOptional() - remote_created_at?: string; - - @ApiPropertyOptional({ - type: String, - example: '2024-10-01T12:00:00Z', - format: 'date-time', - nullable: true, - description: 'The remote modification date of the candidate', - }) - @IsDateString() - @IsOptional() - remote_modified_at?: string; - - @ApiPropertyOptional({ - type: String, - example: '2024-10-01T12:00:00Z', - format: 'date-time', - nullable: true, - description: 'The last interaction date with the candidate', - }) - @IsDateString() - @IsOptional() - last_interaction_at?: string; - - @ApiPropertyOptional({ - type: 'array', - items: { - oneOf: [ - { type: 'string' }, - { $ref: getSchemaPath(UnifiedAtsAttachmentOutput) }, - ], - }, - example: ['801f9ede-c698-4e66-a7fc-48d19eebaa4f'], - nullable: true, - description: 'The attachments UUIDs of the candidate', - }) - @IsString({ each: true }) - @IsOptional() - attachments?: (string | UnifiedAtsAttachmentOutput)[]; - - @ApiPropertyOptional({ - type: 'array', - items: { - oneOf: [ - { type: 'string' }, - { $ref: getSchemaPath(UnifiedAtsApplicationOutput) }, - ], - }, - example: ['801f9ede-c698-4e66-a7fc-48d19eebaa4f'], - nullable: true, - description: 'The applications UUIDs of the candidate', - }) - @IsString({ each: true }) - @IsOptional() - applications?: (string | UnifiedAtsApplicationOutput)[]; - - @ApiPropertyOptional({ - type: 'array', - items: { - oneOf: [{ type: 'string' }, { $ref: getSchemaPath(UnifiedAtsTagOutput) }], - }, - example: ['tag_1', 'tag_2'], - nullable: true, - description: 'The tags of the candidate', - }) - @IsString({ each: true }) - @IsOptional() - tags?: (string | UnifiedAtsTagOutput)[]; - - @ApiPropertyOptional({ - type: [Url], - example: [ - { - url: 'mywebsite.com', - url_type: 'WEBSITE', - }, - ], - nullable: true, - description: - 'The urls of the candidate, possible values for Url type are WEBSITE, BLOG, LINKEDIN, GITHUB, or OTHER', - }) - @IsOptional() - urls?: Url[]; - - @ApiPropertyOptional({ - type: [Phone], - example: [ - { - phone_number: '+33660688899', - phone_type: 'WORK', - }, - ], - nullable: true, - description: 'The phone numbers of the candidate', - }) - @IsOptional() - phone_numbers?: Phone[]; - - @ApiPropertyOptional({ - type: [Email], - example: [ - { - email_address: 'joedoe@gmail.com', - email_address_type: 'WORK', - }, - ], - nullable: true, - description: 'The email addresses of the candidate', - }) - @IsOptional() - email_addresses?: Email[]; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - additionalProperties: true, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsCandidateOutput extends UnifiedAtsCandidateInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the candidate', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: 'The id of the candidate in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: - 'The remote data of the candidate in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The created date of the object', - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The modified date of the object', - }) - @IsOptional() - modified_at?: Date; -} - -export class UnifiedCandidateUrlInput { - @ApiPropertyOptional({ - type: String, - example: 'mywebsite.com', - nullable: true, - description: 'The value of the URL', - }) - @IsString() - @IsOptional() - value?: string; - - @ApiPropertyOptional({ - type: String, - example: 'WEBSITE', - nullable: true, - description: 'The type of the URL', - }) - @IsString() - @IsOptional() - type?: string; - - @ApiPropertyOptional({ - format: 'date-time', - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The creation date of the URL', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - format: 'date-time', - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The modification date of the URL', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the candidate', - }) - @IsUUID() - @IsOptional() - id_ats_candidate?: string; -} - -export class UnifiedCandidateUrlOutput extends UnifiedCandidateUrlInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the URL', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: 'The remote ID of the URL in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: 'The remote data of the URL in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; -} - -export class UnifiedCandidatePhoneNumberInput { - @ApiPropertyOptional({ - type: String, - example: '+33650009898', - nullable: true, - description: 'The phone number value', - }) - @IsString() - @IsOptional() - value?: string; - - @ApiPropertyOptional({ - type: String, - example: 'WORK', - nullable: true, - description: 'The type of phone number', - }) - @IsString() - @IsOptional() - type?: string; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The creation date of the phone number', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The modification date of the phone number', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the candidate', - }) - @IsUUID() - @IsOptional() - id_ats_candidate?: string; -} - -export class UnifiedCandidatePhoneNumberOutput extends UnifiedCandidatePhoneNumberInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the phone number', - }) - @IsUUID() - @IsOptional() - id_ats_candidate_phone_number?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: - 'The remote ID of the phone number in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: - 'The remote data of the phone number in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; -} - -export class UnifiedCandidateEmailAddressInput { - @ApiPropertyOptional({ - type: String, - example: 'joedoe@gmail.com', - description: 'The email address value', - }) - @IsString() - @IsOptional() - value?: string; - - @ApiPropertyOptional({ - type: String, - example: 'WORK', - nullable: true, - description: 'The type of email address', - }) - @IsString() - @IsOptional() - type?: string; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The creation date of the email address', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The modification date of the email address', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the candidate', - }) - @IsUUID() - @IsOptional() - id_ats_candidate?: string; -} - -export class UnifiedCandidateEmailAddressOutput extends UnifiedCandidateEmailAddressInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the email address', - }) - @IsUUID() - @IsOptional() - id_ats_candidate_email_address?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: - 'The remote ID of the email address in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: - 'The remote data of the email address in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; -} diff --git a/packages/api/src/ats/candidate/utils/index.ts b/packages/api/src/ats/candidate/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/candidate/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/department/department.controller.ts b/packages/api/src/ats/department/department.controller.ts deleted file mode 100644 index eb40131c5..000000000 --- a/packages/api/src/ats/department/department.controller.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { DepartmentService } from './services/department.service'; -import { - UnifiedAtsDepartmentInput, - UnifiedAtsDepartmentOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - - -@ApiTags('ats/departments') -@Controller('ats/departments') -export class DepartmentController { - constructor( - private readonly departmentService: DepartmentService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(DepartmentController.name); - } - - @ApiOperation({ - operationId: 'listAtsDepartments', - summary: 'List Departments', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsDepartmentOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getDepartments( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.departmentService.getDepartments( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsDepartment', - summary: 'Retrieve Departments', - description: 'Retrieve Departments from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the department you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsDepartmentOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.departmentService.getDepartment( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/ats/department/department.module.ts b/packages/api/src/ats/department/department.module.ts deleted file mode 100644 index 5585f5005..000000000 --- a/packages/api/src/ats/department/department.module.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Module } from '@nestjs/common'; -import { DepartmentController } from './department.controller'; -import { DepartmentService } from './services/department.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { AshbyService } from './services/ashby'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { AshbyDepartmentMapper } from './services/ashby/mappers'; -import { Utils } from '@ats/@lib/@utils'; - -@Module({ - controllers: [DepartmentController], - providers: [ - DepartmentService, - CoreUnification, - - SyncService, - WebhookService, - - ServiceRegistry, - - IngestDataService, - - Utils, - AshbyDepartmentMapper, - /* PROVIDERS SERVICES */ - AshbyService, - ], - exports: [SyncService], -}) -export class DepartmentModule {} diff --git a/packages/api/src/ats/department/services/ashby/index.ts b/packages/api/src/ats/department/services/ashby/index.ts deleted file mode 100644 index da9e293e1..000000000 --- a/packages/api/src/ats/department/services/ashby/index.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IDepartmentService } from '@ats/department/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 { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { ApiResponse } from '@@core/utils/types'; -import { ServiceRegistry } from '../registry.service'; -import { AshbyDepartmentInput, AshbyDepartmentOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalDepartmentOutput } from '@@core/utils/types/original/original.ats'; -import { SyncParam } from '@@core/utils/types/interface'; - -@Injectable() -export class AshbyService implements IDepartmentService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - AtsObject.department.toUpperCase() + ':' + AshbyService.name, - ); - this.registry.registerService('ashby', this); - } - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - const resp = await axios.post( - `${connection.account_url}/departement.list`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }, - ); - const departments: AshbyDepartmentOutput[] = resp.data.results; - this.logger.log(`Synced ashby departments !`); - - return { - data: departments, - message: 'Ashby departments retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/department/services/ashby/mappers.ts b/packages/api/src/ats/department/services/ashby/mappers.ts deleted file mode 100644 index ee195c525..000000000 --- a/packages/api/src/ats/department/services/ashby/mappers.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { AshbyDepartmentInput, AshbyDepartmentOutput } from './types'; -import { - UnifiedAtsDepartmentInput, - UnifiedAtsDepartmentOutput, -} from '@ats/department/types/model.unified'; -import { IDepartmentMapper } from '@ats/department/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'; - -@Injectable() -export class AshbyDepartmentMapper implements IDepartmentMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('ats', 'department', 'ashby', this); - } - - async desunify( - source: UnifiedAtsDepartmentInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return; - } - - async unify( - source: AshbyDepartmentOutput | AshbyDepartmentOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - if (!Array.isArray(source)) { - return await this.mapSingleDepartmentToUnified( - source, - connectionId, - customFieldMappings, - ); - } - // Handling array of AshbyDepartmentOutput - return Promise.all( - source.map((department) => - this.mapSingleDepartmentToUnified( - department, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleDepartmentToUnified( - department: AshbyDepartmentOutput, - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return { - remote_id: department.id, - remote_data: department, - name: department.name || null, - }; - } -} diff --git a/packages/api/src/ats/department/services/ashby/types.ts b/packages/api/src/ats/department/services/ashby/types.ts deleted file mode 100644 index d3c47509a..000000000 --- a/packages/api/src/ats/department/services/ashby/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface AshbyDepartmentInput { - id: string; - name: string; - isArchived: boolean; - parentId: string; -} - -export type AshbyDepartmentOutput = Partial; diff --git a/packages/api/src/ats/department/services/department.service.ts b/packages/api/src/ats/department/services/department.service.ts deleted file mode 100644 index 5e9356a87..000000000 --- a/packages/api/src/ats/department/services/department.service.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { UnifiedAtsDepartmentOutput } from '../types/model.unified'; - -@Injectable() -export class DepartmentService { - constructor(private prisma: PrismaService, private logger: LoggerService) { - this.logger.setContext(DepartmentService.name); - } - - async getDepartment( - id_ats_department: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const department = await this.prisma.ats_departments.findUnique({ - where: { - id_ats_department: id_ats_department, - }, - }); - - if (!department) { - throw new Error(`Department with ID ${id_ats_department} not found.`); - } - - // Fetch field mappings for the department - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: department.id_ats_department, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsDepartmentOutput format - const unifiedDepartment: UnifiedAtsDepartmentOutput = { - id: department.id_ats_department, - name: department.name, - field_mappings: field_mappings, - remote_id: department.remote_id, - created_at: department.created_at, - modified_at: department.modified_at, - }; - - let res: UnifiedAtsDepartmentOutput = unifiedDepartment; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: department.id_ats_department, - }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.department.pull', - method: 'GET', - url: '/ats/department', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return res; - } catch (error) { - throw error; - } - } - - async getDepartments( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsDepartmentOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = await this.prisma.ats_departments.findFirst({ - where: { - id_connection: connection_id, - id_ats_department: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const departments = await this.prisma.ats_departments.findMany({ - take: limit + 1, - cursor: cursor - ? { - id_ats_department: cursor, - } - : undefined, - orderBy: { - created_at: 'asc', - }, - where: {}, - }); - - if (departments.length === limit + 1) { - next_cursor = Buffer.from( - departments[departments.length - 1].id_ats_department, - ).toString('base64'); - departments.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - - const unifiedDepartments: UnifiedAtsDepartmentOutput[] = - await Promise.all( - departments.map(async (department) => { - // Fetch field mappings for the department - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: department.id_ats_department, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Array.from( - fieldMappingsMap, - ([key, value]) => ({ - [key]: value, - }), - ); - - // Transform to UnifiedAtsDepartmentOutput format - return { - id: department.id_ats_department, - name: department.name, - field_mappings: field_mappings, - remote_id: department.remote_id, - created_at: department.created_at, - modified_at: department.modified_at, - }; - }), - ); - - let res: UnifiedAtsDepartmentOutput[] = unifiedDepartments; - - if (remote_data) { - const remote_array_data: UnifiedAtsDepartmentOutput[] = - await Promise.all( - res.map(async (department) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: department.id, - }, - }); - const remote_data = JSON.parse(resp.data); - return { ...department, remote_data }; - }), - ); - - res = remote_array_data; - } - - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.department.pull', - method: 'GET', - url: '/ats/departments', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { - data: res, - prev_cursor, - next_cursor, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/department/services/registry.service.ts b/packages/api/src/ats/department/services/registry.service.ts deleted file mode 100644 index 179dbc954..000000000 --- a/packages/api/src/ats/department/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IDepartmentService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IDepartmentService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IDepartmentService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/department/sync/sync.service.ts b/packages/api/src/ats/department/sync/sync.service.ts deleted file mode 100644 index 3ae858155..000000000 --- a/packages/api/src/ats/department/sync/sync.service.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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 { IDepartmentService } from '../types'; -import { OriginalDepartmentOutput } from '@@core/utils/types/original/original.ats'; -import { UnifiedAtsDepartmentOutput } from '../types/model.unified'; -import { ats_departments as AtsDepartment } 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'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'department', this); - } - onModuleInit() { -// - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IDepartmentService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: department} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsDepartmentOutput, - OriginalDepartmentOutput, - IDepartmentService - >(integrationId, linkedUserId, 'ats', 'department', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - departments: UnifiedAtsDepartmentOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const departments_results: AtsDepartment[] = []; - - const updateOrCreateDepartment = async ( - department: UnifiedAtsDepartmentOutput, - originId: string, - ) => { - let existingDepartment; - if (!originId) { - existingDepartment = await this.prisma.ats_departments.findFirst({ - where: { - name: department.name, - }, - }); - } else { - existingDepartment = await this.prisma.ats_departments.findFirst({ - where: { - remote_id: originId, - }, - }); - } - - const baseData: any = { - name: department.name ?? null, - modified_at: new Date(), - }; - - if (existingDepartment) { - return await this.prisma.ats_departments.update({ - where: { - id_ats_department: existingDepartment.id_ats_department, - }, - data: baseData, - }); - } else { - return await this.prisma.ats_departments.create({ - data: { - ...baseData, - id_ats_department: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - }; - - for (let i = 0; i < departments.length; i++) { - const department = departments[i]; - const originId = department.remote_id; - - const res = await updateOrCreateDepartment(department, originId); - const department_id = res.id_ats_department; - departments_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - department.field_mappings, - department_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - department_id, - remote_data[i], - ); - } - - return departments_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/department/types/index.ts b/packages/api/src/ats/department/types/index.ts deleted file mode 100644 index 3f1e7eb00..000000000 --- a/packages/api/src/ats/department/types/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedAtsDepartmentInput, - UnifiedAtsDepartmentOutput, -} from './model.unified'; -import { OriginalDepartmentOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface IDepartmentService extends IBaseObjectService { - sync(data: SyncParam): Promise>; -} - -export interface IDepartmentMapper { - desunify( - source: UnifiedAtsDepartmentInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalDepartmentOutput | OriginalDepartmentOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/department/types/model.unified.ts b/packages/api/src/ats/department/types/model.unified.ts deleted file mode 100644 index 677ad2d72..000000000 --- a/packages/api/src/ats/department/types/model.unified.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsUUID, IsOptional, IsString, IsDateString } from 'class-validator'; - -export class UnifiedAtsDepartmentInput { - @ApiPropertyOptional({ - type: String, - example: 'Sales', - nullable: true, - description: 'The name of the department', - }) - @IsString() - @IsOptional() - name?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - additionalProperties: true, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsDepartmentOutput extends UnifiedAtsDepartmentInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the department', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: - 'The remote ID of the department in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { key1: 'value1', key2: 42, key3: true }, - nullable: true, - additionalProperties: true, - description: - 'The remote data of the department in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The created date of the object', - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2023-10-01T12:00:00Z', - nullable: true, - description: 'The modified date of the object', - }) - @IsOptional() - modified_at?: Date; -} diff --git a/packages/api/src/ats/department/utils/index.ts b/packages/api/src/ats/department/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/department/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/eeocs/eeocs.controller.ts b/packages/api/src/ats/eeocs/eeocs.controller.ts deleted file mode 100644 index 6bc1f67ca..000000000 --- a/packages/api/src/ats/eeocs/eeocs.controller.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { - UnifiedAtsEeocsInput, - UnifiedAtsEeocsOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { EeocsService } from './services/eeocs.service'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - - -@ApiTags('ats/eeocs') -@Controller('ats/eeocs') -export class EeocsController { - constructor( - private readonly eeocsService: EeocsService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(EeocsController.name); - } - - @ApiOperation({ - operationId: 'listAtsEeocs', // Updated operationId - summary: 'List Eeocss', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsEeocsOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getEeocss( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.eeocsService.getEeocss( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsEeocs', // Updated operationId - summary: 'Retrieve Eeocs', - description: 'Retrieve a eeocs from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the eeocs you want to retrieve.', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsEeocsOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.eeocsService.getEeocs( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/ats/eeocs/eeocs.module.ts b/packages/api/src/ats/eeocs/eeocs.module.ts deleted file mode 100644 index 62eaa69ac..000000000 --- a/packages/api/src/ats/eeocs/eeocs.module.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Module } from '@nestjs/common'; -import { EeocsController } from './eeocs.controller'; -import { EeocsService } from './services/eeocs.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Utils } from '@ats/@lib/@utils'; - -@Module({ - controllers: [EeocsController], - providers: [ - EeocsService, - - SyncService, - WebhookService, - CoreUnification, - - ServiceRegistry, - - IngestDataService, - - Utils, - /* PROVIDERS SERVICES */ - ], - exports: [SyncService], -}) -export class EeocsModule {} diff --git a/packages/api/src/ats/eeocs/services/eeocs.service.ts b/packages/api/src/ats/eeocs/services/eeocs.service.ts deleted file mode 100644 index 1f9534500..000000000 --- a/packages/api/src/ats/eeocs/services/eeocs.service.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { - EeocsDisabilityStatus, - EeocsGender, - EeocsRace, - EeocsVeteranStatus, - UnifiedAtsEeocsOutput, -} from '../types/model.unified'; - -@Injectable() -export class EeocsService { - constructor(private prisma: PrismaService, private logger: LoggerService) { - this.logger.setContext(EeocsService.name); - } - - async getEeocs( - id_ats_eeoc: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const eeocs = await this.prisma.ats_eeocs.findUnique({ - where: { - id_ats_eeoc: id_ats_eeoc, - }, - }); - - if (!eeocs) { - throw new Error(`EEOC with ID ${id_ats_eeoc} not found.`); - } - - // Fetch field mappings for the EEOC - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: eeocs.id_ats_eeoc, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsEeocsOutput format - const unifiedEeocs: UnifiedAtsEeocsOutput = { - id: eeocs.id_ats_eeoc, - candidate_id: eeocs.id_ats_candidate, - submitted_at: String(eeocs.submitted_at), - race: eeocs.race, - gender: eeocs.gender, - veteran_status: eeocs.veteran_status, - disability_status: eeocs.disability_status, - field_mappings: field_mappings, - remote_id: eeocs.remote_id, - created_at: eeocs.created_at, - modified_at: eeocs.modified_at, - }; - - let res: UnifiedAtsEeocsOutput = unifiedEeocs; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: eeocs.id_ats_eeoc, - }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.eeocs.pull', - method: 'GET', - url: '/ats/eeocs', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return res; - } catch (error) { - throw error; - } - } - - async getEeocss( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsEeocsOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = await this.prisma.ats_eeocs.findFirst({ - where: { - id_connection: connection_id, - id_ats_eeoc: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const eeocss = await this.prisma.ats_eeocs.findMany({ - take: limit + 1, - cursor: cursor - ? { - id_ats_eeoc: cursor, - } - : undefined, - orderBy: { - created_at: 'asc', - }, - where: {}, - }); - - if (eeocss.length === limit + 1) { - next_cursor = Buffer.from( - eeocss[eeocss.length - 1].id_ats_eeoc, - ).toString('base64'); - eeocss.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - - const unifiedEeocss: UnifiedAtsEeocsOutput[] = await Promise.all( - eeocss.map(async (eeocs) => { - // Fetch field mappings for the EEOC - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: eeocs.id_ats_eeoc, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsEeocsOutput format - return { - id: eeocs.id_ats_eeoc, - candidate_id: eeocs.id_ats_candidate, - submitted_at: String(eeocs.submitted_at), - race: eeocs.race, - gender: eeocs.gender, - veteran_status: eeocs.veteran_status, - disability_status: eeocs.disability_status, - field_mappings: field_mappings, - remote_id: eeocs.remote_id, - created_at: eeocs.created_at, - modified_at: eeocs.modified_at, - }; - }), - ); - - let res: UnifiedAtsEeocsOutput[] = unifiedEeocss; - - if (remote_data) { - const remote_array_data: UnifiedAtsEeocsOutput[] = await Promise.all( - res.map(async (eeocs) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: eeocs.id, - }, - }); - const remote_data = JSON.parse(resp.data); - return { ...eeocs, remote_data }; - }), - ); - - res = remote_array_data; - } - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.eeocss.pull', - method: 'GET', - url: '/ats/eeocss', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { - data: res, - prev_cursor, - next_cursor, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/eeocs/services/registry.service.ts b/packages/api/src/ats/eeocs/services/registry.service.ts deleted file mode 100644 index 172895a74..000000000 --- a/packages/api/src/ats/eeocs/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IEeocsService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IEeocsService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IEeocsService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/eeocs/sync/sync.service.ts b/packages/api/src/ats/eeocs/sync/sync.service.ts deleted file mode 100644 index deb189c3d..000000000 --- a/packages/api/src/ats/eeocs/sync/sync.service.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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 { IEeocsService } from '../types'; -import { OriginalEeocsOutput } from '@@core/utils/types/original/original.ats'; -import { UnifiedAtsEeocsOutput } from '../types/model.unified'; -import { ats_eeocs as AtsEeocs } 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'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'eeocs', this); - } - onModuleInit() { -// - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IEeocsService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: eeocs} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsEeocsOutput, - OriginalEeocsOutput, - IEeocsService - >(integrationId, linkedUserId, 'ats', 'eeocs', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - eeocs: UnifiedAtsEeocsOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const eeocs_results: AtsEeocs[] = []; - - const updateOrCreateEeoc = async ( - eeoc: UnifiedAtsEeocsOutput, - originId: string, - ) => { - const existingEeoc = await this.prisma.ats_eeocs.findFirst({ - where: { - remote_id: originId, - }, - }); - - const baseData: any = { - candidate_id: eeoc.candidate_id ?? null, - submitted_at: eeoc.submitted_at ?? null, - race: eeoc.race ?? null, - gender: eeoc.gender ?? null, - veteran_status: eeoc.veteran_status ?? null, - disability_status: eeoc.disability_status ?? null, - modified_at: new Date(), - }; - - if (existingEeoc) { - return await this.prisma.ats_eeocs.update({ - where: { - id_ats_eeoc: existingEeoc.id_ats_eeoc, - }, - data: baseData, - }); - } else { - return await this.prisma.ats_eeocs.create({ - data: { - ...baseData, - id_ats_eeoc: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - }; - - for (let i = 0; i < eeocs.length; i++) { - const eeoc = eeocs[i]; - const originId = eeoc.remote_id; - - if (!originId || originId === '') { - throw new ReferenceError(`Origin id not there, found ${originId}`); - } - - const res = await updateOrCreateEeoc(eeoc, originId); - const eeoc_id = res.id_ats_eeoc; - eeocs_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - eeoc.field_mappings, - eeoc_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData(eeoc_id, remote_data[i]); - } - - return eeocs_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/eeocs/types/index.ts b/packages/api/src/ats/eeocs/types/index.ts deleted file mode 100644 index 2058a3506..000000000 --- a/packages/api/src/ats/eeocs/types/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { UnifiedAtsEeocsInput, UnifiedAtsEeocsOutput } from './model.unified'; -import { OriginalEeocsOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface IEeocsService extends IBaseObjectService { - addEeocs( - eeocsData: DesunifyReturnType, - linkedUserId: string, - ): Promise>; - - sync(data: SyncParam): Promise>; -} - -export interface IEeocsMapper { - desunify( - source: UnifiedAtsEeocsInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalEeocsOutput | OriginalEeocsOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/eeocs/types/model.unified.ts b/packages/api/src/ats/eeocs/types/model.unified.ts deleted file mode 100644 index 1dbb9f15a..000000000 --- a/packages/api/src/ats/eeocs/types/model.unified.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsIn, -} from 'class-validator'; - -export type EeocsRace = - | 'AMERICAN_INDIAN_OR_ALASKAN_NATIVE' - | 'ASIAN' - | 'BLACK_OR_AFRICAN_AMERICAN' - | 'HISPANIC_OR_LATINO' - | 'WHITE' - | 'NATIVE_HAWAIIAN_OR_OTHER_PACIFIC_ISLANDER' - | 'TWO_OR_MORE_RACES' - | 'DECLINE_TO_SELF_IDENTIFY'; - -export type EeocsDisabilityStatus = - | 'YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY' - | 'NO_I_DONT_HAVE_A_DISABILITY' - | 'I_DONT_WISH_TO_ANSWER'; - -export type EeocsGender = - | 'MALE' - | 'FEMALE' - | 'NON_BINARY' - | 'OTHER' - | 'DECLINE_TO_SELF_IDENTIFY'; - -export type EeocsVeteranStatus = - | 'I_AM_NOT_A_PROTECTED_VETERAN' - | 'I_IDENTIFY_AS_ONE_OR_MORE_OF_THE_CLASSIFICATIONS_OF_A_PROTECTED_VETERAN' - | 'I_DONT_WISH_TO_ANSWER'; - -export class UnifiedAtsEeocsInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the candidate', - }) - @IsUUID() - @IsOptional() - candidate_id?: string; - - @ApiPropertyOptional({ - type: String, - example: '2024-10-01T12:00:00Z', - format: 'date-time', - nullable: true, - description: 'The submission date of the EEOC', - }) - @IsDateString() - @IsOptional() - submitted_at?: string; - - @ApiPropertyOptional({ - type: String, - /* enum: [ - 'AMERICAN_INDIAN_OR_ALASKAN_NATIVE', - 'ASIAN', - 'BLACK_OR_AFRICAN_AMERICAN', - 'HISPANIC_OR_LATINO', - 'WHITE', - 'NATIVE_HAWAIIAN_OR_OTHER_PACIFIC_ISLANDER', - 'TWO_OR_MORE_RACES', - 'DECLINE_TO_SELF_IDENTIFY', - ],*/ - example: 'AMERICAN_INDIAN_OR_ALASKAN_NATIVE', - nullable: true, - description: 'The race of the candidate', - }) - /*@IsIn([ - 'AMERICAN_INDIAN_OR_ALASKAN_NATIVE', - 'ASIAN', - 'BLACK_OR_AFRICAN_AMERICAN', - 'HISPANIC_OR_LATINO', - 'WHITE', - 'NATIVE_HAWAIIAN_OR_OTHER_PACIFIC_ISLANDER', - 'TWO_OR_MORE_RACES', - 'DECLINE_TO_SELF_IDENTIFY', - ])*/ - @IsOptional() - race?: EeocsRace | string; - - @ApiPropertyOptional({ - type: String, - example: 'MALE', - // enum: ['MALE', 'FEMALE', 'NON_BINARY', 'OTHER', 'DECLINE_TO_SELF_IDENTIFY'], - nullable: true, - description: 'The gender of the candidate', - }) - //@IsIn(['MALE', 'FEMALE', 'NON_BINARY', 'OTHER', 'DECLINE_TO_SELF_IDENTIFY']) - @IsOptional() - gender?: EeocsGender | string; - - @ApiPropertyOptional({ - type: String, - example: 'I_AM_NOT_A_PROTECTED_VETERAN', - /* enum: [ - 'I_AM_NOT_A_PROTECTED_VETERAN', - 'I_IDENTIFY_AS_ONE_OR_MORE_OF_THE_CLASSIFICATIONS_OF_A_PROTECTED_VETERAN', - 'I_DONT_WISH_TO_ANSWER', - ],*/ - nullable: true, - description: 'The veteran status of the candidate', - }) - /*@IsIn([ - 'I_AM_NOT_A_PROTECTED_VETERAN', - 'I_IDENTIFY_AS_ONE_OR_MORE_OF_THE_CLASSIFICATIONS_OF_A_PROTECTED_VETERAN', - 'I_DONT_WISH_TO_ANSWER', - ])*/ - @IsOptional() - veteran_status?: EeocsVeteranStatus | string; - - @ApiPropertyOptional({ - type: String, - /* enum: [ - 'YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY', - 'NO_I_DONT_HAVE_A_DISABILITY', - 'I_DONT_WISH_TO_ANSWER', - ],*/ - example: 'YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY', - nullable: true, - description: 'The disability status of the candidate', - }) - /*@IsIn([ - 'YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY', - 'NO_I_DONT_HAVE_A_DISABILITY', - 'I_DONT_WISH_TO_ANSWER', - ])*/ - @IsOptional() - disability_status?: EeocsDisabilityStatus | string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - additionalProperties: true, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsEeocsOutput extends UnifiedAtsEeocsInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the EEOC', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: 'The remote ID of the EEOC in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: 'The remote data of the EEOC in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The created date of the object', - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The modified date of the object', - }) - @IsOptional() - modified_at?: Date; -} diff --git a/packages/api/src/ats/eeocs/utils/index.ts b/packages/api/src/ats/eeocs/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/eeocs/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/interview/interview.controller.ts b/packages/api/src/ats/interview/interview.controller.ts deleted file mode 100644 index ade7f5d3f..000000000 --- a/packages/api/src/ats/interview/interview.controller.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { InterviewService } from './services/interview.service'; -import { - UnifiedAtsInterviewInput, - UnifiedAtsInterviewOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, - ApiPostCustomResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - - -@ApiTags('ats/interviews') -@Controller('ats/interviews') -export class InterviewController { - constructor( - private readonly interviewService: InterviewService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(InterviewController.name); - } - - @ApiOperation({ - operationId: 'listAtsInterview', // Updated operationId - summary: 'List Interviews', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsInterviewOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getInterviews( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.interviewService.getInterviews( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsInterview', // Updated operationId - summary: 'Retrieve Interviews', - description: 'Retrieve Interviews from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the interview you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsInterviewOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.interviewService.getInterview( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } - - @ApiOperation({ - operationId: 'createAtsInterview', // Updated operationId - summary: 'Create Interviews', - description: 'Create Interviews in any supported Ats software', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiBody({ type: UnifiedAtsInterviewInput }) - @ApiPostCustomResponse(UnifiedAtsInterviewOutput) - @UseGuards(ApiKeyAuthGuard) - @Post() - async addInterview( - @Body() unifiedInterviewData: UnifiedAtsInterviewInput, - @Headers('x-connection-token') connection_token: string, - @Query('remote_data') remote_data?: boolean, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.interviewService.addInterview( - unifiedInterviewData, - connectionId, - projectId, - remoteSource, - linkedUserId, - remote_data, - ); - } catch (error) { - throw new Error(error); - } - } -} diff --git a/packages/api/src/ats/interview/interview.module.ts b/packages/api/src/ats/interview/interview.module.ts deleted file mode 100644 index 7937a5f8e..000000000 --- a/packages/api/src/ats/interview/interview.module.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Module } from '@nestjs/common'; -import { InterviewController } from './interview.controller'; -import { InterviewService } from './services/interview.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { AshbyService } from './services/ashby'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { AshbyInterviewMapper } from './services/ashby/mappers'; -import { Utils } from '@ats/@lib/@utils'; - -@Module({ - controllers: [InterviewController], - providers: [ - CoreUnification, - - InterviewService, - - SyncService, - WebhookService, - - ServiceRegistry, - - IngestDataService, - - AshbyInterviewMapper, - Utils, - /* PROVIDERS SERVICES */ - AshbyService, - ], - exports: [SyncService], -}) -export class InterviewModule {} diff --git a/packages/api/src/ats/interview/services/ashby/index.ts b/packages/api/src/ats/interview/services/ashby/index.ts deleted file mode 100644 index f83e0fdae..000000000 --- a/packages/api/src/ats/interview/services/ashby/index.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IInterviewService } from '@ats/interview/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 { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { ApiResponse } from '@@core/utils/types'; -import { ServiceRegistry } from '../registry.service'; -import { AshbyInterviewInput, AshbyInterviewOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalInterviewOutput } from '@@core/utils/types/original/original.ats'; -import { SyncParam } from '@@core/utils/types/interface'; - -@Injectable() -export class AshbyService implements IInterviewService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - AtsObject.interview.toUpperCase() + ':' + AshbyService.name, - ); - this.registry.registerService('ashby', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - const resp = await axios.post( - `${connection.account_url}/interviewSchedule.list`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }, - ); - const interviews: AshbyInterviewOutput[] = resp.data.results; - this.logger.log(`Synced ashby interviews !`); - - return { - data: interviews, - message: 'Ashby interviews retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/interview/services/ashby/mappers.ts b/packages/api/src/ats/interview/services/ashby/mappers.ts deleted file mode 100644 index ad06996d1..000000000 --- a/packages/api/src/ats/interview/services/ashby/mappers.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { AshbyInterviewInput, AshbyInterviewOutput } from './types'; -import { - InterviewStatus, - UnifiedAtsInterviewInput, - UnifiedAtsInterviewOutput, -} from '@ats/interview/types/model.unified'; -import { IInterviewMapper } from '@ats/interview/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'; - -@Injectable() -export class AshbyInterviewMapper implements IInterviewMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('ats', 'interview', 'ashby', this); - } - - mapToInterviewStatus( - data: - | 'NeedsScheduling' - | 'WaitingOnCandidateBooking' - | 'WaitingOnCandidateAvailability' - | 'CandidateAvailabilitySubmitted' - | 'Scheduled' - | 'WaitingOnFeedback' - | 'Complete' - | 'Cancelled', - ): InterviewStatus | string { - switch (data) { - case 'Complete': - return 'COMPLETED'; - case 'WaitingOnFeedback': - return 'AWAITING_FEEDBACK'; - case 'Scheduled': - return 'SCHEDULED'; - default: - return data; - } - } - - async desunify( - source: UnifiedAtsInterviewInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return; - } - - async unify( - source: AshbyInterviewOutput | AshbyInterviewOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - if (!Array.isArray(source)) { - return await this.mapSingleInterviewToUnified( - source, - connectionId, - customFieldMappings, - ); - } - // Handling array of AshbyInterviewOutput - return Promise.all( - source.map((interview) => - this.mapSingleInterviewToUnified( - interview, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleInterviewToUnified( - interview: AshbyInterviewOutput, - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - let interviewers; - if (interview.interviewEvents[0].interviewerUserIds) { - for (const uuid of interview.interviewEvents[0].interviewerUserIds) { - const person = await this.utils.getUserUuidFromRemoteId( - uuid, - connectionId, - ); - if (person) interviewers.push(person); - } - } - - return { - remote_id: interview.id, - remote_data: interview, - status: this.mapToInterviewStatus(interview.status as any) ?? null, - application_id: - (await this.utils.getApplicationUuidFromRemoteId( - interview.applicationId, - connectionId, - )) || null, - job_interview_stage_id: - (await this.utils.getStageUuidFromRemoteId( - interview.interviewStageId, - connectionId, - )) || null, - organized_by: null, - interviewers: interviewers || null, - location: interview.interviewEvents[0].location || null, - start_at: interview.interviewEvents[0].startTime || null, - end_at: interview.interviewEvents[0].endTime || null, - remote_created_at: interview.interviewEvents[0].createdAt || null, - }; - } -} diff --git a/packages/api/src/ats/interview/services/ashby/types.ts b/packages/api/src/ats/interview/services/ashby/types.ts deleted file mode 100644 index ecaa2f3f5..000000000 --- a/packages/api/src/ats/interview/services/ashby/types.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface AshbyInterviewInput { - id: string; - status: string; - applicationId: string; - interviewStageId: string; - interviewEvents: InterviewEvent[]; -} - -export type AshbyInterviewOutput = Partial; - -type InterviewEvent = { - id: string; - interviewId: string; - interviewScheduleId: string; - interviewerUserIds: string[]; - createdAt: string; - startTime: string; - endTime: string; - feedbackLink: string; - location: string; - meetingLink: string; - hasSubmittedFeedback: boolean; -}; diff --git a/packages/api/src/ats/interview/services/interview.service.ts b/packages/api/src/ats/interview/services/interview.service.ts deleted file mode 100644 index a9064c464..000000000 --- a/packages/api/src/ats/interview/services/interview.service.ts +++ /dev/null @@ -1,425 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { - InterviewStatus, - UnifiedAtsInterviewInput, - UnifiedAtsInterviewOutput, -} from '../types/model.unified'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ApiResponse } from '@@core/utils/types'; -import { OriginalInterviewOutput } from '@@core/utils/types/original/original.ats'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ServiceRegistry } from './registry.service'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { AtsObject } from '@ats/@lib/@types'; -import { BullQueueService } from '@@core/@core-services/queues/shared.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class InterviewService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private ingestService: IngestDataService, - ) { - this.logger.setContext(InterviewService.name); - } - - async addInterview( - unifiedInterviewData: UnifiedAtsInterviewInput, - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - remote_data?: boolean, - ): Promise { - try { - const linkedUser = await this.validateLinkedUser(linkedUserId); - - const customFieldMappings = - await this.fieldMappingService.getCustomFieldMappings( - integrationId, - linkedUserId, - 'ats.interview', - ); - - const desunifiedObject = - await this.coreUnification.desunify({ - sourceObject: unifiedInterviewData, - targetType: AtsObject.interview, - providerName: integrationId, - vertical: 'ats', - customFieldMappings: unifiedInterviewData.field_mappings - ? customFieldMappings - : [], - }); - - this.logger.log( - 'desunified object is ' + JSON.stringify(desunifiedObject), - ); - - const service = this.serviceRegistry.getService(integrationId); - const resp: ApiResponse = - await service.addInterview(desunifiedObject, linkedUserId); - - const unifiedObject = (await this.coreUnification.unify< - OriginalInterviewOutput[] - >({ - sourceObject: [resp.data], - targetType: AtsObject.interview, - providerName: integrationId, - vertical: 'ats', - connectionId: connection_id, - customFieldMappings: customFieldMappings, - })) as UnifiedAtsInterviewOutput[]; - - const source_interview = resp.data; - const target_interview = unifiedObject[0]; - - const unique_ats_interview_id = await this.saveOrUpdateInterview( - target_interview, - connection_id, - ); - - await this.ingestService.processFieldMappings( - target_interview.field_mappings, - unique_ats_interview_id, - integrationId, - linkedUserId, - ); - - await this.ingestService.processRemoteData( - unique_ats_interview_id, - source_interview, - ); - - const result_interview = await this.getInterview( - unique_ats_interview_id, - undefined, - undefined, - connection_id, - project_id, - remote_data, - ); - - const status_resp = resp.statusCode === 201 ? 'success' : 'fail'; - const event = await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: status_resp, - type: 'ats.interview.created', - method: 'POST', - url: '/ats/interviews', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - await this.webhook.dispatchWebhook( - result_interview, - 'ats.interview.created', - linkedUser.id_project, - event.id_event, - ); - return result_interview; - } catch (error) { - throw error; - } - } - - async validateLinkedUser(linkedUserId: string) { - const linkedUser = await this.prisma.linked_users.findUnique({ - where: { id_linked_user: linkedUserId }, - }); - if (!linkedUser) throw new ReferenceError('Linked User Not Found'); - return linkedUser; - } - - async saveOrUpdateInterview( - interview: UnifiedAtsInterviewOutput, - connection_id: string, - ): Promise { - const existingInterview = await this.prisma.ats_interviews.findFirst({ - where: { remote_id: interview.remote_id, id_connection: connection_id }, - }); - - const data: any = { - status: interview.status, - application_id: interview.application_id, - job_interview_stage_id: interview.job_interview_stage_id, - organized_by: interview.organized_by, - interviewers: interview.interviewers, - location: interview.location, - start_at: interview.start_at, - end_at: interview.end_at, - remote_created_at: interview.remote_created_at, - remote_updated_at: interview.remote_updated_at, - modified_at: new Date(), - }; - - if (existingInterview) { - const res = await this.prisma.ats_interviews.update({ - where: { id_ats_interview: existingInterview.id_ats_interview }, - data: data, - }); - return res.id_ats_interview; - } else { - data.created_at = new Date(); - data.remote_id = interview.remote_id; - data.id_connection = connection_id; - data.id_ats_interview = uuidv4(); - - const newInterview = await this.prisma.ats_interviews.create({ - data: data, - }); - return newInterview.id_ats_interview; - } - } - - async getInterview( - id_ats_interview: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const interview = await this.prisma.ats_interviews.findUnique({ - where: { - id_ats_interview: id_ats_interview, - }, - }); - - // Fetch field mappings for the interview - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: interview.id_ats_interview, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsInterviewOutput format - const unifiedInterview: UnifiedAtsInterviewOutput = { - id: interview.id_ats_interview, - status: interview.status, - application_id: interview.id_ats_application, - job_interview_stage_id: interview.id_ats_job_interview_stage, - organized_by: interview.organized_by, - interviewers: interview.interviewers, - location: interview.location, - start_at: String(interview.start_at), - end_at: String(interview.end_at), - remote_created_at: String(interview.remote_created_at), - remote_updated_at: String(interview.remote_updated_at), - field_mappings: field_mappings, - remote_id: interview.remote_id, - created_at: interview.created_at, - modified_at: interview.modified_at, - }; - - let res: UnifiedAtsInterviewOutput = unifiedInterview; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: interview.id_ats_interview, - }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - if (linkedUserId && integrationId) { - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.interview.pull', - method: 'GET', - url: '/ats/interview', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - } - - return res; - } catch (error) { - throw error; - } - } - - async getInterviews( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsInterviewOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = await this.prisma.ats_interviews.findFirst({ - where: { - id_connection: connection_id, - id_ats_interview: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const interviews = await this.prisma.ats_interviews.findMany({ - take: limit + 1, - cursor: cursor - ? { - id_ats_interview: cursor, - } - : undefined, - orderBy: { - created_at: 'asc', - }, - where: {}, - }); - - if (interviews.length === limit + 1) { - next_cursor = Buffer.from( - interviews[interviews.length - 1].id_ats_interview, - ).toString('base64'); - interviews.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - - const unifiedInterviews: UnifiedAtsInterviewOutput[] = await Promise.all( - interviews.map(async (interview) => { - // Fetch field mappings for the interview - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: interview.id_ats_interview, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - // Convert the map to an object -const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsInterviewOutput format - return { - id: interview.id_ats_interview, - status: interview.status, - application_id: interview.id_ats_application, - job_interview_stage_id: interview.id_ats_job_interview_stage, - organized_by: interview.organized_by, - interviewers: interview.interviewers, - location: interview.location, - start_at: String(interview.start_at), - end_at: String(interview.end_at), - remote_created_at: String(interview.remote_created_at), - remote_updated_at: String(interview.remote_updated_at), - field_mappings: field_mappings, - remote_id: interview.remote_id, - created_at: interview.created_at, - modified_at: interview.modified_at, - }; - }), - ); - - let res: UnifiedAtsInterviewOutput[] = unifiedInterviews; - - if (remote_data) { - const remote_array_data: UnifiedAtsInterviewOutput[] = - await Promise.all( - res.map(async (interview) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: interview.id, - }, - }); - const remote_data = JSON.parse(resp.data); - return { ...interview, remote_data }; - }), - ); - - res = remote_array_data; - } - - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.interview.pull', - method: 'GET', - url: '/ats/interviews', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - return { - data: res, - prev_cursor, - next_cursor, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/interview/services/registry.service.ts b/packages/api/src/ats/interview/services/registry.service.ts deleted file mode 100644 index 9ac97ec68..000000000 --- a/packages/api/src/ats/interview/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IInterviewService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IInterviewService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IInterviewService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/interview/sync/sync.service.ts b/packages/api/src/ats/interview/sync/sync.service.ts deleted file mode 100644 index 930077f1d..000000000 --- a/packages/api/src/ats/interview/sync/sync.service.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { BullQueueService } from '@@core/@core-services/queues/shared.service'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { OriginalInterviewOutput } from '@@core/utils/types/original/original.ats'; -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; -import { ATS_PROVIDERS } from '@panora/shared'; -import { ats_interviews as AtsInterview } from '@prisma/client'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../services/registry.service'; -import { IInterviewService } from '../types'; -import { UnifiedAtsInterviewOutput } from '../types/model.unified'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'interview', this); - } - onModuleInit() { -// - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IInterviewService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: interview} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsInterviewOutput, - OriginalInterviewOutput, - IInterviewService - >(integrationId, linkedUserId, 'ats', 'interview', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - interviews: UnifiedAtsInterviewOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const interviews_results: AtsInterview[] = []; - - const updateOrCreateInterview = async ( - interview: UnifiedAtsInterviewOutput, - originId: string, - ) => { - const existingInterview = await this.prisma.ats_interviews.findFirst({ - where: { - remote_id: originId, - }, - }); - - const baseData: any = { - status: interview.status ?? null, - application_id: interview.application_id ?? null, - job_interview_stage_id: interview.job_interview_stage_id ?? null, - organized_by: interview.organized_by ?? null, - interviewers: interview.interviewers ?? null, - location: interview.location ?? null, - start_at: interview.start_at ?? null, - end_at: interview.end_at ?? null, - remote_created_at: interview.remote_created_at ?? null, - remote_updated_at: interview.remote_updated_at ?? null, - modified_at: new Date(), - }; - - if (existingInterview) { - return await this.prisma.ats_interviews.update({ - where: { - id_ats_interview: existingInterview.id_ats_interview, - }, - data: baseData, - }); - } else { - return await this.prisma.ats_interviews.create({ - data: { - ...baseData, - id_ats_interview: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - }; - - for (let i = 0; i < interviews.length; i++) { - const interview = interviews[i]; - const originId = interview.remote_id; - - if (!originId || originId === '') { - throw new ReferenceError(`Origin id not there, found ${originId}`); - } - - const res = await updateOrCreateInterview(interview, originId); - const interview_id = res.id_ats_interview; - interviews_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - interview.field_mappings, - interview_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - interview_id, - remote_data[i], - ); - } - - return interviews_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/interview/types/index.ts b/packages/api/src/ats/interview/types/index.ts deleted file mode 100644 index cf08e3c89..000000000 --- a/packages/api/src/ats/interview/types/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { UnifiedAtsInterviewInput, UnifiedAtsInterviewOutput } from './model.unified'; -import { OriginalInterviewOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface IInterviewService extends IBaseObjectService { - addInterview?( - interviewData: DesunifyReturnType, - linkedUserId: string, - ): Promise>; - - sync(data: SyncParam): Promise>; -} - -export interface IInterviewMapper { - desunify( - source: UnifiedAtsInterviewInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalInterviewOutput | OriginalInterviewOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/interview/types/model.unified.ts b/packages/api/src/ats/interview/types/model.unified.ts deleted file mode 100644 index 3a1e6e272..000000000 --- a/packages/api/src/ats/interview/types/model.unified.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsArray, - IsIn, -} from 'class-validator'; - -export type InterviewStatus = 'SCHEDULED' | 'AWAITING_FEEDBACK' | 'COMPLETED'; - -export class UnifiedAtsInterviewInput { - @ApiPropertyOptional({ - type: String, - // enum: ['SCHEDULED', 'AWAITING_FEEDBACK', 'COMPLETED'], - example: 'SCHEDULED', - nullable: true, - description: 'The status of the interview', - }) - //@IsIn(['SCHEDULED', 'AWAITING_FEEDBACK', 'COMPLETED']) - @IsOptional() - status?: InterviewStatus | string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the application', - }) - @IsUUID() - @IsOptional() - application_id?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the job interview stage', - }) - @IsUUID() - @IsOptional() - job_interview_stage_id?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the organizer', - }) - @IsUUID() - @IsOptional() - organized_by?: string; - - @ApiPropertyOptional({ - type: [String], - example: ['801f9ede-c698-4e66-a7fc-48d19eebaa4f'], - nullable: true, - description: 'The UUIDs of the interviewers', - }) - @IsArray() - @IsOptional() - interviewers?: string[]; - - @ApiPropertyOptional({ - type: String, - example: 'San Francisco', - nullable: true, - description: 'The location of the interview', - }) - @IsString() - @IsOptional() - location?: string; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The start date and time of the interview', - }) - @IsDateString() - @IsOptional() - start_at?: string; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The end date and time of the interview', - }) - @IsDateString() - @IsOptional() - end_at?: string; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The remote creation date of the interview', - }) - @IsDateString() - @IsOptional() - remote_created_at?: string; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The remote modification date of the interview', - }) - @IsDateString() - @IsOptional() - remote_updated_at?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - additionalProperties: true, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsInterviewOutput extends UnifiedAtsInterviewInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the interview', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: - 'The remote ID of the interview in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: - 'The remote data of the interview in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The created date of the object', - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The modified date of the object', - }) - @IsOptional() - modified_at?: Date; -} diff --git a/packages/api/src/ats/interview/utils/index.ts b/packages/api/src/ats/interview/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/interview/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/job/job.controller.ts b/packages/api/src/ats/job/job.controller.ts deleted file mode 100644 index 60e8e11d0..000000000 --- a/packages/api/src/ats/job/job.controller.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { JobService } from './services/job.service'; -import { UnifiedAtsJobInput, UnifiedAtsJobOutput } from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - - -@ApiTags('ats/jobs') -@Controller('ats/jobs') -export class JobController { - constructor( - private readonly jobService: JobService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(JobController.name); - } - - @ApiOperation({ - operationId: 'listAtsJob', - summary: 'List Jobs', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsJobOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getJobs( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.jobService.getJobs( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsJob', - summary: 'Retrieve Jobs', - description: 'Retrieve Jobs from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the job you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsJobOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.jobService.getJob( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/ats/job/job.module.ts b/packages/api/src/ats/job/job.module.ts deleted file mode 100644 index bf4996e02..000000000 --- a/packages/api/src/ats/job/job.module.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Module } from '@nestjs/common'; -import { JobController } from './job.controller'; -import { JobService } from './services/job.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { AshbyService } from './services/ashby'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { AshbyJobMapper } from './services/ashby/mappers'; -import { Utils } from '@ats/@lib/@utils'; - -@Module({ - controllers: [JobController], - providers: [ - CoreUnification, - - JobService, - - SyncService, - WebhookService, - - ServiceRegistry, - - IngestDataService, - - AshbyJobMapper, - Utils, - /* PROVIDERS SERVICES */ - AshbyService, - ], - exports: [SyncService], -}) -export class JobModule {} diff --git a/packages/api/src/ats/job/services/ashby/index.ts b/packages/api/src/ats/job/services/ashby/index.ts deleted file mode 100644 index 2837c626e..000000000 --- a/packages/api/src/ats/job/services/ashby/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IJobService } from '@ats/job/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 { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { ApiResponse } from '@@core/utils/types'; -import { ServiceRegistry } from '../registry.service'; -import { AshbyJobInput, AshbyJobOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalJobOutput } from '@@core/utils/types/original/original.ats'; -import { SyncParam } from '@@core/utils/types/interface'; - -@Injectable() -export class AshbyService implements IJobService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - AtsObject.job.toUpperCase() + ':' + AshbyService.name, - ); - this.registry.registerService('ashby', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - const resp = await axios.post(`${connection.account_url}/job.list`, { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }); - const jobs: AshbyJobOutput[] = resp.data.results; - this.logger.log(`Synced ashby jobs !`); - - return { - data: jobs, - message: 'Ashby jobs retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/job/services/ashby/mappers.ts b/packages/api/src/ats/job/services/ashby/mappers.ts deleted file mode 100644 index 6f4efde2c..000000000 --- a/packages/api/src/ats/job/services/ashby/mappers.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { AshbyJobInput, AshbyJobOutput } from './types'; -import { - JobStatus, - UnifiedAtsJobInput, - UnifiedAtsJobOutput, -} from '@ats/job/types/model.unified'; -import { IJobMapper } from '@ats/job/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'; - -@Injectable() -export class AshbyJobMapper implements IJobMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('ats', 'job', 'ashby', this); - } - - async desunify( - source: UnifiedAtsJobInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return; - } - - async unify( - source: AshbyJobOutput | AshbyJobOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - if (!Array.isArray(source)) { - return await this.mapSingleJobToUnified( - source, - connectionId, - customFieldMappings, - ); - } - // Handling array of AshbyJobOutput - return Promise.all( - source.map((job) => - this.mapSingleJobToUnified(job, connectionId, customFieldMappings), - ), - ); - } - - private async mapSingleJobToUnified( - job: AshbyJobOutput, - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - let department; - if (job.departmentId) { - department = await this.utils.getDepartmentUuidFromRemoteId( - job.departmentId, - connectionId, - ); - } - let office; - if (job.locationId) { - office = await this.utils.getOfficeUuidFromRemoteId( - job.locationId, - connectionId, - ); - } - let recruiters; - if (job.hiringTeam) { - for (const ht of job.hiringTeam) { - const r = await this.utils.getUserUuidFromRemoteId( - ht.userId, - connectionId, - ); - if (r) recruiters.push(r); - } - } - - return { - remote_id: job.id, - remote_data: job, - name: job.title || null, - description: null, - code: null, - status: (job.status as JobStatus) || null, //todo - type: null, - confidential: job.confidential || null, - departments: department ? [department] : null, - offices: office ? [office] : null, - managers: null, - recruiters: recruiters || null, - remote_updated_at: job.updatedAt || null, - }; - } -} diff --git a/packages/api/src/ats/job/services/ashby/types.ts b/packages/api/src/ats/job/services/ashby/types.ts deleted file mode 100644 index 38a302516..000000000 --- a/packages/api/src/ats/job/services/ashby/types.ts +++ /dev/null @@ -1,81 +0,0 @@ -export interface AshbyJobInput { - id: string; - title: string; - confidential: boolean; - status: string; - employmentType: string; - locationId: string; - departmentId: string; - defaultInterviewPlanId: string; - interviewPlanIds: string[]; - customFields: CustomField[]; - jobPostingIds: string[]; - customRequisitionId: string; - hiringTeam: HiringTeam[]; - updatedAt: string; - openedAt: string; - closedAt: string; - location: Location; - openings: Opening[]; -} - -export type AshbyJobOutput = Partial; - -export interface CustomField { - id: string; - title: string; - value: string; -} - -export interface HiringTeam { - email: string; - firstName: string; - lastName: string; - role: string; - userId: string; -} - -export interface Location { - id: string; - name: string; - isArchived: boolean; - address: Address; - isRemote: boolean; -} - -export interface Address { - postalAddress: PostalAddress; -} - -export interface PostalAddress { - addressCountry: string; - addressRegion: string; - addressLocality: string; -} - -export interface Opening { - id: string; - openedAt: string; - closedAt: string; - isArchived: boolean; - archivedAt: string; - openingState: string; - latestVersion: LatestVersion; -} - -// LatestVersion interface -export interface LatestVersion { - id: string; - identifier: string; - description: string; - authorId: string; - createdAt: string; - teamId: string; - jobIds: string[]; - targetHireDate: string; - targetStartDate: string; - isBackfill: boolean; - employmentType: string; - locationIds: string[]; - hiringTeam: HiringTeam[]; -} diff --git a/packages/api/src/ats/job/services/job.service.ts b/packages/api/src/ats/job/services/job.service.ts deleted file mode 100644 index e8bc0b353..000000000 --- a/packages/api/src/ats/job/services/job.service.ts +++ /dev/null @@ -1,253 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { - JobStatus, - JobType, - UnifiedAtsJobOutput, -} from '../types/model.unified'; - -@Injectable() -export class JobService { - constructor(private prisma: PrismaService, private logger: LoggerService) { - this.logger.setContext(JobService.name); - } - - async getJob( - id_ats_job: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const job = await this.prisma.ats_jobs.findUnique({ - where: { - id_ats_job: id_ats_job, - }, - }); - - if (!job) { - throw new Error(`Job with ID ${id_ats_job} not found.`); - } - - // Fetch field mappings for the job - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: job.id_ats_job, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsJobOutput format - const unifiedJob: UnifiedAtsJobOutput = { - id: job.id_ats_job, - name: job.name, - description: job.description, - code: job.code, - status: job.status, - type: job.type, - confidential: job.confidential, - departments: job.ats_departments, - offices: job.ats_offices, - managers: job.managers, - recruiters: job.recruiters, - remote_created_at: String(job.remote_created_at), - remote_updated_at: String(job.remote_updated_at), - field_mappings: field_mappings, - remote_id: job.remote_id, - created_at: job.created_at, - modified_at: job.modified_at, - }; - - let res: UnifiedAtsJobOutput = unifiedJob; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: job.id_ats_job, - }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.job.pull', - method: 'GET', - url: '/ats/job', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return res; - } catch (error) { - throw error; - } - } - - async getJobs( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsJobOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = await this.prisma.ats_jobs.findFirst({ - where: { - id_connection: connection_id, - id_ats_job: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const jobs = await this.prisma.ats_jobs.findMany({ - take: limit + 1, - cursor: cursor - ? { - id_ats_job: cursor, - } - : undefined, - orderBy: { - created_at: 'asc', - }, - where: {}, - }); - - if (jobs.length === limit + 1) { - next_cursor = Buffer.from(jobs[jobs.length - 1].id_ats_job).toString( - 'base64', - ); - jobs.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - - const unifiedJobs: UnifiedAtsJobOutput[] = await Promise.all( - jobs.map(async (job) => { - // Fetch field mappings for the job - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: job.id_ats_job, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsJobOutput format - return { - id: job.id_ats_job, - name: job.name, - description: job.description, - code: job.code, - status: job.status, - type: job.type, - confidential: job.confidential, - departments: job.ats_departments, - offices: job.ats_offices, - managers: job.managers, - recruiters: job.recruiters, - remote_created_at: String(job.remote_created_at), - remote_updated_at: String(job.remote_updated_at), - field_mappings: field_mappings, - remote_id: job.remote_id, - created_at: job.created_at, - modified_at: job.modified_at, - }; - }), - ); - - let res: UnifiedAtsJobOutput[] = unifiedJobs; - - if (remote_data) { - const remote_array_data: UnifiedAtsJobOutput[] = await Promise.all( - res.map(async (job) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: job.id, - }, - }); - const remote_data = JSON.parse(resp.data); - return { ...job, remote_data }; - }), - ); - - res = remote_array_data; - } - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.job.pull', - method: 'GET', - url: '/ats/jobs', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { data: res, prev_cursor, next_cursor }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/job/services/registry.service.ts b/packages/api/src/ats/job/services/registry.service.ts deleted file mode 100644 index 828166b5b..000000000 --- a/packages/api/src/ats/job/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IJobService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IJobService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IJobService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/job/sync/sync.service.ts b/packages/api/src/ats/job/sync/sync.service.ts deleted file mode 100644 index eb5c8859d..000000000 --- a/packages/api/src/ats/job/sync/sync.service.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { BullQueueService } from '@@core/@core-services/queues/shared.service'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { OriginalJobOutput } from '@@core/utils/types/original/original.ats'; -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; -import { ATS_PROVIDERS } from '@panora/shared'; -import { ats_jobs as AtsJob } from '@prisma/client'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../services/registry.service'; -import { IJobService } from '../types'; -import { UnifiedAtsJobOutput } from '../types/model.unified'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private serviceRegistry: ServiceRegistry, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'job', this); - } - onModuleInit() { -// - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IJobService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: job} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsJobOutput, - OriginalJobOutput, - IJobService - >(integrationId, linkedUserId, 'ats', 'job', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - jobs: UnifiedAtsJobOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const jobs_results: AtsJob[] = []; - - const updateOrCreateJob = async ( - job: UnifiedAtsJobOutput, - originId: string, - ) => { - const existingJob = await this.prisma.ats_jobs.findFirst({ - where: { - remote_id: originId, - }, - }); - - const baseData: any = { - name: job.name ?? null, - description: job.description ?? null, - code: job.code ?? null, - status: job.status ?? null, - type: job.type ?? null, - confidential: job.confidential ?? null, - ats_departments: job.departments ?? null, - ats_offices: job.offices ?? null, - managers: job.managers ?? null, - recruiters: job.recruiters ?? null, - remote_created_at: job.remote_created_at ?? null, - remote_updated_at: job.remote_updated_at ?? null, - modified_at: new Date(), - }; - - if (existingJob) { - return await this.prisma.ats_jobs.update({ - where: { - id_ats_job: existingJob.id_ats_job, - }, - data: baseData, - }); - } else { - return await this.prisma.ats_jobs.create({ - data: { - ...baseData, - id_ats_job: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - }; - - for (let i = 0; i < jobs.length; i++) { - const job = jobs[i]; - const originId = job.remote_id; - - if (!originId || originId === '') { - throw new ReferenceError(`Origin id not there, found ${originId}`); - } - - const res = await updateOrCreateJob(job, originId); - const job_id = res.id_ats_job; - jobs_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - job.field_mappings, - job_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData(job_id, remote_data[i]); - } - - return jobs_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/job/types/index.ts b/packages/api/src/ats/job/types/index.ts deleted file mode 100644 index 8c724cb81..000000000 --- a/packages/api/src/ats/job/types/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { UnifiedAtsJobInput, UnifiedAtsJobOutput } from './model.unified'; -import { OriginalJobOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface IJobService extends IBaseObjectService { - addJob?( - jobData: DesunifyReturnType, - linkedUserId: string, - ): Promise>; - - sync(data: SyncParam): Promise>; -} - -export interface IJobMapper { - desunify( - source: UnifiedAtsJobInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalJobOutput | OriginalJobOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/job/types/model.unified.ts b/packages/api/src/ats/job/types/model.unified.ts deleted file mode 100644 index 6b44bc300..000000000 --- a/packages/api/src/ats/job/types/model.unified.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsBoolean, - IsDateString, - IsArray, - IsIn, -} from 'class-validator'; - -export type JobStatus = 'OPEN' | 'CLOSED' | 'DRAFT' | 'ARCHIVED' | 'PENDING'; -export type JobType = 'POSTING' | 'REQUISITION' | 'PROFILE'; - -export class UnifiedAtsJobInput { - @ApiPropertyOptional({ - type: String, - example: 'Financial Analyst', - nullable: true, - description: 'The name of the job', - }) - @IsString() - @IsOptional() - name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'Extract financial data and write detailed investment thesis', - nullable: true, - description: 'The description of the job', - }) - @IsString() - @IsOptional() - description?: string; - - @ApiPropertyOptional({ - type: String, - example: 'JOB123', - nullable: true, - description: 'The code of the job', - }) - @IsString() - @IsOptional() - code?: string; - - @ApiPropertyOptional({ - type: String, - // enum: ['OPEN', 'CLOSED', 'DRAFT', 'ARCHIVED', 'PENDING'], - example: 'OPEN', - nullable: true, - description: 'The status of the job', - }) - //@IsIn(['OPEN', 'CLOSED', 'DRAFT', 'ARCHIVED', 'PENDING']) - @IsOptional() - status?: JobStatus | string; - - @ApiPropertyOptional({ - type: String, - example: 'POSTING', - // enum: ['POSTING', 'REQUISITION', 'PROFILE'], - nullable: true, - description: 'The type of the job', - }) - //@IsIn(['POSTING', 'REQUISITION', 'PROFILE']) - @IsOptional() - type?: JobType | string; - - @ApiPropertyOptional({ - type: Boolean, - example: true, - nullable: true, - description: 'Whether the job is confidential', - }) - @IsBoolean() - @IsOptional() - confidential?: boolean; - - @ApiPropertyOptional({ - type: [String], - example: ['801f9ede-c698-4e66-a7fc-48d19eebaa4f'], - nullable: true, - description: 'The departments UUIDs associated with the job', - }) - @IsArray() - @IsOptional() - departments?: string[]; - - @ApiPropertyOptional({ - type: [String], - example: ['801f9ede-c698-4e66-a7fc-48d19eebaa4f'], - nullable: true, - description: 'The offices UUIDs associated with the job', - }) - @IsArray() - @IsOptional() - offices?: string[]; - - @ApiPropertyOptional({ - type: [String], - example: ['801f9ede-c698-4e66-a7fc-48d19eebaa4f'], - nullable: true, - description: 'The managers UUIDs associated with the job', - }) - @IsArray() - @IsOptional() - managers?: string[]; - - @ApiPropertyOptional({ - type: [String], - example: ['801f9ede-c698-4e66-a7fc-48d19eebaa4f'], - nullable: true, - description: 'The recruiters UUIDs associated with the job', - }) - @IsArray() - @IsOptional() - recruiters?: string[]; - - @ApiPropertyOptional({ - type: String, - example: '2024-10-01T12:00:00Z', - format: 'date-time', - nullable: true, - description: 'The remote creation date of the job', - }) - @IsDateString() - @IsOptional() - remote_created_at?: string; - - @ApiPropertyOptional({ - type: String, - example: '2024-10-01T12:00:00Z', - format: 'date-time', - nullable: true, - description: 'The remote modification date of the job', - }) - @IsDateString() - @IsOptional() - remote_updated_at?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - additionalProperties: true, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsJobOutput extends UnifiedAtsJobInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the job', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: 'The remote ID of the job in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { key1: 'value1', key2: 42, key3: true }, - nullable: true, - additionalProperties: true, - description: 'The remote data of the job in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The created date of the object', - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2023-10-01T12:00:00Z', - nullable: true, - description: 'The modified date of the object', - }) - @IsOptional() - modified_at?: Date; -} diff --git a/packages/api/src/ats/job/utils/index.ts b/packages/api/src/ats/job/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/job/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/jobinterviewstage/jobinterviewstage.controller.ts b/packages/api/src/ats/jobinterviewstage/jobinterviewstage.controller.ts deleted file mode 100644 index b0d1f09ad..000000000 --- a/packages/api/src/ats/jobinterviewstage/jobinterviewstage.controller.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { JobInterviewStageService } from './services/jobinterviewstage.service'; -import { - UnifiedAtsJobinterviewstageInput, - UnifiedAtsJobinterviewstageOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - - -@ApiTags('ats/jobinterviewstages') -@Controller('ats/jobinterviewstages') -export class JobInterviewStageController { - constructor( - private readonly jobinterviewstageService: JobInterviewStageService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(JobInterviewStageController.name); - } - - @ApiOperation({ - operationId: 'listAtsJobInterviewStage', - summary: 'List JobInterviewStages', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsJobinterviewstageOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getJobInterviewStages( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.jobinterviewstageService.getJobInterviewStages( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsJobInterviewStage', - summary: 'Retrieve Job Interview Stages', - description: - 'Retrieve Job Interview Stages from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the jobinterviewstage you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsJobinterviewstageOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.jobinterviewstageService.getJobInterviewStage( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/ats/jobinterviewstage/jobinterviewstage.module.ts b/packages/api/src/ats/jobinterviewstage/jobinterviewstage.module.ts deleted file mode 100644 index 09e5f2044..000000000 --- a/packages/api/src/ats/jobinterviewstage/jobinterviewstage.module.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Module } from '@nestjs/common'; -import { JobInterviewStageController } from './jobinterviewstage.controller'; -import { JobInterviewStageService } from './services/jobinterviewstage.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { AshbyService } from './services/ashby'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { AshbyJobInterviewStageMapper } from './services/ashby/mappers'; -import { Utils } from '@ats/@lib/@utils'; - -@Module({ - controllers: [JobInterviewStageController], - providers: [ - CoreUnification, - - JobInterviewStageService, - - SyncService, - WebhookService, - - ServiceRegistry, - - IngestDataService, - - Utils, - AshbyJobInterviewStageMapper, - /* PROVIDERS SERVICES */ - AshbyService, - ], - exports: [SyncService], -}) -export class JobInterviewStageModule {} diff --git a/packages/api/src/ats/jobinterviewstage/services/ashby/index.ts b/packages/api/src/ats/jobinterviewstage/services/ashby/index.ts deleted file mode 100644 index f97dd4915..000000000 --- a/packages/api/src/ats/jobinterviewstage/services/ashby/index.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IJobInterviewStageService } from '@ats/jobinterviewstage/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 { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { ApiResponse } from '@@core/utils/types'; -import { ServiceRegistry } from '../registry.service'; -import { - AshbyJobInterviewStageInput, - AshbyJobInterviewStageOutput, -} from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalJobInterviewStageOutput } from '@@core/utils/types/original/original.ats'; -import { SyncParam } from '@@core/utils/types/interface'; - -@Injectable() -export class AshbyService implements IJobInterviewStageService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - AtsObject.jobinterviewstage.toUpperCase() + ':' + AshbyService.name, - ); - this.registry.registerService('ashby', this); - } - - async sync( - data: SyncParam, - ): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - const resp = await axios.post( - `${connection.account_url}/interviewStage.list`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }, - ); - const jobinterviewstages: AshbyJobInterviewStageOutput[] = - resp.data.results; - this.logger.log(`Synced ashby jobinterviewstages !`); - - return { - data: jobinterviewstages, - message: 'Ashby jobinterviewstages retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/jobinterviewstage/services/ashby/mappers.ts b/packages/api/src/ats/jobinterviewstage/services/ashby/mappers.ts deleted file mode 100644 index 13c06d7aa..000000000 --- a/packages/api/src/ats/jobinterviewstage/services/ashby/mappers.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { - AshbyJobInterviewStageInput, - AshbyJobInterviewStageOutput, -} from './types'; -import { - UnifiedAtsJobinterviewstageInput, - UnifiedAtsJobinterviewstageOutput, -} from '@ats/jobinterviewstage/types/model.unified'; -import { IJobInterviewStageMapper } from '@ats/jobinterviewstage/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'; - -@Injectable() -export class AshbyJobInterviewStageMapper implements IJobInterviewStageMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService( - 'ats', - 'jobinterviewstage', - 'ashby', - this, - ); - } - - async desunify( - source: UnifiedAtsJobinterviewstageInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return; - } - - async unify( - source: AshbyJobInterviewStageOutput | AshbyJobInterviewStageOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise< - UnifiedAtsJobinterviewstageOutput | UnifiedAtsJobinterviewstageOutput[] - > { - if (!Array.isArray(source)) { - return await this.mapSingleJobInterviewStageToUnified( - source, - connectionId, - customFieldMappings, - ); - } - // Handling array of AshbyJobInterviewStageOutput - return Promise.all( - source.map((jobinterviewstage) => - this.mapSingleJobInterviewStageToUnified( - jobinterviewstage, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleJobInterviewStageToUnified( - jobinterviewstage: AshbyJobInterviewStageOutput, - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return { - remote_id: jobinterviewstage.id, - remote_data: jobinterviewstage, - name: jobinterviewstage.title || null, - stage_order: jobinterviewstage.orderInInterviewPlan, - }; - } -} diff --git a/packages/api/src/ats/jobinterviewstage/services/ashby/types.ts b/packages/api/src/ats/jobinterviewstage/services/ashby/types.ts deleted file mode 100644 index 032883c99..000000000 --- a/packages/api/src/ats/jobinterviewstage/services/ashby/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface AshbyJobInterviewStageInput { - id: string; - title: string; - type: string; - orderInInterviewPlan: number; - interviewStageGroupId: string; - interviewPlanId: string; -} - -export type AshbyJobInterviewStageOutput = Partial; diff --git a/packages/api/src/ats/jobinterviewstage/services/jobinterviewstage.service.ts b/packages/api/src/ats/jobinterviewstage/services/jobinterviewstage.service.ts deleted file mode 100644 index 7b4f1f67f..000000000 --- a/packages/api/src/ats/jobinterviewstage/services/jobinterviewstage.service.ts +++ /dev/null @@ -1,245 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { UnifiedAtsJobinterviewstageOutput } from '../types/model.unified'; - -@Injectable() -export class JobInterviewStageService { - constructor(private prisma: PrismaService, private logger: LoggerService) { - this.logger.setContext(JobInterviewStageService.name); - } - - async getJobInterviewStage( - id_ats_job_interview_stage: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const stage = await this.prisma.ats_job_interview_stages.findUnique({ - where: { - id_ats_job_interview_stage: id_ats_job_interview_stage, - }, - }); - - if (!stage) { - throw new Error( - `Job interview stage with ID ${id_ats_job_interview_stage} not found.`, - ); - } - - // Fetch field mappings for the job interview stage - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: stage.id_ats_job_interview_stage, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsJobinterviewstageOutput format - const unifiedJobInterviewStage: UnifiedAtsJobinterviewstageOutput = { - id: stage.id_ats_job_interview_stage, - name: stage.name, - stage_order: stage.stage_order, - job_id: stage.id_ats_job, - field_mappings: field_mappings, - remote_id: stage.remote_id, - created_at: stage.created_at, - modified_at: stage.modified_at, - }; - - let res: UnifiedAtsJobinterviewstageOutput = unifiedJobInterviewStage; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: stage.id_ats_job_interview_stage, - }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.jobinterviewstage.pull', - method: 'GET', - url: '/ats/jobinterviewstage', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return res; - } catch (error) { - throw error; - } - } - - async getJobInterviewStages( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsJobinterviewstageOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = - await this.prisma.ats_job_interview_stages.findFirst({ - where: { - id_connection: connection_id, - id_ats_job_interview_stage: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const stages = await this.prisma.ats_job_interview_stages.findMany({ - take: limit + 1, - cursor: cursor - ? { - id_ats_job_interview_stage: cursor, - } - : undefined, - orderBy: { - created_at: 'asc', - }, - where: {}, - }); - - if (stages.length === limit + 1) { - next_cursor = Buffer.from( - stages[stages.length - 1].id_ats_job_interview_stage, - ).toString('base64'); - stages.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - - const unifiedStages: UnifiedAtsJobinterviewstageOutput[] = - await Promise.all( - stages.map(async (stage) => { - // Fetch field mappings for the job interview stage - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: stage.id_ats_job_interview_stage, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Array.from( - fieldMappingsMap, - ([key, value]) => ({ - [key]: value, - }), - ); - - // Transform to UnifiedAtsJobinterviewstageOutput format - return { - id: stage.id_ats_job_interview_stage, - name: stage.name, - stage_order: stage.stage_order, - job_id: stage.id_ats_job, - field_mappings: field_mappings, - remote_id: stage.remote_id, - created_at: stage.created_at, - modified_at: stage.modified_at, - }; - }), - ); - - let res: UnifiedAtsJobinterviewstageOutput[] = unifiedStages; - - if (remote_data) { - const remote_array_data: UnifiedAtsJobinterviewstageOutput[] = - await Promise.all( - res.map(async (stage) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: stage.id, - }, - }); - const remote_data = JSON.parse(resp.data); - return { ...stage, remote_data }; - }), - ); - - res = remote_array_data; - } - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.jobinterviewstage.pull', - method: 'GET', - url: '/ats/jobinterviewstages', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { - data: res, - prev_cursor, - next_cursor, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/jobinterviewstage/services/registry.service.ts b/packages/api/src/ats/jobinterviewstage/services/registry.service.ts deleted file mode 100644 index 4c01cf169..000000000 --- a/packages/api/src/ats/jobinterviewstage/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IJobInterviewStageService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IJobInterviewStageService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IJobInterviewStageService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/jobinterviewstage/sync/sync.service.ts b/packages/api/src/ats/jobinterviewstage/sync/sync.service.ts deleted file mode 100644 index 77949f2ae..000000000 --- a/packages/api/src/ats/jobinterviewstage/sync/sync.service.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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 { IJobInterviewStageService } from '../types'; -import { OriginalJobInterviewStageOutput } from '@@core/utils/types/original/original.ats'; -import { UnifiedAtsJobinterviewstageOutput } from '../types/model.unified'; -import { ats_job_interview_stages as AtsJobInterviewStage } 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'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'jobinterviewstage', this); - } - onModuleInit() { -// - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IJobInterviewStageService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: jobinterviewstage} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsJobinterviewstageOutput, - OriginalJobInterviewStageOutput, - IJobInterviewStageService - >(integrationId, linkedUserId, 'ats', 'jobinterviewstage', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - jobInterviewStages: UnifiedAtsJobinterviewstageOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const jobInterviewStages_results: AtsJobInterviewStage[] = []; - - const updateOrCreateJobInterviewStage = async ( - jobInterviewStage: UnifiedAtsJobinterviewstageOutput, - originId: string, - ) => { - const existingJobInterviewStage = - await this.prisma.ats_job_interview_stages.findFirst({ - where: { - remote_id: originId, - }, - }); - - const baseData: any = { - name: jobInterviewStage.name ?? null, - stage_order: jobInterviewStage.stage_order ?? null, - job_id: jobInterviewStage.job_id ?? null, - modified_at: new Date(), - }; - - if (existingJobInterviewStage) { - return await this.prisma.ats_job_interview_stages.update({ - where: { - id_ats_job_interview_stage: - existingJobInterviewStage.id_ats_job_interview_stage, - }, - data: baseData, - }); - } else { - return await this.prisma.ats_job_interview_stages.create({ - data: { - ...baseData, - id_ats_job_interview_stage: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - }; - - for (let i = 0; i < jobInterviewStages.length; i++) { - const jobInterviewStage = jobInterviewStages[i]; - const originId = jobInterviewStage.remote_id; - - if (!originId || originId === '') { - throw new ReferenceError(`Origin id not there, found ${originId}`); - } - - const res = await updateOrCreateJobInterviewStage( - jobInterviewStage, - originId, - ); - const job_interview_stage_id = res.id_ats_job_interview_stage; - jobInterviewStages_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - jobInterviewStage.field_mappings, - job_interview_stage_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - job_interview_stage_id, - remote_data[i], - ); - } - - return jobInterviewStages_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/jobinterviewstage/types/index.ts b/packages/api/src/ats/jobinterviewstage/types/index.ts deleted file mode 100644 index 1026a633d..000000000 --- a/packages/api/src/ats/jobinterviewstage/types/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedAtsJobinterviewstageInput, - UnifiedAtsJobinterviewstageOutput, -} from './model.unified'; -import { OriginalJobInterviewStageOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface IJobInterviewStageService extends IBaseObjectService { - addJobInterviewStage?( - jobinterviewstageData: DesunifyReturnType, - linkedUserId: string, - ): Promise>; - - sync( - data: SyncParam, - ): Promise>; -} - -export interface IJobInterviewStageMapper { - desunify( - source: UnifiedAtsJobinterviewstageInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalJobInterviewStageOutput | OriginalJobInterviewStageOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/jobinterviewstage/types/model.unified.ts b/packages/api/src/ats/jobinterviewstage/types/model.unified.ts deleted file mode 100644 index 9f3e86188..000000000 --- a/packages/api/src/ats/jobinterviewstage/types/model.unified.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsInt, - IsDateString, -} from 'class-validator'; - -export class UnifiedAtsJobinterviewstageInput { - @ApiPropertyOptional({ - type: String, - example: 'Second Call', - nullable: true, - description: 'The name of the job interview stage', - }) - @IsString() - @IsOptional() - name?: string; - - @ApiPropertyOptional({ - type: Number, - example: 1, - nullable: true, - description: 'The order of the stage', - }) - //@IsInt() - @IsOptional() - stage_order?: number; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the job', - }) - @IsUUID() - @IsOptional() - job_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - additionalProperties: true, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsJobinterviewstageOutput extends UnifiedAtsJobinterviewstageInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the job interview stage', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: - 'The remote ID of the job interview stage in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: - 'The remote data of the job interview stage in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The created date of the object', - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The modified date of the object', - }) - @IsOptional() - modified_at?: Date; -} diff --git a/packages/api/src/ats/jobinterviewstage/utils/index.ts b/packages/api/src/ats/jobinterviewstage/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/jobinterviewstage/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/offer/offer.controller.ts b/packages/api/src/ats/offer/offer.controller.ts deleted file mode 100644 index c98875d43..000000000 --- a/packages/api/src/ats/offer/offer.controller.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { OfferService } from './services/offer.service'; -import { - UnifiedAtsOfferInput, - UnifiedAtsOfferOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - - -@ApiTags('ats/offers') -@Controller('ats/offers') -export class OfferController { - constructor( - private readonly offerService: OfferService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(OfferController.name); - } - - @ApiOperation({ - operationId: 'listAtsOffer', - summary: 'List Offers', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsOfferOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getOffers( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.offerService.getOffers( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsOffer', - summary: 'Retrieve Offers', - description: 'Retrieve Offers from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the offer you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsOfferOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.offerService.getOffer( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/ats/offer/offer.module.ts b/packages/api/src/ats/offer/offer.module.ts deleted file mode 100644 index b003b4f62..000000000 --- a/packages/api/src/ats/offer/offer.module.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Module } from '@nestjs/common'; -import { OfferController } from './offer.controller'; -import { OfferService } from './services/offer.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { AshbyService } from './services/ashby'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { AshbyOfferMapper } from './services/ashby/mappers'; -import { Utils } from '@ats/@lib/@utils'; - -@Module({ - controllers: [OfferController], - providers: [ - OfferService, - - CoreUnification, - - SyncService, - WebhookService, - - ServiceRegistry, - - IngestDataService, - Utils, - - AshbyOfferMapper, - /* PROVIDERS SERVICES */ - AshbyService, - ], - exports: [SyncService], -}) -export class OfferModule {} diff --git a/packages/api/src/ats/offer/services/ashby/index.ts b/packages/api/src/ats/offer/services/ashby/index.ts deleted file mode 100644 index f50148e55..000000000 --- a/packages/api/src/ats/offer/services/ashby/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IOfferService } from '@ats/offer/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 { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { ApiResponse } from '@@core/utils/types'; -import { ServiceRegistry } from '../registry.service'; -import { AshbyOfferInput, AshbyOfferOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalOfferOutput } from '@@core/utils/types/original/original.ats'; -import { SyncParam } from '@@core/utils/types/interface'; - -@Injectable() -export class AshbyService implements IOfferService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - AtsObject.offer.toUpperCase() + ':' + AshbyService.name, - ); - this.registry.registerService('ashby', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - const resp = await axios.post(`${connection.account_url}/offer.list`, { - headers: { - 'Content-Type': 'offer/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }); - const offers: AshbyOfferOutput[] = resp.data.results; - this.logger.log(`Synced ashby offers !`); - - return { - data: offers, - message: 'Ashby offers retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/offer/services/ashby/mappers.ts b/packages/api/src/ats/offer/services/ashby/mappers.ts deleted file mode 100644 index bbb398df6..000000000 --- a/packages/api/src/ats/offer/services/ashby/mappers.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { AshbyOfferInput, AshbyOfferOutput } from './types'; -import { - OfferStatus, - UnifiedAtsOfferInput, - UnifiedAtsOfferOutput, -} from '@ats/offer/types/model.unified'; -import { IOfferMapper } from '@ats/offer/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'; - -@Injectable() -export class AshbyOfferMapper implements IOfferMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('ats', 'offer', 'ashby', this); - } - - mapToOfferStatus( - data: - | 'WaitingOnApprovalStart' - | 'WaitingOnOfferApproval' - | 'WaitingOnApprovalDefinition' - | 'WaitingOnCandidateResponse' - | 'CandidateRejected' - | 'CandidateAccepted' - | 'OfferCancelled', - ): OfferStatus | string { - switch (data) { - case 'OfferCancelled': - return 'DEPRECATED'; - case 'CandidateAccepted': - return 'APPROVED'; - case 'CandidateRejected': - return 'DENIED'; - default: - return data; - } - } - - async desunify( - source: UnifiedAtsOfferInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return; - } - - async unify( - source: AshbyOfferOutput | AshbyOfferOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - if (!Array.isArray(source)) { - return await this.mapSingleOfferToUnified( - source, - connectionId, - customFieldMappings, - ); - } - // Handling array of AshbyOfferOutput - return Promise.all( - source.map((offer) => - this.mapSingleOfferToUnified(offer, connectionId, customFieldMappings), - ), - ); - } - - private async mapSingleOfferToUnified( - offer: AshbyOfferOutput, - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return { - remote_id: offer.id, - remote_data: offer, - closed_at: offer.decidedAt, - start_date: offer.latestVersion.startDate, - status: this.mapToOfferStatus(offer.offerStatus as any) ?? null, - application_id: - (await this.utils.getApplicationUuidFromRemoteId( - offer.applicationId, - connectionId, - )) || null, - remote_created_at: offer.latestVersion.createdAt, - }; - } -} diff --git a/packages/api/src/ats/offer/services/ashby/types.ts b/packages/api/src/ats/offer/services/ashby/types.ts deleted file mode 100644 index 1d03e35f6..000000000 --- a/packages/api/src/ats/offer/services/ashby/types.ts +++ /dev/null @@ -1,65 +0,0 @@ -export interface AshbyOfferInput { - id: string; - decidedAt: string; - applicationId: string; - acceptanceStatus: string; - offerStatus: string; - latestVersion: { - id: string; - startDate: string; - salary: { - currencyCode: string; - value: number; - }; - createdAt: string; - customFields: Array<{ - id: string; - title: string; - value: string; - }>; - fileHandles: Array<{ - id: string; - name: string; - handle: string; - }>; - author: { - id: string; - firstName: string; - lastName: string; - email: string; - globalRole: string; - isEnabled: boolean; - updatedAt: string; - }; - }; - versions: Array<{ - id: string; - startDate: string; - salary: { - currencyCode: string; - value: number; - }; - createdAt: string; - customFields: Array<{ - id: string; - title: string; - value: string; - }>; - fileHandles: Array<{ - id: string; - name: string; - handle: string; - }>; - author: { - id: string; - firstName: string; - lastName: string; - email: string; - globalRole: string; - isEnabled: boolean; - updatedAt: string; - }; - }>; -} - -export type AshbyOfferOutput = Partial; diff --git a/packages/api/src/ats/offer/services/offer.service.ts b/packages/api/src/ats/offer/services/offer.service.ts deleted file mode 100644 index 00eb47175..000000000 --- a/packages/api/src/ats/offer/services/offer.service.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { OfferStatus, UnifiedAtsOfferOutput } from '../types/model.unified'; - -@Injectable() -export class OfferService { - constructor(private prisma: PrismaService, private logger: LoggerService) { - this.logger.setContext(OfferService.name); - } - - async getOffer( - id_ats_offer: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const offer = await this.prisma.ats_offers.findUnique({ - where: { - id_ats_offer: id_ats_offer, - }, - }); - - if (!offer) { - throw new Error(`Offer with ID ${id_ats_offer} not found.`); - } - - // Fetch field mappings for the offer - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: offer.id_ats_offer, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsOfferOutput format - const unifiedOffer: UnifiedAtsOfferOutput = { - id: offer.id_ats_offer, - created_by: offer.created_by, - remote_created_at: String(offer.remote_created_at), - closed_at: String(offer.closed_at), - sent_at: String(offer.sent_at), - start_date: String(offer.start_date), - status: offer.status, - application_id: offer.id_ats_application, - field_mappings: field_mappings, - remote_id: offer.remote_id, - created_at: offer.created_at, - modified_at: offer.modified_at, - }; - - let res: UnifiedAtsOfferOutput = unifiedOffer; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: offer.id_ats_offer, - }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.offer.pull', - method: 'GET', - url: '/ats/offer', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return res; - } catch (error) { - throw error; - } - } - - async getOffers( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsOfferOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = await this.prisma.ats_offers.findFirst({ - where: { - id_connection: connection_id, - id_ats_offer: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const offers = await this.prisma.ats_offers.findMany({ - take: limit + 1, - cursor: cursor - ? { - id_ats_offer: cursor, - } - : undefined, - orderBy: { - created_at: 'asc', - }, - where: {}, - }); - - if (offers.length === limit + 1) { - next_cursor = Buffer.from( - offers[offers.length - 1].id_ats_offer, - ).toString('base64'); - offers.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - const unifiedOffers: UnifiedAtsOfferOutput[] = await Promise.all( - offers.map(async (offer) => { - // Fetch field mappings for the offer - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: offer.id_ats_offer, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsOfferOutput format - return { - id: offer.id_ats_offer, - created_by: offer.created_by, - remote_created_at: String(offer.remote_created_at), - closed_at: String(offer.closed_at), - sent_at: String(offer.sent_at), - start_date: String(offer.start_date), - status: offer.status, - application_id: offer.id_ats_application, - field_mappings: field_mappings, - remote_id: offer.remote_id, - created_at: offer.created_at, - modified_at: offer.modified_at, - }; - }), - ); - - let res: UnifiedAtsOfferOutput[] = unifiedOffers; - - if (remote_data) { - const remote_array_data: UnifiedAtsOfferOutput[] = await Promise.all( - res.map(async (offer) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: offer.id, - }, - }); - const remote_data = JSON.parse(resp.data); - return { ...offer, remote_data }; - }), - ); - - res = remote_array_data; - } - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.offer.pull', - method: 'GET', - url: '/ats/offers', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { - data: res, - prev_cursor, - next_cursor, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/offer/services/registry.service.ts b/packages/api/src/ats/offer/services/registry.service.ts deleted file mode 100644 index b6d43f0b1..000000000 --- a/packages/api/src/ats/offer/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IOfferService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IOfferService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IOfferService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/offer/sync/sync.service.ts b/packages/api/src/ats/offer/sync/sync.service.ts deleted file mode 100644 index 5cf0ce4c9..000000000 --- a/packages/api/src/ats/offer/sync/sync.service.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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 { IOfferService } from '../types'; -import { OriginalOfferOutput } from '@@core/utils/types/original/original.ats'; -import { UnifiedAtsOfferOutput } from '../types/model.unified'; -import { ats_offers as AtsOffer } 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'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'offer', this); - } - onModuleInit() { -// - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IOfferService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: offer} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsOfferOutput, - OriginalOfferOutput, - IOfferService - >(integrationId, linkedUserId, 'ats', 'offer', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - offers: UnifiedAtsOfferOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const offers_results: AtsOffer[] = []; - - const updateOrCreateOffer = async ( - offer: UnifiedAtsOfferOutput, - originId: string, - ) => { - const existingOffer = await this.prisma.ats_offers.findFirst({ - where: { - remote_id: originId, - }, - }); - - const baseData: any = { - created_by: offer.created_by ?? null, - remote_created_at: offer.remote_created_at ?? null, - closed_at: offer.closed_at ?? null, - sent_at: offer.sent_at ?? null, - start_date: offer.start_date ?? null, - status: offer.status ?? null, - application_id: offer.application_id ?? null, - modified_at: new Date(), - }; - - if (existingOffer) { - return await this.prisma.ats_offers.update({ - where: { - id_ats_offer: existingOffer.id_ats_offer, - }, - data: baseData, - }); - } else { - return await this.prisma.ats_offers.create({ - data: { - ...baseData, - id_ats_offer: uuidv4(), - created_at: new Date(), - id_linked_user: linkedUserId, - remote_id: originId, - }, - }); - } - }; - - for (let i = 0; i < offers.length; i++) { - const offer = offers[i]; - const originId = offer.remote_id; - - if (!originId || originId === '') { - throw new ReferenceError(`Origin id not there, found ${originId}`); - } - - const res = await updateOrCreateOffer(offer, originId); - const offer_id = res.id_ats_offer; - offers_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - offer.field_mappings, - offer_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData(offer_id, remote_data[i]); - } - - return offers_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/offer/types/index.ts b/packages/api/src/ats/offer/types/index.ts deleted file mode 100644 index daccb0571..000000000 --- a/packages/api/src/ats/offer/types/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { UnifiedAtsOfferInput, UnifiedAtsOfferOutput } from './model.unified'; -import { OriginalOfferOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface IOfferService extends IBaseObjectService { - addOffer?( - offerData: DesunifyReturnType, - linkedUserId: string, - ): Promise>; - - sync(data: SyncParam): Promise>; -} - -export interface IOfferMapper { - desunify( - source: UnifiedAtsOfferInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalOfferOutput | OriginalOfferOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/offer/types/model.unified.ts b/packages/api/src/ats/offer/types/model.unified.ts deleted file mode 100644 index 704e84c32..000000000 --- a/packages/api/src/ats/offer/types/model.unified.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsIn, -} from 'class-validator'; - -export type OfferStatus = - | 'DRAFT' - | 'APPROVAL_SENT' - | 'APPROVED' - | 'SENT' - | 'SENT_MANUALLY' - | 'OPENED' - | 'DENIED' - | 'SIGNED' - | 'DEPRECATED'; -export class UnifiedAtsOfferInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - description: 'The UUID of the creator', - nullable: true, - }) - @IsUUID() - @IsOptional() - created_by?: string; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - description: 'The remote creation date of the offer', - nullable: true, - }) - @IsDateString() - @IsOptional() - remote_created_at?: string; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - description: 'The closing date of the offer', - nullable: true, - }) - @IsDateString() - @IsOptional() - closed_at?: string; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - description: 'The sending date of the offer', - nullable: true, - }) - @IsDateString() - @IsOptional() - sent_at?: string; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - description: 'The start date of the offer', - nullable: true, - }) - @IsDateString() - @IsOptional() - start_date?: string; - - @ApiPropertyOptional({ - type: String, - example: 'DRAFT', - /* enum: [ - 'DRAFT', - 'APPROVAL_SENT', - 'APPROVED', - 'SENT', - 'SENT_MANUALLY', - 'OPENED', - 'DENIED', - 'SIGNED', - 'DEPRECATED', - ],*/ - description: 'The status of the offer', - nullable: true, - }) - /*@IsIn([ - 'DRAFT', - 'APPROVAL_SENT', - 'APPROVED', - 'SENT', - 'SENT_MANUALLY', - 'OPENED', - 'DENIED', - 'SIGNED', - 'DEPRECATED', - ])*/ - @IsOptional() - status?: OfferStatus | string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - description: 'The UUID of the application', - nullable: true, - }) - @IsUUID() - @IsOptional() - application_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - nullable: true, - additionalProperties: true, - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsOfferOutput extends UnifiedAtsOfferInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - description: 'The UUID of the offer', - nullable: true, - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - description: 'The remote ID of the offer in the context of the 3rd Party', - nullable: true, - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - description: 'The remote data of the offer in the context of the 3rd Party', - nullable: true, - additionalProperties: true, - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Object, - description: 'The created date of the object', - nullable: true, - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Object, - description: 'The modified date of the object', - nullable: true, - }) - @IsOptional() - modified_at?: Date; -} diff --git a/packages/api/src/ats/offer/utils/index.ts b/packages/api/src/ats/offer/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/offer/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/office/office.controller.ts b/packages/api/src/ats/office/office.controller.ts deleted file mode 100644 index b3b243fda..000000000 --- a/packages/api/src/ats/office/office.controller.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { OfficeService } from './services/office.service'; -import { - UnifiedAtsOfficeInput, - UnifiedAtsOfficeOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - - -@ApiTags('ats/offices') -@Controller('ats/offices') -export class OfficeController { - constructor( - private readonly officeService: OfficeService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(OfficeController.name); - } - - @ApiOperation({ - operationId: 'listAtsOffice', - summary: 'List Offices', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsOfficeOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getOffices( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.officeService.getOffices( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsOffice', - summary: 'Retrieve Offices', - description: 'Retrieve Offices from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the office you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsOfficeOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.officeService.getOffice( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/ats/office/office.module.ts b/packages/api/src/ats/office/office.module.ts deleted file mode 100644 index 618d25067..000000000 --- a/packages/api/src/ats/office/office.module.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Module } from '@nestjs/common'; -import { OfficeController } from './office.controller'; -import { OfficeService } from './services/office.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { AshbyService } from './services/ashby'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { AshbyOfficeMapper } from './services/ashby/mappers'; -import { Utils } from '@ats/@lib/@utils'; - -@Module({ - controllers: [OfficeController], - providers: [ - OfficeService, - - SyncService, - WebhookService, - CoreUnification, - - Utils, - - ServiceRegistry, - - IngestDataService, - - AshbyOfficeMapper, - /* PROVIDERS SERVICES */ - AshbyService, - ], - exports: [SyncService], -}) -export class OfficeModule {} diff --git a/packages/api/src/ats/office/services/ashby/index.ts b/packages/api/src/ats/office/services/ashby/index.ts deleted file mode 100644 index 6223712ac..000000000 --- a/packages/api/src/ats/office/services/ashby/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IOfficeService } from '@ats/office/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 { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { ApiResponse } from '@@core/utils/types'; -import { ServiceRegistry } from '../registry.service'; -import { AshbyOfficeInput, AshbyOfficeOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalOfficeOutput } from '@@core/utils/types/original/original.ats'; -import { SyncParam } from '@@core/utils/types/interface'; - -@Injectable() -export class AshbyService implements IOfficeService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - AtsObject.office.toUpperCase() + ':' + AshbyService.name, - ); - this.registry.registerService('ashby', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - const resp = await axios.post(`${connection.account_url}/location.list`, { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }); - const offices: AshbyOfficeOutput[] = resp.data.results; - this.logger.log(`Synced ashby offices !`); - - return { - data: offices, - message: 'Ashby offices retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/office/services/ashby/mappers.ts b/packages/api/src/ats/office/services/ashby/mappers.ts deleted file mode 100644 index c44e11ca9..000000000 --- a/packages/api/src/ats/office/services/ashby/mappers.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { AshbyOfficeInput, AshbyOfficeOutput } from './types'; -import { - UnifiedAtsOfficeInput, - UnifiedAtsOfficeOutput, -} from '@ats/office/types/model.unified'; -import { IOfficeMapper } from '@ats/office/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'; - -@Injectable() -export class AshbyOfficeMapper implements IOfficeMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('ats', 'office', 'ashby', this); - } - - async desunify( - source: UnifiedAtsOfficeInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return; - } - - async unify( - source: AshbyOfficeOutput | AshbyOfficeOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - if (!Array.isArray(source)) { - return await this.mapSingleOfficeToUnified( - source, - connectionId, - customFieldMappings, - ); - } - // Handling array of AshbyOfficeOutput - return Promise.all( - source.map((office) => - this.mapSingleOfficeToUnified( - office, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleOfficeToUnified( - office: AshbyOfficeOutput, - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return { - remote_id: office.id, - remote_data: office, - name: office.name || null, - location: `${office.address.postalAddress.addressLocality}, ${office.address.postalAddress.addressRegion}, ${office.address.postalAddress.addressCountry}`, - }; - } -} diff --git a/packages/api/src/ats/office/services/ashby/types.ts b/packages/api/src/ats/office/services/ashby/types.ts deleted file mode 100644 index 066e33383..000000000 --- a/packages/api/src/ats/office/services/ashby/types.ts +++ /dev/null @@ -1,19 +0,0 @@ -export interface AshbyOfficeInput { - id: string; - name: string; - isArchived: boolean; - address: Address; - isRemote: boolean; -} - -export type AshbyOfficeOutput = Partial; - -export interface AddressPostal { - addressCountry: string; - addressRegion: string; - addressLocality: string; -} - -export interface Address { - postalAddress: AddressPostal; -} diff --git a/packages/api/src/ats/office/services/office.service.ts b/packages/api/src/ats/office/services/office.service.ts deleted file mode 100644 index 975ee6784..000000000 --- a/packages/api/src/ats/office/services/office.service.ts +++ /dev/null @@ -1,233 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { UnifiedAtsOfficeOutput } from '../types/model.unified'; - -@Injectable() -export class OfficeService { - constructor(private prisma: PrismaService, private logger: LoggerService) { - this.logger.setContext(OfficeService.name); - } - - async getOffice( - id_ats_office: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const office = await this.prisma.ats_offices.findUnique({ - where: { - id_ats_office: id_ats_office, - }, - }); - - if (!office) { - throw new Error(`Office with ID ${id_ats_office} not found.`); - } - - // Fetch field mappings for the office - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: office.id_ats_office, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsOfficeOutput format - const unifiedOffice: UnifiedAtsOfficeOutput = { - id: office.id_ats_office, - name: office.name, - location: office.location, - field_mappings: field_mappings, - remote_id: office.remote_id, - created_at: office.created_at, - modified_at: office.modified_at, - }; - - let res: UnifiedAtsOfficeOutput = unifiedOffice; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: office.id_ats_office, - }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.office.pull', - method: 'GET', - url: '/ats/office', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return res; - } catch (error) { - throw error; - } - } - - async getOffices( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsOfficeOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = await this.prisma.ats_offices.findFirst({ - where: { - id_connection: connection_id, - id_ats_office: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const offices = await this.prisma.ats_offices.findMany({ - take: limit + 1, - cursor: cursor - ? { - id_ats_office: cursor, - } - : undefined, - orderBy: { - created_at: 'asc', - }, - where: {}, - }); - - if (offices.length === limit + 1) { - next_cursor = Buffer.from( - offices[offices.length - 1].id_ats_office, - ).toString('base64'); - offices.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - - const unifiedOffices: UnifiedAtsOfficeOutput[] = await Promise.all( - offices.map(async (office) => { - // Fetch field mappings for the office - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: office.id_ats_office, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsOfficeOutput format - return { - id: office.id_ats_office, - name: office.name, - location: office.location, - field_mappings: field_mappings, - remote_id: office.remote_id, - created_at: office.created_at, - modified_at: office.modified_at, - }; - }), - ); - - let res: UnifiedAtsOfficeOutput[] = unifiedOffices; - - if (remote_data) { - const remote_array_data: UnifiedAtsOfficeOutput[] = await Promise.all( - res.map(async (office) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: office.id, - }, - }); - const remote_data = JSON.parse(resp.data); - return { ...office, remote_data }; - }), - ); - - res = remote_array_data; - } - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.office.pull', - method: 'GET', - url: '/ats/offices', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { - data: res, - prev_cursor, - next_cursor, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/office/services/registry.service.ts b/packages/api/src/ats/office/services/registry.service.ts deleted file mode 100644 index 0e5d1bb19..000000000 --- a/packages/api/src/ats/office/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IOfficeService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IOfficeService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IOfficeService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/office/sync/sync.service.ts b/packages/api/src/ats/office/sync/sync.service.ts deleted file mode 100644 index e58b6b3fe..000000000 --- a/packages/api/src/ats/office/sync/sync.service.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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 { IOfficeService } from '../types'; -import { OriginalOfficeOutput } from '@@core/utils/types/original/original.ats'; -import { UnifiedAtsOfficeOutput } from '../types/model.unified'; -import { ats_offices as AtsOffice } 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'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'office', this); - } - onModuleInit() { -// - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IOfficeService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: office} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsOfficeOutput, - OriginalOfficeOutput, - IOfficeService - >(integrationId, linkedUserId, 'ats', 'office', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - offices: UnifiedAtsOfficeOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const offices_results: AtsOffice[] = []; - - const updateOrCreateOffice = async ( - office: UnifiedAtsOfficeOutput, - originId: string, - ) => { - let existingOffice; - if (!originId) { - existingOffice = await this.prisma.ats_offices.findFirst({ - where: { - name: office.name, - }, - }); - } else { - existingOffice = await this.prisma.ats_offices.findFirst({ - where: { - remote_id: originId, - }, - }); - } - - const baseData: any = { - name: office.name ?? null, - location: office.location ?? null, - modified_at: new Date(), - }; - - if (existingOffice) { - return await this.prisma.ats_offices.update({ - where: { - id_ats_office: existingOffice.id_ats_office, - }, - data: baseData, - }); - } else { - return await this.prisma.ats_offices.create({ - data: { - ...baseData, - id_ats_office: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - }; - - for (let i = 0; i < offices.length; i++) { - const office = offices[i]; - const originId = office.remote_id; - - const res = await updateOrCreateOffice(office, originId); - const office_id = res.id_ats_office; - offices_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - office.field_mappings, - office_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData(office_id, remote_data[i]); - } - - return offices_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/office/types/index.ts b/packages/api/src/ats/office/types/index.ts deleted file mode 100644 index cbed85b28..000000000 --- a/packages/api/src/ats/office/types/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { UnifiedAtsOfficeInput, UnifiedAtsOfficeOutput } from './model.unified'; -import { OriginalOfficeOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface IOfficeService extends IBaseObjectService { - addOffice?( - officeData: DesunifyReturnType, - linkedUserId: string, - ): Promise>; - - sync(data: SyncParam): Promise>; -} - -export interface IOfficeMapper { - desunify( - source: UnifiedAtsOfficeInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalOfficeOutput | OriginalOfficeOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/office/types/model.unified.ts b/packages/api/src/ats/office/types/model.unified.ts deleted file mode 100644 index bf5fa8962..000000000 --- a/packages/api/src/ats/office/types/model.unified.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsUUID, IsOptional, IsString, IsDateString } from 'class-validator'; - -export class UnifiedAtsOfficeInput { - @ApiPropertyOptional({ - type: String, - example: 'Condo Office 5th', - nullable: true, - description: 'The name of the office', - }) - @IsString() - @IsOptional() - name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'New York', - nullable: true, - description: 'The location of the office', - }) - @IsString() - @IsOptional() - location?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - additionalProperties: true, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsOfficeOutput extends UnifiedAtsOfficeInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - description: 'The UUID of the office', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: 'The remote ID of the office in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: - 'The remote data of the office in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The created date of the object', - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The modified date of the object', - }) - @IsOptional() - modified_at?: Date; -} diff --git a/packages/api/src/ats/office/utils/index.ts b/packages/api/src/ats/office/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/office/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/rejectreason/rejectreason.controller.ts b/packages/api/src/ats/rejectreason/rejectreason.controller.ts deleted file mode 100644 index 6f51ab6d6..000000000 --- a/packages/api/src/ats/rejectreason/rejectreason.controller.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { RejectReasonService } from './services/rejectreason.service'; -import { - UnifiedAtsRejectreasonInput, - UnifiedAtsRejectreasonOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - - -@ApiTags('ats/rejectreasons') -@Controller('ats/rejectreasons') -export class RejectReasonController { - constructor( - private readonly rejectreasonService: RejectReasonService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(RejectReasonController.name); - } - - @ApiOperation({ - operationId: 'listAtsRejectReasons', - summary: 'List RejectReasons', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsRejectreasonOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getRejectReasons( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.rejectreasonService.getRejectReasons( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsRejectReason', - summary: 'Retrieve Reject Reasons', - description: 'Retrieve Reject Reasons from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the rejectreason you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsRejectreasonOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.rejectreasonService.getRejectReason( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/ats/rejectreason/rejectreason.module.ts b/packages/api/src/ats/rejectreason/rejectreason.module.ts deleted file mode 100644 index 96d2d5336..000000000 --- a/packages/api/src/ats/rejectreason/rejectreason.module.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Module } from '@nestjs/common'; -import { RejectReasonController } from './rejectreason.controller'; -import { ServiceRegistry } from './services/registry.service'; -import { RejectReasonService } from './services/rejectreason.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { AshbyService } from './services/ashby'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { AshbyRejectReasonMapper } from './services/ashby/mappers'; -import { Utils } from '@ats/@lib/@utils'; - -@Module({ - controllers: [RejectReasonController], - providers: [ - RejectReasonService, - - SyncService, - CoreUnification, - - WebhookService, - - ServiceRegistry, - - Utils, - IngestDataService, - - AshbyRejectReasonMapper, - /* PROVIDERS SERVICES */ - AshbyService, - ], - exports: [SyncService], -}) -export class RejectReasonModule {} diff --git a/packages/api/src/ats/rejectreason/services/ashby/index.ts b/packages/api/src/ats/rejectreason/services/ashby/index.ts deleted file mode 100644 index 31f0f9dd9..000000000 --- a/packages/api/src/ats/rejectreason/services/ashby/index.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { AtsObject } from '@ats/@lib/@types'; -import { IRejectReasonService } from '@ats/rejectreason/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { AshbyRejectReasonOutput } from './types'; - -@Injectable() -export class AshbyService implements IRejectReasonService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - AtsObject.rejectreason.toUpperCase() + ':' + AshbyService.name, - ); - this.registry.registerService('ashby', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - const resp = await axios.post( - `${connection.account_url}/archiveReason.list`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }, - ); - const rejectreasons: AshbyRejectReasonOutput[] = resp.data.results; - this.logger.log(`Synced ashby rejectreasons !`); - - return { - data: rejectreasons, - message: 'Ashby rejectreasons retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/rejectreason/services/ashby/mappers.ts b/packages/api/src/ats/rejectreason/services/ashby/mappers.ts deleted file mode 100644 index da10be56c..000000000 --- a/packages/api/src/ats/rejectreason/services/ashby/mappers.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { AshbyRejectReasonInput, AshbyRejectReasonOutput } from './types'; -import { - UnifiedAtsRejectreasonInput, - UnifiedAtsRejectreasonOutput, -} from '@ats/rejectreason/types/model.unified'; -import { IRejectReasonMapper } from '@ats/rejectreason/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'; - -@Injectable() -export class AshbyRejectReasonMapper implements IRejectReasonMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('ats', 'rejectreason', 'ashby', this); - } - - async desunify( - source: UnifiedAtsRejectreasonInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return; - } - - async unify( - source: AshbyRejectReasonOutput | AshbyRejectReasonOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - if (!Array.isArray(source)) { - return await this.mapSingleRejectReasonToUnified( - source, - connectionId, - customFieldMappings, - ); - } - // Handling array of AshbyRejectReasonOutput - return Promise.all( - source.map((rejectreason) => - this.mapSingleRejectReasonToUnified( - rejectreason, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleRejectReasonToUnified( - rejectreason: AshbyRejectReasonOutput, - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return { - remote_id: rejectreason.id, - remote_data: rejectreason, - name: rejectreason.text || null, - }; - } -} diff --git a/packages/api/src/ats/rejectreason/services/ashby/types.ts b/packages/api/src/ats/rejectreason/services/ashby/types.ts deleted file mode 100644 index 1801c1d0c..000000000 --- a/packages/api/src/ats/rejectreason/services/ashby/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface AshbyRejectReasonInput { - id: string; - text: string; - reasonType: string; - isArchived: boolean; -} - -export type AshbyRejectReasonOutput = Partial; diff --git a/packages/api/src/ats/rejectreason/services/registry.service.ts b/packages/api/src/ats/rejectreason/services/registry.service.ts deleted file mode 100644 index 8e077bdff..000000000 --- a/packages/api/src/ats/rejectreason/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IRejectReasonService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IRejectReasonService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IRejectReasonService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/rejectreason/services/rejectreason.service.ts b/packages/api/src/ats/rejectreason/services/rejectreason.service.ts deleted file mode 100644 index 2ee3b2803..000000000 --- a/packages/api/src/ats/rejectreason/services/rejectreason.service.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { UnifiedAtsRejectreasonOutput } from '../types/model.unified'; - -@Injectable() -export class RejectReasonService { - constructor(private prisma: PrismaService, private logger: LoggerService) { - this.logger.setContext(RejectReasonService.name); - } - - async getRejectReason( - id_ats_reject_reason: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const rejectReason = await this.prisma.ats_reject_reasons.findUnique({ - where: { - id_ats_reject_reason: id_ats_reject_reason, - }, - }); - - if (!rejectReason) { - throw new Error( - `Reject reason with ID ${id_ats_reject_reason} not found.`, - ); - } - - // Fetch field mappings for the reject reason - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: rejectReason.id_ats_reject_reason, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsRejectreasonOutput format - const unifiedRejectReason: UnifiedAtsRejectreasonOutput = { - id: rejectReason.id_ats_reject_reason, - name: rejectReason.name, - field_mappings: field_mappings, - remote_id: rejectReason.remote_id, - created_at: rejectReason.created_at, - modified_at: rejectReason.modified_at, - }; - - let res: UnifiedAtsRejectreasonOutput = unifiedRejectReason; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: rejectReason.id_ats_reject_reason, - }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.rejectreason.pull', - method: 'GET', - url: '/ats/rejectreason', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return res; - } catch (error) { - throw error; - } - } - - async getRejectReasons( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsRejectreasonOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = await this.prisma.ats_reject_reasons.findFirst({ - where: { - id_connection: connection_id, - id_ats_reject_reason: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const rejectReasons = await this.prisma.ats_reject_reasons.findMany({ - take: limit + 1, - cursor: cursor - ? { - id_ats_reject_reason: cursor, - } - : undefined, - orderBy: { - created_at: 'asc', - }, - where: {}, - }); - - if (rejectReasons.length === limit + 1) { - next_cursor = Buffer.from( - rejectReasons[rejectReasons.length - 1].id_ats_reject_reason, - ).toString('base64'); - rejectReasons.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - const unifiedRejectReasons: UnifiedAtsRejectreasonOutput[] = - await Promise.all( - rejectReasons.map(async (rejectReason) => { - // Fetch field mappings for the reject reason - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: rejectReason.id_ats_reject_reason, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Array.from( - fieldMappingsMap, - ([key, value]) => ({ - [key]: value, - }), - ); - - // Transform to UnifiedAtsRejectreasonOutput format - return { - id: rejectReason.id_ats_reject_reason, - name: rejectReason.name, - field_mappings: field_mappings, - remote_id: rejectReason.remote_id, - created_at: rejectReason.created_at, - modified_at: rejectReason.modified_at, - }; - }), - ); - - let res: UnifiedAtsRejectreasonOutput[] = unifiedRejectReasons; - - if (remote_data) { - const remote_array_data: UnifiedAtsRejectreasonOutput[] = - await Promise.all( - res.map(async (rejectReason) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: rejectReason.id, - }, - }); - const remote_data = JSON.parse(resp.data); - return { ...rejectReason, remote_data }; - }), - ); - - res = remote_array_data; - } - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.rejectreason.pull', - method: 'GET', - url: '/ats/rejectreasons', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { - data: res, - prev_cursor, - next_cursor, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/rejectreason/sync/sync.service.ts b/packages/api/src/ats/rejectreason/sync/sync.service.ts deleted file mode 100644 index 155550f9f..000000000 --- a/packages/api/src/ats/rejectreason/sync/sync.service.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { BullQueueService } from '@@core/@core-services/queues/shared.service'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { OriginalRejectReasonOutput } from '@@core/utils/types/original/original.ats'; -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; -import { ATS_PROVIDERS } from '@panora/shared'; -import { ats_reject_reasons as AtsRejectReason } from '@prisma/client'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../services/registry.service'; -import { IRejectReasonService } from '../types'; -import { UnifiedAtsRejectreasonOutput } from '../types/model.unified'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'rejectreason', this); - } - onModuleInit() { -// - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IRejectReasonService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: rejectreason} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsRejectreasonOutput, - OriginalRejectReasonOutput, - IRejectReasonService - >(integrationId, linkedUserId, 'ats', 'rejectreason', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - rejectReasons: UnifiedAtsRejectreasonOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const rejectReasons_results: AtsRejectReason[] = []; - - const updateOrCreateRejectReason = async ( - rejectReason: UnifiedAtsRejectreasonOutput, - originId: string, - ) => { - let existingRejectReason; - if (!originId) { - existingRejectReason = await this.prisma.ats_reject_reasons.findFirst( - { - where: { - name: rejectReason.name, - }, - }, - ); - } else { - existingRejectReason = await this.prisma.ats_reject_reasons.findFirst( - { - where: { - remote_id: originId, - }, - }, - ); - } - - const baseData: any = { - name: rejectReason.name ?? null, - modified_at: new Date(), - }; - - if (existingRejectReason) { - return await this.prisma.ats_reject_reasons.update({ - where: { - id_ats_reject_reason: existingRejectReason.id_ats_reject_reason, - }, - data: baseData, - }); - } else { - return await this.prisma.ats_reject_reasons.create({ - data: { - ...baseData, - id_ats_reject_reason: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - }; - - for (let i = 0; i < rejectReasons.length; i++) { - const rejectReason = rejectReasons[i]; - const originId = rejectReason.remote_id; - - const res = await updateOrCreateRejectReason(rejectReason, originId); - const reject_reason_id = res.id_ats_reject_reason; - rejectReasons_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - rejectReason.field_mappings, - reject_reason_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - reject_reason_id, - remote_data[i], - ); - } - return rejectReasons_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/rejectreason/types/index.ts b/packages/api/src/ats/rejectreason/types/index.ts deleted file mode 100644 index c292be8ed..000000000 --- a/packages/api/src/ats/rejectreason/types/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ApiResponse } from '@@core/utils/types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; -import { OriginalRejectReasonOutput } from '@@core/utils/types/original/original.ats'; -import { - UnifiedAtsRejectreasonInput, - UnifiedAtsRejectreasonOutput, -} from './model.unified'; - -export interface IRejectReasonService extends IBaseObjectService { - sync(data: SyncParam): Promise>; -} - -export interface IRejectReasonMapper { - desunify( - source: UnifiedAtsRejectreasonInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalRejectReasonOutput | OriginalRejectReasonOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/rejectreason/types/model.unified.ts b/packages/api/src/ats/rejectreason/types/model.unified.ts deleted file mode 100644 index cfd5ab737..000000000 --- a/packages/api/src/ats/rejectreason/types/model.unified.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsUUID, IsOptional, IsString } from 'class-validator'; - -export class UnifiedAtsRejectreasonInput { - @ApiPropertyOptional({ - type: String, - example: 'Candidate inexperienced', - nullable: true, - description: 'The name of the reject reason', - }) - @IsString() - @IsOptional() - name?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - additionalProperties: true, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsRejectreasonOutput extends UnifiedAtsRejectreasonInput { - @ApiPropertyOptional({ - type: String, - nullable: true, - description: 'The UUID of the reject reason', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - nullable: true, - description: - 'The remote ID of the reject reason in the context of the 3rd Party', - example: 'id_1', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: - 'The remote data of the reject reason in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The created date of the object', - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The modified date of the object', - }) - @IsOptional() - modified_at?: Date; -} diff --git a/packages/api/src/ats/rejectreason/utils/index.ts b/packages/api/src/ats/rejectreason/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/rejectreason/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/scorecard/scorecard.controller.ts b/packages/api/src/ats/scorecard/scorecard.controller.ts deleted file mode 100644 index 045d8f115..000000000 --- a/packages/api/src/ats/scorecard/scorecard.controller.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { ScoreCardService } from './services/scorecard.service'; -import { - UnifiedAtsScorecardInput, - UnifiedAtsScorecardOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - - -@ApiTags('ats/scorecards') -@Controller('ats/scorecards') -export class ScoreCardController { - constructor( - private readonly scorecardService: ScoreCardService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(ScoreCardController.name); - } - - @ApiOperation({ - operationId: 'listAtsScorecard', - summary: 'List ScoreCards', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsScorecardOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getScoreCards( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.scorecardService.getScoreCards( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsScorecard', - summary: 'Retrieve Score Cards', - description: 'Retrieve Score Cards from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the scorecard you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsScorecardOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.scorecardService.getScoreCard( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/ats/scorecard/scorecard.module.ts b/packages/api/src/ats/scorecard/scorecard.module.ts deleted file mode 100644 index 1685340d3..000000000 --- a/packages/api/src/ats/scorecard/scorecard.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Module } from '@nestjs/common'; -import { ScoreCardController } from './scorecard.controller'; -import { ServiceRegistry } from './services/registry.service'; -import { ScoreCardService } from './services/scorecard.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Utils } from '@ats/@lib/@utils'; - -@Module({ - controllers: [ScoreCardController], - providers: [ - ScoreCardService, - - SyncService, - WebhookService, - - CoreUnification, - - FieldMappingService, - ServiceRegistry, - - IngestDataService, - Utils, - - /* PROVIDERS SERVICES */ - ], - exports: [SyncService], -}) -export class ScoreCardModule {} diff --git a/packages/api/src/ats/scorecard/services/registry.service.ts b/packages/api/src/ats/scorecard/services/registry.service.ts deleted file mode 100644 index 25a81ebd0..000000000 --- a/packages/api/src/ats/scorecard/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IScoreCardService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IScoreCardService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IScoreCardService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/scorecard/services/scorecard.service.ts b/packages/api/src/ats/scorecard/services/scorecard.service.ts deleted file mode 100644 index 6391606ec..000000000 --- a/packages/api/src/ats/scorecard/services/scorecard.service.ts +++ /dev/null @@ -1,243 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { - ScoreCardRecommendation, - UnifiedAtsScorecardOutput, -} from '../types/model.unified'; - -@Injectable() -export class ScoreCardService { - constructor(private prisma: PrismaService, private logger: LoggerService) { - this.logger.setContext(ScoreCardService.name); - } - - async getScoreCard( - id_ats_scorecard: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const scorecard = await this.prisma.ats_scorecards.findUnique({ - where: { - id_ats_scorecard: id_ats_scorecard, - }, - }); - - if (!scorecard) { - throw new Error(`ScoreCard with ID ${id_ats_scorecard} not found.`); - } - - // Fetch field mappings for the scorecard - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: scorecard.id_ats_scorecard, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsScorecardOutput format - const unifiedScoreCard: UnifiedAtsScorecardOutput = { - id: scorecard.id_ats_scorecard, - overall_recommendation: scorecard.overall_recommendation, - application_id: scorecard.id_ats_application, - interview_id: scorecard.id_ats_interview, - remote_created_at: String(scorecard.remote_created_at), - submitted_at: String(scorecard.submitted_at), - field_mappings: field_mappings, - remote_id: scorecard.remote_id, - created_at: scorecard.created_at, - modified_at: scorecard.modified_at, - }; - - let res: UnifiedAtsScorecardOutput = unifiedScoreCard; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: scorecard.id_ats_scorecard, - }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.scorecard.pull', - method: 'GET', - url: '/ats/scorecard', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return res; - } catch (error) { - throw error; - } - } - - async getScoreCards( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsScorecardOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = await this.prisma.ats_scorecards.findFirst({ - where: { - id_connection: connection_id, - id_ats_scorecard: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const scorecards = await this.prisma.ats_scorecards.findMany({ - take: limit + 1, - cursor: cursor - ? { - id_ats_scorecard: cursor, - } - : undefined, - orderBy: { - created_at: 'asc', - }, - where: {}, - }); - - if (scorecards.length === limit + 1) { - next_cursor = Buffer.from( - scorecards[scorecards.length - 1].id_ats_scorecard, - ).toString('base64'); - scorecards.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - - const unifiedScoreCards: UnifiedAtsScorecardOutput[] = await Promise.all( - scorecards.map(async (scorecard) => { - // Fetch field mappings for the scorecard - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: scorecard.id_ats_scorecard, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsScorecardOutput format - return { - id: scorecard.id_ats_scorecard, - overall_recommendation: scorecard.overall_recommendation, - application_id: scorecard.id_ats_application, - interview_id: scorecard.id_ats_interview, - remote_created_at: String(scorecard.remote_created_at), - submitted_at: String(scorecard.submitted_at), - field_mappings: field_mappings, - remote_id: scorecard.remote_id, - created_at: scorecard.created_at, - modified_at: scorecard.modified_at, - }; - }), - ); - - let res: UnifiedAtsScorecardOutput[] = unifiedScoreCards; - - if (remote_data) { - const remote_array_data: UnifiedAtsScorecardOutput[] = - await Promise.all( - res.map(async (scorecard) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: scorecard.id, - }, - }); - const remote_data = JSON.parse(resp.data); - return { ...scorecard, remote_data }; - }), - ); - - res = remote_array_data; - } - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.scorecard.pull', - method: 'GET', - url: '/ats/scorecards', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { - data: res, - prev_cursor, - next_cursor, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/scorecard/sync/sync.service.ts b/packages/api/src/ats/scorecard/sync/sync.service.ts deleted file mode 100644 index d86af665e..000000000 --- a/packages/api/src/ats/scorecard/sync/sync.service.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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 { IScoreCardService } from '../types'; -import { OriginalScoreCardOutput } from '@@core/utils/types/original/original.ats'; -import { UnifiedAtsScorecardOutput } from '../types/model.unified'; -import { ats_scorecards as AtsScoreCard } 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'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'scorecard', this); - } - onModuleInit() { -// - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IScoreCardService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: scorecard} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsScorecardOutput, - OriginalScoreCardOutput, - IScoreCardService - >(integrationId, linkedUserId, 'ats', 'scorecard', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - scoreCards: UnifiedAtsScorecardOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const scoreCards_results: AtsScoreCard[] = []; - - const updateOrCreateScoreCard = async ( - scoreCard: UnifiedAtsScorecardOutput, - originId: string, - ) => { - let existingScoreCard; - if (!originId) { - existingScoreCard = await this.prisma.ats_scorecards.findFirst({ - where: { - overall_recommendation: scoreCard.overall_recommendation, - id_ats_application: scoreCard.application_id, - }, - }); - } else { - existingScoreCard = await this.prisma.ats_scorecards.findFirst({ - where: { - remote_id: originId, - }, - }); - } - - const baseData: any = { - overall_recommendation: scoreCard.overall_recommendation ?? null, - application_id: scoreCard.application_id ?? null, - interview_id: scoreCard.interview_id ?? null, - remote_created_at: scoreCard.remote_created_at ?? null, - submitted_at: scoreCard.submitted_at ?? null, - modified_at: new Date(), - }; - - if (existingScoreCard) { - return await this.prisma.ats_scorecards.update({ - where: { - id_ats_scorecard: existingScoreCard.id_ats_scorecard, - }, - data: baseData, - }); - } else { - return await this.prisma.ats_scorecards.create({ - data: { - ...baseData, - id_ats_scorecard: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - }; - - for (let i = 0; i < scoreCards.length; i++) { - const scoreCard = scoreCards[i]; - const originId = scoreCard.remote_id; - - const res = await updateOrCreateScoreCard(scoreCard, originId); - const score_card_id = res.id_ats_scorecard; - scoreCards_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - scoreCard.field_mappings, - score_card_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - score_card_id, - remote_data[i], - ); - } - return scoreCards_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/scorecard/types/index.ts b/packages/api/src/ats/scorecard/types/index.ts deleted file mode 100644 index 1a770a668..000000000 --- a/packages/api/src/ats/scorecard/types/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { UnifiedAtsScorecardInput, UnifiedAtsScorecardOutput } from './model.unified'; -import { OriginalScoreCardOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface IScoreCardService extends IBaseObjectService { - addScoreCard( - scorecardData: DesunifyReturnType, - linkedUserId: string, - ): Promise>; - - sync(data: SyncParam): Promise>; -} - -export interface IScoreCardMapper { - desunify( - source: UnifiedAtsScorecardInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalScoreCardOutput | OriginalScoreCardOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/scorecard/types/model.unified.ts b/packages/api/src/ats/scorecard/types/model.unified.ts deleted file mode 100644 index e75a05c45..000000000 --- a/packages/api/src/ats/scorecard/types/model.unified.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsIn, -} from 'class-validator'; - -export type ScoreCardRecommendation = - | 'DEFINITELY_NO' - | 'NO' - | 'YES' - | 'STRONG_YES' - | 'NO_DECISION'; - -export class UnifiedAtsScorecardInput { - @ApiPropertyOptional({ - type: String, - // enum: ['DEFINITELY_NO', 'NO', 'YES', 'STRONG_YES', 'NO_DECISION'], - example: 'YES', - nullable: true, - description: 'The overall recommendation', - }) - //@IsIn(['DEFINITELY_NO', 'NO', 'YES', 'STRONG_YES', 'NO_DECISION']) - @IsOptional() - overall_recommendation?: ScoreCardRecommendation | string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the application', - }) - @IsUUID() - @IsOptional() - application_id?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the interview', - }) - @IsUUID() - @IsOptional() - interview_id?: string; - - @ApiPropertyOptional({ - type: String, - example: '2024-10-01T12:00:00Z', - format: 'date-time', - nullable: true, - description: 'The remote creation date of the scorecard', - }) - @IsDateString() - @IsOptional() - remote_created_at?: string; - - @ApiPropertyOptional({ - type: String, - example: '2024-10-01T12:00:00Z', - format: 'date-time', - nullable: true, - description: 'The submission date of the scorecard', - }) - @IsDateString() - @IsOptional() - submitted_at?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - additionalProperties: true, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsScorecardOutput extends UnifiedAtsScorecardInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - description: 'The UUID of the scorecard', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: - 'The remote ID of the scorecard in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: - 'The remote data of the scorecard in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The created date of the object', - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - nullable: true, - description: 'The modified date of the object', - }) - @IsOptional() - modified_at?: Date; -} diff --git a/packages/api/src/ats/scorecard/utils/index.ts b/packages/api/src/ats/scorecard/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/scorecard/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/tag/services/ashby/index.ts b/packages/api/src/ats/tag/services/ashby/index.ts deleted file mode 100644 index 410ca03bd..000000000 --- a/packages/api/src/ats/tag/services/ashby/index.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ITagService } from '@ats/tag/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 { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { ApiResponse } from '@@core/utils/types'; -import { ServiceRegistry } from '../registry.service'; -import { AshbyTagInput, AshbyTagOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalTagOutput } from '@@core/utils/types/original/original.ats'; -import { SyncParam } from '@@core/utils/types/interface'; - -@Injectable() -export class AshbyService implements ITagService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - AtsObject.tag.toUpperCase() + ':' + AshbyService.name, - ); - this.registry.registerService('ashby', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - vertical: 'ats', - }, - }); - const resp = await axios.post( - `${connection.account_url}/candidateTag.list`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${Buffer.from( - `${this.cryptoService.decrypt(connection.access_token)}:`, - ).toString('base64')}`, - }, - }, - ); - const tags: AshbyTagOutput[] = resp.data.results; - this.logger.log(`Synced ashby tags !`); - - return { - data: tags, - message: 'Ashby tags retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/tag/services/ashby/mappers.ts b/packages/api/src/ats/tag/services/ashby/mappers.ts deleted file mode 100644 index 5cb8d6c1a..000000000 --- a/packages/api/src/ats/tag/services/ashby/mappers.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { AshbyTagInput, AshbyTagOutput } from './types'; -import { - UnifiedAtsTagInput, - UnifiedAtsTagOutput, -} from '@ats/tag/types/model.unified'; -import { ITagMapper } from '@ats/tag/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 { - OriginalAttachmentOutput, - OriginalTagOutput, -} from '@@core/utils/types/original/original.ats'; -import { AtsObject } from '@ats/@lib/@types'; -import { UnifiedAtsAttachmentOutput } from '@ats/attachment/types/model.unified'; - -@Injectable() -export class AshbyTagMapper implements ITagMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('ats', 'tag', 'ashby', this); - } - - async desunify( - source: UnifiedAtsTagInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return; - } - - async unify( - source: AshbyTagOutput | AshbyTagOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - if (!Array.isArray(source)) { - return await this.mapSingleTagToUnified( - source, - connectionId, - customFieldMappings, - ); - } - // Handling array of AshbyTagOutput - return Promise.all( - source.map((tag) => - this.mapSingleTagToUnified(tag, connectionId, customFieldMappings), - ), - ); - } - - private async mapSingleTagToUnified( - tag: AshbyTagOutput, - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - return { - remote_id: tag.id, - remote_data: tag, - name: tag.title, - }; - } -} diff --git a/packages/api/src/ats/tag/services/ashby/types.ts b/packages/api/src/ats/tag/services/ashby/types.ts deleted file mode 100644 index fe38264db..000000000 --- a/packages/api/src/ats/tag/services/ashby/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface AshbyTagInput { - id: string; - title: string; - isArchived: boolean; -} - -export type AshbyTagOutput = Partial; diff --git a/packages/api/src/ats/tag/services/registry.service.ts b/packages/api/src/ats/tag/services/registry.service.ts deleted file mode 100644 index a7506a19e..000000000 --- a/packages/api/src/ats/tag/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ITagService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: ITagService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): ITagService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/tag/services/tag.service.ts b/packages/api/src/ats/tag/services/tag.service.ts deleted file mode 100644 index a0aaaf155..000000000 --- a/packages/api/src/ats/tag/services/tag.service.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { UnifiedAtsTagOutput } from '../types/model.unified'; -import { v4 as uuidv4 } from 'uuid'; -@Injectable() -export class TagService { - constructor(private prisma: PrismaService, private logger: LoggerService) { - this.logger.setContext(TagService.name); - } - - async getTag( - id_ats_candidate_tag: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const tag = await this.prisma.ats_candidate_tags.findUnique({ - where: { - id_ats_candidate_tag: id_ats_candidate_tag, - }, - }); - - if (!tag) { - throw new Error(`Tag with ID ${id_ats_candidate_tag} not found.`); - } - - // Fetch field mappings for the tag - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: tag.id_ats_candidate_tag, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsTagOutput format - const unifiedTag: UnifiedAtsTagOutput = { - id: tag.id_ats_candidate_tag, - name: tag.name, - field_mappings: field_mappings, - remote_id: tag.remote_id, - created_at: String(tag.created_at), - modified_at: String(tag.modified_at), - }; - - let res: UnifiedAtsTagOutput = unifiedTag; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: tag.id_ats_candidate_tag, - }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.tag.pull', - method: 'GET', - url: '/ats/tag', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return res; - } catch (error) { - throw error; - } - } - - async getTags( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsTagOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = await this.prisma.ats_candidate_tags.findFirst({ - where: { - id_connection: connection_id, - id_ats_candidate_tag: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const tags = await this.prisma.ats_candidate_tags.findMany({ - take: limit + 1, - cursor: cursor - ? { - id_ats_candidate_tag: cursor, - } - : undefined, - orderBy: { - created_at: 'asc', - }, - where: {}, - }); - - if (tags.length === limit + 1) { - next_cursor = Buffer.from( - tags[tags.length - 1].id_ats_candidate_tag, - ).toString('base64'); - tags.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - - const unifiedTags: UnifiedAtsTagOutput[] = await Promise.all( - tags.map(async (tag) => { - // Fetch field mappings for the tag - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: tag.id_ats_candidate_tag, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsTagOutput format - return { - id: tag.id_ats_candidate_tag, - name: tag.name, - remote_created_at: null, - remote_modified_at: null, - field_mappings: field_mappings, - remote_id: tag.remote_id, - created_at: String(tag.created_at), - modified_at: String(tag.modified_at), - }; - }), - ); - - let res: UnifiedAtsTagOutput[] = unifiedTags; - - if (remote_data) { - const remote_array_data: UnifiedAtsTagOutput[] = await Promise.all( - res.map(async (tag) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: tag.id, - }, - }); - const remote_data = JSON.parse(resp.data); - return { ...tag, remote_data }; - }), - ); - - res = remote_array_data; - } - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.tag.pull', - method: 'GET', - url: '/ats/tags', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { - data: res, - prev_cursor, - next_cursor, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/tag/sync/sync.service.ts b/packages/api/src/ats/tag/sync/sync.service.ts deleted file mode 100644 index eba9351ff..000000000 --- a/packages/api/src/ats/tag/sync/sync.service.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { BullQueueService } from '@@core/@core-services/queues/shared.service'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { OriginalTagOutput } from '@@core/utils/types/original/original.ats'; -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; -import { ATS_PROVIDERS, FILESTORAGE_PROVIDERS } from '@panora/shared'; -import { ats_candidate_tags as AtsTag } from '@prisma/client'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../services/registry.service'; -import { ITagService } from '../types'; -import { UnifiedAtsTagOutput } from '../types/model.unified'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'tag', this); - } - onModuleInit() { -// - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: ITagService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: tag} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsTagOutput, - OriginalTagOutput, - ITagService - >(integrationId, linkedUserId, 'ats', 'tag', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - tags: UnifiedAtsTagOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const tags_results: AtsTag[] = []; - - const updateOrCreateTag = async ( - tag: UnifiedAtsTagOutput, - originId: string, - ) => { - let existingTag; - if (!originId) { - existingTag = await this.prisma.ats_candidate_tags.findFirst({ - where: { - name: tag.name, - }, - }); - } else { - existingTag = await this.prisma.ats_candidate_tags.findFirst({ - where: { - remote_id: originId, - }, - }); - } - - const baseData: any = { - name: tag.name ?? null, - modified_at: new Date(), - }; - - if (existingTag) { - return await this.prisma.ats_candidate_tags.update({ - where: { - id_ats_candidate_tag: existingTag.id_ats_candidate_tag, - }, - data: baseData, - }); - } else { - return await this.prisma.ats_candidate_tags.create({ - data: { - ...baseData, - id_ats_candidate_tag: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - }; - - for (let i = 0; i < tags.length; i++) { - const tag = tags[i]; - const originId = tag.remote_id; - - const res = await updateOrCreateTag(tag, originId); - const tag_id = res.id_ats_candidate_tag; - tags_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - tag.field_mappings, - tag_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData(tag_id, remote_data[i]); - } - return tags_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/tag/tag.controller.ts b/packages/api/src/ats/tag/tag.controller.ts deleted file mode 100644 index ca6173b88..000000000 --- a/packages/api/src/ats/tag/tag.controller.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, -} from '@nestjs/swagger'; - -import { TagService } from './services/tag.service'; -import { UnifiedAtsTagInput, UnifiedAtsTagOutput } from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - - -@ApiTags('ats/tags') -@Controller('ats/tags') -export class TagController { - constructor( - private readonly tagService: TagService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(TagController.name); - } - - @ApiOperation({ - operationId: 'listAtsTags', - summary: 'List Tags', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsTagOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getTags( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.tagService.getTags( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsTag', - summary: 'Retrieve Tags', - description: 'Retrieve Tags from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the tag you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsTagOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.tagService.getTag( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/ats/tag/tag.module.ts b/packages/api/src/ats/tag/tag.module.ts deleted file mode 100644 index 72be05bf4..000000000 --- a/packages/api/src/ats/tag/tag.module.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Module } from '@nestjs/common'; -import { ServiceRegistry } from './services/registry.service'; -import { TagService } from './services/tag.service'; -import { SyncService } from './sync/sync.service'; -import { TagController } from './tag.controller'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { AshbyService } from './services/ashby'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { AshbyTagMapper } from './services/ashby/mappers'; -import { Utils } from '@ats/@lib/@utils'; - -@Module({ - controllers: [TagController], - providers: [ - TagService, - - SyncService, - WebhookService, - - Utils, - ServiceRegistry, - CoreUnification, - - IngestDataService, - - AshbyTagMapper, - /* PROVIDERS SERVICES */ - AshbyService, - ], - exports: [SyncService], -}) -export class TagModule {} diff --git a/packages/api/src/ats/tag/types/index.ts b/packages/api/src/ats/tag/types/index.ts deleted file mode 100644 index 131e9650d..000000000 --- a/packages/api/src/ats/tag/types/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { UnifiedAtsTagInput, UnifiedAtsTagOutput } from './model.unified'; -import { OriginalTagOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface ITagService extends IBaseObjectService { - sync(data: SyncParam): Promise>; -} - -export interface ITagMapper { - desunify( - source: UnifiedAtsTagInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalTagOutput | OriginalTagOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/tag/types/model.unified.ts b/packages/api/src/ats/tag/types/model.unified.ts deleted file mode 100644 index f3067eb61..000000000 --- a/packages/api/src/ats/tag/types/model.unified.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsUUID, IsOptional, IsString, IsDateString } from 'class-validator'; - -export class UnifiedAtsTagInput { - @ApiPropertyOptional({ - type: String, - example: 'Important', - nullable: true, - description: 'The name of the tag', - }) - @IsString() - @IsOptional() - name?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the candidate', - }) - @IsUUID() - @IsOptional() - id_ats_candidate?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - additionalProperties: true, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsTagOutput extends UnifiedAtsTagInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the tag', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: 'The remote ID of the tag in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - nullable: true, - additionalProperties: true, - description: 'The remote data of the tag in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - nullable: true, - example: '2024-10-01T12:00:00Z', - description: 'The creation date of the tag', - }) - @IsDateString() - @IsOptional() - created_at?: string; - - @ApiPropertyOptional({ - type: Date, - nullable: true, - example: '2024-10-01T12:00:00Z', - description: 'The modification date of the tag', - }) - @IsDateString() - @IsOptional() - modified_at?: string; -} diff --git a/packages/api/src/ats/tag/utils/index.ts b/packages/api/src/ats/tag/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/tag/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ats/user/services/ashby/index.ts b/packages/api/src/ats/user/services/ashby/index.ts deleted file mode 100644 index 418605db6..000000000 --- a/packages/api/src/ats/user/services/ashby/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -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 { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { ApiResponse } from '@@core/utils/types'; -import { ServiceRegistry } from '../registry.service'; -import { AshbyUserInput, AshbyUserOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalUserOutput } from '@@core/utils/types/original/original.ats'; -import { SyncParam } from '@@core/utils/types/interface'; - -@Injectable() -export class AshbyService implements IUserService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - AtsObject.user.toUpperCase() + ':' + AshbyService.name, - ); - this.registry.registerService('ashby', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'ashby', - 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: AshbyUserOutput[] = resp.data.results; - this.logger.log(`Synced ashby users !`); - - return { - data: users, - message: 'Ashby users retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/user/services/ashby/mappers.ts b/packages/api/src/ats/user/services/ashby/mappers.ts deleted file mode 100644 index 7354f0b4a..000000000 --- a/packages/api/src/ats/user/services/ashby/mappers.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { AshbyUserInput, AshbyUserOutput } from './types'; -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'; - -@Injectable() -export class AshbyUserMapper implements IUserMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('ats', 'user', 'ashby', 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: AshbyUserOutput | AshbyUserOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise { - if (!Array.isArray(source)) { - return await this.mapSingleUserToUnified( - source, - connectionId, - customFieldMappings, - ); - } - // Handling array of AshbyUserOutput - return Promise.all( - source.map((user) => - this.mapSingleUserToUnified(user, connectionId, customFieldMappings), - ), - ); - } - - private async mapSingleUserToUnified( - user: AshbyUserOutput, - 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/ashby/types.ts b/packages/api/src/ats/user/services/ashby/types.ts deleted file mode 100644 index 8f0d91c20..000000000 --- a/packages/api/src/ats/user/services/ashby/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface AshbyUserInput { - id: string; - firstName: string; - lastName: string; - email: string; - globalRole: string; - isEnabled: boolean; - updatedAt: string; -} - -export type AshbyUserOutput = Partial; diff --git a/packages/api/src/ats/user/services/registry.service.ts b/packages/api/src/ats/user/services/registry.service.ts deleted file mode 100644 index 52ee80849..000000000 --- a/packages/api/src/ats/user/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IUserService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IUserService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IUserService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/ats/user/services/user.service.ts b/packages/api/src/ats/user/services/user.service.ts deleted file mode 100644 index 995c940d9..000000000 --- a/packages/api/src/ats/user/services/user.service.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { UnifiedAtsUserOutput, UserAccessRole } from '../types/model.unified'; - -@Injectable() -export class UserService { - constructor(private prisma: PrismaService, private logger: LoggerService) { - this.logger.setContext(UserService.name); - } - - async getUser( - id_ats_user: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const user = await this.prisma.ats_users.findUnique({ - where: { - id_ats_user: id_ats_user, - }, - }); - - if (!user) { - throw new Error(`User with ID ${id_ats_user} not found.`); - } - - // Fetch field mappings for the user - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: user.id_ats_user, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsUserOutput format - const unifiedUser: UnifiedAtsUserOutput = { - id: user.id_ats_user, - first_name: user.first_name, - last_name: user.last_name, - email: user.email, - disabled: user.disabled, - access_role: user.access_role, - remote_created_at: String(user.remote_created_at), - remote_modified_at: String(user.remote_modified_at), - field_mappings: field_mappings, - remote_id: user.remote_id, - created_at: user.created_at, - modified_at: user.modified_at, - }; - - let res: UnifiedAtsUserOutput = unifiedUser; - if (remote_data) { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: user.id_ats_user, - }, - }); - const remote_data = JSON.parse(resp.data); - - res = { - ...res, - remote_data: remote_data, - }; - } - await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: 'success', - type: 'ats.user.pull', - method: 'GET', - url: '/ats/user', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return res; - } catch (error) { - throw error; - } - } - - async getUsers( - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedAtsUserOutput[]; - prev_cursor: null | string; - next_cursor: null | string; - }> { - try { - let prev_cursor = null; - let next_cursor = null; - - if (cursor) { - const isCursorPresent = await this.prisma.ats_users.findFirst({ - where: { - id_connection: connection_id, - id_ats_user: cursor, - }, - }); - if (!isCursorPresent) { - throw new ReferenceError(`The provided cursor does not exist!`); - } - } - - const users = await this.prisma.ats_users.findMany({ - take: limit + 1, - cursor: cursor - ? { - id_ats_user: cursor, - } - : undefined, - orderBy: { - created_at: 'asc', - }, - where: {}, - }); - - if (users.length === limit + 1) { - next_cursor = Buffer.from(users[users.length - 1].id_ats_user).toString( - 'base64', - ); - users.pop(); - } - - if (cursor) { - prev_cursor = Buffer.from(cursor).toString('base64'); - } - const unifiedUsers: UnifiedAtsUserOutput[] = await Promise.all( - users.map(async (user) => { - // Fetch field mappings for the user - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: user.id_ats_user, - }, - }, - include: { - attribute: true, - }, - }); - - // Create a map to store unique field mappings - const fieldMappingsMap = new Map(); - - values.forEach((value) => { - fieldMappingsMap.set(value.attribute.slug, value.data); - }); - - // Convert the map to an array of objects - const field_mappings = Object.fromEntries(fieldMappingsMap); - - // Transform to UnifiedAtsUserOutput format - return { - id: user.id_ats_user, - first_name: user.first_name, - last_name: user.last_name, - email: user.email, - disabled: user.disabled, - access_role: user.access_role, - remote_created_at: String(user.remote_created_at), - remote_modified_at: String(user.remote_modified_at), - field_mappings: field_mappings, - remote_id: user.remote_id, - created_at: user.created_at, - modified_at: user.modified_at, - }; - }), - ); - - let res: UnifiedAtsUserOutput[] = unifiedUsers; - - if (remote_data) { - const remote_array_data: UnifiedAtsUserOutput[] = await Promise.all( - res.map(async (user) => { - const resp = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: user.id, - }, - }); - const remote_data = JSON.parse(resp.data); - return { ...user, remote_data }; - }), - ); - - res = remote_array_data; - } - await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: 'success', - type: 'ats.user.pull', - method: 'GET', - url: '/ats/users', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - return { - data: res, - prev_cursor, - next_cursor, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/user/sync/sync.service.ts b/packages/api/src/ats/user/sync/sync.service.ts deleted file mode 100644 index abd89aabf..000000000 --- a/packages/api/src/ats/user/sync/sync.service.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private bullQueueService: BullQueueService, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('ats', 'user', this); - } - onModuleInit() { -// - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = ATS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IUserService = - this.serviceRegistry.getService(integrationId); - if (!service) { - this.logger.log( - `No service found in {vertical:ats, commonObject: user} for integration ID: ${integrationId}`, - ); - return; - } - - await this.ingestService.syncForLinkedUser< - UnifiedAtsUserOutput, - OriginalUserOutput, - IUserService - >(integrationId, linkedUserId, 'ats', 'user', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - users: UnifiedAtsUserOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const users_results: AtsUser[] = []; - - const updateOrCreateUser = async ( - user: UnifiedAtsUserOutput, - originId: string, - ) => { - const existingUser = await this.prisma.ats_users.findFirst({ - where: { - remote_id: originId, - }, - }); - - const baseData: any = { - first_name: user.first_name ?? null, - last_name: user.last_name ?? null, - email: user.email ?? null, - disabled: user.disabled ?? null, - access_role: user.access_role ?? null, - remote_created_at: user.remote_created_at ?? null, - remote_modified_at: user.remote_modified_at ?? null, - modified_at: new Date(), - }; - - if (existingUser) { - return await this.prisma.ats_users.update({ - where: { - id_ats_user: existingUser.id_ats_user, - }, - data: baseData, - }); - } else { - return await this.prisma.ats_users.create({ - data: { - ...baseData, - id_ats_user: uuidv4(), - created_at: new Date(), - remote_id: originId, - }, - }); - } - }; - - for (let i = 0; i < users.length; i++) { - const user = users[i]; - const originId = user.remote_id; - - if (!originId || originId === '') { - throw new ReferenceError(`Origin id not there, found ${originId}`); - } - - const res = await updateOrCreateUser(user, originId); - const user_id = res.id_ats_user; - users_results.push(res); - - // Process field mappings - await this.ingestService.processFieldMappings( - user.field_mappings, - user_id, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData(user_id, remote_data[i]); - } - return users_results; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/ats/user/types/index.ts b/packages/api/src/ats/user/types/index.ts deleted file mode 100644 index 4d5f82ebe..000000000 --- a/packages/api/src/ats/user/types/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { UnifiedAtsUserInput, UnifiedAtsUserOutput } from './model.unified'; -import { OriginalUserOutput } from '@@core/utils/types/original/original.ats'; -import { ApiResponse } from '@@core/utils/types'; -import { IBaseObjectService, SyncParam } from '@@core/utils/types/interface'; - -export interface IUserService extends IBaseObjectService { - sync(data: SyncParam): Promise>; -} - -export interface IUserMapper { - desunify( - source: UnifiedAtsUserInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalUserOutput | OriginalUserOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/ats/user/types/model.unified.ts b/packages/api/src/ats/user/types/model.unified.ts deleted file mode 100644 index 6b7f06c0e..000000000 --- a/packages/api/src/ats/user/types/model.unified.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsBoolean, - IsDateString, - IsIn, -} from 'class-validator'; - -export type UserAccessRole = - | 'SUPER_ADMIN' - | 'ADMIN' - | 'TEAM_MEMBER' - | 'LIMITED_TEAM_MEMBER' - | 'INTERVIEWER'; - -export class UnifiedAtsUserInput { - @ApiPropertyOptional({ - type: String, - example: 'John', - description: 'The first name of the user', - nullable: true, - }) - @IsString() - @IsOptional() - first_name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'Doe', - description: 'The last name of the user', - nullable: true, - }) - @IsString() - @IsOptional() - last_name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'john.doe@example.com', - description: 'The email of the user', - nullable: true, - }) - @IsString() - @IsOptional() - email?: string; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - description: 'Whether the user is disabled', - nullable: true, - }) - @IsBoolean() - @IsOptional() - disabled?: boolean; - - @ApiPropertyOptional({ - type: String, - example: 'ADMIN', - /* enum: [ - 'SUPER_ADMIN', - 'ADMIN', - 'TEAM_MEMBER', - 'LIMITED_TEAM_MEMBER', - 'INTERVIEWER', - ],*/ - description: 'The access role of the user', - nullable: true, - }) - /*@IsIn([ - 'SUPER_ADMIN', - 'ADMIN', - 'TEAM_MEMBER', - 'LIMITED_TEAM_MEMBER', - 'INTERVIEWER', - ])*/ - @IsOptional() - access_role?: UserAccessRole | string; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - description: 'The remote creation date of the user', - nullable: true, - }) - @IsDateString() - @IsOptional() - remote_created_at?: string; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - description: 'The remote modification date of the user', - nullable: true, - }) - @IsDateString() - @IsOptional() - remote_modified_at?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - nullable: true, - additionalProperties: true, - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedAtsUserOutput extends UnifiedAtsUserInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - description: 'The UUID of the user', - nullable: true, - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - description: 'The remote ID of the user in the context of the 3rd Party', - nullable: true, - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - fav_dish: 'broccoli', - fav_color: 'red', - }, - description: 'The remote data of the user in the context of the 3rd Party', - nullable: true, - additionalProperties: true, - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - description: 'The created date of the object', - nullable: true, - }) - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - example: '2024-10-01T12:00:00Z', - type: Date, - description: 'The modified date of the object', - nullable: true, - }) - @IsOptional() - modified_at?: Date; -} diff --git a/packages/api/src/ats/user/user.controller.ts b/packages/api/src/ats/user/user.controller.ts deleted file mode 100644 index 9b2b50fcb..000000000 --- a/packages/api/src/ats/user/user.controller.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { UserService } from './services/user.service'; -import { - UnifiedAtsUserInput, - UnifiedAtsUserOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('ats/users') -@Controller('ats/users') -export class UserController { - constructor( - private readonly userService: UserService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(UserController.name); - } - - @ApiOperation({ - operationId: 'listAtsUsers', - summary: 'List Users', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedAtsUserOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - @UsePipes( - new ValidationPipe({ - transform: true, - transformOptions: { enableImplicitConversion: true }, - }), - ) - async getUsers( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.userService.getUsers( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveAtsUser', - summary: 'Retrieve Users', - description: 'Retrieve Users from any connected Ats software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the user you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Ats software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedAtsUserOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.userService.getUser( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/ats/user/user.module.ts b/packages/api/src/ats/user/user.module.ts deleted file mode 100644 index a92806ad7..000000000 --- a/packages/api/src/ats/user/user.module.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Module } from '@nestjs/common'; -import { ServiceRegistry } from './services/registry.service'; -import { UserService } from './services/user.service'; -import { SyncService } from './sync/sync.service'; -import { UserController } from './user.controller'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { AshbyService } from './services/ashby'; - -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { AshbyUserMapper } from './services/ashby/mappers'; -import { Utils } from '@ats/@lib/@utils'; - -@Module({ - controllers: [UserController], - providers: [ - UserService, - - SyncService, - WebhookService, - - ServiceRegistry, - - IngestDataService, - Utils, - - AshbyUserMapper, - /* PROVIDERS SERVICES */ - AshbyService, - ], - exports: [SyncService], -}) -export class UserModule {} diff --git a/packages/api/src/ats/user/utils/index.ts b/packages/api/src/ats/user/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/ats/user/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ecommerce/fulfillmentorders/sync/sync.service.ts b/packages/api/src/ecommerce/fulfillmentorders/sync/sync.service.ts index 286afc060..cc62dc746 100644 --- a/packages/api/src/ecommerce/fulfillmentorders/sync/sync.service.ts +++ b/packages/api/src/ecommerce/fulfillmentorders/sync/sync.service.ts @@ -10,7 +10,8 @@ import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; import { OriginalFulfillmentOrdersOutput } from '@@core/utils/types/original/original.ecommerce'; import { Injectable, OnModuleInit } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; -import { ATS_PROVIDERS, ECOMMERCE_PROVIDERS } from '@panora/shared'; +// The following line are commented because they use code from the ATS Module, which was removed from the project +// import { ATS_PROVIDERS, ECOMMERCE_PROVIDERS } from '@panora/shared'; import { v4 as uuidv4 } from 'uuid'; import { ServiceRegistry } from '../services/registry.service'; import { IFulfillmentOrdersService } from '../types'; diff --git a/packages/api/src/hris/@lib/@types/index.ts b/packages/api/src/hris/@lib/@types/index.ts deleted file mode 100644 index 152fe0763..000000000 --- a/packages/api/src/hris/@lib/@types/index.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { ICompanyService } from '@crm/company/types'; -import { IBankInfoService } from '@hris/bankinfo/types'; -import { - UnifiedHrisBankinfoInput, - UnifiedHrisBankinfoOutput, -} from '@hris/bankinfo/types/model.unified'; -import { IBenefitService } from '@hris/benefit/types'; -import { - UnifiedHrisBenefitInput, - UnifiedHrisBenefitOutput, -} from '@hris/benefit/types/model.unified'; -import { - UnifiedHrisCompanyInput, - UnifiedHrisCompanyOutput, -} from '@hris/company/types/model.unified'; -import { IDependentService } from '@hris/dependent/types'; -import { - UnifiedHrisDependentInput, - UnifiedHrisDependentOutput, -} from '@hris/dependent/types/model.unified'; -import { IEmployeeService } from '@hris/employee/types'; -import { - UnifiedHrisEmployeeInput, - UnifiedHrisEmployeeOutput, -} from '@hris/employee/types/model.unified'; -import { IEmployeePayrollRunService } from '@hris/employeepayrollrun/types'; -import { - UnifiedHrisEmployeepayrollrunInput, - UnifiedHrisEmployeepayrollrunOutput, -} from '@hris/employeepayrollrun/types/model.unified'; -import { IEmployerBenefitService } from '@hris/employerbenefit/types'; -import { - UnifiedHrisEmployerbenefitInput, - UnifiedHrisEmployerbenefitOutput, -} from '@hris/employerbenefit/types/model.unified'; -import { IEmploymentService } from '@hris/employment/types'; -import { - UnifiedHrisEmploymentInput, - UnifiedHrisEmploymentOutput, -} from '@hris/employment/types/model.unified'; -import { - UnifiedHrisGroupInput, - UnifiedHrisGroupOutput, -} from '@hris/group/types/model.unified'; -import { ILocationService } from '@hris/location/types'; -import { - UnifiedHrisLocationInput, - UnifiedHrisLocationOutput, -} from '@hris/location/types/model.unified'; -import { IPayGroupService } from '@hris/paygroup/types'; -import { - UnifiedHrisPaygroupInput, - UnifiedHrisPaygroupOutput, -} from '@hris/paygroup/types/model.unified'; -import { IPayrollRunService } from '@hris/payrollrun/types'; -import { - UnifiedHrisPayrollrunInput, - UnifiedHrisPayrollrunOutput, -} from '@hris/payrollrun/types/model.unified'; -import { ITimeoffService } from '@hris/timeoff/types'; -import { - UnifiedHrisTimeoffInput, - UnifiedHrisTimeoffOutput, -} from '@hris/timeoff/types/model.unified'; -import { ITimeoffBalanceService } from '@hris/timeoffbalance/types'; -import { - UnifiedHrisTimeoffbalanceInput, - UnifiedHrisTimeoffbalanceOutput, -} from '@hris/timeoffbalance/types/model.unified'; -import { ITimesheetentryService } from '@hris/timesheetentry/types'; -import { - UnifiedHrisTimesheetEntryInput, - UnifiedHrisTimesheetEntryOutput, -} from '@hris/timesheetentry/types/model.unified'; - -export enum HrisObject { - bankinfo = 'bankinfo', - benefit = 'benefit', - company = 'company', - dependent = 'dependent', - employee = 'employee', - employeepayrollrun = 'employeepayrollrun', - employerbenefit = 'employerbenefit', - employment = 'employment', - group = 'group', - location = 'location', - paygroup = 'paygroup', - payrollrun = 'payrollrun', - timeoff = 'timeoff', - timeoffbalance = 'timeoffbalance', - timesheetentry = 'timesheetentry', -} - -export type UnifiedHris = - | UnifiedHrisBankinfoInput - | UnifiedHrisBankinfoOutput - | UnifiedHrisBenefitInput - | UnifiedHrisBenefitOutput - | UnifiedHrisCompanyInput - | UnifiedHrisCompanyOutput - | UnifiedHrisEmployeepayrollrunInput - | UnifiedHrisEmployeepayrollrunOutput - | UnifiedHrisEmployeeInput - | UnifiedHrisEmployeeOutput - | UnifiedHrisDependentInput - | UnifiedHrisDependentOutput - | UnifiedHrisTimeoffInput - | UnifiedHrisTimeoffOutput - | UnifiedHrisTimeoffbalanceInput - | UnifiedHrisTimeoffbalanceOutput - | UnifiedHrisPayrollrunInput - | UnifiedHrisPayrollrunOutput - | UnifiedHrisEmployerbenefitInput - | UnifiedHrisEmployerbenefitOutput - | UnifiedHrisEmploymentInput - | UnifiedHrisEmploymentOutput - | UnifiedHrisGroupInput - | UnifiedHrisGroupOutput - | UnifiedHrisLocationInput - | UnifiedHrisLocationOutput - | UnifiedHrisPaygroupInput - | UnifiedHrisPaygroupOutput - | UnifiedHrisTimesheetEntryInput - | UnifiedHrisTimesheetEntryOutput; - -export type IHrisService = - | IBankInfoService - | IBenefitService - | ICompanyService - | IDependentService - | IEmployeeService - | IEmployeePayrollRunService - | IEmployerBenefitService - | IEmploymentService - | ITimeoffService - | ITimeoffBalanceService - | IPayrollRunService - | IPayGroupService - | ILocationService - | ITimesheetentryService; diff --git a/packages/api/src/hris/@lib/@unification/index.ts b/packages/api/src/hris/@lib/@unification/index.ts deleted file mode 100644 index 7ece6c3dc..000000000 --- a/packages/api/src/hris/@lib/@unification/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { HrisObject } from '@hris/@lib/@types'; -import { Unified, UnifyReturnType } from '@@core/utils/types'; -import { UnifySourceType } from '@@core/utils/types/unify.output'; -import { HrisObjectInput } from '@@core/utils/types/original/original.hris'; -import { IUnification } from '@@core/utils/types/interface'; -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { UnificationRegistry } from '@@core/@core-services/registries/unification.registry'; -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class HrisUnificationService implements IUnification { - constructor( - private registry: UnificationRegistry, - private mappersRegistry: MappersRegistry, - ) { - this.registry.registerService('hris', this); - } - - async desunify({ - sourceObject, - targetType_, - providerName, - customFieldMappings, - }: { - sourceObject: T; - targetType_: HrisObject; - providerName: string; - customFieldMappings?: { - slug: string; - remote_id: string; - }[]; - }): Promise { - const mapping = this.mappersRegistry.getService( - 'hris', - targetType_, - providerName, - ); - - if (mapping) { - return mapping.desunify(sourceObject, customFieldMappings); - } - - throw new Error( - `Unsupported target type for ${providerName}: ${targetType_}`, - ); - } - - async unify({ - sourceObject, - targetType_, - providerName, - connectionId, - customFieldMappings, - }: { - sourceObject: T; - targetType_: HrisObject; - providerName: string; - connectionId: string; - customFieldMappings?: { - slug: string; - remote_id: string; - }[]; - }): Promise { - const mapping = this.mappersRegistry.getService( - 'hris', - targetType_, - providerName, - ); - - if (mapping) { - return mapping.unify(sourceObject, connectionId, customFieldMappings); - } - - throw new Error( - `Unsupported target type for ${providerName}: ${targetType_}`, - ); - } -} diff --git a/packages/api/src/hris/@lib/@utils/index.ts b/packages/api/src/hris/@lib/@utils/index.ts deleted file mode 100644 index d4b9be7af..000000000 --- a/packages/api/src/hris/@lib/@utils/index.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; - -@Injectable() -export class Utils { - constructor(private readonly prisma: PrismaService) {} - - async getEmployeeUuidFromRemoteId(id: string, connection_id: string) { - try { - const res = await this.prisma.hris_employees.findFirst({ - where: { - remote_id: id, - id_connection: connection_id, - }, - }); - if (!res) return; - return res.id_hris_employee; - } catch (error) { - throw error; - } - } - - async getCompanyUuidFromRemoteId(id: string, connection_id: string) { - try { - const res = await this.prisma.hris_companies.findFirst({ - where: { - remote_id: id, - id_connection: connection_id, - }, - }); - if (!res) return; - return res.id_hris_company; - } catch (error) { - throw error; - } - } - - async getGroupUuidFromRemoteId(id: string, connection_id: string) { - try { - const res = await this.prisma.hris_groups.findFirst({ - where: { - remote_id: id, - id_connection: connection_id, - }, - }); - if (!res) return; - return res.id_hris_group; - } catch (error) { - throw error; - } - } - - async getEmployerBenefitUuidFromRemoteId(id: string, connection_id: string) { - try { - const res = await this.prisma.hris_employer_benefits.findFirst({ - where: { - remote_id: id, - id_connection: connection_id, - }, - }); - if (!res) return; - return res.id_hris_employer_benefit; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/bankinfo/bankinfo.controller.ts b/packages/api/src/hris/bankinfo/bankinfo.controller.ts deleted file mode 100644 index b1c2a6db7..000000000 --- a/packages/api/src/hris/bankinfo/bankinfo.controller.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { BankInfoService } from './services/bankinfo.service'; -import { - UnifiedHrisBankinfoInput, - UnifiedHrisBankinfoOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('hris/bankinfos') -@Controller('hris/bankinfos') -export class BankinfoController { - constructor( - private readonly bankInfoService: BankInfoService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(BankinfoController.name); - } - - @ApiOperation({ - operationId: 'listHrisBankInfo', - summary: 'List Bank Info', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisBankinfoOutput) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getBankInfo( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.bankInfoService.getBankinfos( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisBankInfo', - summary: 'Retrieve Bank Info', - description: 'Retrieve Bank Info from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the bank info you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisBankinfoOutput) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.bankInfoService.getBankinfo( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/hris/bankinfo/bankinfo.module.ts b/packages/api/src/hris/bankinfo/bankinfo.module.ts deleted file mode 100644 index a18499a54..000000000 --- a/packages/api/src/hris/bankinfo/bankinfo.module.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Module } from '@nestjs/common'; -import { BankinfoController } from './bankinfo.controller'; -import { SyncService } from './sync/sync.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { BankInfoService } from './services/bankinfo.service'; -import { ServiceRegistry } from './services/registry.service'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; - -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { BullModule } from '@nestjs/bull'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; - -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; - -@Module({ - controllers: [BankinfoController], - providers: [ - BankInfoService, - - SyncService, - WebhookService, - - ServiceRegistry, - - IngestDataService, - CoreUnification, - - /* PROVIDERS SERVICES */ - ], - exports: [SyncService], -}) -export class BankInfoModule {} diff --git a/packages/api/src/hris/bankinfo/services/bankinfo.service.ts b/packages/api/src/hris/bankinfo/services/bankinfo.service.ts deleted file mode 100644 index 87de1f944..000000000 --- a/packages/api/src/hris/bankinfo/services/bankinfo.service.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { Injectable } from '@nestjs/common'; -import { UnifiedHrisBankinfoOutput } from '../types/model.unified'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ServiceRegistry } from './registry.service'; -import { v4 as uuidv4 } from 'uuid'; - -@Injectable() -export class BankInfoService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(BankInfoService.name); - } - - async getBankinfo( - id_hris_bank_info: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const bankInfo = await this.prisma.hris_bank_infos.findUnique({ - where: { id_hris_bank_info: id_hris_bank_info }, - }); - - if (!bankInfo) { - throw new Error(`Bank info with ID ${id_hris_bank_info} not found.`); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: bankInfo.id_hris_bank_info }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedBankInfo: UnifiedHrisBankinfoOutput = { - id: bankInfo.id_hris_bank_info, - account_type: bankInfo.account_type, - bank_name: bankInfo.bank_name, - account_number: bankInfo.account_number, - routing_number: bankInfo.routing_number, - employee_id: bankInfo.id_hris_employee, - field_mappings: field_mappings, - remote_id: bankInfo.remote_id, - remote_created_at: bankInfo.remote_created_at, - created_at: bankInfo.created_at, - modified_at: bankInfo.modified_at, - remote_was_deleted: bankInfo.remote_was_deleted, - }; - - const res: UnifiedHrisBankinfoOutput = unifiedBankInfo; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: bankInfo.id_hris_bank_info }, - }); - res.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.bankinfo.pull', - method: 'GET', - url: '/hris/bankinfo', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return res; - } catch (error) { - throw error; - } - } - - async getBankinfos( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisBankinfoOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const bankInfos = await this.prisma.hris_bank_infos.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_bank_info: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }); - - const hasNextPage = bankInfos.length > limit; - if (hasNextPage) bankInfos.pop(); - - const unifiedBankInfos = await Promise.all( - bankInfos.map(async (bankInfo) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: bankInfo.id_hris_bank_info }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedBankInfo: UnifiedHrisBankinfoOutput = { - id: bankInfo.id_hris_bank_info, - account_type: bankInfo.account_type, - bank_name: bankInfo.bank_name, - account_number: bankInfo.account_number, - routing_number: bankInfo.routing_number, - employee_id: bankInfo.id_hris_employee, - field_mappings: field_mappings, - remote_id: bankInfo.remote_id, - remote_created_at: bankInfo.remote_created_at, - created_at: bankInfo.created_at, - modified_at: bankInfo.modified_at, - remote_was_deleted: bankInfo.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: bankInfo.id_hris_bank_info }, - }); - unifiedBankInfo.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedBankInfo; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.bankinfo.pull', - method: 'GET', - url: '/hris/bankinfos', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedBankInfos, - next_cursor: hasNextPage - ? bankInfos[bankInfos.length - 1].id_hris_bank_info - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/bankinfo/services/registry.service.ts b/packages/api/src/hris/bankinfo/services/registry.service.ts deleted file mode 100644 index 14497f351..000000000 --- a/packages/api/src/hris/bankinfo/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IBankInfoService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IBankInfoService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IBankInfoService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/bankinfo/sync/sync.service.ts b/packages/api/src/hris/bankinfo/sync/sync.service.ts deleted file mode 100644 index d889bbfa6..000000000 --- a/packages/api/src/hris/bankinfo/sync/sync.service.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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 { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { UnifiedHrisBankinfoOutput } from '../types/model.unified'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_bank_infos as HrisBankInfo } from '@prisma/client'; -import { OriginalBankInfoOutput } from '@@core/utils/types/original/original.hris'; -import { IBankInfoService } from '../types'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'bankinfo', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IBankInfoService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisBankinfoOutput, - OriginalBankInfoOutput, - IBankInfoService - >(integrationId, linkedUserId, 'hris', 'bankinfo', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - bankInfos: UnifiedHrisBankinfoOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const bankInfoResults: HrisBankInfo[] = []; - - for (let i = 0; i < bankInfos.length; i++) { - const bankInfo = bankInfos[i]; - const originId = bankInfo.remote_id; - - let existingBankInfo = await this.prisma.hris_bank_infos.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const bankInfoData = { - account_type: bankInfo.account_type, - bank_name: bankInfo.bank_name, - account_number: bankInfo.account_number, - routing_number: bankInfo.routing_number, - id_hris_employee: bankInfo.employee_id, - remote_id: originId, - remote_created_at: bankInfo.remote_created_at - ? new Date(bankInfo.remote_created_at) - : null, - modified_at: new Date(), - remote_was_deleted: bankInfo.remote_was_deleted, - }; - - if (existingBankInfo) { - existingBankInfo = await this.prisma.hris_bank_infos.update({ - where: { id_hris_bank_info: existingBankInfo.id_hris_bank_info }, - data: bankInfoData, - }); - } else { - existingBankInfo = await this.prisma.hris_bank_infos.create({ - data: { - ...bankInfoData, - id_hris_bank_info: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - bankInfoResults.push(existingBankInfo); - - // Process field mappings - await this.ingestService.processFieldMappings( - bankInfo.field_mappings, - existingBankInfo.id_hris_bank_info, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingBankInfo.id_hris_bank_info, - remote_data[i], - ); - } - - return bankInfoResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/bankinfo/types/index.ts b/packages/api/src/hris/bankinfo/types/index.ts deleted file mode 100644 index c1eb22cf9..000000000 --- a/packages/api/src/hris/bankinfo/types/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisBankinfoInput, - UnifiedHrisBankinfoOutput, -} from './model.unified'; -import { OriginalBankInfoOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface IBankInfoService { - sync(data: SyncParam): Promise>; -} - -export interface IBankinfoMapper { - desunify( - source: UnifiedHrisBankinfoInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalBankInfoOutput | OriginalBankInfoOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/hris/bankinfo/types/model.unified.ts b/packages/api/src/hris/bankinfo/types/model.unified.ts deleted file mode 100644 index 750acc65a..000000000 --- a/packages/api/src/hris/bankinfo/types/model.unified.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsBoolean, -} from 'class-validator'; - -export type AccountType = 'SAVINGS' | 'CHECKING'; - -export class UnifiedHrisBankinfoInput { - @ApiPropertyOptional({ - type: String, - example: 'CHECKING', - // enum: ['SAVINGS', 'CHECKING'], - nullable: true, - description: 'The type of the bank account', - }) - @IsString() - @IsOptional() - account_type?: AccountType | string; - - @ApiPropertyOptional({ - type: String, - example: 'Bank of America', - nullable: true, - description: 'The name of the bank', - }) - @IsString() - @IsOptional() - bank_name?: string; - - @ApiPropertyOptional({ - type: String, - example: '1234567890', - nullable: true, - description: 'The account number', - }) - @IsString() - @IsOptional() - account_number?: string; - - @ApiPropertyOptional({ - type: String, - example: '021000021', - nullable: true, - description: 'The routing number of the bank', - }) - @IsString() - @IsOptional() - routing_number?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the associated employee', - }) - @IsUUID() - @IsOptional() - employee_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisBankinfoOutput extends UnifiedHrisBankinfoInput { - @ApiProperty({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the bank info record', - }) - @IsUUID() - id: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: - 'The remote ID of the bank info in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the bank info in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: - 'The date when the bank info was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at: Date; - - @ApiProperty({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The created date of the bank info record', - }) - @IsDateString() - created_at: Date; - - @ApiProperty({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The last modified date of the bank info record', - }) - @IsDateString() - modified_at: Date; - - @ApiProperty({ - type: Boolean, - example: false, - nullable: true, - description: 'Indicates if the bank info was deleted in the remote system', - }) - @IsBoolean() - remote_was_deleted: boolean; -} diff --git a/packages/api/src/hris/bankinfo/utils/index.ts b/packages/api/src/hris/bankinfo/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/bankinfo/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/benefit/benefit.controller.ts b/packages/api/src/hris/benefit/benefit.controller.ts deleted file mode 100644 index b19e25efc..000000000 --- a/packages/api/src/hris/benefit/benefit.controller.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { BenefitService } from './services/benefit.service'; -import { - UnifiedHrisBenefitInput, - UnifiedHrisBenefitOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('hris/benefits') -@Controller('hris/benefits') -export class BenefitController { - constructor( - private readonly benefitService: BenefitService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(BenefitController.name); - } - - @ApiOperation({ - operationId: 'listHrisBenefits', - summary: 'List Benefits', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisBenefitOutput) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @UseGuards(ApiKeyAuthGuard) - @Get() - async getBenefits( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.benefitService.getBenefits( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisBenefit', - summary: 'Retrieve Benefit', - description: 'Retrieve a Benefit from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the benefit you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisBenefitOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.benefitService.getBenefit( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/hris/benefit/benefit.module.ts b/packages/api/src/hris/benefit/benefit.module.ts deleted file mode 100644 index 30ef672e5..000000000 --- a/packages/api/src/hris/benefit/benefit.module.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Module } from '@nestjs/common'; -import { BenefitController } from './benefit.controller'; -import { BenefitService } from './services/benefit.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { GustoService } from './services/gusto'; -import { GustoBenefitMapper } from './services/gusto/mappers'; -import { Utils } from '@hris/@lib/@utils'; - -@Module({ - controllers: [BenefitController], - providers: [ - BenefitService, - SyncService, - WebhookService, - ServiceRegistry, - IngestDataService, - CoreUnification, - Utils, - GustoBenefitMapper, - /* PROVIDERS SERVICES */ - GustoService, - ], - exports: [SyncService], -}) -export class BenefitModule {} diff --git a/packages/api/src/hris/benefit/services/benefit.service.ts b/packages/api/src/hris/benefit/services/benefit.service.ts deleted file mode 100644 index f98e3422f..000000000 --- a/packages/api/src/hris/benefit/services/benefit.service.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Injectable } from '@nestjs/common'; -import { v4 as uuidv4 } from 'uuid'; -import { UnifiedHrisBenefitOutput } from '../types/model.unified'; -import { ServiceRegistry } from './registry.service'; - -@Injectable() -export class BenefitService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(BenefitService.name); - } - - async getBenefit( - id_hris_benefit: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const benefit = await this.prisma.hris_benefits.findUnique({ - where: { id_hris_benefit: id_hris_benefit }, - }); - - if (!benefit) { - throw new Error(`Benefit with ID ${id_hris_benefit} not found.`); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: benefit.id_hris_benefit }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedBenefit: UnifiedHrisBenefitOutput = { - id: benefit.id_hris_benefit, - provider_name: benefit.provider_name, - employee_id: benefit.id_hris_employee, - employee_contribution: Number(benefit.employee_contribution), - company_contribution: Number(benefit.company_contribution), - start_date: benefit.start_date, - end_date: benefit.end_date, - employer_benefit_id: benefit.id_hris_employer_benefit, - field_mappings: field_mappings, - remote_id: benefit.remote_id, - remote_created_at: benefit.remote_created_at, - created_at: benefit.created_at, - modified_at: benefit.modified_at, - remote_was_deleted: benefit.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: benefit.id_hris_benefit }, - }); - unifiedBenefit.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.benefit.pull', - method: 'GET', - url: '/hris/benefit', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedBenefit; - } catch (error) { - throw error; - } - } - - async getBenefits( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisBenefitOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const benefits = await this.prisma.hris_benefits.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_benefit: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }); - - const hasNextPage = benefits.length > limit; - if (hasNextPage) benefits.pop(); - - const unifiedBenefits = await Promise.all( - benefits.map(async (benefit) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: benefit.id_hris_benefit }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedBenefit: UnifiedHrisBenefitOutput = { - id: benefit.id_hris_benefit, - provider_name: benefit.provider_name, - employee_id: benefit.id_hris_employee, - employee_contribution: Number(benefit.employee_contribution), - company_contribution: Number(benefit.company_contribution), - start_date: benefit.start_date, - end_date: benefit.end_date, - employer_benefit_id: benefit.id_hris_employer_benefit, - field_mappings: field_mappings, - remote_id: benefit.remote_id, - remote_created_at: benefit.remote_created_at, - created_at: benefit.created_at, - modified_at: benefit.modified_at, - remote_was_deleted: benefit.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: benefit.id_hris_benefit }, - }); - unifiedBenefit.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedBenefit; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.benefit.pull', - method: 'GET', - url: '/hris/benefits', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedBenefits, - next_cursor: hasNextPage - ? benefits[benefits.length - 1].id_hris_benefit - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/benefit/services/gusto/index.ts b/packages/api/src/hris/benefit/services/gusto/index.ts deleted file mode 100644 index 7686d0d78..000000000 --- a/packages/api/src/hris/benefit/services/gusto/index.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { HrisObject } from '@hris/@lib/@types'; -import { IBenefitService } from '@hris/benefit/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { GustoBenefitOutput } from './types'; - -@Injectable() -export class GustoService implements IBenefitService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - HrisObject.benefit.toUpperCase() + ':' + GustoService.name, - ); - this.registry.registerService('gusto', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId, id_employee } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gusto', - vertical: 'hris', - }, - }); - - const employee = await this.prisma.hris_employees.findUnique({ - where: { - id_hris_employee: id_employee as string, - }, - select: { - remote_id: true, - }, - }); - - const resp = await axios.get( - `${connection.account_url}/v1/employees/${employee.remote_id}/employee_benefits`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - this.logger.log(`Synced gusto benefits !`); - - return { - data: resp.data, - message: 'Gusto benefits retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/benefit/services/gusto/mappers.ts b/packages/api/src/hris/benefit/services/gusto/mappers.ts deleted file mode 100644 index 6552a5fc5..000000000 --- a/packages/api/src/hris/benefit/services/gusto/mappers.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Injectable } from '@nestjs/common'; -import { GustoBenefitOutput } from './types'; -import { - UnifiedHrisBenefitInput, - UnifiedHrisBenefitOutput, -} from '@hris/benefit/types/model.unified'; -import { IBenefitMapper } from '@hris/benefit/types'; -import { Utils } from '@hris/@lib/@utils'; - -@Injectable() -export class GustoBenefitMapper implements IBenefitMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'benefit', 'gusto', this); - } - - async desunify( - source: UnifiedHrisBenefitInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return; - } - - async unify( - source: GustoBenefitOutput | GustoBenefitOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleBenefitToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((benefit) => - this.mapSingleBenefitToUnified( - benefit, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleBenefitToUnified( - benefit: GustoBenefitOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - const opts: any = {}; - - if (benefit.employee_uuid) { - const employee_id = await this.utils.getEmployeeUuidFromRemoteId( - benefit.employee_uuid, - connectionId, - ); - if (employee_id) { - opts.employee_id = employee_id; - } - } - if (benefit.company_benefit_uuid) { - const id = await this.utils.getEmployerBenefitUuidFromRemoteId( - benefit.company_benefit_uuid, - connectionId, - ); - if (id) { - opts.employer_benefit_id = id; - } - } - - return { - remote_id: benefit.uuid || null, - remote_data: benefit, - ...opts, - employee_contribution: benefit.employee_deduction - ? parseFloat(benefit.employee_deduction) - : null, - company_contribution: benefit.company_contribution - ? parseFloat(benefit.company_contribution) - : null, - remote_was_deleted: null, - }; - } -} diff --git a/packages/api/src/hris/benefit/services/gusto/types.ts b/packages/api/src/hris/benefit/services/gusto/types.ts deleted file mode 100644 index 1f923b5a6..000000000 --- a/packages/api/src/hris/benefit/services/gusto/types.ts +++ /dev/null @@ -1,28 +0,0 @@ -export type GustoBenefitOutput = Partial<{ - version: string; - employee_uuid: string; - company_benefit_uuid: string; - active: boolean; - uuid: string; - employee_deduction: string; - company_contribution: string; - employee_deduction_annual_maximum: string; - company_contribution_annual_maximum: string; - limit_option: string; - deduct_as_percentage: boolean; - contribute_as_percentage: boolean; - catch_up: boolean; - coverage_amount: string; - contribution: { - type: 'amount' | 'percentage' | 'tiered'; - value: - | string - | number - | Array<{ threshold: number; amount: string | number }>; - }; - deduction_reduces_taxable_income: - | 'unset' - | 'reduces_taxable_income' - | 'does_not_reduce_taxable_income'; - coverage_salary_multiplier: string; -}>; diff --git a/packages/api/src/hris/benefit/services/registry.service.ts b/packages/api/src/hris/benefit/services/registry.service.ts deleted file mode 100644 index a47f772f8..000000000 --- a/packages/api/src/hris/benefit/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IBenefitService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IBenefitService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IBenefitService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/benefit/sync/sync.service.ts b/packages/api/src/hris/benefit/sync/sync.service.ts deleted file mode 100644 index 8cf0ed048..000000000 --- a/packages/api/src/hris/benefit/sync/sync.service.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { OriginalBenefitOutput } from '@@core/utils/types/original/original.hris'; -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_benefits as HrisBenefit } from '@prisma/client'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../services/registry.service'; -import { IBenefitService } from '../types'; -import { UnifiedHrisBenefitOutput } from '../types/model.unified'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'benefit', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId, id_employee } = param; - const service: IBenefitService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisBenefitOutput, - OriginalBenefitOutput, - IBenefitService - >(integrationId, linkedUserId, 'hris', 'benefit', service, [ - { - param: id_employee, - paramName: 'id_employee', - shouldPassToService: true, - shouldPassToIngest: true, - }, - ]); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - benefits: UnifiedHrisBenefitOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const benefitResults: HrisBenefit[] = []; - - for (let i = 0; i < benefits.length; i++) { - const benefit = benefits[i]; - const originId = benefit.remote_id; - - let existingBenefit = await this.prisma.hris_benefits.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const benefitData = { - provider_name: benefit.provider_name, - id_hris_employee: benefit.employee_id, - employee_contribution: benefit.employee_contribution - ? BigInt(benefit.employee_contribution) - : null, - company_contribution: benefit.company_contribution - ? BigInt(benefit.company_contribution) - : null, - start_date: benefit.start_date ? new Date(benefit.start_date) : null, - end_date: benefit.end_date ? new Date(benefit.end_date) : null, - id_hris_employer_benefit: benefit.employer_benefit_id, - remote_id: originId, - remote_created_at: benefit.remote_created_at - ? new Date(benefit.remote_created_at) - : null, - modified_at: new Date(), - remote_was_deleted: benefit.remote_was_deleted || false, - }; - - if (existingBenefit) { - existingBenefit = await this.prisma.hris_benefits.update({ - where: { id_hris_benefit: existingBenefit.id_hris_benefit }, - data: benefitData, - }); - } else { - existingBenefit = await this.prisma.hris_benefits.create({ - data: { - ...benefitData, - id_hris_benefit: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - benefitResults.push(existingBenefit); - - // Process field mappings - await this.ingestService.processFieldMappings( - benefit.field_mappings, - existingBenefit.id_hris_benefit, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingBenefit.id_hris_benefit, - remote_data[i], - ); - } - - return benefitResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/benefit/types/index.ts b/packages/api/src/hris/benefit/types/index.ts deleted file mode 100644 index ae78e2b9a..000000000 --- a/packages/api/src/hris/benefit/types/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisBenefitInput, - UnifiedHrisBenefitOutput, -} from './model.unified'; -import { OriginalBenefitOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface IBenefitService { - sync(data: SyncParam): Promise>; -} - -export interface IBenefitMapper { - desunify( - source: UnifiedHrisBenefitInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalBenefitOutput | OriginalBenefitOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/hris/benefit/types/model.unified.ts b/packages/api/src/hris/benefit/types/model.unified.ts deleted file mode 100644 index 5c2e16f94..000000000 --- a/packages/api/src/hris/benefit/types/model.unified.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsNumber, -} from 'class-validator'; - -export class UnifiedHrisBenefitInput { - @ApiPropertyOptional({ - type: String, - example: 'Health Insurance Provider', - nullable: true, - description: 'The name of the benefit provider', - }) - @IsString() - @IsOptional() - provider_name?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the associated employee', - }) - @IsUUID() - @IsOptional() - employee_id?: string; - - @ApiPropertyOptional({ - type: Number, - example: 100, - nullable: true, - description: 'The employee contribution amount', - }) - @IsNumber() - @IsOptional() - employee_contribution?: number; - - @ApiPropertyOptional({ - type: Number, - example: 200, - nullable: true, - description: 'The company contribution amount', - }) - @IsNumber() - @IsOptional() - company_contribution?: number; - - @ApiPropertyOptional({ - type: Date, - example: '2024-01-01T00:00:00Z', - nullable: true, - description: 'The start date of the benefit', - }) - @IsDateString() - @IsOptional() - start_date?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-12-31T23:59:59Z', - nullable: true, - description: 'The end date of the benefit', - }) - @IsDateString() - @IsOptional() - end_date?: Date; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the associated employer benefit', - }) - @IsUUID() - @IsOptional() - employer_benefit_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisBenefitOutput extends UnifiedHrisBenefitInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the benefit record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'benefit_1234', - nullable: true, - description: 'The remote ID of the benefit in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the benefit in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: - 'The date when the benefit was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The created date of the benefit record', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The last modified date of the benefit record', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: 'Indicates if the benefit was deleted in the remote system', - }) - @IsOptional() - remote_was_deleted?: boolean; -} diff --git a/packages/api/src/hris/benefit/utils/index.ts b/packages/api/src/hris/benefit/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/benefit/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/company/company.controller.ts b/packages/api/src/hris/company/company.controller.ts deleted file mode 100644 index 842101332..000000000 --- a/packages/api/src/hris/company/company.controller.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - Controller, - Get, - Headers, - Param, - Query, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { - ApiHeader, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, -} from '@nestjs/swagger'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { CompanyService } from './services/company.service'; -import { UnifiedHrisCompanyOutput } from './types/model.unified'; - -@ApiTags('hris/companies') -@Controller('hris/companies') -export class CompanyController { - constructor( - private readonly companyService: CompanyService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(CompanyController.name); - } - - @ApiOperation({ - operationId: 'listHrisCompanies', - summary: 'List Companies', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisCompanyOutput) - @UseGuards(ApiKeyAuthGuard) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @Get() - async getCompanies( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.companyService.getCompanies( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisCompany', - summary: 'Retrieve Company', - description: 'Retrieve a Company from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the company you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisCompanyOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.companyService.getCompany( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } catch (error) { - throw new Error(error); - } - } -} diff --git a/packages/api/src/hris/company/company.module.ts b/packages/api/src/hris/company/company.module.ts deleted file mode 100644 index a1cfc0614..000000000 --- a/packages/api/src/hris/company/company.module.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Module } from '@nestjs/common'; -import { CompanyController } from './company.controller'; -import { CompanyService } from './services/company.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { GustoCompanyMapper } from './services/gusto/mappers'; -import { GustoService } from './services/gusto'; -import { Utils } from '@hris/@lib/@utils'; -import { DeelService } from './services/deel'; -import { DeelCompanyMapper } from './services/deel/mappers'; -@Module({ - controllers: [CompanyController], - providers: [ - CompanyService, - CoreUnification, - SyncService, - WebhookService, - Utils, - ServiceRegistry, - IngestDataService, - GustoCompanyMapper, - DeelCompanyMapper, - /* PROVIDERS SERVICES */ - GustoService, - DeelService, - ], - exports: [SyncService], -}) -export class CompanyModule {} diff --git a/packages/api/src/hris/company/services/company.service.ts b/packages/api/src/hris/company/services/company.service.ts deleted file mode 100644 index 9f9e0b4a7..000000000 --- a/packages/api/src/hris/company/services/company.service.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Injectable } from '@nestjs/common'; -import { v4 as uuidv4 } from 'uuid'; -import { UnifiedHrisCompanyOutput } from '../types/model.unified'; -import { ServiceRegistry } from './registry.service'; - -@Injectable() -export class CompanyService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(CompanyService.name); - } - - async getCompany( - id_hris_company: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const company = await this.prisma.hris_companies.findUnique({ - where: { id_hris_company: id_hris_company }, - }); - - if (!company) { - throw new Error(`Company with ID ${id_hris_company} not found.`); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: company.id_hris_company }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const locations = await this.prisma.hris_locations.findMany({ - where: { - id_hris_company: company.id_hris_company, - }, - }); - - const unifiedCompany: UnifiedHrisCompanyOutput = { - id: company.id_hris_company, - legal_name: company.legal_name, - display_name: company.display_name, - eins: company.eins, - field_mappings: field_mappings, - locations: locations.map((loc) => loc.id_hris_location), - remote_id: company.remote_id, - remote_created_at: company.remote_created_at, - created_at: company.created_at, - modified_at: company.modified_at, - remote_was_deleted: company.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: company.id_hris_company }, - }); - unifiedCompany.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.company.pull', - method: 'GET', - url: '/hris/company', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedCompany; - } catch (error) { - throw error; - } - } - - async getCompanies( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisCompanyOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const companies = await this.prisma.hris_companies.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_company: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }); - - const hasNextPage = companies.length > limit; - if (hasNextPage) companies.pop(); - - const unifiedCompanies = await Promise.all( - companies.map(async (company) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: company.id_hris_company }, - }, - include: { attribute: true }, - }); - - const locations = await this.prisma.hris_locations.findMany({ - where: { - id_hris_company: company.id_hris_company, - }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedCompany: UnifiedHrisCompanyOutput = { - id: company.id_hris_company, - legal_name: company.legal_name, - display_name: company.display_name, - eins: company.eins, - field_mappings: field_mappings, - locations: locations.map((loc) => loc.id_hris_location), - remote_id: company.remote_id, - remote_created_at: company.remote_created_at, - created_at: company.created_at, - modified_at: company.modified_at, - remote_was_deleted: company.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: company.id_hris_company }, - }); - unifiedCompany.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedCompany; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.company.pull', - method: 'GET', - url: '/hris/companies', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedCompanies, - next_cursor: hasNextPage - ? companies[companies.length - 1].id_hris_company - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/company/services/deel/index.ts b/packages/api/src/hris/company/services/deel/index.ts deleted file mode 100644 index 6eb47d4ea..000000000 --- a/packages/api/src/hris/company/services/deel/index.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { HrisObject } from '@hris/@lib/@types'; -import { ICompanyService } from '@hris/company/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { DeelCompanyOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalCompanyOutput } from '@@core/utils/types/original/original.hris'; - -@Injectable() -export class DeelService implements ICompanyService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - HrisObject.company.toUpperCase() + ':' + DeelService.name, - ); - this.registry.registerService('deel', this); - } - - addCompany( - companyData: DesunifyReturnType, - linkedUserId: string, - ): Promise> { - throw new Error('Method not implemented.'); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'deel', - vertical: 'hris', - }, - }); - - const resp = await axios.get( - `${connection.account_url}/rest/v2/legal-entities`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - this.logger.log(`Synced deel companys !`); - - return { - data: resp.data.data, - message: 'Deel companys retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/company/services/deel/mappers.ts b/packages/api/src/hris/company/services/deel/mappers.ts deleted file mode 100644 index 5bdc9293c..000000000 --- a/packages/api/src/hris/company/services/deel/mappers.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Injectable } from '@nestjs/common'; -import { DeelCompanyOutput } from './types'; -import { - UnifiedHrisCompanyInput, - UnifiedHrisCompanyOutput, -} from '@hris/company/types/model.unified'; -import { ICompanyMapper } from '@hris/company/types'; -import { Utils } from '@hris/@lib/@utils'; - -@Injectable() -export class DeelCompanyMapper implements ICompanyMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'company', 'deel', this); - } - - async desunify( - source: UnifiedHrisCompanyInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - // Implementation for desunify (if needed) - return; - } - - async unify( - source: DeelCompanyOutput | DeelCompanyOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleCompanyToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((company) => - this.mapSingleCompanyToUnified( - company, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleCompanyToUnified( - company: DeelCompanyOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return { - remote_id: company.id || null, - legal_name: company.name || null, - display_name: company.name || null, // Using name for display_name as well - eins: [], // Deel doesn't provide EINs in this data structure - remote_data: company, - locations: [], // Deel doesn't provide locations in this data structure - field_mappings: {}, - }; - } -} diff --git a/packages/api/src/hris/company/services/deel/types.ts b/packages/api/src/hris/company/services/deel/types.ts deleted file mode 100644 index ec793698b..000000000 --- a/packages/api/src/hris/company/services/deel/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface DeelCompanyOutput { - id: string; - name: string; - country: string; - entity_type: 'individual' | string; - entity_subtype: string; -} diff --git a/packages/api/src/hris/company/services/gusto/index.ts b/packages/api/src/hris/company/services/gusto/index.ts deleted file mode 100644 index bfc8bfa99..000000000 --- a/packages/api/src/hris/company/services/gusto/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { HrisObject } from '@hris/@lib/@types'; -import { ICompanyService } from '@hris/company/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { GustoCompanyOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalCompanyOutput } from '@@core/utils/types/original/original.hris'; - -@Injectable() -export class GustoService implements ICompanyService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - HrisObject.company.toUpperCase() + ':' + GustoService.name, - ); - this.registry.registerService('gusto', this); - } - - addCompany( - companyData: DesunifyReturnType, - linkedUserId: string, - ): Promise> { - throw new Error('Method not implemented.'); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gusto', - vertical: 'hris', - }, - }); - - const resp = await axios.get(`${connection.account_url}/v1/token_info`, { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }); - const company_uuid = resp.data.resource.uuid; - const resp_ = await axios.get( - `${connection.account_url}/v1/companies/${company_uuid}`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - this.logger.log(`Synced gusto companys !`); - - return { - data: [resp_.data], - message: 'Gusto companys retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/company/services/gusto/mappers.ts b/packages/api/src/hris/company/services/gusto/mappers.ts deleted file mode 100644 index 91edecfbe..000000000 --- a/packages/api/src/hris/company/services/gusto/mappers.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Injectable } from '@nestjs/common'; -import { GustoCompanyOutput } from './types'; -import { - UnifiedHrisCompanyInput, - UnifiedHrisCompanyOutput, -} from '@hris/company/types/model.unified'; -import { ICompanyMapper } from '@hris/company/types'; -import { Utils } from '@hris/@lib/@utils'; -import { UnifiedHrisLocationOutput } from '@hris/location/types/model.unified'; -import { GustoLocationOutput } from '@hris/location/services/gusto/types'; -import { HrisObject } from '@hris/@lib/@types'; - -@Injectable() -export class GustoCompanyMapper implements ICompanyMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'company', 'gusto', this); - } - - async desunify( - source: UnifiedHrisCompanyInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return; - } - - async unify( - source: GustoCompanyOutput | GustoCompanyOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleCompanyToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((company) => - this.mapSingleCompanyToUnified( - company, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleCompanyToUnified( - company: GustoCompanyOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - const opts: any = {}; - if (company.locations && company.locations.length > 0) { - const locations = await this.ingestService.ingestData< - UnifiedHrisLocationOutput, - GustoLocationOutput - >( - company.locations, - 'gusto', - connectionId, - 'hris', - HrisObject.location, - [], - ); - if (locations) { - opts.locations = locations.map((loc) => loc.id_hris_location); - } - } - return { - remote_id: company.uuid || null, - legal_name: company.name || null, - display_name: company.trade_name || null, - eins: company.ein ? [company.ein] : [], - remote_data: company, - ...opts, - }; - } -} diff --git a/packages/api/src/hris/company/services/gusto/types.ts b/packages/api/src/hris/company/services/gusto/types.ts deleted file mode 100644 index 3bfa8539d..000000000 --- a/packages/api/src/hris/company/services/gusto/types.ts +++ /dev/null @@ -1,76 +0,0 @@ -export type GustoCompanyOutput = Partial<{ - ein: string; // The Federal Employer Identification Number of the company. - entity_type: - | 'C-Corporation' - | 'S-Corporation' - | 'Sole proprietor' - | 'LLC' - | 'LLP' - | 'Limited partnership' - | 'Co-ownership' - | 'Association' - | 'Trusteeship' - | 'General partnership' - | 'Joint venture' - | 'Non-Profit'; // The tax payer type of the company. - tier: - | 'simple' - | 'plus' - | 'premium' - | 'core' - | 'complete' - | 'concierge' - | 'contractor_only' - | 'basic' - | null; // The Gusto product tier of the company. - is_suspended: boolean; // Whether or not the company is suspended in Gusto. - company_status: 'Approved' | 'Not Approved' | 'Suspended'; // The status of the company in Gusto. - uuid: string; // A unique identifier of the company in Gusto. - name: string; // The name of the company. - slug: string; // The slug of the name of the company. - trade_name: string; // The trade name of the company. - is_partner_managed: boolean; // Whether the company is fully managed by a partner via the API - pay_schedule_type: - | 'single' - | 'hourly_salaried' - | 'by_employee' - | 'by_department'; // The pay schedule assignment type. - join_date: string; // Company's first invoiceable event date - funding_type: 'ach' | 'reverse_wire' | 'wire_in' | 'brex'; // Company's default funding type - locations: Array
; // The locations of the company, with status - compensations: { - hourly: CompensationRate[]; // The available hourly compensation rates for the company. - fixed: CompensationRate[]; // The available fixed compensation rates for the company. - }; - paid_time_off: PaidTimeOff[]; // The available types of paid time off for the company. - primary_signatory: Person; // The primary signatory of the company. - primary_payroll_admin: Omit; // The primary payroll admin of the company. -}>; - -type Address = { - street_1: string; - street_2: string | null; - city: string; - state: string; - zip: string; - country: string; // Defaults to USA -}; - -type CompensationRate = { - name: string; - multiple?: number; // For hourly compensation - fixed?: number; // For fixed compensation -}; - -type PaidTimeOff = { - name: string; -}; - -type Person = { - first_name: string; - middle_initial?: string; - last_name: string; - phone: string; - email: string; - home_address?: Address; -}; diff --git a/packages/api/src/hris/company/services/registry.service.ts b/packages/api/src/hris/company/services/registry.service.ts deleted file mode 100644 index cb30233d5..000000000 --- a/packages/api/src/hris/company/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ICompanyService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: ICompanyService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): ICompanyService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/company/sync/sync.service.ts b/packages/api/src/hris/company/sync/sync.service.ts deleted file mode 100644 index 30bdbda98..000000000 --- a/packages/api/src/hris/company/sync/sync.service.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { ApiResponse } from '@@core/utils/types'; -import { v4 as uuidv4 } from 'uuid'; -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 { UnifiedHrisCompanyOutput } from '../types/model.unified'; -import { ICompanyService } from '../types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_companies as HrisCompany } from '@prisma/client'; -import { OriginalCompanyOutput } from '@@core/utils/types/original/original.hris'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'company', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: ICompanyService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisCompanyOutput, - OriginalCompanyOutput, - ICompanyService - >(integrationId, linkedUserId, 'hris', 'company', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - companies: UnifiedHrisCompanyOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const companyResults: HrisCompany[] = []; - - for (let i = 0; i < companies.length; i++) { - const company = companies[i]; - const originId = company.remote_id; - - let existingCompany = await this.prisma.hris_companies.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const companyData = { - legal_name: company.legal_name, - display_name: company.display_name, - eins: company.eins || [], - remote_id: originId, - remote_created_at: company.remote_created_at - ? new Date(company.remote_created_at) - : null, - modified_at: new Date(), - remote_was_deleted: company.remote_was_deleted || false, - }; - - if (existingCompany) { - existingCompany = await this.prisma.hris_companies.update({ - where: { id_hris_company: existingCompany.id_hris_company }, - data: companyData, - }); - } else { - existingCompany = await this.prisma.hris_companies.create({ - data: { - ...companyData, - id_hris_company: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - if (company.locations) { - for (const loc of company.locations) { - await this.prisma.hris_locations.update({ - where: { - id_hris_location: loc, - }, - data: { - id_hris_company: existingCompany.id_hris_company, - }, - }); - } - } - - companyResults.push(existingCompany); - - // Process field mappings - await this.ingestService.processFieldMappings( - company.field_mappings, - existingCompany.id_hris_company, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingCompany.id_hris_company, - remote_data[i], - ); - } - - return companyResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/company/types/index.ts b/packages/api/src/hris/company/types/index.ts deleted file mode 100644 index d07fe0dc7..000000000 --- a/packages/api/src/hris/company/types/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisCompanyInput, - UnifiedHrisCompanyOutput, -} from './model.unified'; -import { OriginalCompanyOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface ICompanyService { - sync(data: SyncParam): Promise>; -} - -export interface ICompanyMapper { - desunify( - source: UnifiedHrisCompanyInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalCompanyOutput | OriginalCompanyOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/hris/company/types/model.unified.ts b/packages/api/src/hris/company/types/model.unified.ts deleted file mode 100644 index 038a0a441..000000000 --- a/packages/api/src/hris/company/types/model.unified.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsArray, - IsDateString, - IsBoolean, -} from 'class-validator'; - -export class UnifiedHrisCompanyInput { - @ApiPropertyOptional({ - type: String, - example: 'Acme Corporation', - nullable: true, - description: 'The legal name of the company', - }) - @IsString() - @IsOptional() - legal_name?: string; - - @ApiPropertyOptional({ - type: [String], - example: ['801f9ede-c698-4e66-a7fc-48d19eebaa4f'], - nullable: true, - description: 'UUIDs of the of the Location associated with the company', - }) - @IsString() - @IsOptional() - locations?: string[]; - - @ApiPropertyOptional({ - type: String, - example: 'Acme Corp', - nullable: true, - description: 'The display name of the company', - }) - @IsString() - @IsOptional() - display_name?: string; - - @ApiPropertyOptional({ - type: [String], - example: ['12-3456789', '98-7654321'], - nullable: true, - description: 'The Employer Identification Numbers (EINs) of the company', - }) - @IsArray() - @IsString({ each: true }) - @IsOptional() - eins?: string[]; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisCompanyOutput extends UnifiedHrisCompanyInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the company record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'company_1234', - nullable: true, - description: 'The remote ID of the company in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the company in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: - 'The date when the company was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The created date of the company record', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The last modified date of the company record', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: 'Indicates if the company was deleted in the remote system', - }) - @IsBoolean() - @IsOptional() - remote_was_deleted?: boolean; -} diff --git a/packages/api/src/hris/company/utils/index.ts b/packages/api/src/hris/company/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/company/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/dependent/dependent.controller.ts b/packages/api/src/hris/dependent/dependent.controller.ts deleted file mode 100644 index 90fa2e949..000000000 --- a/packages/api/src/hris/dependent/dependent.controller.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { DependentService } from './services/dependent.service'; -import { - UnifiedHrisDependentInput, - UnifiedHrisDependentOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('hris/dependents') -@Controller('hris/dependents') -export class DependentController { - constructor( - private readonly dependentService: DependentService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(DependentController.name); - } - - @ApiOperation({ - operationId: 'listHrisDependents', - summary: 'List Dependents', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisDependentOutput) - @UseGuards(ApiKeyAuthGuard) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @Get() - async getDependents( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.dependentService.getDependents( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisDependent', - summary: 'Retrieve Dependent', - description: 'Retrieve a Dependent from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the dependent you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisDependentOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.dependentService.getDependent( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/hris/dependent/dependent.module.ts b/packages/api/src/hris/dependent/dependent.module.ts deleted file mode 100644 index 22423cbdc..000000000 --- a/packages/api/src/hris/dependent/dependent.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Module } from '@nestjs/common'; -import { DependentController } from './dependent.controller'; -import { DependentService } from './services/dependent.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Utils } from '@hris/@lib/@utils'; -@Module({ - controllers: [DependentController], - providers: [ - DependentService, - Utils, - CoreUnification, - SyncService, - WebhookService, - ServiceRegistry, - IngestDataService, - /* PROVIDERS SERVICES */ - ], - exports: [SyncService], -}) -export class DependentModule {} diff --git a/packages/api/src/hris/dependent/services/dependent.service.ts b/packages/api/src/hris/dependent/services/dependent.service.ts deleted file mode 100644 index 378b22dc7..000000000 --- a/packages/api/src/hris/dependent/services/dependent.service.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Injectable } from '@nestjs/common'; -import { v4 as uuidv4 } from 'uuid'; -import { UnifiedHrisDependentOutput } from '../types/model.unified'; -import { ServiceRegistry } from './registry.service'; - -@Injectable() -export class DependentService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(DependentService.name); - } - - async getDependent( - id_hris_dependent: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const dependent = await this.prisma.hris_dependents.findUnique({ - where: { id_hris_dependents: id_hris_dependent }, - }); - - if (!dependent) { - throw new Error(`Dependent with ID ${id_hris_dependent} not found.`); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: dependent.id_hris_dependents }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedDependent: UnifiedHrisDependentOutput = { - id: dependent.id_hris_dependents, - first_name: dependent.first_name, - last_name: dependent.last_name, - middle_name: dependent.middle_name, - relationship: dependent.relationship, - date_of_birth: dependent.date_of_birth, - gender: dependent.gender, - phone_number: dependent.phone_number, - home_location: dependent.home_location, - is_student: dependent.is_student, - ssn: dependent.ssn, - employee_id: dependent.id_hris_employee, - field_mappings: field_mappings, - remote_id: dependent.remote_id, - remote_created_at: dependent.remote_created_at, - created_at: dependent.created_at, - modified_at: dependent.modified_at, - remote_was_deleted: dependent.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: dependent.id_hris_dependents }, - }); - unifiedDependent.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.dependent.pull', - method: 'GET', - url: '/hris/dependent', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedDependent; - } catch (error) { - throw error; - } - } - - async getDependents( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisDependentOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const dependents = await this.prisma.hris_dependents.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_dependents: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }); - - const hasNextPage = dependents.length > limit; - if (hasNextPage) dependents.pop(); - - const unifiedDependents = await Promise.all( - dependents.map(async (dependent) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: dependent.id_hris_dependents }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedDependent: UnifiedHrisDependentOutput = { - id: dependent.id_hris_dependents, - first_name: dependent.first_name, - last_name: dependent.last_name, - middle_name: dependent.middle_name, - relationship: dependent.relationship, - date_of_birth: dependent.date_of_birth, - gender: dependent.gender, - phone_number: dependent.phone_number, - home_location: dependent.home_location, - is_student: dependent.is_student, - ssn: dependent.ssn, - employee_id: dependent.id_hris_employee, - field_mappings: field_mappings, - remote_id: dependent.remote_id, - remote_created_at: dependent.remote_created_at, - created_at: dependent.created_at, - modified_at: dependent.modified_at, - remote_was_deleted: dependent.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: dependent.id_hris_dependents }, - }); - unifiedDependent.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedDependent; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.dependent.pull', - method: 'GET', - url: '/hris/dependents', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedDependents, - next_cursor: hasNextPage - ? dependents[dependents.length - 1].id_hris_dependents - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/dependent/services/registry.service.ts b/packages/api/src/hris/dependent/services/registry.service.ts deleted file mode 100644 index d770c7efa..000000000 --- a/packages/api/src/hris/dependent/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IDependentService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IDependentService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IDependentService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/dependent/sync/sync.service.ts b/packages/api/src/hris/dependent/sync/sync.service.ts deleted file mode 100644 index 3e91fa1f3..000000000 --- a/packages/api/src/hris/dependent/sync/sync.service.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { ApiResponse } from '@@core/utils/types'; -import { v4 as uuidv4 } from 'uuid'; -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 { UnifiedHrisDependentOutput } from '../types/model.unified'; -import { IDependentService } from '../types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_dependents as HrisDependent } from '@prisma/client'; -import { OriginalDependentOutput } from '@@core/utils/types/original/original.hris'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'dependent', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IDependentService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisDependentOutput, - OriginalDependentOutput, - IDependentService - >(integrationId, linkedUserId, 'hris', 'dependent', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - dependents: UnifiedHrisDependentOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const dependentResults: HrisDependent[] = []; - - for (let i = 0; i < dependents.length; i++) { - const dependent = dependents[i]; - const originId = dependent.remote_id; - - let existingDependent = await this.prisma.hris_dependents.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const dependentData = { - first_name: dependent.first_name, - last_name: dependent.last_name, - middle_name: dependent.middle_name, - relationship: dependent.relationship, - date_of_birth: dependent.date_of_birth - ? new Date(dependent.date_of_birth) - : null, - gender: dependent.gender, - phone_number: dependent.phone_number, - home_location: dependent.home_location, - is_student: dependent.is_student, - ssn: dependent.ssn, - id_hris_employee: dependent.employee_id, - remote_id: originId, - remote_created_at: dependent.remote_created_at - ? new Date(dependent.remote_created_at) - : null, - modified_at: new Date(), - remote_was_deleted: dependent.remote_was_deleted || false, - }; - - if (existingDependent) { - existingDependent = await this.prisma.hris_dependents.update({ - where: { id_hris_dependents: existingDependent.id_hris_dependents }, - data: dependentData, - }); - } else { - existingDependent = await this.prisma.hris_dependents.create({ - data: { - ...dependentData, - id_hris_dependents: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - dependentResults.push(existingDependent); - - // Process field mappings - await this.ingestService.processFieldMappings( - dependent.field_mappings, - existingDependent.id_hris_dependents, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingDependent.id_hris_dependents, - remote_data[i], - ); - } - - return dependentResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/dependent/types/index.ts b/packages/api/src/hris/dependent/types/index.ts deleted file mode 100644 index deea11064..000000000 --- a/packages/api/src/hris/dependent/types/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisDependentInput, - UnifiedHrisDependentOutput, -} from './model.unified'; -import { OriginalDependentOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface IDependentService { - sync(data: SyncParam): Promise>; -} - -export interface IDependentMapper { - desunify( - source: UnifiedHrisDependentInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalDependentOutput | OriginalDependentOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/hris/dependent/types/model.unified.ts b/packages/api/src/hris/dependent/types/model.unified.ts deleted file mode 100644 index c1e2add3f..000000000 --- a/packages/api/src/hris/dependent/types/model.unified.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsBoolean, -} from 'class-validator'; - -export type Gender = - | 'MALE' - | 'FEMALE' - | 'NON-BINARY' - | 'OTHER' - | 'PREFER_NOT_TO_DISCLOSE'; - -export type Relationship = 'CHILD' | 'SPOUSE' | 'DOMESTIC_PARTNER'; - -export class UnifiedHrisDependentInput { - @ApiPropertyOptional({ - type: String, - example: 'John', - nullable: true, - description: 'The first name of the dependent', - }) - @IsString() - @IsOptional() - first_name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'Doe', - nullable: true, - description: 'The last name of the dependent', - }) - @IsString() - @IsOptional() - last_name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'Michael', - nullable: true, - description: 'The middle name of the dependent', - }) - @IsString() - @IsOptional() - middle_name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'CHILD', - // enum: ['CHILD', 'SPOUSE', 'DOMESTIC_PARTNER'], - nullable: true, - description: 'The relationship of the dependent to the employee', - }) - @IsString() - @IsOptional() - relationship?: Relationship | string; - - @ApiPropertyOptional({ - type: Date, - example: '2020-01-01', - nullable: true, - description: 'The date of birth of the dependent', - }) - @IsDateString() - @IsOptional() - date_of_birth?: Date; - - @ApiPropertyOptional({ - type: String, - example: 'MALE', - // enum: ['MALE', 'FEMALE', 'NON-BINARY', 'OTHER', 'PREFER_NOT_TO_DISCLOSE'], - nullable: true, - description: 'The gender of the dependent', - }) - @IsString() - @IsOptional() - gender?: Gender | string; - - @ApiPropertyOptional({ - type: String, - example: '+1234567890', - nullable: true, - description: 'The phone number of the dependent', - }) - @IsString() - @IsOptional() - phone_number?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the home location', - }) - @IsUUID() - @IsOptional() - home_location?: string; - - @ApiPropertyOptional({ - type: Boolean, - example: true, - nullable: true, - description: 'Indicates if the dependent is a student', - }) - @IsBoolean() - @IsOptional() - is_student?: boolean; - - @ApiPropertyOptional({ - type: String, - example: '123-45-6789', - nullable: true, - description: 'The Social Security Number of the dependent', - }) - @IsString() - @IsOptional() - ssn?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the associated employee', - }) - @IsUUID() - @IsOptional() - employee_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisDependentOutput extends UnifiedHrisDependentInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the dependent record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'dependent_1234', - nullable: true, - description: - 'The remote ID of the dependent in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the dependent in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: - 'The date when the dependent was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The created date of the dependent record', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The last modified date of the dependent record', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: 'Indicates if the dependent was deleted in the remote system', - }) - @IsBoolean() - @IsOptional() - remote_was_deleted?: boolean; -} diff --git a/packages/api/src/hris/dependent/utils/index.ts b/packages/api/src/hris/dependent/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/dependent/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/employee/employee.controller.ts b/packages/api/src/hris/employee/employee.controller.ts deleted file mode 100644 index d0274e026..000000000 --- a/packages/api/src/hris/employee/employee.controller.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { EmployeeService } from './services/employee.service'; -import { - UnifiedHrisEmployeeInput, - UnifiedHrisEmployeeOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, - ApiPostCustomResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('hris/employees') -@Controller('hris/employees') -export class EmployeeController { - constructor( - private readonly employeeService: EmployeeService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(EmployeeController.name); - } - - @ApiOperation({ - operationId: 'listHrisEmployees', - summary: 'List Employees', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisEmployeeOutput) - @UseGuards(ApiKeyAuthGuard) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @Get() - async getEmployees( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.employeeService.getEmployees( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisEmployee', - summary: 'Retrieve Employee', - description: 'Retrieve an Employee from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the employee you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisEmployeeOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.employeeService.getEmployee( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } - - @ApiOperation({ - operationId: 'createHrisEmployee', - summary: 'Create Employees', - description: 'Create Employees in any supported Hris software', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - }) - @ApiBody({ type: UnifiedHrisEmployeeInput }) - @ApiPostCustomResponse(UnifiedHrisEmployeeOutput) - @UseGuards(ApiKeyAuthGuard) - @Post() - async addEmployee( - @Body() unifiedEmployeeData: UnifiedHrisEmployeeInput, - @Headers('x-connection-token') connection_token: string, - @Query('remote_data') remote_data?: boolean, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.employeeService.addEmployee( - unifiedEmployeeData, - connectionId, - projectId, - remoteSource, - linkedUserId, - remote_data, - ); - } catch (error) { - throw new Error(error); - } - } -} diff --git a/packages/api/src/hris/employee/employee.module.ts b/packages/api/src/hris/employee/employee.module.ts deleted file mode 100644 index e01e841db..000000000 --- a/packages/api/src/hris/employee/employee.module.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Module } from '@nestjs/common'; -import { EmployeeController } from './employee.controller'; -import { EmployeeService } from './services/employee.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { GustoEmployeeMapper } from './services/gusto/mappers'; -import { GustoService } from './services/gusto'; -import { Utils } from '@hris/@lib/@utils'; -import { SageEmployeeMapper } from './services/sage/mappers'; -import { SageService } from './services/sage'; -import { DeelService } from './services/deel'; -import { DeelEmployeeMapper } from './services/deel/mappers'; -@Module({ - controllers: [EmployeeController], - providers: [ - EmployeeService, - CoreUnification, - SyncService, - Utils, - WebhookService, - ServiceRegistry, - IngestDataService, - GustoEmployeeMapper, - SageEmployeeMapper, - DeelEmployeeMapper, - /* PROVIDERS SERVICES */ - GustoService, - SageService, - DeelService, - ], - exports: [SyncService], -}) -export class EmployeeModule {} diff --git a/packages/api/src/hris/employee/services/deel/index.ts b/packages/api/src/hris/employee/services/deel/index.ts deleted file mode 100644 index 966c3ff66..000000000 --- a/packages/api/src/hris/employee/services/deel/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { HrisObject } from '@hris/@lib/@types'; -import { IEmployeeService } from '@hris/employee/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { DeelEmployeeOutput } from './types'; - -@Injectable() -export class DeelService implements IEmployeeService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - HrisObject.employee.toUpperCase() + ':' + DeelService.name, - ); - this.registry.registerService('deel', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'deel', - vertical: 'hris', - }, - }); - - const resp = await axios.get(`${connection.account_url}/rest/v2/people`, { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }); - this.logger.log(`Synced deel employees !`); - - return { - data: resp.data.data, - message: 'Deel employees retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/employee/services/deel/mappers.ts b/packages/api/src/hris/employee/services/deel/mappers.ts deleted file mode 100644 index 0912bdb4d..000000000 --- a/packages/api/src/hris/employee/services/deel/mappers.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { DeelEmployeeOutput } from './types'; -import { - UnifiedHrisEmployeeInput, - UnifiedHrisEmployeeOutput, -} from '@hris/employee/types/model.unified'; -import { IEmployeeMapper } from '@hris/employee/types'; -import { Utils } from '@hris/@lib/@utils'; -import { HrisObject } from '@panora/shared'; -import { UnifiedHrisEmploymentOutput } from '@hris/employment/types/model.unified'; -import { DeelEmploymentOutput } from '@hris/employment/services/deel/types'; -import { DeelLocationOutput } from '@hris/location/services/deel/types'; -import { UnifiedHrisLocationOutput } from '@hris/location/types/model.unified'; - -@Injectable() -export class DeelEmployeeMapper implements IEmployeeMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'employee', 'deel', this); - } - - async desunify( - source: UnifiedHrisEmployeeInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - // Implementation for desunify (if needed) - return; - } - - async unify( - source: DeelEmployeeOutput | DeelEmployeeOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleEmployeeToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((employee) => - this.mapSingleEmployeeToUnified( - employee, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleEmployeeToUnified( - employee: DeelEmployeeOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - const opts: any = {}; - - if (employee.client_legal_entity) { - const company_id = await this.utils.getCompanyUuidFromRemoteId( - employee.client_legal_entity.id, // Assuming client_legal_entity has an id field - connectionId, - ); - if (company_id) { - opts.company_id = company_id; - } - } - - if (employee.direct_manager) { - const manager_id = await this.utils.getEmployeeUuidFromRemoteId( - employee.direct_manager.id, - connectionId, - ); - if (manager_id) { - opts.manager_id = manager_id; - } - } - - if (employee.employments) { - const employments = await this.ingestService.ingestData< - UnifiedHrisEmploymentOutput, - DeelEmploymentOutput - >( - employee.employments, - 'deel', - connectionId, - 'hris', - HrisObject.employment, - [], - ); - if (employments) { - opts.employments = employments.map((emp) => emp.id_hris_employment); - } - } - - if (employee.addresses) { - const addresses = await this.ingestService.ingestData< - UnifiedHrisLocationOutput, - DeelLocationOutput - >( - employee.addresses.map((add) => { - return { - ...add, - type: 'HOME', - }; - }), - 'deel', - connectionId, - 'hris', - HrisObject.location, - [], - ); - if (addresses) { - opts.locations = addresses.map((add) => add.id_hris_location); - } - } - - const primaryEmployment = employee.employments.find((emp) => !emp.is_ended); - - return { - remote_id: employee.id, - remote_data: employee, - first_name: employee.first_name, - last_name: employee.last_name, - preferred_name: null, // Deel doesn't provide this information - display_full_name: employee.full_name, - work_email: - employee.emails.find((email) => email.type === 'work')?.value || null, - personal_email: - employee.emails.find((email) => email.type === 'personal')?.value || - null, - mobile_phone_number: null, // Deel doesn't provide this information in the given structure - start_date: primaryEmployment - ? new Date(primaryEmployment.start_date) - : null, - termination_date: employee.completion_date - ? new Date(employee.completion_date) - : null, - employment_status: this.mapEmploymentStatus(employee.hiring_status), - date_of_birth: employee.birth_date ? new Date(employee.birth_date) : null, - avatar_url: null, // Deel doesn't provide this information in the given structure - gender: null, // Deel doesn't provide this information in the given structure - ethnicity: null, // Deel doesn't provide this information in the given structure - marital_status: null, // Deel doesn't provide this information in the given structure - job_title: primaryEmployment ? primaryEmployment.job_title : null, - ...opts, - }; - } - - private mapEmploymentStatus( - status: string, - ): 'ACTIVE' | 'PENDING' | 'INACTIVE' { - switch (status.toUpperCase()) { - case 'ACTIVE': - return 'ACTIVE'; - case 'PENDING': - return 'PENDING'; - default: - return 'INACTIVE'; - } - } -} diff --git a/packages/api/src/hris/employee/services/deel/types.ts b/packages/api/src/hris/employee/services/deel/types.ts deleted file mode 100644 index 65eb03f3d..000000000 --- a/packages/api/src/hris/employee/services/deel/types.ts +++ /dev/null @@ -1,100 +0,0 @@ -export type DeelEmployeeOutput = Partial<{ - id: string; - created_at: string; // Date-time in string format - first_name: string; - last_name: string; - full_name: string; - addresses: DeelAddress[]; - emails: DeelEmail[]; - birth_date: string; - start_date: string; // Date in string format - nationalities: string[]; - client_legal_entity: DeelClientLegalEntity; - state: string; - seniority: string; - completion_date: string | null; - direct_manager: DeelDirectManager | null; - direct_reports: DeelDirectManager[] | null; - direct_reports_count: number; - employments: DeelEmployment[]; - hiring_status: string; - new_hiring_status: string; - hiring_type: string; - job_title: string; - country: string; - timezone: string; - department: DeelDepartment; - work_location: string; - updated_at: string | null; // Date-time in string format -}>; - -export interface DeelAddress { - streetAddress: string; - locality: string; - region: string; - postalCode: string; - country: string; -} - -export interface DeelEmail { - type: string; - value: string; -} - -export interface DeelClientLegalEntity { - id: string; - name: string; -} - -export interface DeelDirectManager { - id: string; - last_name: string; - first_name: string; - work_email: string; -} - -export interface DeelTeam { - id: string; - name: string; -} - -export interface DeelPayment { - rate: number; - scale: string; - currency: string; - contract_name: string; -} -export interface DeelDepartment { - id: string; - name: string; - parent: string; -} - -export interface DeelEmployment { - id: string; - name: string; - team: DeelTeam; - email: string; - state: string; - country: string; - payment: DeelPayment; - is_ended: boolean; - timezone: string; - job_title: string; - seniority: string; - start_date: string; // Date in string format - work_email: string; - hiring_type: string; - hiring_status: string; - completion_date: string; - contract_status: string; - voluntarily_left: string; - contract_coverage: string[]; - new_hiring_status: string; - client_legal_entity: DeelClientLegalEntity; - has_eor_termination: string; - contract_is_archived: boolean; - contract_has_contractor: boolean; - is_user_contract_deleted: boolean; - hris_direct_employee_invitation: string; -} diff --git a/packages/api/src/hris/employee/services/employee.service.ts b/packages/api/src/hris/employee/services/employee.service.ts deleted file mode 100644 index 7fe253309..000000000 --- a/packages/api/src/hris/employee/services/employee.service.ts +++ /dev/null @@ -1,397 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Injectable } from '@nestjs/common'; -import { v4 as uuidv4 } from 'uuid'; -import { - UnifiedHrisEmployeeInput, - UnifiedHrisEmployeeOutput, -} from '../types/model.unified'; -import { ServiceRegistry } from './registry.service'; -import { ApiResponse } from '@@core/utils/types'; -import { OriginalEmployeeOutput } from '@@core/utils/types/original/original.hris'; -import { HrisObject } from '@panora/shared'; -import { IEmployeeService } from '../types'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class EmployeeService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private coreUnification: CoreUnification, - private ingestService: IngestDataService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(EmployeeService.name); - } - - async validateLinkedUser(linkedUserId: string) { - const linkedUser = await this.prisma.linked_users.findUnique({ - where: { id_linked_user: linkedUserId }, - }); - if (!linkedUser) throw new ReferenceError('Linked User Not Found'); - return linkedUser; - } - - async addEmployee( - unifiedEmployeeData: UnifiedHrisEmployeeInput, - connection_id: string, - project_id: string, - integrationId: string, - linkedUserId: string, - remote_data?: boolean, - ): Promise { - try { - const linkedUser = await this.validateLinkedUser(linkedUserId); - - const desunifiedObject = - await this.coreUnification.desunify({ - sourceObject: unifiedEmployeeData, - targetType: HrisObject.employee, - providerName: integrationId, - vertical: 'hris', - customFieldMappings: [], - }); - - const service: IEmployeeService = - this.serviceRegistry.getService(integrationId); - const resp: ApiResponse = - await service.addEmployee(desunifiedObject, linkedUserId); - - const unifiedObject = (await this.coreUnification.unify< - OriginalEmployeeOutput[] - >({ - sourceObject: [resp.data], - targetType: HrisObject.employee, - providerName: integrationId, - vertical: 'hris', - connectionId: connection_id, - customFieldMappings: [], - })) as UnifiedHrisEmployeeOutput[]; - - const source_employee = resp.data; - const target_employee = unifiedObject[0]; - - const unique_hris_employee_id = await this.saveOrUpdateEmployee( - target_employee, - connection_id, - ); - - await this.ingestService.processRemoteData( - unique_hris_employee_id, - source_employee, - ); - - const result_employee = await this.getEmployee( - unique_hris_employee_id, - undefined, - undefined, - connection_id, - project_id, - remote_data, - ); - - const status_resp = resp.statusCode === 201 ? 'success' : 'fail'; - const event = await this.prisma.events.create({ - data: { - id_connection: connection_id, - id_project: project_id, - id_event: uuidv4(), - status: status_resp, - type: 'hris.employee.push', - method: 'POST', - url: '/hris/employees', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - await this.webhook.dispatchWebhook( - result_employee, - 'hris.employee.created', - linkedUser.id_project, - event.id_event, - ); - - return result_employee; - } catch (error) { - throw error; - } - } - - async saveOrUpdateEmployee( - employee: UnifiedHrisEmployeeOutput, - connectionId: string, - ): Promise { - const existingEmployee = await this.prisma.hris_employees.findFirst({ - where: { remote_id: employee.remote_id, id_connection: connectionId }, - }); - - const data: any = { - groups: employee.groups || [], - employee_number: employee.employee_number, - id_hris_company: employee.company_id, - first_name: employee.first_name, - last_name: employee.last_name, - preferred_name: employee.preferred_name, - display_full_name: employee.display_full_name, - username: employee.username, - work_email: employee.work_email, - personal_email: employee.personal_email, - mobile_phone_number: employee.mobile_phone_number, - employments: employee.employments || [], - ssn: employee.ssn, - gender: employee.gender, - ethnicity: employee.ethnicity, - marital_status: employee.marital_status, - date_of_birth: employee.date_of_birth, - start_date: employee.start_date, - employment_status: employee.employment_status, - termination_date: employee.termination_date, - avatar_url: employee.avatar_url, - modified_at: new Date(), - }; - - if (existingEmployee) { - const res = await this.prisma.hris_employees.update({ - where: { id_hris_employee: existingEmployee.id_hris_employee }, - data: data, - }); - - return res.id_hris_employee; - } else { - data.created_at = new Date(); - data.remote_id = employee.remote_id; - data.id_connection = connectionId; - data.id_hris_employee = uuidv4(); - data.remote_was_deleted = employee.remote_was_deleted ?? false; - data.remote_created_at = employee.remote_created_at - ? new Date(employee.remote_created_at) - : null; - - const newEmployee = await this.prisma.hris_employees.create({ - data: data, - }); - - return newEmployee.id_hris_employee; - } - } - - async getEmployee( - id_hris_employee: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const employee = await this.prisma.hris_employees.findUnique({ - where: { id_hris_employee: id_hris_employee }, - }); - - if (!employee) { - throw new Error(`Employee with ID ${id_hris_employee} not found.`); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: employee.id_hris_employee }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const locations = await this.prisma.hris_locations.findMany({ - where: { - id_hris_employee: employee.id_hris_employee, - }, - }); - - const unifiedEmployee: UnifiedHrisEmployeeOutput = { - id: employee.id_hris_employee, - groups: employee.groups, - employee_number: employee.employee_number, - company_id: employee.id_hris_company, - first_name: employee.first_name, - last_name: employee.last_name, - preferred_name: employee.preferred_name, - display_full_name: employee.display_full_name, - username: employee.username, - work_email: employee.work_email, - personal_email: employee.personal_email, - mobile_phone_number: employee.mobile_phone_number, - employments: employee.employments, - ssn: employee.ssn, - manager_id: employee.manager, - gender: employee.gender, - ethnicity: employee.ethnicity, - marital_status: employee.marital_status, - date_of_birth: employee.date_of_birth, - start_date: employee.start_date, - employment_status: employee.employment_status, - termination_date: employee.termination_date, - avatar_url: employee.avatar_url, - locations: locations.map((loc) => loc.id_hris_location), - field_mappings: field_mappings, - remote_id: employee.remote_id, - remote_created_at: employee.remote_created_at, - created_at: employee.created_at, - modified_at: employee.modified_at, - remote_was_deleted: employee.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: employee.id_hris_employee }, - }); - unifiedEmployee.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.employee.pull', - method: 'GET', - url: '/hris/employee', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedEmployee; - } catch (error) { - throw error; - } - } - - async getEmployees( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisEmployeeOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const employees = await this.prisma.hris_employees.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_employee: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }); - - const hasNextPage = employees.length > limit; - if (hasNextPage) employees.pop(); - - const unifiedEmployees = await Promise.all( - employees.map(async (employee) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: employee.id_hris_employee }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const locations = await this.prisma.hris_locations.findMany({ - where: { - id_hris_employee: employee.id_hris_employee, - }, - }); - - const unifiedEmployee: UnifiedHrisEmployeeOutput = { - id: employee.id_hris_employee, - groups: employee.groups, - employee_number: employee.employee_number, - company_id: employee.id_hris_company, - first_name: employee.first_name, - last_name: employee.last_name, - preferred_name: employee.preferred_name, - display_full_name: employee.display_full_name, - username: employee.username, - locations: locations.map((loc) => loc.id_hris_location), - manager_id: employee.manager, - work_email: employee.work_email, - personal_email: employee.personal_email, - mobile_phone_number: employee.mobile_phone_number, - employments: employee.employments, - ssn: employee.ssn, - gender: employee.gender, - ethnicity: employee.ethnicity, - marital_status: employee.marital_status, - date_of_birth: employee.date_of_birth, - start_date: employee.start_date, - employment_status: employee.employment_status, - termination_date: employee.termination_date, - avatar_url: employee.avatar_url, - field_mappings: field_mappings, - remote_id: employee.remote_id, - remote_created_at: employee.remote_created_at, - }; - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: employee.id_hris_employee }, - }); - unifiedEmployee.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedEmployee; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.employee.pull', - method: 'GET', - url: '/hris/employees', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedEmployees, - next_cursor: hasNextPage - ? employees[employees.length - 1].id_hris_employee - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/employee/services/gusto/index.ts b/packages/api/src/hris/employee/services/gusto/index.ts deleted file mode 100644 index 942f1a548..000000000 --- a/packages/api/src/hris/employee/services/gusto/index.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { HrisObject } from '@hris/@lib/@types'; -import { IEmployeeService } from '@hris/employee/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { GustoEmployeeOutput } from './types'; - -@Injectable() -export class GustoService implements IEmployeeService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - HrisObject.employee.toUpperCase() + ':' + GustoService.name, - ); - this.registry.registerService('gusto', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId, id_company } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gusto', - vertical: 'hris', - }, - }); - - const company = await this.prisma.hris_companies.findUnique({ - where: { - id_hris_company: id_company as string, - }, - select: { - remote_id: true, - }, - }); - - const resp = await axios.get( - `${connection.account_url}/v1/companies/${company.remote_id}/employees`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - this.logger.log(`Synced gusto employees !`); - - return { - data: resp.data, - message: 'Gusto employees retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/employee/services/gusto/mappers.ts b/packages/api/src/hris/employee/services/gusto/mappers.ts deleted file mode 100644 index 6ea34d7f6..000000000 --- a/packages/api/src/hris/employee/services/gusto/mappers.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Injectable } from '@nestjs/common'; -import { GustoEmployeeOutput } from './types'; -import { - UnifiedHrisEmployeeInput, - UnifiedHrisEmployeeOutput, -} from '@hris/employee/types/model.unified'; -import { IEmployeeMapper } from '@hris/employee/types'; -import { Utils } from '@hris/@lib/@utils'; -import { Job } from 'bull'; -import { HrisObject, TicketingObject } from '@panora/shared'; -import { ZendeskTagOutput } from '@ticketing/tag/services/zendesk/types'; -import axios from 'axios'; -import { UnifiedHrisLocationOutput } from '@hris/location/types/model.unified'; -import { UnifiedHrisEmploymentOutput } from '@hris/employment/types/model.unified'; -import { GustoEmploymentOutput } from '@hris/employment/services/gusto/types'; - -@Injectable() -export class GustoEmployeeMapper implements IEmployeeMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'employee', 'gusto', this); - } - - async desunify( - source: UnifiedHrisEmployeeInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return; - } - - async unify( - source: GustoEmployeeOutput | GustoEmployeeOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleEmployeeToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((employee) => - this.mapSingleEmployeeToUnified( - employee, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleEmployeeToUnified( - employee: GustoEmployeeOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - const opts: any = {}; - if (employee.company_uuid) { - const company_id = await this.utils.getCompanyUuidFromRemoteId( - employee.company_uuid, - connectionId, - ); - if (company_id) { - opts.company_id = company_id; - } - } - if (employee.manager_uuid) { - const manager_id = await this.utils.getEmployeeUuidFromRemoteId( - employee.manager_uuid, - connectionId, - ); - if (manager_id) { - opts.manager_id = manager_id; - } - } - - if (employee.jobs) { - const compensationObjects = employee.jobs.map((job) => { - const compensation = - job.compensations.find( - (compensation) => - compensation.uuid === job.current_compensation_uuid, - ) || null; - - return { - ...compensation, - title: job.title, - }; - }); - const employments = await this.ingestService.ingestData< - UnifiedHrisEmploymentOutput, - GustoEmploymentOutput - >( - compensationObjects, - 'gusto', - connectionId, - 'hris', - HrisObject.employment, - [], - ); - if (employments) { - opts.employments = employments.map((emp) => emp.id_hris_employment); - } - } - - const primaryJob = employee.jobs.find((job) => job.primary); - - return { - remote_id: employee.uuid, - remote_data: employee, - first_name: employee.first_name, - last_name: employee.last_name, - preferred_name: employee.preferred_first_name, - display_full_name: `${employee.first_name} ${employee.last_name}`, - work_email: employee.work_email, - personal_email: employee.email, - mobile_phone_number: employee.phone, - start_date: primaryJob ? new Date(primaryJob.hire_date) : null, - termination_date: - employee.terminations.length > 0 - ? new Date(employee.terminations[0].effective_date) - : null, - employment_status: employee.current_employment_status, - date_of_birth: employee.date_of_birth - ? new Date(employee.date_of_birth) - : null, - ...opts, - }; - } -} diff --git a/packages/api/src/hris/employee/services/gusto/types.ts b/packages/api/src/hris/employee/services/gusto/types.ts deleted file mode 100644 index 881500b5f..000000000 --- a/packages/api/src/hris/employee/services/gusto/types.ts +++ /dev/null @@ -1,123 +0,0 @@ -export type GustoEmployeeOutput = { - uuid: string; // The UUID of the employee in Gusto. - first_name: string; // The first name of the employee. - middle_initial: string | null; // The middle initial of the employee. - last_name: string; // The last name of the employee. - email: string | null; // The personal email address of the employee. - company_uuid: string; // The UUID of the company the employee is employed by. - manager_uuid: string; // The UUID of the employee's manager. - version: string; // The current version of the employee. - department: string | null; // The employee's department in the company. - terminated: boolean; // Whether the employee is terminated. - two_percent_shareholder: boolean; // Whether the employee is a two percent shareholder of the company. - onboarded: boolean; // Whether the employee has completed onboarding. - onboarding_status: - | 'onboarding_completed' - | 'admin_onboarding_incomplete' - | 'self_onboarding_pending_invite' - | 'self_onboarding_invited' - | 'self_onboarding_invited_started' - | 'self_onboarding_invited_overdue' - | 'self_onboarding_completed_by_employee' - | 'self_onboarding_awaiting_admin_review'; // The current onboarding status of the employee. - jobs: Job[]; // The jobs held by the employee. - terminations: Termination[]; // The terminations of the employee. - garnishments: Garnishment[]; // The garnishments of the employee. - custom_fields?: CustomField[]; // Custom fields for the employee. - date_of_birth: string | null; // The date of birth of the employee. - has_ssn: boolean; // Indicates whether the employee has an SSN in Gusto. - ssn: string; // Deprecated. This field always returns an empty string. - phone: string; // The phone number of the employee. - preferred_first_name: string; // The preferred first name of the employee. - payment_method: 'Direct Deposit' | 'Check' | null; // The employee's payment method. - work_email: string | null; // The work email address of the employee. - current_employment_status: - | 'full_time' - | 'part_time_under_twenty_hours' - | 'part_time_twenty_plus_hours' - | 'variable' - | 'seasonal' - | null; // The current employment status of the employee. -}; - -type Job = { - uuid: string; // The UUID of the job. - version: string; // The current version of the job. - employee_uuid: string; // The UUID of the employee to which the job belongs. - hire_date: string; // The date when the employee was hired or rehired for the job. - title: string | null; // The title for the job. - primary: boolean; // Whether this is the employee's primary job. - rate: string; // The current compensation rate of the job. - payment_unit: string; // The payment unit of the current compensation for the job. - current_compensation_uuid: string; // The UUID of the current compensation of the job. - two_percent_shareholder: boolean; // Whether the employee owns at least 2% of the company. - state_wc_covered: boolean; // Whether this job is eligible for workers' compensation coverage in the state of Washington (WA). - state_wc_class_code: string; // The risk class code for workers' compensation in Washington state. - compensations: Compensation[]; // The compensations associated with the job. -}; - -type Compensation = { - uuid: string; // The UUID of the compensation in Gusto. - version: string; // The current version of the compensation. - job_uuid: string; // The UUID of the job to which the compensation belongs. - rate: string; // The dollar amount paid per payment unit. - payment_unit: 'Hour' | 'Week' | 'Month' | 'Year' | 'Paycheck'; // The unit accompanying the compensation rate. - flsa_status: - | 'Exempt' - | 'Salaried Nonexempt' - | 'Nonexempt' - | 'Owner' - | 'Commission Only Exempt' - | 'Commission Only Nonexempt'; // The FLSA status for this compensation. - effective_date: string; // The effective date for this compensation. - adjust_for_minimum_wage: boolean; // Indicates if the compensation could be adjusted to minimum wage during payroll calculation. - eligible_paid_time_off: EligiblePaidTimeOff[]; // The available types of paid time off for the compensation. -}; - -type EligiblePaidTimeOff = { - name: string; // The name of the paid time off type. - policy_name: string; // The name of the time off policy. - policy_uuid: string; // The UUID of the time off policy. - accrual_unit: string; // The unit the PTO type is accrued in. - accrual_rate: string; // The number of accrual units accrued per accrual period. - accrual_method: string; // The accrual method of the time off policy. - accrual_period: string; // The frequency at which the PTO type is accrued. - accrual_balance: string; // The number of accrual units accrued. - maximum_accrual_balance: string | null; // The maximum number of accrual units allowed. - paid_at_termination: boolean; // Whether the accrual balance is paid to the employee upon termination. -}; - -type Termination = { - uuid: string; // The UUID of the termination object. - version: string; // The current version of the termination. - employee_uuid: string; // The UUID of the employee to which this termination is attached. - active: boolean; // Whether the employee's termination has gone into effect. - cancelable: boolean; // Whether the employee's termination is cancelable. - effective_date: string; // The employee's last day of work. - run_termination_payroll: boolean; // Whether the employee should receive their final wages via an off-cycle payroll. -}; - -type Garnishment = { - uuid: string; // The UUID of the garnishment in Gusto. - version: string; // The current version of the garnishment. - employee_uuid: string; // The UUID of the employee to which this garnishment belongs. - active: boolean; // Whether or not this garnishment is currently active. - amount: string; // The amount of the garnishment. - description: string; // The description of the garnishment. - court_ordered: boolean; // Whether the garnishment is court ordered. - times: number | null; // The number of times to apply the garnishment. - recurring: boolean; // Whether the garnishment should recur indefinitely. - annual_maximum: string | null; // The maximum deduction per annum. - pay_period_maximum: string | null; // The maximum deduction per pay period. - deduct_as_percentage: boolean; // Whether the amount should be treated as a percentage to be deducted per pay period. -}; - -type CustomField = { - id: string; // The ID of the custom field. - company_custom_field_id: string; // The ID of the company custom field. - name: string; // The name of the custom field. - type: 'text' | 'currency' | 'number' | 'date' | 'radio'; // Input type for the custom field. - description: string; // The description of the custom field. - value: string; // The value of the custom field. - selection_options: string[] | null; // An array of options for fields of type radio. -}; diff --git a/packages/api/src/hris/employee/services/registry.service.ts b/packages/api/src/hris/employee/services/registry.service.ts deleted file mode 100644 index 5368f0ef5..000000000 --- a/packages/api/src/hris/employee/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IEmployeeService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IEmployeeService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IEmployeeService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/employee/services/sage/index.ts b/packages/api/src/hris/employee/services/sage/index.ts deleted file mode 100644 index be8e2937c..000000000 --- a/packages/api/src/hris/employee/services/sage/index.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { HrisObject } from '@hris/@lib/@types'; -import { IEmployeeService } from '@hris/employee/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { SageEmployeeOutput } from './types'; - -@Injectable() -export class SageService implements IEmployeeService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - HrisObject.employee.toUpperCase() + ':' + SageService.name, - ); - this.registry.registerService('sage', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'sage', - vertical: 'hris', - }, - }); - - const resp = await axios.get(`${connection.account_url}/api/employees`, { - headers: { - 'Content-Type': 'application/json', - 'X-Auth-Token': this.cryptoService.decrypt(connection.access_token), - }, - }); - this.logger.log(`Synced sage employees !`); - - return { - data: resp.data.data, - message: 'Sage employees retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/employee/services/sage/mappers.ts b/packages/api/src/hris/employee/services/sage/mappers.ts deleted file mode 100644 index 3c0064346..000000000 --- a/packages/api/src/hris/employee/services/sage/mappers.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Utils } from '@hris/@lib/@utils'; -import { IEmployeeMapper } from '@hris/employee/types'; -import { - EmploymentStatus, - Gender, - MartialStatus, - UnifiedHrisEmployeeInput, - UnifiedHrisEmployeeOutput, -} from '@hris/employee/types/model.unified'; -import { Injectable } from '@nestjs/common'; -import { SageEmployeeOutput } from './types'; - -@Injectable() -export class SageEmployeeMapper implements IEmployeeMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'employee', 'sage', this); - } - - async desunify( - source: UnifiedHrisEmployeeInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - // Implementation for desunify (if needed) - return; - } - - async unify( - source: SageEmployeeOutput | SageEmployeeOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleEmployeeToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((employee) => - this.mapSingleEmployeeToUnified( - employee, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleEmployeeToUnified( - employee: SageEmployeeOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return { - remote_id: employee.id.toString(), - remote_data: employee, - first_name: employee.first_name, - last_name: employee.last_name, - display_full_name: `${employee.first_name} ${employee.last_name}`, - work_email: employee.email, - mobile_phone_number: employee.mobile_phone, - employments: [], // We would need to process employment history to populate this - groups: [employee.team], - start_date: new Date(employee.employment_start_date), - employment_status: this.mapEmploymentStatus(employee.employment_status), - date_of_birth: new Date(employee.date_of_birth), - gender: this.mapGender(employee.gender), - marital_status: this.mapMaritalStatus(employee.marital_status), - avatar_url: employee.picture_url, - ssn: employee.personal_identification_number, - employee_number: employee.employee_number, - }; - } - - private mapGender(sageGender: string): Gender { - switch (sageGender.toLowerCase()) { - case 'male': - return 'MALE'; - case 'female': - return 'FEMALE'; - default: - return 'OTHER'; - } - } - - private mapMaritalStatus(sageMaritalStatus: string): MartialStatus { - switch (sageMaritalStatus.toLowerCase()) { - case 'married': - return 'MARRIED_FILING_JOINTLY'; - case 'single': - return 'SINGLE'; - default: - return 'SINGLE'; // Default to single if unknown - } - } - - private mapEmploymentStatus(sageEmploymentStatus: string): EmploymentStatus { - switch (sageEmploymentStatus.toLowerCase()) { - case 'full-time': - case 'part-time': - return 'ACTIVE'; - default: - return 'INACTIVE'; - } - } -} diff --git a/packages/api/src/hris/employee/services/sage/types.ts b/packages/api/src/hris/employee/services/sage/types.ts deleted file mode 100644 index c4f986e70..000000000 --- a/packages/api/src/hris/employee/services/sage/types.ts +++ /dev/null @@ -1,55 +0,0 @@ -export type SageEmployeeOutput = Partial<{ - id: number; - email: string; - first_name: string; - last_name: string; - picture_url: string; - employment_start_date: string; - date_of_birth: string; - team: string; - team_id: number; - position: string; - position_id: number; - reports_to_employee_id: number; - work_phone: string; - home_phone: string; - mobile_phone: string; - gender: string; - street_first: string; - street_second: string; - city: string; - post_code: number; - country: string; - employee_number: string; - employment_status: string; - nationality: string; - marital_status: string; - personal_identification_number: string; - tax_number: string; - irregular_contract_worker: boolean; - team_history: SageTeamHistory[]; - employment_status_history: SageEmploymentStatusHistory[]; - position_history: SagePositionHistory[]; -}>; - -export interface SageTeamHistory { - team_id: number; - start_date: string; - end_date: string; - team_name: string; -} - -export interface SageEmploymentStatusHistory { - employment_status_id: number; - start_date: string; - end_date: string; - employment_statu_name: string; // Note: This seems to be a typo in the original data -} - -export interface SagePositionHistory { - position_id: number; - start_date: string; - end_date: string; - position_name: string; - position_code: string; -} diff --git a/packages/api/src/hris/employee/sync/sync.service.ts b/packages/api/src/hris/employee/sync/sync.service.ts deleted file mode 100644 index 732dc99ff..000000000 --- a/packages/api/src/hris/employee/sync/sync.service.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { ApiResponse } from '@@core/utils/types'; -import { v4 as uuidv4 } from 'uuid'; -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 { UnifiedHrisEmployeeOutput } from '../types/model.unified'; -import { IEmployeeService } from '../types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_employees as HrisEmployee } from '@prisma/client'; -import { OriginalEmployeeOutput } from '@@core/utils/types/original/original.hris'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'employee', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId, id_company } = param; - const service: IEmployeeService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisEmployeeOutput, - OriginalEmployeeOutput, - IEmployeeService - >(integrationId, linkedUserId, 'hris', 'employee', service, [ - { - param: id_company, - paramName: 'id_company', - shouldPassToService: true, - shouldPassToIngest: true, - }, - ]); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - employees: UnifiedHrisEmployeeOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const employeeResults: HrisEmployee[] = []; - - for (let i = 0; i < employees.length; i++) { - const employee = employees[i]; - const originId = employee.remote_id; - - let existingEmployee = await this.prisma.hris_employees.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const employeeData = { - groups: employee.groups || [], - employee_number: employee.employee_number, - id_hris_company: employee.company_id, - first_name: employee.first_name, - last_name: employee.last_name, - preferred_name: employee.preferred_name, - display_full_name: employee.display_full_name, - username: employee.username, - work_email: employee.work_email, - personal_email: employee.personal_email, - mobile_phone_number: employee.mobile_phone_number, - employments: employee.employments || [], - ssn: employee.ssn, - gender: employee.gender, - manager_id: employee.manager_id, - ethnicity: employee.ethnicity, - marital_status: employee.marital_status, - date_of_birth: employee.date_of_birth - ? new Date(employee.date_of_birth) - : null, - start_date: employee.start_date - ? new Date(employee.start_date) - : null, - employment_status: employee.employment_status, - termination_date: employee.termination_date - ? new Date(employee.termination_date) - : null, - avatar_url: employee.avatar_url, - remote_id: originId, - remote_created_at: employee.remote_created_at - ? new Date(employee.remote_created_at) - : null, - modified_at: new Date(), - remote_was_deleted: employee.remote_was_deleted || false, - }; - - if (existingEmployee) { - existingEmployee = await this.prisma.hris_employees.update({ - where: { id_hris_employee: existingEmployee.id_hris_employee }, - data: employeeData, - }); - } else { - existingEmployee = await this.prisma.hris_employees.create({ - data: { - ...employeeData, - id_hris_employee: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - employeeResults.push(existingEmployee); - - // Process field mappings - await this.ingestService.processFieldMappings( - employee.field_mappings, - existingEmployee.id_hris_employee, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingEmployee.id_hris_employee, - remote_data[i], - ); - } - - return employeeResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/employee/types/index.ts b/packages/api/src/hris/employee/types/index.ts deleted file mode 100644 index fc44e8583..000000000 --- a/packages/api/src/hris/employee/types/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisEmployeeInput, - UnifiedHrisEmployeeOutput, -} from './model.unified'; -import { OriginalEmployeeOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface IEmployeeService { - addEmployee?( - employeeData: DesunifyReturnType, - linkedUserId: string, - ): Promise>; - - sync(data: SyncParam): Promise>; -} - -export interface IEmployeeMapper { - desunify( - source: UnifiedHrisEmployeeInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalEmployeeOutput | OriginalEmployeeOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/hris/employee/types/model.unified.ts b/packages/api/src/hris/employee/types/model.unified.ts deleted file mode 100644 index 335e2ba49..000000000 --- a/packages/api/src/hris/employee/types/model.unified.ts +++ /dev/null @@ -1,382 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsArray, - IsDateString, - IsEmail, - IsUrl, -} from 'class-validator'; - -export type Gender = - | 'MALE' - | 'FEMALE' - | 'NON-BINARY' - | 'OTHER' - | 'PREFER_NOT_TO_DISCLOSE'; - -export type Ethnicity = - | 'AMERICAN_INDIAN_OR_ALASKA_NATIVE' - | 'ASIAN_OR_INDIAN_SUBCONTINENT' - | 'BLACK_OR_AFRICAN_AMERICAN' - | 'HISPANIC_OR_LATINO' - | 'NATIVE_HAWAIIAN_OR_OTHER_PACIFIC_ISLANDER' - | 'TWO_OR_MORE_RACES' - | 'WHITE' - | 'PREFER_NOT_TO_DISCLOSE'; - -export type MartialStatus = - | 'SINGLE' - | 'MARRIED_FILING_JOINTLY' - | 'MARRIED_FILING_SEPARATELY' - | 'HEAD_OF_HOUSEHOLD' - | 'QUALIFYING_WIDOW_OR_WIDOWER_WITH_DEPENDENT_CHILD'; - -export type EmploymentStatus = 'ACTIVE' | 'PENDING' | 'INACTIVE'; - -export class UnifiedHrisEmployeeInput { - @ApiPropertyOptional({ - type: [String], - example: ['Group1', 'Group2'], - nullable: true, - description: 'The groups the employee belongs to', - }) - @IsArray() - @IsString({ each: true }) - @IsOptional() - groups?: string[]; - - @ApiPropertyOptional({ - type: [String], - example: ['801f9ede-c698-4e66-a7fc-48d19eebaa4f'], - nullable: true, - description: 'UUIDs of the of the Location associated with the company', - }) - @IsString() - @IsOptional() - locations?: string[]; - - @ApiPropertyOptional({ - type: String, - example: 'EMP001', - nullable: true, - description: 'The employee number', - }) - @IsString() - @IsOptional() - employee_number?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the associated company', - }) - @IsUUID() - @IsOptional() - company_id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'John', - nullable: true, - description: 'The first name of the employee', - }) - @IsString() - @IsOptional() - first_name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'Doe', - nullable: true, - description: 'The last name of the employee', - }) - @IsString() - @IsOptional() - last_name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'Johnny', - nullable: true, - description: 'The preferred name of the employee', - }) - @IsString() - @IsOptional() - preferred_name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'John Doe', - nullable: true, - description: 'The full display name of the employee', - }) - @IsString() - @IsOptional() - display_full_name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'johndoe', - nullable: true, - description: 'The username of the employee', - }) - @IsString() - @IsOptional() - username?: string; - - @ApiPropertyOptional({ - type: String, - example: 'john.doe@company.com', - nullable: true, - description: 'The work email of the employee', - }) - @IsEmail() - @IsOptional() - work_email?: string; - - @ApiPropertyOptional({ - type: String, - example: 'john.doe@personal.com', - nullable: true, - description: 'The personal email of the employee', - }) - @IsEmail() - @IsOptional() - personal_email?: string; - - @ApiPropertyOptional({ - type: String, - example: '+1234567890', - nullable: true, - description: 'The mobile phone number of the employee', - }) - @IsString() - @IsOptional() - mobile_phone_number?: string; - - @ApiPropertyOptional({ - type: [String], - example: [ - '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - ], - nullable: true, - description: 'The employments of the employee', - }) - @IsArray() - @IsString({ each: true }) - @IsOptional() - employments?: string[]; - - @ApiPropertyOptional({ - type: String, - example: '123-45-6789', - nullable: true, - description: 'The Social Security Number of the employee', - }) - @IsString() - @IsOptional() - ssn?: string; - - @ApiPropertyOptional({ - type: String, - example: 'MALE', - // enum: ['MALE', 'FEMALE', 'NON-BINARY', 'OTHER', 'PREFER_NOT_TO_DISCLOSE'], - nullable: true, - description: 'The gender of the employee', - }) - @IsString() - @IsOptional() - gender?: Gender | string; - - @ApiPropertyOptional({ - type: String, - example: 'AMERICAN_INDIAN_OR_ALASKA_NATIVE', - /* enum: [ - 'AMERICAN_INDIAN_OR_ALASKA_NATIVE', - 'ASIAN_OR_INDIAN_SUBCONTINENT', - 'BLACK_OR_AFRICAN_AMERICAN', - 'HISPANIC_OR_LATINO', - 'NATIVE_HAWAIIAN_OR_OTHER_PACIFIC_ISLANDER', - 'TWO_OR_MORE_RACES', - 'WHITE', - 'PREFER_NOT_TO_DISCLOSE', - ],*/ - nullable: true, - description: 'The ethnicity of the employee', - }) - @IsString() - @IsOptional() - ethnicity?: Ethnicity | string; - - @ApiPropertyOptional({ - type: String, - example: 'Married', - /* enum: [ - 'SINGLE', - 'MARRIED_FILING_JOINTLY', - 'MARRIED_FILING_SEPARATELY', - 'HEAD_OF_HOUSEHOLD', - 'QUALIFYING_WIDOW_OR_WIDOWER_WITH_DEPENDENT_CHILD', - ],*/ - nullable: true, - description: 'The marital status of the employee', - }) - @IsString() - @IsOptional() - marital_status?: MartialStatus | string; - - @ApiPropertyOptional({ - type: Date, - example: '1990-01-01', - nullable: true, - description: 'The date of birth of the employee', - }) - @IsDateString() - @IsOptional() - date_of_birth?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2020-01-01', - nullable: true, - description: 'The start date of the employee', - }) - @IsDateString() - @IsOptional() - start_date?: Date; - - @ApiPropertyOptional({ - type: String, - example: 'ACTIVE', - // enum: ['ACTIVE', 'PENDING', 'INACTIVE'], - nullable: true, - description: 'The employment status of the employee', - }) - @IsString() - @IsOptional() - employment_status?: EmploymentStatus | string; - - @ApiPropertyOptional({ - type: Date, - example: '2025-01-01', - nullable: true, - description: 'The termination date of the employee', - }) - @IsDateString() - @IsOptional() - termination_date?: Date; - - @ApiPropertyOptional({ - type: String, - example: 'https://example.com/avatar.jpg', - nullable: true, - description: "The URL of the employee's avatar", - }) - @IsUrl() - @IsOptional() - avatar_url?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'UUID of the manager (employee) of the employee', - }) - @IsUrl() - @IsOptional() - manager_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisEmployeeOutput extends UnifiedHrisEmployeeInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the employee record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'employee_1234', - nullable: true, - description: - 'The remote ID of the employee in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the employee in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: - 'The date when the employee was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The created date of the employee record', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The last modified date of the employee record', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: 'Indicates if the employee was deleted in the remote system', - }) - @IsOptional() - remote_was_deleted?: boolean; -} diff --git a/packages/api/src/hris/employee/utils/index.ts b/packages/api/src/hris/employee/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/employee/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/employeepayrollrun/employeepayrollrun.controller.ts b/packages/api/src/hris/employeepayrollrun/employeepayrollrun.controller.ts deleted file mode 100644 index ec3cddb5e..000000000 --- a/packages/api/src/hris/employeepayrollrun/employeepayrollrun.controller.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { EmployeePayrollRunService } from './services/employeepayrollrun.service'; -import { - UnifiedHrisEmployeepayrollrunInput, - UnifiedHrisEmployeepayrollrunOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('hris/employeepayrollruns') -@Controller('hris/employeepayrollruns') -export class EmployeePayrollRunController { - constructor( - private readonly employeepayrollrunService: EmployeePayrollRunService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(EmployeePayrollRunController.name); - } - - @ApiOperation({ - operationId: 'listHrisEmployeePayrollRun', - summary: 'List Employee Payroll Runs', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisEmployeepayrollrunOutput) - @UseGuards(ApiKeyAuthGuard) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @Get() - async getEmployeePayrollRuns( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.employeepayrollrunService.getEmployeePayrollRuns( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisEmployeePayrollRun', - summary: 'Retrieve Employee Payroll Run', - description: - 'Retrieve Employee Payroll Run from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the employeepayrollrun you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisEmployeepayrollrunOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.employeepayrollrunService.getEmployeePayrollRun( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/hris/employeepayrollrun/employeepayrollrun.module.ts b/packages/api/src/hris/employeepayrollrun/employeepayrollrun.module.ts deleted file mode 100644 index e918446e9..000000000 --- a/packages/api/src/hris/employeepayrollrun/employeepayrollrun.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Module } from '@nestjs/common'; -import { EmployeePayrollRunController } from './employeepayrollrun.controller'; -import { EmployeePayrollRunService } from './services/employeepayrollrun.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Utils } from '@hris/@lib/@utils'; - -@Module({ - controllers: [EmployeePayrollRunController], - providers: [ - EmployeePayrollRunService, - CoreUnification, - Utils, - SyncService, - WebhookService, - ServiceRegistry, - IngestDataService, - /* PROVIDERS SERVICES */ - ], - exports: [SyncService], -}) -export class EmployeePayrollRunModule {} diff --git a/packages/api/src/hris/employeepayrollrun/services/employeepayrollrun.service.ts b/packages/api/src/hris/employeepayrollrun/services/employeepayrollrun.service.ts deleted file mode 100644 index 4b0ef884a..000000000 --- a/packages/api/src/hris/employeepayrollrun/services/employeepayrollrun.service.ts +++ /dev/null @@ -1,257 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Injectable } from '@nestjs/common'; -import { v4 as uuidv4 } from 'uuid'; -import { UnifiedHrisEmployeepayrollrunOutput } from '../types/model.unified'; -import { ServiceRegistry } from './registry.service'; - -@Injectable() -export class EmployeePayrollRunService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(EmployeePayrollRunService.name); - } - - async getEmployeePayrollRun( - id_hris_employee_payroll_run: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const employeePayrollRun = - await this.prisma.hris_employee_payroll_runs.findUnique({ - where: { id_hris_employee_payroll_run: id_hris_employee_payroll_run }, - include: { - hris_employee_payroll_runs_deductions: true, - hris_employee_payroll_runs_earnings: true, - hris_employee_payroll_runs_taxes: true, - }, - }); - - if (!employeePayrollRun) { - throw new Error( - `Employee Payroll Run with ID ${id_hris_employee_payroll_run} not found.`, - ); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: employeePayrollRun.id_hris_employee_payroll_run, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedEmployeePayrollRun: UnifiedHrisEmployeepayrollrunOutput = { - id: employeePayrollRun.id_hris_employee_payroll_run, - employee_id: employeePayrollRun.id_hris_employee, - payroll_run_id: employeePayrollRun.id_hris_payroll_run, - gross_pay: Number(employeePayrollRun.gross_pay), - net_pay: Number(employeePayrollRun.net_pay), - start_date: employeePayrollRun.start_date, - end_date: employeePayrollRun.end_date, - check_date: employeePayrollRun.check_date, - deductions: - employeePayrollRun.hris_employee_payroll_runs_deductions.map((d) => ({ - name: d.name, - employee_deduction: Number(d.employee_deduction), - company_deduction: Number(d.company_deduction), - })), - earnings: employeePayrollRun.hris_employee_payroll_runs_earnings.map( - (e) => ({ - amount: Number(e.amount), - type: e.type, - }), - ), - taxes: employeePayrollRun.hris_employee_payroll_runs_taxes.map((t) => ({ - name: t.name, - amount: Number(t.amount), - employer_tax: t.employer_tax, - })), - field_mappings: field_mappings, - remote_id: employeePayrollRun.remote_id, - remote_created_at: employeePayrollRun.remote_created_at, - created_at: employeePayrollRun.created_at, - modified_at: employeePayrollRun.modified_at, - remote_was_deleted: employeePayrollRun.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: employeePayrollRun.id_hris_employee_payroll_run, - }, - }); - unifiedEmployeePayrollRun.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.employee_payroll_run.pull', - method: 'GET', - url: '/hris/employee_payroll_run', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedEmployeePayrollRun; - } catch (error) { - throw error; - } - } - - async getEmployeePayrollRuns( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisEmployeepayrollrunOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const employeePayrollRuns = - await this.prisma.hris_employee_payroll_runs.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_employee_payroll_run: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - include: { - hris_employee_payroll_runs_deductions: true, - hris_employee_payroll_runs_earnings: true, - hris_employee_payroll_runs_taxes: true, - }, - }); - - const hasNextPage = employeePayrollRuns.length > limit; - if (hasNextPage) employeePayrollRuns.pop(); - - const unifiedEmployeePayrollRuns = await Promise.all( - employeePayrollRuns.map(async (employeePayrollRun) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: - employeePayrollRun.id_hris_employee_payroll_run, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedEmployeePayrollRun: UnifiedHrisEmployeepayrollrunOutput = - { - id: employeePayrollRun.id_hris_employee_payroll_run, - employee_id: employeePayrollRun.id_hris_employee, - payroll_run_id: employeePayrollRun.id_hris_payroll_run, - gross_pay: Number(employeePayrollRun.gross_pay), - net_pay: Number(employeePayrollRun.net_pay), - start_date: employeePayrollRun.start_date, - end_date: employeePayrollRun.end_date, - check_date: employeePayrollRun.check_date, - deductions: - employeePayrollRun.hris_employee_payroll_runs_deductions.map( - (d) => ({ - name: d.name, - employee_deduction: Number(d.employee_deduction), - company_deduction: Number(d.company_deduction), - }), - ), - earnings: - employeePayrollRun.hris_employee_payroll_runs_earnings.map( - (e) => ({ - amount: Number(e.amount), - type: e.type, - }), - ), - taxes: employeePayrollRun.hris_employee_payroll_runs_taxes.map( - (t) => ({ - name: t.name, - amount: Number(t.amount), - employer_tax: t.employer_tax, - }), - ), - field_mappings: field_mappings, - remote_id: employeePayrollRun.remote_id, - remote_created_at: employeePayrollRun.remote_created_at, - created_at: employeePayrollRun.created_at, - modified_at: employeePayrollRun.modified_at, - remote_was_deleted: employeePayrollRun.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: - employeePayrollRun.id_hris_employee_payroll_run, - }, - }); - unifiedEmployeePayrollRun.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedEmployeePayrollRun; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.employee_payroll_run.pull', - method: 'GET', - url: '/hris/employee_payroll_runs', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedEmployeePayrollRuns, - next_cursor: hasNextPage - ? employeePayrollRuns[employeePayrollRuns.length - 1] - .id_hris_employee_payroll_run - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/employeepayrollrun/services/registry.service.ts b/packages/api/src/hris/employeepayrollrun/services/registry.service.ts deleted file mode 100644 index 65b307002..000000000 --- a/packages/api/src/hris/employeepayrollrun/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IEmployeePayrollRunService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IEmployeePayrollRunService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IEmployeePayrollRunService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/employeepayrollrun/sync/sync.service.ts b/packages/api/src/hris/employeepayrollrun/sync/sync.service.ts deleted file mode 100644 index ee6b0669c..000000000 --- a/packages/api/src/hris/employeepayrollrun/sync/sync.service.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { ApiResponse } from '@@core/utils/types'; -import { v4 as uuidv4 } from 'uuid'; -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 { UnifiedHrisEmployeepayrollrunOutput } from '../types/model.unified'; -import { IEmployeePayrollRunService } from '../types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_employee_payroll_runs as HrisEmployeePayrollRun } from '@prisma/client'; -import { OriginalEmployeePayrollRunOutput } from '@@core/utils/types/original/original.hris'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'employeepayrollrun', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IEmployeePayrollRunService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisEmployeepayrollrunOutput, - OriginalEmployeePayrollRunOutput, - IEmployeePayrollRunService - >(integrationId, linkedUserId, 'hris', 'employeepayrollrun', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - employeePayrollRuns: UnifiedHrisEmployeepayrollrunOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const employeePayrollRunResults: HrisEmployeePayrollRun[] = []; - - for (let i = 0; i < employeePayrollRuns.length; i++) { - const employeePayrollRun = employeePayrollRuns[i]; - const originId = employeePayrollRun.remote_id; - - let existingEmployeePayrollRun = - await this.prisma.hris_employee_payroll_runs.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const employeePayrollRunData = { - id_hris_employee: employeePayrollRun.employee_id, - id_hris_payroll_run: employeePayrollRun.payroll_run_id, - gross_pay: employeePayrollRun.gross_pay - ? BigInt(employeePayrollRun.gross_pay) - : null, - net_pay: employeePayrollRun.net_pay - ? BigInt(employeePayrollRun.net_pay) - : null, - start_date: employeePayrollRun.start_date - ? new Date(employeePayrollRun.start_date) - : null, - end_date: employeePayrollRun.end_date - ? new Date(employeePayrollRun.end_date) - : null, - check_date: employeePayrollRun.check_date - ? new Date(employeePayrollRun.check_date) - : null, - remote_id: originId, - remote_created_at: employeePayrollRun.remote_created_at - ? new Date(employeePayrollRun.remote_created_at) - : null, - modified_at: new Date(), - remote_was_deleted: employeePayrollRun.remote_was_deleted || false, - }; - - if (existingEmployeePayrollRun) { - existingEmployeePayrollRun = - await this.prisma.hris_employee_payroll_runs.update({ - where: { - id_hris_employee_payroll_run: - existingEmployeePayrollRun.id_hris_employee_payroll_run, - }, - data: employeePayrollRunData, - }); - } else { - existingEmployeePayrollRun = - await this.prisma.hris_employee_payroll_runs.create({ - data: { - ...employeePayrollRunData, - id_hris_employee_payroll_run: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - employeePayrollRunResults.push(existingEmployeePayrollRun); - - // Process field mappings - await this.ingestService.processFieldMappings( - employeePayrollRun.field_mappings, - existingEmployeePayrollRun.id_hris_employee_payroll_run, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingEmployeePayrollRun.id_hris_employee_payroll_run, - remote_data[i], - ); - - // Process deductions, earnings, and taxes - await this.processDeductions( - existingEmployeePayrollRun.id_hris_employee_payroll_run, - employeePayrollRun.deductions, - ); - await this.processEarnings( - existingEmployeePayrollRun.id_hris_employee_payroll_run, - employeePayrollRun.earnings, - ); - await this.processTaxes( - existingEmployeePayrollRun.id_hris_employee_payroll_run, - employeePayrollRun.taxes, - ); - } - - return employeePayrollRunResults; - } catch (error) { - throw error; - } - } - - private async processDeductions( - id_hris_employee_payroll_run: string, - deductions: any[], - ) { - // Implementation for processing deductions - } - - private async processEarnings( - id_hris_employee_payroll_run: string, - earnings: any[], - ) { - // Implementation for processing earnings - } - - private async processTaxes( - id_hris_employee_payroll_run: string, - taxes: any[], - ) { - // Implementation for processing taxes - } -} diff --git a/packages/api/src/hris/employeepayrollrun/types/index.ts b/packages/api/src/hris/employeepayrollrun/types/index.ts deleted file mode 100644 index 71ef9f61e..000000000 --- a/packages/api/src/hris/employeepayrollrun/types/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisEmployeepayrollrunInput, - UnifiedHrisEmployeepayrollrunOutput, -} from './model.unified'; -import { OriginalEmployeePayrollRunOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface IEmployeePayrollRunService { - sync( - data: SyncParam, - ): Promise>; -} - -export interface IEmployeePayrollRunMapper { - desunify( - source: UnifiedHrisEmployeepayrollrunInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: - | OriginalEmployeePayrollRunOutput - | OriginalEmployeePayrollRunOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise< - UnifiedHrisEmployeepayrollrunOutput | UnifiedHrisEmployeepayrollrunOutput[] - >; -} diff --git a/packages/api/src/hris/employeepayrollrun/types/model.unified.ts b/packages/api/src/hris/employeepayrollrun/types/model.unified.ts deleted file mode 100644 index 6d41b69b9..000000000 --- a/packages/api/src/hris/employeepayrollrun/types/model.unified.ts +++ /dev/null @@ -1,287 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsNumber, - IsDateString, - IsBoolean, - IsArray, -} from 'class-validator'; - -class DeductionItem { - @ApiPropertyOptional({ - type: String, - example: 'Health Insurance', - nullable: true, - description: 'The name of the deduction', - }) - @IsString() - @IsOptional() - name?: string; - - @ApiPropertyOptional({ - type: Number, - example: 100, - nullable: true, - description: 'The amount of employee deduction', - }) - @IsNumber() - @IsOptional() - employee_deduction?: number; - - @ApiPropertyOptional({ - type: Number, - example: 200, - nullable: true, - description: 'The amount of company deduction', - }) - @IsNumber() - @IsOptional() - company_deduction?: number; -} - -class EarningItem { - @ApiPropertyOptional({ - type: Number, - example: 1000, - nullable: true, - description: 'The amount of the earning', - }) - @IsNumber() - @IsOptional() - amount?: number; - - @ApiPropertyOptional({ - type: String, - example: 'Salary', - nullable: true, - description: 'The type of the earning', - }) - @IsString() - @IsOptional() - type?: string; -} - -class TaxItem { - @ApiPropertyOptional({ - type: String, - example: 'Federal Income Tax', - nullable: true, - description: 'The name of the tax', - }) - @IsString() - @IsOptional() - name?: string; - - @ApiPropertyOptional({ - type: Number, - example: 250, - nullable: true, - description: 'The amount of the tax', - }) - @IsNumber() - @IsOptional() - amount?: number; - - @ApiPropertyOptional({ - type: Boolean, - example: true, - nullable: true, - description: 'Indicates if this is an employer tax', - }) - @IsBoolean() - @IsOptional() - employer_tax?: boolean; -} - -export class UnifiedHrisEmployeepayrollrunInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the associated employee', - }) - @IsUUID() - @IsOptional() - employee_id?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the associated payroll run', - }) - @IsUUID() - @IsOptional() - payroll_run_id?: string; - - @ApiPropertyOptional({ - type: Number, - example: 5000, - nullable: true, - description: 'The gross pay amount', - }) - @IsNumber() - @IsOptional() - gross_pay?: number; - - @ApiPropertyOptional({ - type: Number, - example: 4000, - nullable: true, - description: 'The net pay amount', - }) - @IsNumber() - @IsOptional() - net_pay?: number; - - @ApiPropertyOptional({ - type: Date, - example: '2023-01-01T00:00:00Z', - nullable: true, - description: 'The start date of the pay period', - }) - @IsDateString() - @IsOptional() - start_date?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2023-01-15T23:59:59Z', - nullable: true, - description: 'The end date of the pay period', - }) - @IsDateString() - @IsOptional() - end_date?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2023-01-20T00:00:00Z', - nullable: true, - description: 'The date the check was issued', - }) - @IsDateString() - @IsOptional() - check_date?: Date; - - @ApiPropertyOptional({ - type: [DeductionItem], - nullable: true, - description: 'The list of deductions for this payroll run', - }) - @IsArray() - @IsOptional() - deductions?: DeductionItem[]; - - @ApiPropertyOptional({ - type: [EarningItem], - nullable: true, - description: 'The list of earnings for this payroll run', - }) - @IsArray() - @IsOptional() - earnings?: EarningItem[]; - - @ApiPropertyOptional({ - type: [TaxItem], - nullable: true, - description: 'The list of taxes for this payroll run', - }) - @IsArray() - @IsOptional() - taxes?: TaxItem[]; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisEmployeepayrollrunOutput extends UnifiedHrisEmployeepayrollrunInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the employee payroll run record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'payroll_run_1234', - nullable: true, - description: - 'The remote ID of the employee payroll run in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the employee payroll run in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: - 'The date when the employee payroll run was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The created date of the employee payroll run record', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The last modified date of the employee payroll run record', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: - 'Indicates if the employee payroll run was deleted in the remote system', - }) - @IsBoolean() - @IsOptional() - remote_was_deleted?: boolean; -} diff --git a/packages/api/src/hris/employeepayrollrun/utils/index.ts b/packages/api/src/hris/employeepayrollrun/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/employeepayrollrun/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/employerbenefit/employerbenefit.controller.ts b/packages/api/src/hris/employerbenefit/employerbenefit.controller.ts deleted file mode 100644 index d0067f6cc..000000000 --- a/packages/api/src/hris/employerbenefit/employerbenefit.controller.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - Controller, - Get, - Headers, - Param, - Query, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { - ApiHeader, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, -} from '@nestjs/swagger'; - -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { EmployerBenefitService } from './services/employerbenefit.service'; -import { UnifiedHrisEmployerbenefitOutput } from './types/model.unified'; - -@ApiTags('hris/employerbenefits') -@Controller('hris/employerbenefits') -export class EmployerBenefitController { - constructor( - private readonly employerbenefitService: EmployerBenefitService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(EmployerBenefitController.name); - } - - @ApiOperation({ - operationId: 'listHrisEmployerBenefits', - summary: 'List Employer Benefits', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisEmployerbenefitOutput) - @UseGuards(ApiKeyAuthGuard) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @Get() - async getEmployerBenefits( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.employerbenefitService.getEmployerBenefits( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisEmployerBenefit', - summary: 'Retrieve Employer Benefit', - description: - 'Retrieve an Employer Benefit from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the employer benefit you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisEmployerbenefitOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.employerbenefitService.getEmployerBenefit( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/hris/employerbenefit/employerbenefit.module.ts b/packages/api/src/hris/employerbenefit/employerbenefit.module.ts deleted file mode 100644 index 29a62ed01..000000000 --- a/packages/api/src/hris/employerbenefit/employerbenefit.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Module } from '@nestjs/common'; -import { EmployerBenefitController } from './employerbenefit.controller'; -import { EmployerBenefitService } from './services/employerbenefit.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { GustoEmployerbenefitMapper } from './services/gusto/mappers'; -import { GustoService } from './services/gusto'; -import { Utils } from '@hris/@lib/@utils'; -@Module({ - controllers: [EmployerBenefitController], - providers: [ - EmployerBenefitService, - CoreUnification, - SyncService, - WebhookService, - ServiceRegistry, - Utils, - IngestDataService, - GustoEmployerbenefitMapper, - /* PROVIDERS SERVICES */ - GustoService, - ], - exports: [SyncService], -}) -export class EmployerBenefitModule {} diff --git a/packages/api/src/hris/employerbenefit/services/employerbenefit.service.ts b/packages/api/src/hris/employerbenefit/services/employerbenefit.service.ts deleted file mode 100644 index fedd3cb88..000000000 --- a/packages/api/src/hris/employerbenefit/services/employerbenefit.service.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { ApiResponse } from '@@core/utils/types'; -import { throwTypedError } from '@@core/utils/errors'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { - UnifiedHrisEmployerbenefitInput, - UnifiedHrisEmployerbenefitOutput, -} from '../types/model.unified'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ServiceRegistry } from './registry.service'; - -@Injectable() -export class EmployerBenefitService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(EmployerBenefitService.name); - } - - async getEmployerBenefit( - id_hris_employer_benefit: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const employerBenefit = - await this.prisma.hris_employer_benefits.findUnique({ - where: { id_hris_employer_benefit: id_hris_employer_benefit }, - }); - - if (!employerBenefit) { - throw new Error( - `Employer Benefit with ID ${id_hris_employer_benefit} not found.`, - ); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: employerBenefit.id_hris_employer_benefit, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedEmployerBenefit: UnifiedHrisEmployerbenefitOutput = { - id: employerBenefit.id_hris_employer_benefit, - benefit_plan_type: employerBenefit.benefit_plan_type, - name: employerBenefit.name, - description: employerBenefit.description, - deduction_code: employerBenefit.deduction_code, - field_mappings: field_mappings, - remote_id: employerBenefit.remote_id, - remote_created_at: employerBenefit.remote_created_at, - created_at: employerBenefit.created_at, - modified_at: employerBenefit.modified_at, - remote_was_deleted: employerBenefit.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: employerBenefit.id_hris_employer_benefit, - }, - }); - unifiedEmployerBenefit.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.employer_benefit.pull', - method: 'GET', - url: '/hris/employer_benefit', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedEmployerBenefit; - } catch (error) { - throw error; - } - } - - async getEmployerBenefits( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisEmployerbenefitOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const employerBenefits = - await this.prisma.hris_employer_benefits.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_employer_benefit: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }); - - const hasNextPage = employerBenefits.length > limit; - if (hasNextPage) employerBenefits.pop(); - - const unifiedEmployerBenefits = await Promise.all( - employerBenefits.map(async (employerBenefit) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: employerBenefit.id_hris_employer_benefit, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedEmployerBenefit: UnifiedHrisEmployerbenefitOutput = { - id: employerBenefit.id_hris_employer_benefit, - benefit_plan_type: employerBenefit.benefit_plan_type, - name: employerBenefit.name, - description: employerBenefit.description, - deduction_code: employerBenefit.deduction_code, - field_mappings: field_mappings, - remote_id: employerBenefit.remote_id, - remote_created_at: employerBenefit.remote_created_at, - created_at: employerBenefit.created_at, - modified_at: employerBenefit.modified_at, - remote_was_deleted: employerBenefit.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: employerBenefit.id_hris_employer_benefit, - }, - }); - unifiedEmployerBenefit.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedEmployerBenefit; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.employer_benefit.pull', - method: 'GET', - url: '/hris/employer_benefits', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedEmployerBenefits, - next_cursor: hasNextPage - ? employerBenefits[employerBenefits.length - 1] - .id_hris_employer_benefit - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/employerbenefit/services/gusto/index.ts b/packages/api/src/hris/employerbenefit/services/gusto/index.ts deleted file mode 100644 index 2024d7b4a..000000000 --- a/packages/api/src/hris/employerbenefit/services/gusto/index.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { HrisObject } from '@hris/@lib/@types'; -import { IEmployerBenefitService } from '@hris/employerbenefit/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { GustoEmployerbenefitOutput } from './types'; - -@Injectable() -export class GustoService implements IEmployerBenefitService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - HrisObject.employerbenefit.toUpperCase() + ':' + GustoService.name, - ); - this.registry.registerService('gusto', this); - } - - async sync( - data: SyncParam, - ): Promise> { - try { - const { linkedUserId, id_company } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gusto', - vertical: 'hris', - }, - }); - - const company = await this.prisma.hris_companies.findUnique({ - where: { - id_hris_company: id_company as string, - }, - select: { - remote_id: true, - }, - }); - - const resp = await axios.get( - `${connection.account_url}/v1/companies/${company.remote_id}/company_benefits`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - - const resp_ = await axios.get(`${connection.account_url}/v1/benefits`, { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }); - - const res = []; - for (const employerBenefit of resp.data) { - const pick = resp_.data.filter( - (item) => item.benefit_type == employerBenefit.benefit_type, - ); - res.push({ - ...employerBenefit, - category: pick.category, - name: pick.name, - }); - } - - this.logger.log(`Synced gusto employerbenefits !`); - - return { - data: res, - message: 'Gusto employerbenefits retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/employerbenefit/services/gusto/mappers.ts b/packages/api/src/hris/employerbenefit/services/gusto/mappers.ts deleted file mode 100644 index 3d2c4d07f..000000000 --- a/packages/api/src/hris/employerbenefit/services/gusto/mappers.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Injectable } from '@nestjs/common'; -import { GustoCategory, GustoEmployerbenefitOutput } from './types'; -import { - BenefitPlanType, - UnifiedHrisEmployerbenefitInput, - UnifiedHrisEmployerbenefitOutput, -} from '@hris/employerbenefit/types/model.unified'; -import { IEmployerBenefitMapper } from '@hris/employerbenefit/types'; -import { Utils } from '@hris/@lib/@utils'; - -@Injectable() -export class GustoEmployerbenefitMapper implements IEmployerBenefitMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService( - 'hris', - 'employerbenefit', - 'gusto', - this, - ); - } - - async desunify( - source: UnifiedHrisEmployerbenefitInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return; - } - - async unify( - source: GustoEmployerbenefitOutput | GustoEmployerbenefitOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise< - UnifiedHrisEmployerbenefitOutput | UnifiedHrisEmployerbenefitOutput[] - > { - if (!Array.isArray(source)) { - return this.mapSingleEmployerbenefitToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((employerbenefit) => - this.mapSingleEmployerbenefitToUnified( - employerbenefit, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleEmployerbenefitToUnified( - employerbenefit: GustoEmployerbenefitOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return { - remote_id: employerbenefit.uuid || null, - remote_data: employerbenefit, - benefit_plan_type: this.mapGustoBenefitToPanora(employerbenefit.category), - name: employerbenefit.name, - description: employerbenefit.description, - }; - } - - mapGustoBenefitToPanora( - category: GustoCategory | string, - ): BenefitPlanType | string { - switch (category) { - case 'Health': - return 'MEDICAL'; - case 'Savings and Retirement': - return 'RETIREMENT'; - case 'Other': - return 'OTHER'; - default: - return category; - } - } -} diff --git a/packages/api/src/hris/employerbenefit/services/gusto/types.ts b/packages/api/src/hris/employerbenefit/services/gusto/types.ts deleted file mode 100644 index 6834a7094..000000000 --- a/packages/api/src/hris/employerbenefit/services/gusto/types.ts +++ /dev/null @@ -1,20 +0,0 @@ -export type GustoEmployerbenefitOutput = Partial<{ - uuid: string; - version: string; - company_uuid: string; - benefit_type: number; - active: boolean; - description: string; - deletable: boolean; - supports_percentage_amounts: boolean; - responsible_for_employer_taxes: boolean; - responsible_for_employee_w2: boolean; - category: string; - name: string; -}>; - -export type GustoCategory = - | 'Health' - | 'Savings and Retirement' - | 'Transportation' - | 'Other'; diff --git a/packages/api/src/hris/employerbenefit/services/registry.service.ts b/packages/api/src/hris/employerbenefit/services/registry.service.ts deleted file mode 100644 index 609cc0fda..000000000 --- a/packages/api/src/hris/employerbenefit/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IEmployerBenefitService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IEmployerBenefitService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IEmployerBenefitService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/employerbenefit/sync/sync.service.ts b/packages/api/src/hris/employerbenefit/sync/sync.service.ts deleted file mode 100644 index 771bbabe7..000000000 --- a/packages/api/src/hris/employerbenefit/sync/sync.service.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { ApiResponse } from '@@core/utils/types'; -import { v4 as uuidv4 } from 'uuid'; -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 { UnifiedHrisEmployerbenefitOutput } from '../types/model.unified'; -import { IEmployerBenefitService } from '../types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_employer_benefits as HrisEmployerBenefit } from '@prisma/client'; -import { OriginalEmployerBenefitOutput } from '@@core/utils/types/original/original.hris'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'employerbenefit', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId, id_company } = param; - const service: IEmployerBenefitService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisEmployerbenefitOutput, - OriginalEmployerBenefitOutput, - IEmployerBenefitService - >(integrationId, linkedUserId, 'hris', 'employerbenefit', service, [ - { - param: id_company, - paramName: 'id_company', - shouldPassToService: true, - shouldPassToIngest: true, - }, - ]); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - employerBenefits: UnifiedHrisEmployerbenefitOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const employerBenefitResults: HrisEmployerBenefit[] = []; - - for (let i = 0; i < employerBenefits.length; i++) { - const employerBenefit = employerBenefits[i]; - const originId = employerBenefit.remote_id; - - let existingEmployerBenefit = - await this.prisma.hris_employer_benefits.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const employerBenefitData = { - benefit_plan_type: employerBenefit.benefit_plan_type, - name: employerBenefit.name, - description: employerBenefit.description, - deduction_code: employerBenefit.deduction_code, - remote_id: originId, - remote_created_at: employerBenefit.remote_created_at - ? new Date(employerBenefit.remote_created_at) - : null, - modified_at: new Date(), - remote_was_deleted: employerBenefit.remote_was_deleted || false, - }; - - if (existingEmployerBenefit) { - existingEmployerBenefit = - await this.prisma.hris_employer_benefits.update({ - where: { - id_hris_employer_benefit: - existingEmployerBenefit.id_hris_employer_benefit, - }, - data: employerBenefitData, - }); - } else { - existingEmployerBenefit = - await this.prisma.hris_employer_benefits.create({ - data: { - ...employerBenefitData, - id_hris_employer_benefit: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - employerBenefitResults.push(existingEmployerBenefit); - - // Process field mappings - await this.ingestService.processFieldMappings( - employerBenefit.field_mappings, - existingEmployerBenefit.id_hris_employer_benefit, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingEmployerBenefit.id_hris_employer_benefit, - remote_data[i], - ); - } - - return employerBenefitResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/employerbenefit/types/index.ts b/packages/api/src/hris/employerbenefit/types/index.ts deleted file mode 100644 index af9533a28..000000000 --- a/packages/api/src/hris/employerbenefit/types/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisEmployerbenefitInput, - UnifiedHrisEmployerbenefitOutput, -} from './model.unified'; -import { OriginalEmployerBenefitOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface IEmployerBenefitService { - sync(data: SyncParam): Promise>; -} - -export interface IEmployerBenefitMapper { - desunify( - source: UnifiedHrisEmployerbenefitInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalEmployerBenefitOutput | OriginalEmployerBenefitOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise< - UnifiedHrisEmployerbenefitOutput | UnifiedHrisEmployerbenefitOutput[] - >; -} diff --git a/packages/api/src/hris/employerbenefit/types/model.unified.ts b/packages/api/src/hris/employerbenefit/types/model.unified.ts deleted file mode 100644 index 46852f391..000000000 --- a/packages/api/src/hris/employerbenefit/types/model.unified.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsBoolean, - IsDateString, - IsOptional, - IsString, - IsUUID, -} from 'class-validator'; - -export type BenefitPlanType = - | 'MEDICAL' - | 'HEALTH_SAVINGS' - | 'INSURANCE' - | 'RETIREMENT' - | 'OTHER'; -export class UnifiedHrisEmployerbenefitInput { - @ApiPropertyOptional({ - type: String, - example: 'Health Insurance', - // enum: ['MEDICAL', 'HEALTH_SAVINGS', 'INSURANCE', 'RETIREMENT', 'OTHER'], - nullable: true, - description: 'The type of the benefit plan', - }) - @IsString() - @IsOptional() - benefit_plan_type?: BenefitPlanType | string; - - @ApiPropertyOptional({ - type: String, - example: 'Company Health Plan', - nullable: true, - description: 'The name of the employer benefit', - }) - @IsString() - @IsOptional() - name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'Comprehensive health insurance coverage for employees', - nullable: true, - description: 'The description of the employer benefit', - }) - @IsString() - @IsOptional() - description?: string; - - @ApiPropertyOptional({ - type: String, - example: 'HEALTH-001', - nullable: true, - description: 'The deduction code for the employer benefit', - }) - @IsString() - @IsOptional() - deduction_code?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisEmployerbenefitOutput extends UnifiedHrisEmployerbenefitInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the employer benefit record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'benefit_1234', - nullable: true, - description: - 'The remote ID of the employer benefit in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the employer benefit in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: - 'The date when the employer benefit was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The created date of the employer benefit record', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The last modified date of the employer benefit record', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: - 'Indicates if the employer benefit was deleted in the remote system', - }) - @IsBoolean() - @IsOptional() - remote_was_deleted?: boolean; -} diff --git a/packages/api/src/hris/employerbenefit/utils/index.ts b/packages/api/src/hris/employerbenefit/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/employerbenefit/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/employment/employment.controller.ts b/packages/api/src/hris/employment/employment.controller.ts deleted file mode 100644 index 3c8a010b7..000000000 --- a/packages/api/src/hris/employment/employment.controller.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { EmploymentService } from './services/employment.service'; -import { - UnifiedHrisEmploymentInput, - UnifiedHrisEmploymentOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('hris/employments') -@Controller('hris/employments') -export class EmploymentController { - constructor( - private readonly employmentService: EmploymentService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(EmploymentController.name); - } - - @ApiOperation({ - operationId: 'listHrisEmployments', - summary: 'List Employments', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisEmploymentOutput) - @UseGuards(ApiKeyAuthGuard) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @Get() - async getEmployments( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.employmentService.getEmployments( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisEmployment', - summary: 'Retrieve Employment', - description: 'Retrieve an Employment from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the employment you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisEmploymentOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.employmentService.getEmployment( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/hris/employment/employment.module.ts b/packages/api/src/hris/employment/employment.module.ts deleted file mode 100644 index 2ef02de82..000000000 --- a/packages/api/src/hris/employment/employment.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Module } from '@nestjs/common'; -import { EmploymentController } from './employment.controller'; -import { EmploymentService } from './services/employment.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { GustoEmploymentMapper } from './services/gusto/mappers'; -import { Utils } from '@hris/@lib/@utils'; -import { DeelEmploymentMapper } from './services/deel/mappers'; -@Module({ - controllers: [EmploymentController], - providers: [ - EmploymentService, - CoreUnification, - SyncService, - WebhookService, - ServiceRegistry, - IngestDataService, - Utils, - GustoEmploymentMapper, - DeelEmploymentMapper, - /* PROVIDERS SERVICES */ - ], - exports: [SyncService], -}) -export class EmploymentModule {} diff --git a/packages/api/src/hris/employment/services/deel/mappers.ts b/packages/api/src/hris/employment/services/deel/mappers.ts deleted file mode 100644 index bb892abf0..000000000 --- a/packages/api/src/hris/employment/services/deel/mappers.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Utils } from '@hris/@lib/@utils'; -import { IEmploymentMapper } from '@hris/employment/types'; -import { - FlsaStatus, - UnifiedHrisEmploymentInput, - UnifiedHrisEmploymentOutput, - EmploymentType, - PayFrequency, - PayPeriod, -} from '@hris/employment/types/model.unified'; -import { DeelEmploymentOutput } from './types'; -import { CurrencyCode } from '@@core/utils/types'; - -@Injectable() -export class DeelEmploymentMapper implements IEmploymentMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'employment', 'deel', this); - } - - async desunify( - source: UnifiedHrisEmploymentInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - // Implementation for desunify (if needed) - return; - } - - async unify( - source: DeelEmploymentOutput | DeelEmploymentOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleEmploymentToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((employment) => - this.mapSingleEmploymentToUnified( - employment, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleEmploymentToUnified( - employment: DeelEmploymentOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return { - remote_id: employment.id, - remote_data: employment, - job_title: employment.job_title, - pay_rate: employment.payment?.rate, - pay_period: this.mapPayPeriod(employment.payment?.scale), - pay_frequency: this.mapPayFrequency(employment.payment?.scale), - pay_currency: employment.payment?.currency as CurrencyCode, - flsa_status: this.mapFlsaStatus(employment.hiring_type), - effective_date: employment.start_date - ? new Date(employment.start_date) - : null, - employment_type: this.mapEmploymentType(employment.hiring_type), - }; - } - - private mapPayPeriod(scale?: string): PayPeriod | undefined { - switch (scale?.toLowerCase()) { - case 'yearly': - return 'YEAR'; - case 'monthly': - return 'MONTH'; - case 'weekly': - return 'WEEK'; - case 'daily': - return 'DAY'; - case 'hourly': - return 'HOUR'; - default: - return undefined; - } - } - - private mapPayFrequency(scale?: string): PayFrequency | undefined { - switch (scale?.toLowerCase()) { - case 'yearly': - return 'ANNUALLY'; - case 'monthly': - return 'MONTHLY'; - case 'weekly': - return 'WEEKLY'; - case 'daily': - return 'WEEKLY'; // Assuming daily payment is done weekly - case 'hourly': - return 'WEEKLY'; // Assuming hourly payment is done weekly - default: - return undefined; - } - } - - private mapFlsaStatus(hiringType?: string): FlsaStatus | undefined { - switch (hiringType?.toLowerCase()) { - case 'employee': - return 'EXEMPT'; // Assuming employees are exempt - case 'contractor': - return 'NONEXEMPT'; // Assuming contractors are nonexempt - default: - return undefined; - } - } - - private mapEmploymentType(hiringType?: string): EmploymentType | undefined { - switch (hiringType?.toLowerCase()) { - case 'employee': - return 'FULL_TIME'; // Assuming employees are full-time - case 'contractor': - return 'CONTRACTOR'; - default: - return undefined; - } - } -} diff --git a/packages/api/src/hris/employment/services/deel/types.ts b/packages/api/src/hris/employment/services/deel/types.ts deleted file mode 100644 index 8b4ad5ae9..000000000 --- a/packages/api/src/hris/employment/services/deel/types.ts +++ /dev/null @@ -1,50 +0,0 @@ -export type DeelEmploymentOutput = Partial<{ - id: string; - name: string; - team: DeelTeam; - email: string; - state: string; - country: string; - payment: DeelPayment; - is_ended: boolean; - timezone: string; - job_title: string; - seniority: string; - start_date: string; // Date in string format - work_email: string; - hiring_type: string; - hiring_status: string; - completion_date: string; - contract_status: string; - voluntarily_left: string; - contract_coverage: string[]; - new_hiring_status: string; - client_legal_entity: DeelClientLegalEntity; - has_eor_termination: string; - contract_is_archived: boolean; - contract_has_contractor: boolean; - is_user_contract_deleted: boolean; - hris_direct_employee_invitation: string; -}>; - -export interface DeelTeam { - id: string; - name: string; -} - -export interface DeelPayment { - rate: number; - scale: string; - currency: string; - contract_name: string; -} -export interface DeelDepartment { - id: string; - name: string; - parent: string; -} - -export interface DeelClientLegalEntity { - id: string; - name: string; -} diff --git a/packages/api/src/hris/employment/services/employment.service.ts b/packages/api/src/hris/employment/services/employment.service.ts deleted file mode 100644 index ba0ab6d81..000000000 --- a/packages/api/src/hris/employment/services/employment.service.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Injectable } from '@nestjs/common'; -import { v4 as uuidv4 } from 'uuid'; -import { UnifiedHrisEmploymentOutput } from '../types/model.unified'; -import { ServiceRegistry } from './registry.service'; -import { CurrencyCode } from '@@core/utils/types'; - -@Injectable() -export class EmploymentService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(EmploymentService.name); - } - - async getEmployment( - id_hris_employment: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const employment = await this.prisma.hris_employments.findUnique({ - where: { id_hris_employment: id_hris_employment }, - }); - - if (!employment) { - throw new Error(`Employment with ID ${id_hris_employment} not found.`); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: employment.id_hris_employment, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedEmployment: UnifiedHrisEmploymentOutput = { - id: employment.id_hris_employment, - job_title: employment.job_title, - pay_rate: Number(employment.pay_rate), - pay_period: employment.pay_period, - pay_frequency: employment.pay_frequency, - pay_currency: employment.pay_currency as CurrencyCode, - flsa_status: employment.flsa_status, - effective_date: employment.effective_date, - employment_type: employment.employment_type, - field_mappings: field_mappings, - remote_id: employment.remote_id, - remote_created_at: employment.remote_created_at, - created_at: employment.created_at, - modified_at: employment.modified_at, - remote_was_deleted: employment.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: employment.id_hris_employment, - }, - }); - unifiedEmployment.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.employment.pull', - method: 'GET', - url: '/hris/employment', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedEmployment; - } catch (error) { - throw error; - } - } - - async getEmployments( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisEmploymentOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const employments = await this.prisma.hris_employments.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_employment: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }); - - const hasNextPage = employments.length > limit; - if (hasNextPage) employments.pop(); - - const unifiedEmployments = await Promise.all( - employments.map(async (employment) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: employment.id_hris_employment, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedEmployment: UnifiedHrisEmploymentOutput = { - id: employment.id_hris_employment, - job_title: employment.job_title, - pay_rate: Number(employment.pay_rate), - pay_period: employment.pay_period, - pay_frequency: employment.pay_frequency, - pay_currency: employment.pay_currency as CurrencyCode, - flsa_status: employment.flsa_status, - effective_date: employment.effective_date, - employment_type: employment.employment_type, - field_mappings: field_mappings, - remote_id: employment.remote_id, - remote_created_at: employment.remote_created_at, - created_at: employment.created_at, - modified_at: employment.modified_at, - remote_was_deleted: employment.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: employment.id_hris_employment, - }, - }); - unifiedEmployment.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedEmployment; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.employment.pull', - method: 'GET', - url: '/hris/employments', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedEmployments, - next_cursor: hasNextPage - ? employments[employments.length - 1].id_hris_employment - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/employment/services/gusto/mappers.ts b/packages/api/src/hris/employment/services/gusto/mappers.ts deleted file mode 100644 index af9fd3366..000000000 --- a/packages/api/src/hris/employment/services/gusto/mappers.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Utils } from '@hris/@lib/@utils'; -import { IEmploymentMapper } from '@hris/employment/types'; -import { - FlsaStatus, - UnifiedHrisEmploymentInput, - UnifiedHrisEmploymentOutput, -} from '@hris/employment/types/model.unified'; -import { Injectable } from '@nestjs/common'; -import { GustoEmploymentOutput } from './types'; - -@Injectable() -export class GustoEmploymentMapper implements IEmploymentMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'employment', 'gusto', this); - } - - async desunify( - source: UnifiedHrisEmploymentInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return; - } - - async unify( - source: GustoEmploymentOutput | GustoEmploymentOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleEmploymentToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((employment) => - this.mapSingleEmploymentToUnified( - employment, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleEmploymentToUnified( - employment: GustoEmploymentOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return { - remote_id: employment.uuid, - remote_data: employment, - effective_date: new Date(employment.effective_date), - job_title: employment.title, - pay_rate: Number(employment.rate), - flsa_status: this.mapFlsaStatusToPanora(employment.flsa_status), - }; - } - - mapFlsaStatusToPanora( - str: - | 'Exempt' - | 'Salaried Nonexempt' - | 'Nonexempt' - | 'Owner' - | 'Commission Only Exempt' - | 'Commission Only Nonexempt', - ): FlsaStatus | string { - switch (str) { - case 'Exempt': - return 'EXEMPT'; - case 'Salaried Nonexempt': - return 'SALARIED_NONEXEMPT'; - case 'Nonexempt': - return 'NONEXEMPT'; - case 'Owner': - return 'OWNER'; - default: - return str; - } - } -} diff --git a/packages/api/src/hris/employment/services/gusto/types.ts b/packages/api/src/hris/employment/services/gusto/types.ts deleted file mode 100644 index 9cc0b0112..000000000 --- a/packages/api/src/hris/employment/services/gusto/types.ts +++ /dev/null @@ -1,31 +0,0 @@ -export type GustoEmploymentOutput = Partial<{ - uuid: string; // The UUID of the compensation in Gusto. - version: string; // The current version of the compensation. - job_uuid: string; // The UUID of the job to which the compensation belongs. - title: string; - rate: string; // The dollar amount paid per payment unit. - payment_unit: 'Hour' | 'Week' | 'Month' | 'Year' | 'Paycheck'; // The unit accompanying the compensation rate. - flsa_status: - | 'Exempt' - | 'Salaried Nonexempt' - | 'Nonexempt' - | 'Owner' - | 'Commission Only Exempt' - | 'Commission Only Nonexempt'; // The FLSA status for this compensation. - effective_date: string; // The effective date for this compensation. - adjust_for_minimum_wage: boolean; // Indicates if the compensation could be adjusted to minimum wage during payroll calculation. - eligible_paid_time_off: EligiblePaidTimeOff[]; // The available types of paid time off for the compensation. -}>; - -type EligiblePaidTimeOff = { - name: string; // The name of the paid time off type. - policy_name: string; // The name of the time off policy. - policy_uuid: string; // The UUID of the time off policy. - accrual_unit: string; // The unit the PTO type is accrued in. - accrual_rate: string; // The number of accrual units accrued per accrual period. - accrual_method: string; // The accrual method of the time off policy. - accrual_period: string; // The frequency at which the PTO type is accrued. - accrual_balance: string; // The number of accrual units accrued. - maximum_accrual_balance: string | null; // The maximum number of accrual units allowed. - paid_at_termination: boolean; // Whether the accrual balance is paid to the employee upon termination. -}; diff --git a/packages/api/src/hris/employment/services/registry.service.ts b/packages/api/src/hris/employment/services/registry.service.ts deleted file mode 100644 index cdc4a5c8f..000000000 --- a/packages/api/src/hris/employment/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IEmploymentService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IEmploymentService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IEmploymentService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/employment/sync/sync.service.ts b/packages/api/src/hris/employment/sync/sync.service.ts deleted file mode 100644 index 61ad1a706..000000000 --- a/packages/api/src/hris/employment/sync/sync.service.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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 { UnifiedHrisEmploymentOutput } from '../types/model.unified'; -import { IEmploymentService } from '../types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_employments as HrisEmployment } from '@prisma/client'; -import { OriginalEmploymentOutput } from '@@core/utils/types/original/original.hris'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'employment', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IEmploymentService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisEmploymentOutput, - OriginalEmploymentOutput, - IEmploymentService - >(integrationId, linkedUserId, 'hris', 'employment', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - employments: UnifiedHrisEmploymentOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const employmentResults: HrisEmployment[] = []; - - for (let i = 0; i < employments.length; i++) { - const employment = employments[i]; - const originId = employment.remote_id; - - let existingEmployment = await this.prisma.hris_employments.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const employmentData = { - job_title: employment.job_title, - pay_rate: employment.pay_rate ? BigInt(employment.pay_rate) : null, - pay_period: employment.pay_period, - pay_frequency: employment.pay_frequency, - pay_currency: employment.pay_currency, - flsa_status: employment.flsa_status, - effective_date: employment.effective_date - ? new Date(employment.effective_date) - : null, - employment_type: employment.employment_type, - remote_id: originId, - remote_created_at: employment.remote_created_at - ? new Date(employment.remote_created_at) - : null, - modified_at: new Date(), - remote_was_deleted: employment.remote_was_deleted || false, - }; - - if (existingEmployment) { - existingEmployment = await this.prisma.hris_employments.update({ - where: { - id_hris_employment: existingEmployment.id_hris_employment, - }, - data: employmentData, - }); - } else { - existingEmployment = await this.prisma.hris_employments.create({ - data: { - ...employmentData, - id_hris_employment: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - employmentResults.push(existingEmployment); - - // Process field mappings - await this.ingestService.processFieldMappings( - employment.field_mappings, - existingEmployment.id_hris_employment, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingEmployment.id_hris_employment, - remote_data[i], - ); - } - - return employmentResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/employment/types/index.ts b/packages/api/src/hris/employment/types/index.ts deleted file mode 100644 index 3a3ec8bdc..000000000 --- a/packages/api/src/hris/employment/types/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisEmploymentInput, - UnifiedHrisEmploymentOutput, -} from './model.unified'; -import { OriginalEmploymentOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface IEmploymentService { - sync(data: SyncParam): Promise>; -} - -export interface IEmploymentMapper { - desunify( - source: UnifiedHrisEmploymentInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalEmploymentOutput | OriginalEmploymentOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/hris/employment/types/model.unified.ts b/packages/api/src/hris/employment/types/model.unified.ts deleted file mode 100644 index 857744110..000000000 --- a/packages/api/src/hris/employment/types/model.unified.ts +++ /dev/null @@ -1,263 +0,0 @@ -import { CurrencyCode } from '@@core/utils/types'; -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsNumber, - IsDateString, - IsBoolean, -} from 'class-validator'; - -export type FlsaStatus = - | 'EXEMPT' - | 'SALARIED_NONEXEMPT' - | 'NONEXEMPT' - | 'OWNER'; - -export type EmploymentType = - | 'FULL_TIME' - | 'PART_TIME' - | 'INTERN' - | 'CONTRACTOR' - | 'FREELANCE'; - -export type PayFrequency = - | 'WEEKLY' - | 'BIWEEKLY' - | 'MONTHLY' - | 'QUARTERLY' - | 'SEMIANNUALLY' - | 'ANNUALLY' - | 'THIRTEEN-MONTHLY' - | 'PRO_RATA' - | 'SEMIMONTHLY'; - -export type PayPeriod = - | 'HOUR' - | 'DAY' - | 'WEEK' - | 'EVERY_TWO_WEEKS' - | 'SEMIMONTHLY' - | 'MONTH' - | 'QUARTER' - | 'EVERY_SIX_MONTHS' - | 'YEAR'; - -export class UnifiedHrisEmploymentInput { - @ApiPropertyOptional({ - type: String, - example: 'Software Engineer', - nullable: true, - description: 'The job title of the employment', - }) - @IsString() - @IsOptional() - job_title?: string; - - @ApiPropertyOptional({ - type: Number, - example: 100000, - nullable: true, - description: 'The pay rate of the employment', - }) - @IsNumber() - @IsOptional() - pay_rate?: number; - - @ApiPropertyOptional({ - type: String, - example: 'MONTHLY', - /* enum: [ - 'HOUR', - 'DAY', - 'WEEK', - 'EVERY_TWO_WEEKS', - 'SEMIMONTHLY', - 'MONTH', - 'QUARTER', - 'EVERY_SIX_MONTHS', - 'YEAR', - ],*/ - nullable: true, - description: 'The pay period of the employment', - }) - @IsString() - @IsOptional() - pay_period?: PayPeriod | string; - - @ApiPropertyOptional({ - type: String, - example: 'WEEKLY', - /* enum: [ - 'WEEKLY', - 'BIWEEKLY', - 'MONTHLY', - 'QUARTERLY', - 'SEMIANNUALLY', - 'ANNUALLY', - 'THIRTEEN-MONTHLY', - 'PRO_RATA', - 'SEMIMONTHLY', - ],*/ - nullable: true, - description: 'The pay frequency of the employment', - }) - @IsString() - @IsOptional() - pay_frequency?: PayFrequency | string; - - @ApiPropertyOptional({ - type: String, - example: 'USD', - // enum: CurrencyCode, - nullable: true, - description: 'The currency of the pay', - }) - @IsString() - @IsOptional() - pay_currency?: CurrencyCode; - - @ApiPropertyOptional({ - type: String, - example: 'EXEMPT', - // enum: ['EXEMPT', 'SALARIED_NONEXEMPT', 'NONEXEMPT', 'OWNER'], - nullable: true, - description: 'The FLSA status of the employment', - }) - @IsString() - @IsOptional() - flsa_status?: FlsaStatus | string; - - @ApiPropertyOptional({ - type: Date, - example: '2023-01-01', - nullable: true, - description: 'The effective date of the employment', - }) - @IsDateString() - @IsOptional() - effective_date?: Date; - - @ApiPropertyOptional({ - type: String, - example: 'FULL_TIME', - // enum: ['FULL_TIME', 'PART_TIME', 'INTERN', 'CONTRACTOR', 'FREELANCE'], - nullable: true, - description: 'The type of employment', - }) - @IsString() - @IsOptional() - employment_type?: EmploymentType | string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the associated pay group', - }) - @IsUUID() - @IsOptional() - pay_group_id?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the associated employee', - }) - @IsUUID() - @IsOptional() - employee_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisEmploymentOutput extends UnifiedHrisEmploymentInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the employment record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'employment_1234', - nullable: true, - description: - 'The remote ID of the employment in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the employment in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: - 'The date when the employment was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The created date of the employment record', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The last modified date of the employment record', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: 'Indicates if the employment was deleted in the remote system', - }) - @IsBoolean() - @IsOptional() - remote_was_deleted?: boolean; -} diff --git a/packages/api/src/hris/employment/utils/index.ts b/packages/api/src/hris/employment/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/employment/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/group/group.controller.ts b/packages/api/src/hris/group/group.controller.ts deleted file mode 100644 index 73fe0fe09..000000000 --- a/packages/api/src/hris/group/group.controller.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { GroupService } from './services/group.service'; -import { - UnifiedHrisGroupInput, - UnifiedHrisGroupOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('hris/groups') -@Controller('hris/groups') -export class GroupController { - constructor( - private readonly groupService: GroupService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(GroupController.name); - } - - @ApiOperation({ - operationId: 'listHrisGroups', - summary: 'List Groups', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisGroupOutput) - @UseGuards(ApiKeyAuthGuard) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @Get() - async getGroups( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.groupService.getGroups( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisGroup', - summary: 'Retrieve Group', - description: 'Retrieve a Group from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the group you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisGroupOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.groupService.getGroup( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/hris/group/group.module.ts b/packages/api/src/hris/group/group.module.ts deleted file mode 100644 index 78f6a35ee..000000000 --- a/packages/api/src/hris/group/group.module.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Module } from '@nestjs/common'; -import { GroupController } from './group.controller'; -import { GroupService } from './services/group.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { GustoGroupMapper } from './services/gusto/mappers'; -import { GustoService } from './services/gusto'; -import { Utils } from '@hris/@lib/@utils'; -import { SageService } from './services/sage'; -import { SageGroupMapper } from './services/sage/mappers'; -import { DeelService } from './services/deel'; -import { DeelGroupMapper } from './services/deel/mappers'; -@Module({ - controllers: [GroupController], - providers: [ - GroupService, - SyncService, - WebhookService, - CoreUnification, - ServiceRegistry, - IngestDataService, - GustoGroupMapper, - Utils, - SageGroupMapper, - DeelGroupMapper, - /* PROVIDERS SERVICES */ - GustoService, - SageService, - DeelService, - ], - exports: [SyncService], -}) -export class GroupModule {} diff --git a/packages/api/src/hris/group/services/deel/index.ts b/packages/api/src/hris/group/services/deel/index.ts deleted file mode 100644 index f593070b1..000000000 --- a/packages/api/src/hris/group/services/deel/index.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { HrisObject } from '@hris/@lib/@types'; -import { IGroupService } from '@hris/group/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { DeelGroupOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalGroupOutput } from '@@core/utils/types/original/original.hris'; - -@Injectable() -export class DeelService implements IGroupService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - HrisObject.group.toUpperCase() + ':' + DeelService.name, - ); - this.registry.registerService('deel', this); - } - addGroup( - groupData: DesunifyReturnType, - linkedUserId: string, - ): Promise> { - throw new Error('Method not implemented.'); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'deel', - vertical: 'hris', - }, - }); - - const resp = await axios.get(`${connection.account_url}/rest/v2/teams`, { - headers: { - 'Content-Type': 'application/json', - 'X-Auth-Token': this.cryptoService.decrypt(connection.access_token), - }, - }); - this.logger.log(`Synced deel groups !`); - - return { - data: resp.data.data, - message: 'Deel groups retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/group/services/deel/mappers.ts b/packages/api/src/hris/group/services/deel/mappers.ts deleted file mode 100644 index 356c44695..000000000 --- a/packages/api/src/hris/group/services/deel/mappers.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Utils } from '@hris/@lib/@utils'; -import { IGroupMapper } from '@hris/group/types'; -import { - UnifiedHrisGroupInput, - UnifiedHrisGroupOutput, -} from '@hris/group/types/model.unified'; -import { Injectable } from '@nestjs/common'; -import { DeelGroupOutput } from './types'; - -@Injectable() -export class DeelGroupMapper implements IGroupMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'group', 'deel', this); - } - - async desunify( - source: UnifiedHrisGroupInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return; - } - - async unify( - source: DeelGroupOutput | DeelGroupOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleGroupToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((group) => - this.mapSingleGroupToUnified(group, connectionId, customFieldMappings), - ), - ); - } - - private async mapSingleGroupToUnified( - group: DeelGroupOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return { - remote_id: group.id, - remote_data: group, - name: group.name, - }; - } -} diff --git a/packages/api/src/hris/group/services/deel/types.ts b/packages/api/src/hris/group/services/deel/types.ts deleted file mode 100644 index 8964f34e3..000000000 --- a/packages/api/src/hris/group/services/deel/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type DeelGroupOutput = { - id: string; - name: string; -}; diff --git a/packages/api/src/hris/group/services/group.service.ts b/packages/api/src/hris/group/services/group.service.ts deleted file mode 100644 index 378b9af64..000000000 --- a/packages/api/src/hris/group/services/group.service.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { - UnifiedHrisGroupInput, - UnifiedHrisGroupOutput, -} from '../types/model.unified'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ServiceRegistry } from './registry.service'; - -@Injectable() -export class GroupService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(GroupService.name); - } - - async getGroup( - id_hris_group: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const group = await this.prisma.hris_groups.findUnique({ - where: { id_hris_group: id_hris_group }, - }); - - if (!group) { - throw new Error(`Group with ID ${id_hris_group} not found.`); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: group.id_hris_group, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedGroup: UnifiedHrisGroupOutput = { - id: group.id_hris_group, - parent_group: group.parent_group, - name: group.name, - type: group.type, - field_mappings: field_mappings, - remote_id: group.remote_id, - remote_created_at: group.remote_created_at, - created_at: group.created_at, - modified_at: group.modified_at, - remote_was_deleted: group.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: group.id_hris_group, - }, - }); - unifiedGroup.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.group.pull', - method: 'GET', - url: '/hris/group', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedGroup; - } catch (error) { - throw error; - } - } - - async getGroups( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisGroupOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const groups = await this.prisma.hris_groups.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_group: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }); - - const hasNextPage = groups.length > limit; - if (hasNextPage) groups.pop(); - - const unifiedGroups = await Promise.all( - groups.map(async (group) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: group.id_hris_group, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedGroup: UnifiedHrisGroupOutput = { - id: group.id_hris_group, - parent_group: group.parent_group, - name: group.name, - type: group.type, - field_mappings: field_mappings, - remote_id: group.remote_id, - remote_created_at: group.remote_created_at, - created_at: group.created_at, - modified_at: group.modified_at, - remote_was_deleted: group.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: group.id_hris_group, - }, - }); - unifiedGroup.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedGroup; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.group.pull', - method: 'GET', - url: '/hris/groups', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedGroups, - next_cursor: hasNextPage - ? groups[groups.length - 1].id_hris_group - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/group/services/gusto/index.ts b/packages/api/src/hris/group/services/gusto/index.ts deleted file mode 100644 index a2741d417..000000000 --- a/packages/api/src/hris/group/services/gusto/index.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { HrisObject } from '@hris/@lib/@types'; -import { IGroupService } from '@hris/group/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { GustoGroupOutput } from './types'; - -@Injectable() -export class GustoService implements IGroupService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - HrisObject.group.toUpperCase() + ':' + GustoService.name, - ); - this.registry.registerService('gusto', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId, id_company } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gusto', - vertical: 'hris', - }, - }); - - const company = await this.prisma.hris_companies.findUnique({ - where: { - id_hris_company: id_company as string, - }, - select: { - remote_id: true, - }, - }); - - const resp = await axios.get( - `${connection.account_url}/v1/companies/${company.remote_id}/departments`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - this.logger.log(`Synced gusto groups !`); - - return { - data: resp.data, - message: 'Gusto groups retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/group/services/gusto/mappers.ts b/packages/api/src/hris/group/services/gusto/mappers.ts deleted file mode 100644 index b41edfe47..000000000 --- a/packages/api/src/hris/group/services/gusto/mappers.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Injectable } from '@nestjs/common'; -import { GustoGroupOutput } from './types'; -import { - UnifiedHrisGroupInput, - UnifiedHrisGroupOutput, -} from '@hris/group/types/model.unified'; -import { IGroupMapper } from '@hris/group/types'; -import { Utils } from '@hris/@lib/@utils'; - -@Injectable() -export class GustoGroupMapper implements IGroupMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'group', 'gusto', this); - } - - async desunify( - source: UnifiedHrisGroupInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return; - } - - async unify( - source: GustoGroupOutput | GustoGroupOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleGroupToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((group) => - this.mapSingleGroupToUnified(group, connectionId, customFieldMappings), - ), - ); - } - - private async mapSingleGroupToUnified( - group: GustoGroupOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return { - remote_id: group.uuid || null, - remote_data: group, - name: group.title, - }; - } -} diff --git a/packages/api/src/hris/group/services/gusto/types.ts b/packages/api/src/hris/group/services/gusto/types.ts deleted file mode 100644 index b1b3bdaac..000000000 --- a/packages/api/src/hris/group/services/gusto/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -export type GustoGroupOutput = Partial<{ - uuid: string; - company_uuid: string; - title: string; - version: string; - employees: [ - { - uuid: string; - }, - ]; - contractors: any[]; -}>; diff --git a/packages/api/src/hris/group/services/registry.service.ts b/packages/api/src/hris/group/services/registry.service.ts deleted file mode 100644 index 4b8fbe631..000000000 --- a/packages/api/src/hris/group/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IGroupService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IGroupService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IGroupService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/group/services/sage/index.ts b/packages/api/src/hris/group/services/sage/index.ts deleted file mode 100644 index 08dff20ec..000000000 --- a/packages/api/src/hris/group/services/sage/index.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { HrisObject } from '@hris/@lib/@types'; -import { IGroupService } from '@hris/group/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { SageGroupOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalGroupOutput } from '@@core/utils/types/original/original.hris'; - -@Injectable() -export class SageService implements IGroupService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - HrisObject.group.toUpperCase() + ':' + SageService.name, - ); - this.registry.registerService('sage', this); - } - addGroup( - groupData: DesunifyReturnType, - linkedUserId: string, - ): Promise> { - throw new Error('Method not implemented.'); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'sage', - vertical: 'hris', - }, - }); - - const resp = await axios.get(`${connection.account_url}/api/teams`, { - headers: { - 'Content-Type': 'application/json', - 'X-Auth-Token': this.cryptoService.decrypt(connection.access_token), - }, - }); - this.logger.log(`Synced sage groups !`); - - return { - data: resp.data.data, - message: 'Sage groups retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/group/services/sage/mappers.ts b/packages/api/src/hris/group/services/sage/mappers.ts deleted file mode 100644 index c40072b9f..000000000 --- a/packages/api/src/hris/group/services/sage/mappers.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Utils } from '@hris/@lib/@utils'; -import { IGroupMapper } from '@hris/group/types'; -import { - UnifiedHrisGroupInput, - UnifiedHrisGroupOutput, -} from '@hris/group/types/model.unified'; -import { Injectable } from '@nestjs/common'; -import { SageGroupOutput } from './types'; - -@Injectable() -export class SageGroupMapper implements IGroupMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'group', 'sage', this); - } - - async desunify( - source: UnifiedHrisGroupInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return; - } - - async unify( - source: SageGroupOutput | SageGroupOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleGroupToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((group) => - this.mapSingleGroupToUnified(group, connectionId, customFieldMappings), - ), - ); - } - - private async mapSingleGroupToUnified( - group: SageGroupOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return { - remote_id: String(group.id), - remote_data: group, - name: group.name, - }; - } -} diff --git a/packages/api/src/hris/group/services/sage/types.ts b/packages/api/src/hris/group/services/sage/types.ts deleted file mode 100644 index dcd2d590b..000000000 --- a/packages/api/src/hris/group/services/sage/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type SageGroupOutput = { - id: number; - name: string; - manager_ids: string[]; - employee_ids: string[]; -}; diff --git a/packages/api/src/hris/group/sync/sync.service.ts b/packages/api/src/hris/group/sync/sync.service.ts deleted file mode 100644 index ba1225742..000000000 --- a/packages/api/src/hris/group/sync/sync.service.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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 { UnifiedHrisGroupOutput } from '../types/model.unified'; -import { IGroupService } from '../types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_groups as HrisGroup } from '@prisma/client'; -import { OriginalGroupOutput } from '@@core/utils/types/original/original.hris'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'group', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId, id_company } = param; - const service: IGroupService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisGroupOutput, - OriginalGroupOutput, - IGroupService - >(integrationId, linkedUserId, 'hris', 'group', service, [ - { - param: id_company, - paramName: 'id_company', - shouldPassToService: true, - shouldPassToIngest: true, - }, - ]); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - groups: UnifiedHrisGroupOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const groupResults: HrisGroup[] = []; - - for (let i = 0; i < groups.length; i++) { - const group = groups[i]; - const originId = group.remote_id; - - let existingGroup = await this.prisma.hris_groups.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const groupData = { - parent_group: group.parent_group, - name: group.name, - type: group.type, - remote_id: originId, - remote_created_at: group.remote_created_at - ? new Date(group.remote_created_at) - : new Date(), - modified_at: new Date(), - remote_was_deleted: group.remote_was_deleted || false, - }; - - if (existingGroup) { - existingGroup = await this.prisma.hris_groups.update({ - where: { - id_hris_group: existingGroup.id_hris_group, - }, - data: groupData, - }); - } else { - existingGroup = await this.prisma.hris_groups.create({ - data: { - ...groupData, - id_hris_group: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - groupResults.push(existingGroup); - - // Process field mappings - await this.ingestService.processFieldMappings( - group.field_mappings, - existingGroup.id_hris_group, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingGroup.id_hris_group, - remote_data[i], - ); - } - - return groupResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/group/types/index.ts b/packages/api/src/hris/group/types/index.ts deleted file mode 100644 index b34ea68bd..000000000 --- a/packages/api/src/hris/group/types/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { UnifiedHrisGroupInput, UnifiedHrisGroupOutput } from './model.unified'; -import { OriginalGroupOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface IGroupService { - sync(data: SyncParam): Promise>; -} - -export interface IGroupMapper { - desunify( - source: UnifiedHrisGroupInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalGroupOutput | OriginalGroupOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/hris/group/types/model.unified.ts b/packages/api/src/hris/group/types/model.unified.ts deleted file mode 100644 index 7ecb4085b..000000000 --- a/packages/api/src/hris/group/types/model.unified.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsBoolean, -} from 'class-validator'; - -export type Type = - | 'TEAM' - | 'DEPARTMENT' - | 'COST_CENTER' - | 'BUSINESS_UNIT' - | 'GROUP'; -export class UnifiedHrisGroupInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the parent group', - }) - @IsUUID() - @IsOptional() - parent_group?: string; - - @ApiPropertyOptional({ - type: String, - example: 'Engineering Team', - nullable: true, - description: 'The name of the group', - }) - @IsString() - @IsOptional() - name?: string; - - @ApiPropertyOptional({ - type: String, - example: 'DEPARTMENT', - // enum: ['TEAM', 'DEPARTMENT', 'COST_CENTER', 'BUSINESS_UNIT', 'GROUP'], - nullable: true, - description: 'The type of the group', - }) - @IsString() - @IsOptional() - type?: Type | string; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisGroupOutput extends UnifiedHrisGroupInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the group record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'group_1234', - nullable: true, - description: 'The remote ID of the group in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: 'The remote data of the group in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The date when the group was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The created date of the group record', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The last modified date of the group record', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: 'Indicates if the group was deleted in the remote system', - }) - @IsBoolean() - @IsOptional() - remote_was_deleted?: boolean; -} diff --git a/packages/api/src/hris/group/utils/index.ts b/packages/api/src/hris/group/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/group/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/hris.module.ts b/packages/api/src/hris/hris.module.ts deleted file mode 100644 index 87f23517b..000000000 --- a/packages/api/src/hris/hris.module.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Module } from '@nestjs/common'; -import { BankInfoModule } from './bankinfo/bankinfo.module'; -import { BenefitModule } from './benefit/benefit.module'; -import { CompanyModule } from './company/company.module'; -import { DependentModule } from './dependent/dependent.module'; -import { EmployeePayrollRunModule } from './employeepayrollrun/employeepayrollrun.module'; -import { EmployeeModule } from './employee/employee.module'; -import { EmployerBenefitModule } from './employerbenefit/employerbenefit.module'; -import { EmploymentModule } from './employment/employment.module'; -import { GroupModule } from './group/group.module'; -import { LocationModule } from './location/location.module'; -import { PayGroupModule } from './paygroup/paygroup.module'; -import { PayrollRunModule } from './payrollrun/payrollrun.module'; -import { TimeoffModule } from './timeoff/timeoff.module'; -import { TimeoffBalanceModule } from './timeoffbalance/timeoffbalance.module'; -import { TimesheetentryModule } from './timesheetentry/timesheetentry.module'; -import { HrisUnificationService } from './@lib/@unification'; - -@Module({ - exports: [ - BankInfoModule, - BenefitModule, - CompanyModule, - DependentModule, - EmployeePayrollRunModule, - EmployeeModule, - EmployerBenefitModule, - EmploymentModule, - GroupModule, - LocationModule, - PayGroupModule, - PayrollRunModule, - TimeoffModule, - TimeoffBalanceModule, - TimesheetentryModule, - ], - providers: [HrisUnificationService], - imports: [ - BankInfoModule, - BenefitModule, - CompanyModule, - DependentModule, - EmployeePayrollRunModule, - EmployeeModule, - EmployerBenefitModule, - EmploymentModule, - GroupModule, - LocationModule, - PayGroupModule, - PayrollRunModule, - TimeoffModule, - TimeoffBalanceModule, - TimesheetentryModule, - ], -}) -export class HrisModule {} diff --git a/packages/api/src/hris/location/location.controller.ts b/packages/api/src/hris/location/location.controller.ts deleted file mode 100644 index 009a75e0e..000000000 --- a/packages/api/src/hris/location/location.controller.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { LocationService } from './services/location.service'; -import { - UnifiedHrisLocationInput, - UnifiedHrisLocationOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('hris/locations') -@Controller('hris/locations') -export class LocationController { - constructor( - private readonly locationService: LocationService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(LocationController.name); - } - - @ApiOperation({ - operationId: 'listHrisLocations', - summary: 'List Locations', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisLocationOutput) - @UseGuards(ApiKeyAuthGuard) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @Get() - async getLocations( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.locationService.getLocations( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisLocation', - summary: 'Retrieve Location', - description: 'Retrieve a Location from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the location you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisLocationOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.locationService.getLocation( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/hris/location/location.module.ts b/packages/api/src/hris/location/location.module.ts deleted file mode 100644 index c2f147465..000000000 --- a/packages/api/src/hris/location/location.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Module } from '@nestjs/common'; -import { LocationController } from './location.controller'; -import { LocationService } from './services/location.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Utils } from '@hris/@lib/@utils'; -import { GustoLocationMapper } from './services/gusto/mappers'; -import { GustoService } from './services/gusto'; -import { DeelLocationMapper } from './services/deel/mappers'; -@Module({ - controllers: [LocationController], - providers: [ - LocationService, - CoreUnification, - Utils, - SyncService, - WebhookService, - ServiceRegistry, - IngestDataService, - GustoLocationMapper, - DeelLocationMapper, - /* PROVIDERS SERVICES */ - GustoService, - ], - exports: [SyncService], -}) -export class LocationModule {} diff --git a/packages/api/src/hris/location/services/deel/mappers.ts b/packages/api/src/hris/location/services/deel/mappers.ts deleted file mode 100644 index cecf44401..000000000 --- a/packages/api/src/hris/location/services/deel/mappers.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Injectable } from '@nestjs/common'; -import { DeelLocationOutput } from './types'; -import { - UnifiedHrisLocationInput, - UnifiedHrisLocationOutput, -} from '@hris/location/types/model.unified'; -import { ILocationMapper } from '@hris/location/types'; -import { Utils } from '@hris/@lib/@utils'; - -@Injectable() -export class DeelLocationMapper implements ILocationMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'location', 'deel', this); - } - - async desunify( - source: UnifiedHrisLocationInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return; - } - - async unify( - source: DeelLocationOutput | DeelLocationOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleLocationToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((location) => - this.mapSingleLocationToUnified( - location, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleLocationToUnified( - location: DeelLocationOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return { - remote_id: null, - remote_data: location, - street_1: location.streetAddress, - city: location.locality, - state: location.region, - zip_code: location.postalCode, - country: location.country, - location_type: location.type, - }; - } -} diff --git a/packages/api/src/hris/location/services/deel/types.ts b/packages/api/src/hris/location/services/deel/types.ts deleted file mode 100644 index 98db26aa7..000000000 --- a/packages/api/src/hris/location/services/deel/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type DeelLocationOutput = Partial<{ - streetAddress: string; - locality: string; - region: string; - postalCode: string; - country: string; - type: 'WORK' | 'HOME'; -}>; diff --git a/packages/api/src/hris/location/services/gusto/index.ts b/packages/api/src/hris/location/services/gusto/index.ts deleted file mode 100644 index 4787af093..000000000 --- a/packages/api/src/hris/location/services/gusto/index.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { HrisObject } from '@hris/@lib/@types'; -import { ILocationService } from '@hris/location/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { GustoLocationOutput } from './types'; - -@Injectable() -export class GustoService implements ILocationService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - HrisObject.location.toUpperCase() + ':' + GustoService.name, - ); - this.registry.registerService('gusto', this); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId, id_employee } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gusto', - vertical: 'hris', - }, - }); - - const employee = await this.prisma.hris_employees.findUnique({ - where: { - id_hris_employee: id_employee as string, - }, - select: { - remote_id: true, - }, - }); - - const resp = await axios.get( - `${connection.account_url}/v1/employees/${employee.remote_id}/home_addresses`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - - const resp_ = await axios.get( - `${connection.account_url}/v1/employees/${employee.remote_id}/work_addresses`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - this.logger.log(`Synced gusto locations !`); - const resp_home = Array.isArray(resp.data) - ? resp.data.map((add) => ({ - ...add, - type: 'HOME', - })) - : []; - - const resp_work = Array.isArray(resp_.data) - ? resp_.data.map((add) => ({ - ...add, - type: 'WORK', - })) - : []; - - return { - data: [...resp_home, ...resp_work], - message: 'Gusto locations retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/location/services/gusto/mappers.ts b/packages/api/src/hris/location/services/gusto/mappers.ts deleted file mode 100644 index b7486ba71..000000000 --- a/packages/api/src/hris/location/services/gusto/mappers.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Injectable } from '@nestjs/common'; -import { GustoLocationOutput } from './types'; -import { - UnifiedHrisLocationInput, - UnifiedHrisLocationOutput, -} from '@hris/location/types/model.unified'; -import { ILocationMapper } from '@hris/location/types'; -import { Utils } from '@hris/@lib/@utils'; - -@Injectable() -export class GustoLocationMapper implements ILocationMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'location', 'gusto', this); - } - - async desunify( - source: UnifiedHrisLocationInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return; - } - - async unify( - source: GustoLocationOutput | GustoLocationOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleLocationToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((location) => - this.mapSingleLocationToUnified( - location, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleLocationToUnified( - location: GustoLocationOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return { - remote_id: location.uuid || null, - remote_data: location, - street_1: location.street_1, - street_2: location.street_2, - city: location.city, - state: location.state, - zip_code: location.zip, - country: location.country, - location_type: location.type, - }; - } -} diff --git a/packages/api/src/hris/location/services/gusto/types.ts b/packages/api/src/hris/location/services/gusto/types.ts deleted file mode 100644 index f53987155..000000000 --- a/packages/api/src/hris/location/services/gusto/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type GustoLocationOutput = Partial<{ - uuid: string; - street_1: string; - street_2: string; - city: string; - state: string; - zip: string; - country: string; - active: boolean; - type: 'WORK' | 'HOME'; -}>; diff --git a/packages/api/src/hris/location/services/location.service.ts b/packages/api/src/hris/location/services/location.service.ts deleted file mode 100644 index 111eab050..000000000 --- a/packages/api/src/hris/location/services/location.service.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { - UnifiedHrisLocationInput, - UnifiedHrisLocationOutput, -} from '../types/model.unified'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ServiceRegistry } from './registry.service'; - -@Injectable() -export class LocationService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(LocationService.name); - } - - async getLocation( - id_hris_location: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const location = await this.prisma.hris_locations.findUnique({ - where: { id_hris_location: id_hris_location }, - }); - - if (!location) { - throw new Error(`Location with ID ${id_hris_location} not found.`); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: location.id_hris_location, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedLocation: UnifiedHrisLocationOutput = { - id: location.id_hris_location, - name: location.name, - phone_number: location.phone_number, - street_1: location.street_1, - street_2: location.street_2, - city: location.city, - state: location.state, - zip_code: location.zip_code, - country: location.country, - employee_id: location.id_hris_employee || null, - company_id: location.id_hris_company || null, - location_type: location.location_type, - field_mappings: field_mappings, - remote_id: location.remote_id, - remote_created_at: location.remote_created_at, - created_at: location.created_at, - modified_at: location.modified_at, - remote_was_deleted: location.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: location.id_hris_location, - }, - }); - unifiedLocation.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.location.pull', - method: 'GET', - url: '/hris/location', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedLocation; - } catch (error) { - throw error; - } - } - - async getLocations( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisLocationOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const locations = await this.prisma.hris_locations.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_location: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }); - - const hasNextPage = locations.length > limit; - if (hasNextPage) locations.pop(); - - const unifiedLocations = await Promise.all( - locations.map(async (location) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: location.id_hris_location, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedLocation: UnifiedHrisLocationOutput = { - id: location.id_hris_location, - name: location.name, - phone_number: location.phone_number, - street_1: location.street_1, - street_2: location.street_2, - city: location.city, - employee_id: location.id_hris_employee || null, - company_id: location.id_hris_company || null, - state: location.state, - zip_code: location.zip_code, - country: location.country, - location_type: location.location_type, - field_mappings: field_mappings, - remote_id: location.remote_id, - remote_created_at: location.remote_created_at, - created_at: location.created_at, - modified_at: location.modified_at, - remote_was_deleted: location.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: location.id_hris_location, - }, - }); - unifiedLocation.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedLocation; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.location.pull', - method: 'GET', - url: '/hris/locations', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedLocations, - next_cursor: hasNextPage - ? locations[locations.length - 1].id_hris_location - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/location/services/registry.service.ts b/packages/api/src/hris/location/services/registry.service.ts deleted file mode 100644 index 691b8ed65..000000000 --- a/packages/api/src/hris/location/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ILocationService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: ILocationService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): ILocationService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/location/sync/sync.service.ts b/packages/api/src/hris/location/sync/sync.service.ts deleted file mode 100644 index d2131dbcf..000000000 --- a/packages/api/src/hris/location/sync/sync.service.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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 { UnifiedHrisLocationOutput } from '../types/model.unified'; -import { ILocationService } from '../types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_locations as HrisLocation } from '@prisma/client'; -import { OriginalLocationOutput } from '@@core/utils/types/original/original.hris'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'location', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId, id_employee } = param; - const service: ILocationService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisLocationOutput, - OriginalLocationOutput, - ILocationService - >(integrationId, linkedUserId, 'hris', 'location', service, [ - { - param: id_employee, - paramName: 'id_employee', - shouldPassToService: true, - shouldPassToIngest: true, - }, - ]); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - locations: UnifiedHrisLocationOutput[], - originSource: string, - remote_data: Record[], - id_employee?: string, - ): Promise { - try { - const locationResults: HrisLocation[] = []; - - for (let i = 0; i < locations.length; i++) { - const location = locations[i]; - const originId = location.remote_id; - - let existingLocation = await this.prisma.hris_locations.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const locationData = { - name: location.name, - phone_number: location.phone_number, - street_1: location.street_1, - street_2: location.street_2, - id_hris_employee: id_employee || null, - id_hris_company: location.company_id || null, - city: location.city, - state: location.state, - zip_code: location.zip_code, - country: location.country, - location_type: location.location_type, - remote_id: originId, - remote_created_at: location.remote_created_at - ? new Date(location.remote_created_at) - : new Date(), - modified_at: new Date(), - remote_was_deleted: location.remote_was_deleted || false, - }; - - if (existingLocation) { - existingLocation = await this.prisma.hris_locations.update({ - where: { - id_hris_location: existingLocation.id_hris_location, - }, - data: locationData, - }); - } else { - existingLocation = await this.prisma.hris_locations.create({ - data: { - ...locationData, - id_hris_location: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - locationResults.push(existingLocation); - - // Process field mappings - await this.ingestService.processFieldMappings( - location.field_mappings, - existingLocation.id_hris_location, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingLocation.id_hris_location, - remote_data[i], - ); - } - - return locationResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/location/types/index.ts b/packages/api/src/hris/location/types/index.ts deleted file mode 100644 index d045e44b6..000000000 --- a/packages/api/src/hris/location/types/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisLocationInput, - UnifiedHrisLocationOutput, -} from './model.unified'; -import { OriginalLocationOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface ILocationService { - sync(data: SyncParam): Promise>; -} - -export interface ILocationMapper { - desunify( - source: UnifiedHrisLocationInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalLocationOutput | OriginalLocationOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/hris/location/types/model.unified.ts b/packages/api/src/hris/location/types/model.unified.ts deleted file mode 100644 index 5bf6593cb..000000000 --- a/packages/api/src/hris/location/types/model.unified.ts +++ /dev/null @@ -1,215 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsBoolean, - IsIn, -} from 'class-validator'; - -export type LocationType = 'WORK' | 'HOME'; -export class UnifiedHrisLocationInput { - @ApiPropertyOptional({ - type: String, - example: 'Headquarters', - nullable: true, - description: 'The name of the location', - }) - @IsString() - @IsOptional() - name?: string; - - @ApiPropertyOptional({ - type: String, - example: '+1234567890', - nullable: true, - description: 'The phone number of the location', - }) - @IsString() - @IsOptional() - phone_number?: string; - - @ApiPropertyOptional({ - type: String, - example: '123 Main St', - nullable: true, - description: 'The first line of the street address', - }) - @IsString() - @IsOptional() - street_1?: string; - - @ApiPropertyOptional({ - type: String, - example: 'Suite 456', - nullable: true, - description: 'The second line of the street address', - }) - @IsString() - @IsOptional() - street_2?: string; - - @ApiPropertyOptional({ - type: String, - example: 'San Francisco', - nullable: true, - description: 'The city of the location', - }) - @IsString() - @IsOptional() - city?: string; - - @ApiPropertyOptional({ - type: String, - example: 'CA', - nullable: true, - description: 'The state or region of the location', - }) - @IsString() - @IsOptional() - state?: string; - - @ApiPropertyOptional({ - type: String, - example: '94105', - nullable: true, - description: 'The zip or postal code of the location', - }) - @IsString() - @IsOptional() - zip_code?: string; - - @ApiPropertyOptional({ - type: String, - example: 'USA', - nullable: true, - description: 'The country of the location', - }) - @IsString() - @IsOptional() - country?: string; - - @ApiPropertyOptional({ - type: String, - example: 'WORK', - // enum: ['WORK', 'HOME'], - nullable: true, - description: 'The type of the location', - }) - @IsString() - //@IsIn(['WORK', 'HOME']) - @IsOptional() - location_type?: LocationType | string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the company associated with the location', - }) - @IsUUID() - @IsOptional() - company_id?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the employee associated with the location', - }) - @IsUUID() - @IsOptional() - employee_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisLocationOutput extends UnifiedHrisLocationInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the location record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'location_1234', - nullable: true, - description: - 'The remote ID of the location in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the location in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: - 'The date when the location was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The created date of the location record', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The last modified date of the location record', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: 'Indicates if the location was deleted in the remote system', - }) - @IsBoolean() - @IsOptional() - remote_was_deleted?: boolean; -} diff --git a/packages/api/src/hris/location/utils/index.ts b/packages/api/src/hris/location/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/location/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/paygroup/paygroup.controller.ts b/packages/api/src/hris/paygroup/paygroup.controller.ts deleted file mode 100644 index 81e7d69a5..000000000 --- a/packages/api/src/hris/paygroup/paygroup.controller.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { PayGroupService } from './services/paygroup.service'; -import { - UnifiedHrisPaygroupInput, - UnifiedHrisPaygroupOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('hris/paygroups') -@Controller('hris/paygroups') -export class PayGroupController { - constructor( - private readonly paygroupService: PayGroupService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(PayGroupController.name); - } - - @ApiOperation({ - operationId: 'listHrisPaygroups', - summary: 'List Pay Groups', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisPaygroupOutput) - @UseGuards(ApiKeyAuthGuard) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @Get() - async getPayGroups( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.paygroupService.getPayGroups( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisPaygroup', - summary: 'Retrieve Pay Group', - description: 'Retrieve a Pay Group from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the paygroup you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisPaygroupOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.paygroupService.getPayGroup( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/hris/paygroup/paygroup.module.ts b/packages/api/src/hris/paygroup/paygroup.module.ts deleted file mode 100644 index d13fe5594..000000000 --- a/packages/api/src/hris/paygroup/paygroup.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Module } from '@nestjs/common'; -import { PayGroupController } from './paygroup.controller'; -import { PayGroupService } from './services/paygroup.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Utils } from '@hris/@lib/@utils'; -@Module({ - controllers: [PayGroupController], - providers: [ - PayGroupService, - Utils, - CoreUnification, - SyncService, - WebhookService, - ServiceRegistry, - IngestDataService, - /* PROVIDERS SERVICES */ - ], - exports: [SyncService], -}) -export class PayGroupModule {} diff --git a/packages/api/src/hris/paygroup/services/paygroup.service.ts b/packages/api/src/hris/paygroup/services/paygroup.service.ts deleted file mode 100644 index b21213494..000000000 --- a/packages/api/src/hris/paygroup/services/paygroup.service.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { - UnifiedHrisPaygroupInput, - UnifiedHrisPaygroupOutput, -} from '../types/model.unified'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ServiceRegistry } from './registry.service'; - -@Injectable() -export class PayGroupService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(PayGroupService.name); - } - - async getPayGroup( - id_hris_pay_group: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const paygroup = await this.prisma.hris_pay_groups.findUnique({ - where: { id_hris_pay_group: id_hris_pay_group }, - }); - - if (!paygroup) { - throw new Error(`PayGroup with ID ${id_hris_pay_group} not found.`); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: paygroup.id_hris_pay_group, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedPayGroup: UnifiedHrisPaygroupOutput = { - id: paygroup.id_hris_pay_group, - pay_group_name: paygroup.pay_group_name, - field_mappings: field_mappings, - remote_id: paygroup.remote_id, - remote_created_at: paygroup.remote_created_at, - created_at: paygroup.created_at, - modified_at: paygroup.modified_at, - remote_was_deleted: paygroup.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: paygroup.id_hris_pay_group, - }, - }); - unifiedPayGroup.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.paygroup.pull', - method: 'GET', - url: '/hris/paygroup', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedPayGroup; - } catch (error) { - throw error; - } - } - - async getPayGroups( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisPaygroupOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const paygroups = await this.prisma.hris_pay_groups.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_pay_group: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }); - - const hasNextPage = paygroups.length > limit; - if (hasNextPage) paygroups.pop(); - - const unifiedPayGroups = await Promise.all( - paygroups.map(async (paygroup) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: paygroup.id_hris_pay_group, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedPayGroup: UnifiedHrisPaygroupOutput = { - id: paygroup.id_hris_pay_group, - pay_group_name: paygroup.pay_group_name, - field_mappings: field_mappings, - remote_id: paygroup.remote_id, - remote_created_at: paygroup.remote_created_at, - created_at: paygroup.created_at, - modified_at: paygroup.modified_at, - remote_was_deleted: paygroup.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: paygroup.id_hris_pay_group, - }, - }); - unifiedPayGroup.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedPayGroup; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.paygroup.pull', - method: 'GET', - url: '/hris/paygroups', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedPayGroups, - next_cursor: hasNextPage - ? paygroups[paygroups.length - 1].id_hris_pay_group - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/paygroup/services/registry.service.ts b/packages/api/src/hris/paygroup/services/registry.service.ts deleted file mode 100644 index b3e1db9f7..000000000 --- a/packages/api/src/hris/paygroup/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IPayGroupService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IPayGroupService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IPayGroupService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/paygroup/sync/sync.service.ts b/packages/api/src/hris/paygroup/sync/sync.service.ts deleted file mode 100644 index e14a4c2b7..000000000 --- a/packages/api/src/hris/paygroup/sync/sync.service.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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 { UnifiedHrisPaygroupOutput } from '../types/model.unified'; -import { IPayGroupService } from '../types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_pay_groups as HrisPayGroup } from '@prisma/client'; -import { OriginalPayGroupOutput } from '@@core/utils/types/original/original.hris'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'paygroup', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IPayGroupService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisPaygroupOutput, - OriginalPayGroupOutput, - IPayGroupService - >(integrationId, linkedUserId, 'hris', 'paygroup', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - paygroups: UnifiedHrisPaygroupOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const paygroupResults: HrisPayGroup[] = []; - - for (let i = 0; i < paygroups.length; i++) { - const paygroup = paygroups[i]; - const originId = paygroup.remote_id; - - let existingPayGroup = await this.prisma.hris_pay_groups.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const paygroupData = { - pay_group_name: paygroup.pay_group_name, - remote_id: originId, - remote_created_at: paygroup.remote_created_at - ? new Date(paygroup.remote_created_at) - : new Date(), - modified_at: new Date(), - remote_was_deleted: paygroup.remote_was_deleted || false, - }; - - if (existingPayGroup) { - existingPayGroup = await this.prisma.hris_pay_groups.update({ - where: { - id_hris_pay_group: existingPayGroup.id_hris_pay_group, - }, - data: paygroupData, - }); - } else { - existingPayGroup = await this.prisma.hris_pay_groups.create({ - data: { - ...paygroupData, - id_hris_pay_group: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - paygroupResults.push(existingPayGroup); - - // Process field mappings - await this.ingestService.processFieldMappings( - paygroup.field_mappings, - existingPayGroup.id_hris_pay_group, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingPayGroup.id_hris_pay_group, - remote_data[i], - ); - } - - return paygroupResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/paygroup/types/index.ts b/packages/api/src/hris/paygroup/types/index.ts deleted file mode 100644 index d302e1edc..000000000 --- a/packages/api/src/hris/paygroup/types/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisPaygroupInput, - UnifiedHrisPaygroupOutput, -} from './model.unified'; -import { OriginalPayGroupOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface IPayGroupService { - sync(data: SyncParam): Promise>; -} - -export interface IPayGroupMapper { - desunify( - source: UnifiedHrisPaygroupInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalPayGroupOutput | OriginalPayGroupOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/hris/paygroup/types/model.unified.ts b/packages/api/src/hris/paygroup/types/model.unified.ts deleted file mode 100644 index cb8c35487..000000000 --- a/packages/api/src/hris/paygroup/types/model.unified.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsBoolean, -} from 'class-validator'; - -export class UnifiedHrisPaygroupInput { - @ApiPropertyOptional({ - type: String, - example: 'Monthly Salaried', - nullable: true, - description: 'The name of the pay group', - }) - @IsString() - @IsOptional() - pay_group_name?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisPaygroupOutput extends UnifiedHrisPaygroupInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the pay group record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'paygroup_1234', - nullable: true, - description: - 'The remote ID of the pay group in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the pay group in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: - 'The date when the pay group was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The created date of the pay group record', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The last modified date of the pay group record', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: 'Indicates if the pay group was deleted in the remote system', - }) - @IsBoolean() - @IsOptional() - remote_was_deleted?: boolean; -} diff --git a/packages/api/src/hris/paygroup/utils/index.ts b/packages/api/src/hris/paygroup/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/paygroup/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/payrollrun/payrollrun.controller.ts b/packages/api/src/hris/payrollrun/payrollrun.controller.ts deleted file mode 100644 index b0f31b6a7..000000000 --- a/packages/api/src/hris/payrollrun/payrollrun.controller.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { PayrollRunService } from './services/payrollrun.service'; -import { - UnifiedHrisPayrollrunInput, - UnifiedHrisPayrollrunOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('hris/payrollruns') -@Controller('hris/payrollruns') -export class PayrollRunController { - constructor( - private readonly payrollrunService: PayrollRunService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(PayrollRunController.name); - } - - @ApiOperation({ - operationId: 'listHrisPayrollRuns', - summary: 'List Payroll Runs', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisPayrollrunOutput) - @UseGuards(ApiKeyAuthGuard) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @Get() - async getPayrollRuns( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.payrollrunService.getPayrollRuns( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisPayrollRun', - summary: 'Retrieve Payroll Run', - description: 'Retrieve a Payroll Run from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the payroll run you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisPayrollrunOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.payrollrunService.getPayrollRun( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/hris/payrollrun/payrollrun.module.ts b/packages/api/src/hris/payrollrun/payrollrun.module.ts deleted file mode 100644 index a7ffb3724..000000000 --- a/packages/api/src/hris/payrollrun/payrollrun.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Module } from '@nestjs/common'; -import { PayrollRunController } from './payrollrun.controller'; -import { PayrollRunService } from './services/payrollrun.service'; -import { ServiceRegistry } from './services/registry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Utils } from '@hris/@lib/@utils'; -@Module({ - controllers: [PayrollRunController], - providers: [ - PayrollRunService, - CoreUnification, - Utils, - SyncService, - WebhookService, - ServiceRegistry, - IngestDataService, - /* PROVIDERS SERVICES */ - ], - exports: [SyncService], -}) -export class PayrollRunModule {} diff --git a/packages/api/src/hris/payrollrun/services/payrollrun.service.ts b/packages/api/src/hris/payrollrun/services/payrollrun.service.ts deleted file mode 100644 index 3c66e6ff1..000000000 --- a/packages/api/src/hris/payrollrun/services/payrollrun.service.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Injectable } from '@nestjs/common'; -import { v4 as uuidv4 } from 'uuid'; -import { UnifiedHrisPayrollrunOutput } from '../types/model.unified'; -import { ServiceRegistry } from './registry.service'; - -@Injectable() -export class PayrollRunService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(PayrollRunService.name); - } - - async getPayrollRun( - id_hris_payroll_run: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const payrollRun = await this.prisma.hris_payroll_runs.findUnique({ - where: { id_hris_payroll_run: id_hris_payroll_run }, - }); - - if (!payrollRun) { - throw new Error(`PayrollRun with ID ${id_hris_payroll_run} not found.`); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: payrollRun.id_hris_payroll_run, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedPayrollRun: UnifiedHrisPayrollrunOutput = { - id: payrollRun.id_hris_payroll_run, - run_state: payrollRun.run_state, - run_type: payrollRun.run_type, - start_date: payrollRun.start_date, - end_date: payrollRun.end_date, - check_date: payrollRun.check_date, - field_mappings: field_mappings, - remote_id: payrollRun.remote_id, - remote_created_at: payrollRun.remote_created_at, - created_at: payrollRun.created_at, - modified_at: payrollRun.modified_at, - remote_was_deleted: payrollRun.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: payrollRun.id_hris_payroll_run, - }, - }); - unifiedPayrollRun.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.payrollrun.pull', - method: 'GET', - url: '/hris/payrollrun', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedPayrollRun; - } catch (error) { - throw error; - } - } - - async getPayrollRuns( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisPayrollrunOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const payrollRuns = await this.prisma.hris_payroll_runs.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_payroll_run: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }); - - const hasNextPage = payrollRuns.length > limit; - if (hasNextPage) payrollRuns.pop(); - - const unifiedPayrollRuns = await Promise.all( - payrollRuns.map(async (payrollRun) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: payrollRun.id_hris_payroll_run, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedPayrollRun: UnifiedHrisPayrollrunOutput = { - id: payrollRun.id_hris_payroll_run, - run_state: payrollRun.run_state, - run_type: payrollRun.run_type, - start_date: payrollRun.start_date, - end_date: payrollRun.end_date, - check_date: payrollRun.check_date, - field_mappings: field_mappings, - remote_id: payrollRun.remote_id, - remote_created_at: payrollRun.remote_created_at, - created_at: payrollRun.created_at, - modified_at: payrollRun.modified_at, - remote_was_deleted: payrollRun.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: payrollRun.id_hris_payroll_run, - }, - }); - unifiedPayrollRun.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedPayrollRun; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.payrollrun.pull', - method: 'GET', - url: '/hris/payrollruns', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedPayrollRuns, - next_cursor: hasNextPage - ? payrollRuns[payrollRuns.length - 1].id_hris_payroll_run - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/payrollrun/services/registry.service.ts b/packages/api/src/hris/payrollrun/services/registry.service.ts deleted file mode 100644 index ab6ccce3b..000000000 --- a/packages/api/src/hris/payrollrun/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { IPayrollRunService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: IPayrollRunService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): IPayrollRunService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/payrollrun/sync/sync.service.ts b/packages/api/src/hris/payrollrun/sync/sync.service.ts deleted file mode 100644 index b8e2923a3..000000000 --- a/packages/api/src/hris/payrollrun/sync/sync.service.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; -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 { UnifiedHrisPayrollrunOutput } from '../types/model.unified'; -import { IPayrollRunService } from '../types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_payroll_runs as HrisPayrollRun } from '@prisma/client'; -import { OriginalPayrollRunOutput } from '@@core/utils/types/original/original.hris'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'payrollrun', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: IPayrollRunService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisPayrollrunOutput, - OriginalPayrollRunOutput, - IPayrollRunService - >(integrationId, linkedUserId, 'hris', 'payrollrun', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - payrollRuns: UnifiedHrisPayrollrunOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const payrollRunResults: HrisPayrollRun[] = []; - - for (let i = 0; i < payrollRuns.length; i++) { - const payrollRun = payrollRuns[i]; - const originId = payrollRun.remote_id; - - let existingPayrollRun = await this.prisma.hris_payroll_runs.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const payrollRunData = { - run_state: payrollRun.run_state, - run_type: payrollRun.run_type, - start_date: payrollRun.start_date - ? new Date(payrollRun.start_date) - : null, - end_date: payrollRun.end_date ? new Date(payrollRun.end_date) : null, - check_date: payrollRun.check_date - ? new Date(payrollRun.check_date) - : null, - remote_id: originId, - remote_created_at: payrollRun.remote_created_at - ? new Date(payrollRun.remote_created_at) - : new Date(), - modified_at: new Date(), - remote_was_deleted: payrollRun.remote_was_deleted || false, - }; - - if (existingPayrollRun) { - existingPayrollRun = await this.prisma.hris_payroll_runs.update({ - where: { - id_hris_payroll_run: existingPayrollRun.id_hris_payroll_run, - }, - data: payrollRunData, - }); - } else { - existingPayrollRun = await this.prisma.hris_payroll_runs.create({ - data: { - ...payrollRunData, - id_hris_payroll_run: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - payrollRunResults.push(existingPayrollRun); - - // Process field mappings - await this.ingestService.processFieldMappings( - payrollRun.field_mappings, - existingPayrollRun.id_hris_payroll_run, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingPayrollRun.id_hris_payroll_run, - remote_data[i], - ); - } - - return payrollRunResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/payrollrun/types/index.ts b/packages/api/src/hris/payrollrun/types/index.ts deleted file mode 100644 index a8f390412..000000000 --- a/packages/api/src/hris/payrollrun/types/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisPayrollrunInput, - UnifiedHrisPayrollrunOutput, -} from './model.unified'; -import { OriginalPayrollRunOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface IPayrollRunService { - sync(data: SyncParam): Promise>; -} - -export interface IPayrollRunMapper { - desunify( - source: UnifiedHrisPayrollrunInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalPayrollRunOutput | OriginalPayrollRunOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/hris/payrollrun/types/model.unified.ts b/packages/api/src/hris/payrollrun/types/model.unified.ts deleted file mode 100644 index 23e77e40f..000000000 --- a/packages/api/src/hris/payrollrun/types/model.unified.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsBoolean, -} from 'class-validator'; - -export type RunState = 'PAID' | 'DRAFT' | 'APPROVED' | 'FAILED' | 'CLOSE'; -export type RunType = - | 'REGULAR' - | 'OFF_CYCLE' - | 'CORRECTION' - | 'TERMINATION' - | 'SIGN_ON_BONUS'; -export class UnifiedHrisPayrollrunInput { - @ApiPropertyOptional({ - type: String, - example: 'PAID', - // enum: ['PAID', 'DRAFT', 'APPROVED', 'FAILED', 'CLOSE'], - nullable: true, - description: 'The state of the payroll run', - }) - @IsString() - @IsOptional() - run_state?: RunState | string; - - @ApiPropertyOptional({ - type: String, - example: 'REGULAR', - /* enum: [ - 'REGULAR', - 'OFF_CYCLE', - 'CORRECTION', - 'TERMINATION', - 'SIGN_ON_BONUS', - ],*/ - nullable: true, - description: 'The type of the payroll run', - }) - @IsString() - @IsOptional() - run_type?: RunType | string; - - @ApiPropertyOptional({ - type: Date, - example: '2024-01-01T00:00:00Z', - nullable: true, - description: 'The start date of the payroll run', - }) - @IsDateString() - @IsOptional() - start_date?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-01-15T23:59:59Z', - nullable: true, - description: 'The end date of the payroll run', - }) - @IsDateString() - @IsOptional() - end_date?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-01-20T00:00:00Z', - nullable: true, - description: 'The check date of the payroll run', - }) - @IsDateString() - @IsOptional() - check_date?: Date; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisPayrollrunOutput extends UnifiedHrisPayrollrunInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the payroll run record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'payroll_run_1234', - nullable: true, - description: - 'The remote ID of the payroll run in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the payroll run in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: - 'The date when the payroll run was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The created date of the payroll run record', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: 'The last modified date of the payroll run record', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: - 'Indicates if the payroll run was deleted in the remote system', - }) - @IsBoolean() - @IsOptional() - remote_was_deleted?: boolean; - - @ApiPropertyOptional({ - type: [String], - example: ['801f9ede-c698-4e66-a7fc-48d19eebaa4f'], - nullable: true, - description: - 'The UUIDs of the employee payroll runs associated with this payroll run', - }) - @IsOptional() - employee_payroll_runs?: string[]; -} diff --git a/packages/api/src/hris/payrollrun/utils/index.ts b/packages/api/src/hris/payrollrun/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/payrollrun/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/timeoff/services/registry.service.ts b/packages/api/src/hris/timeoff/services/registry.service.ts deleted file mode 100644 index 91fd28973..000000000 --- a/packages/api/src/hris/timeoff/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ITimeoffService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: ITimeoffService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): ITimeoffService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/timeoff/services/sage/index.ts b/packages/api/src/hris/timeoff/services/sage/index.ts deleted file mode 100644 index c83e2d187..000000000 --- a/packages/api/src/hris/timeoff/services/sage/index.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { HrisObject } from '@hris/@lib/@types'; -import { ITimeoffService } from '@hris/timeoff/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { SageTimeoffOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { OriginalTimeoffOutput } from '@@core/utils/types/original/original.hris'; - -@Injectable() -export class SageService implements ITimeoffService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - HrisObject.timeoff.toUpperCase() + ':' + SageService.name, - ); - this.registry.registerService('sage', this); - } - addTimeoff( - timeoffData: DesunifyReturnType, - linkedUserId: string, - ): Promise> { - throw new Error('Method not implemented.'); - } - - async sync(data: SyncParam): Promise> { - try { - const { linkedUserId } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'sage', - vertical: 'hris', - }, - }); - - const resp = await axios.get( - `${connection.account_url}/api/leave-management/requests`, - { - headers: { - 'Content-Type': 'application/json', - 'X-Auth-Token': this.cryptoService.decrypt(connection.access_token), - }, - }, - ); - this.logger.log(`Synced sage timeoffs !`); - - return { - data: resp.data.data, - message: 'Sage timeoffs retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/timeoff/services/sage/mappers.ts b/packages/api/src/hris/timeoff/services/sage/mappers.ts deleted file mode 100644 index d8eeff03d..000000000 --- a/packages/api/src/hris/timeoff/services/sage/mappers.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Utils } from '@hris/@lib/@utils'; -import { ITimeoffMapper } from '@hris/timeoff/types'; -import { - UnifiedHrisTimeoffInput, - UnifiedHrisTimeoffOutput, - Status, - RequestType, -} from '@hris/timeoff/types/model.unified'; -import { SageTimeoffOutput } from './types'; - -@Injectable() -export class SageTimeoffMapper implements ITimeoffMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService('hris', 'timeoff', 'sage', this); - } - - async desunify( - source: UnifiedHrisTimeoffInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - // Implementation for desunify (if needed) - return; - } - - async unify( - source: SageTimeoffOutput | SageTimeoffOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - if (!Array.isArray(source)) { - return this.mapSingleTimeoffToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((timeoff) => - this.mapSingleTimeoffToUnified( - timeoff, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleTimeoffToUnified( - timeoff: SageTimeoffOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - const employee = await this.utils.getEmployeeUuidFromRemoteId( - timeoff.employee_id.toString(), - connectionId, - ); - - return { - remote_id: timeoff.id.toString(), - remote_data: timeoff, - employee: employee, - status: this.mapStatus(timeoff.status_code), - employee_note: timeoff.details, - units: 'HOURS', - amount: timeoff.hours, - request_type: this.inferRequestType(timeoff.details), - start_time: new Date(timeoff.start_date), - end_time: new Date(timeoff.end_date), - }; - } - - private mapStatus(statusCode: string): Status { - switch (statusCode.toLowerCase()) { - case 'approved': - return 'APPROVED'; - case 'declined': - return 'DECLINED'; - case 'cancelled': - return 'CANCELLED'; - case 'deleted': - return 'DELETED'; - default: - return 'REQUESTED'; - } - } - - private inferRequestType(details: string): RequestType { - const lowerDetails = details.toLowerCase(); - if (lowerDetails.includes('vacation') || lowerDetails.includes('holiday')) { - return 'VACATION'; - } else if ( - lowerDetails.includes('sick') || - lowerDetails.includes('illness') - ) { - return 'SICK'; - } else if (lowerDetails.includes('jury')) { - return 'JURY_DUTY'; - } else if (lowerDetails.includes('bereavement')) { - return 'BEREAVEMENT'; - } else if (lowerDetails.includes('volunteer')) { - return 'VOLUNTEER'; - } else { - return 'PERSONAL'; - } - } -} diff --git a/packages/api/src/hris/timeoff/services/sage/types.ts b/packages/api/src/hris/timeoff/services/sage/types.ts deleted file mode 100644 index f7b59bd94..000000000 --- a/packages/api/src/hris/timeoff/services/sage/types.ts +++ /dev/null @@ -1,30 +0,0 @@ -export interface SageTimeoffReplacement { - id: number; - full_name: string; -} - -export interface SageTimeoffField { - title: string; - answer: string; -} - -export type SageTimeoffOutput = Partial<{ - id: number; - status: string; - status_code: string; - policy_id: number; - employee_id: number; - replacement: SageTimeoffReplacement; - details: string; - is_multi_date: boolean; - is_single_day: boolean; - is_part_of_day: boolean; - first_part_of_day: boolean; - second_part_of_day: boolean; - start_date: string; - end_date: string; - request_date: string; - approval_date: string | null; - hours: number; - fields: SageTimeoffField[]; -}>; diff --git a/packages/api/src/hris/timeoff/services/timeoff.service.ts b/packages/api/src/hris/timeoff/services/timeoff.service.ts deleted file mode 100644 index 780c1d37a..000000000 --- a/packages/api/src/hris/timeoff/services/timeoff.service.ts +++ /dev/null @@ -1,354 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { ApiResponse } from '@@core/utils/types'; -import { throwTypedError } from '@@core/utils/errors'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { - UnifiedHrisTimeoffInput, - UnifiedHrisTimeoffOutput, -} from '../types/model.unified'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ServiceRegistry } from './registry.service'; -import { OriginalTimeoffOutput } from '@@core/utils/types/original/original.hris'; -import { HrisObject } from '@panora/shared'; -import { ITimeoffService } from '../types'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class TimeoffService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private coreUnification: CoreUnification, - private ingestService: IngestDataService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(TimeoffService.name); - } - - async validateLinkedUser(linkedUserId: string) { - const linkedUser = await this.prisma.linked_users.findUnique({ - where: { id_linked_user: linkedUserId }, - }); - if (!linkedUser) throw new ReferenceError('Linked User Not Found'); - return linkedUser; - } - - async addTimeoff( - unifiedTimeoffData: UnifiedHrisTimeoffInput, - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - remote_data?: boolean, - ): Promise { - try { - const linkedUser = await this.validateLinkedUser(linkedUserId); - // Add any necessary validations here, e.g., validateEmployeeId if needed - - const desunifiedObject = - await this.coreUnification.desunify({ - sourceObject: unifiedTimeoffData, - targetType: HrisObject.timeoff, - providerName: integrationId, - vertical: 'hris', - customFieldMappings: [], - }); - - const service: ITimeoffService = - this.serviceRegistry.getService(integrationId); - const resp: ApiResponse = await service.addTimeoff( - desunifiedObject, - linkedUserId, - ); - - const unifiedObject = (await this.coreUnification.unify< - OriginalTimeoffOutput[] - >({ - sourceObject: [resp.data], - targetType: HrisObject.timeoff, - providerName: integrationId, - vertical: 'hris', - connectionId: connectionId, - customFieldMappings: [], - })) as UnifiedHrisTimeoffOutput[]; - - const source_timeoff = resp.data; - const target_timeoff = unifiedObject[0]; - - const unique_hris_timeoff_id = await this.saveOrUpdateTimeoff( - target_timeoff, - connectionId, - ); - - await this.ingestService.processRemoteData( - unique_hris_timeoff_id, - source_timeoff, - ); - - const result_timeoff = await this.getTimeoff( - unique_hris_timeoff_id, - undefined, - undefined, - connectionId, - projectId, - remote_data, - ); - - const status_resp = resp.statusCode === 201 ? 'success' : 'fail'; - const event = await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: status_resp, - type: 'hris.timeoff.push', - method: 'POST', - url: '/hris/timeoff', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - await this.webhook.dispatchWebhook( - result_timeoff, - 'hris.timeoff.created', - linkedUser.id_project, - event.id_event, - ); - - return result_timeoff; - } catch (error) { - throw error; - } - } - - async saveOrUpdateTimeoff( - timeoff: UnifiedHrisTimeoffOutput, - connectionId: string, - ): Promise { - const existingTimeoff = await this.prisma.hris_time_off.findFirst({ - where: { remote_id: timeoff.remote_id, id_connection: connectionId }, - }); - - const data: any = { - employee: timeoff.employee, - approver: timeoff.approver, - status: timeoff.status, - employee_note: timeoff.employee_note, - units: timeoff.units, - amount: timeoff.amount ? BigInt(timeoff.amount) : null, - request_type: timeoff.request_type, - start_time: timeoff.start_time ? new Date(timeoff.start_time) : null, - end_time: timeoff.end_time ? new Date(timeoff.end_time) : null, - field_mappings: timeoff.field_mappings, - modified_at: new Date(), - }; - - if (existingTimeoff) { - const res = await this.prisma.hris_time_off.update({ - where: { id_hris_time_off: existingTimeoff.id_hris_time_off }, - data: data, - }); - - return res.id_hris_time_off; - } else { - data.created_at = new Date(); - data.remote_id = timeoff.remote_id; - data.id_connection = connectionId; - data.id_hris_time_off = uuidv4(); - data.remote_was_deleted = timeoff.remote_was_deleted ?? false; - data.remote_created_at = timeoff.remote_created_at - ? new Date(timeoff.remote_created_at) - : null; - - const newTimeoff = await this.prisma.hris_time_off.create({ data: data }); - - return newTimeoff.id_hris_time_off; - } - } - - async getTimeoff( - id_hris_time_off: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const timeOff = await this.prisma.hris_time_off.findUnique({ - where: { id_hris_time_off: id_hris_time_off }, - }); - - if (!timeOff) { - throw new Error(`Time off with ID ${id_hris_time_off} not found.`); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: timeOff.id_hris_time_off }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedTimeOff: UnifiedHrisTimeoffOutput = { - id: timeOff.id_hris_time_off, - employee: timeOff.employee, - approver: timeOff.approver, - status: timeOff.status, - employee_note: timeOff.employee_note, - units: timeOff.units, - amount: timeOff.amount ? Number(timeOff.amount) : undefined, - request_type: timeOff.request_type, - start_time: timeOff.start_time, - end_time: timeOff.end_time, - field_mappings: field_mappings, - remote_id: timeOff.remote_id, - remote_created_at: timeOff.remote_created_at, - created_at: timeOff.created_at, - modified_at: timeOff.modified_at, - remote_was_deleted: timeOff.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: timeOff.id_hris_time_off }, - }); - unifiedTimeOff.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.time_off.pull', - method: 'GET', - url: '/hris/time_off', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedTimeOff; - } catch (error) { - throw error; - } - } - - async getTimeoffs( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisTimeoffOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const timeOffs = await this.prisma.hris_time_off.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_time_off: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }); - - const hasNextPage = timeOffs.length > limit; - if (hasNextPage) timeOffs.pop(); - - const unifiedTimeOffs = await Promise.all( - timeOffs.map(async (timeOff) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { ressource_owner_id: timeOff.id_hris_time_off }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedTimeOff: UnifiedHrisTimeoffOutput = { - id: timeOff.id_hris_time_off, - employee: timeOff.employee, - approver: timeOff.approver, - status: timeOff.status, - employee_note: timeOff.employee_note, - units: timeOff.units, - amount: timeOff.amount ? Number(timeOff.amount) : undefined, - request_type: timeOff.request_type, - start_time: timeOff.start_time, - end_time: timeOff.end_time, - field_mappings: field_mappings, - remote_id: timeOff.remote_id, - remote_created_at: timeOff.remote_created_at, - created_at: timeOff.created_at, - modified_at: timeOff.modified_at, - remote_was_deleted: timeOff.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: timeOff.id_hris_time_off, - }, - }); - unifiedTimeOff.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedTimeOff; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.timeoff.pull', - method: 'GET', - url: '/hris/timeoffs', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedTimeOffs, - next_cursor: hasNextPage - ? timeOffs[timeOffs.length - 1].id_hris_time_off - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/timeoff/sync/sync.service.ts b/packages/api/src/hris/timeoff/sync/sync.service.ts deleted file mode 100644 index 9214c389e..000000000 --- a/packages/api/src/hris/timeoff/sync/sync.service.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { ApiResponse } from '@@core/utils/types'; -import { v4 as uuidv4 } from 'uuid'; -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 { UnifiedHrisTimeoffOutput } from '../types/model.unified'; -import { ITimeoffService } from '../types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_time_off as HrisTimeOff } from '@prisma/client'; -import { OriginalTimeoffOutput } from '@@core/utils/types/original/original.hris'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'timeoff', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: ITimeoffService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisTimeoffOutput, - OriginalTimeoffOutput, - ITimeoffService - >(integrationId, linkedUserId, 'hris', 'timeoff', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - timeOffs: UnifiedHrisTimeoffOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const timeOffResults: HrisTimeOff[] = []; - - for (let i = 0; i < timeOffs.length; i++) { - const timeOff = timeOffs[i]; - const originId = timeOff.remote_id; - - let existingTimeOff = await this.prisma.hris_time_off.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const timeOffData = { - employee: timeOff.employee, - approver: timeOff.approver, - status: timeOff.status, - employee_note: timeOff.employee_note, - units: timeOff.units, - amount: timeOff.amount ? BigInt(timeOff.amount) : null, - request_type: timeOff.request_type, - start_time: timeOff.start_time ? new Date(timeOff.start_time) : null, - end_time: timeOff.end_time ? new Date(timeOff.end_time) : null, - remote_id: originId, - remote_created_at: timeOff.remote_created_at - ? new Date(timeOff.remote_created_at) - : null, - modified_at: new Date(), - remote_was_deleted: timeOff.remote_was_deleted || false, - }; - - if (existingTimeOff) { - existingTimeOff = await this.prisma.hris_time_off.update({ - where: { id_hris_time_off: existingTimeOff.id_hris_time_off }, - data: timeOffData, - }); - } else { - existingTimeOff = await this.prisma.hris_time_off.create({ - data: { - ...timeOffData, - id_hris_time_off: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - timeOffResults.push(existingTimeOff); - - // Process field mappings - await this.ingestService.processFieldMappings( - timeOff.field_mappings, - existingTimeOff.id_hris_time_off, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingTimeOff.id_hris_time_off, - remote_data[i], - ); - } - - return timeOffResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/timeoff/timeoff.controller.ts b/packages/api/src/hris/timeoff/timeoff.controller.ts deleted file mode 100644 index d8895ecc1..000000000 --- a/packages/api/src/hris/timeoff/timeoff.controller.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { TimeoffService } from './services/timeoff.service'; -import { - UnifiedHrisTimeoffInput, - UnifiedHrisTimeoffOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, - ApiPostCustomResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('hris/timeoffs') -@Controller('hris/timeoffs') -export class TimeoffController { - constructor( - private readonly timeoffService: TimeoffService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(TimeoffController.name); - } - - @ApiOperation({ - operationId: 'listHrisTimeoffs', - summary: 'List Time Offs', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisTimeoffOutput) - @UseGuards(ApiKeyAuthGuard) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @Get() - async getTimeoffs( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.timeoffService.getTimeoffs( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisTimeoff', - summary: 'Retrieve Time Off', - description: 'Retrieve a Time Off from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the time off you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisTimeoffOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.timeoffService.getTimeoff( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } - - @ApiOperation({ - operationId: 'createHrisTimeoff', - summary: 'Create Timeoffs', - description: 'Create Timeoffs in any supported Hris software', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - }) - @ApiBody({ type: UnifiedHrisTimeoffInput }) - @ApiPostCustomResponse(UnifiedHrisTimeoffOutput) - @UseGuards(ApiKeyAuthGuard) - @Post() - async addTimeoff( - @Body() unifiedTimeoffData: UnifiedHrisTimeoffInput, - @Headers('x-connection-token') connection_token: string, - @Query('remote_data') remote_data?: boolean, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.timeoffService.addTimeoff( - unifiedTimeoffData, - connectionId, - projectId, - remoteSource, - linkedUserId, - remote_data, - ); - } catch (error) { - throw new Error(error); - } - } -} diff --git a/packages/api/src/hris/timeoff/timeoff.module.ts b/packages/api/src/hris/timeoff/timeoff.module.ts deleted file mode 100644 index 0f66654af..000000000 --- a/packages/api/src/hris/timeoff/timeoff.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ServiceRegistry } from './services/registry.service'; -import { TimeoffService } from './services/timeoff.service'; -import { SyncService } from './sync/sync.service'; -import { TimeoffController } from './timeoff.controller'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Utils } from '@hris/@lib/@utils'; -import { SageTimeoffMapper } from './services/sage/mappers'; -import { SageService } from './services/sage'; -@Module({ - controllers: [TimeoffController], - providers: [ - TimeoffService, - CoreUnification, - Utils, - SyncService, - WebhookService, - ServiceRegistry, - IngestDataService, - SageTimeoffMapper, - /* PROVIDERS SERVICES */ - SageService, - ], - exports: [SyncService], -}) -export class TimeoffModule {} diff --git a/packages/api/src/hris/timeoff/types/index.ts b/packages/api/src/hris/timeoff/types/index.ts deleted file mode 100644 index e26f0d3ab..000000000 --- a/packages/api/src/hris/timeoff/types/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisTimeoffInput, - UnifiedHrisTimeoffOutput, -} from './model.unified'; -import { OriginalTimeoffOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface ITimeoffService { - addTimeoff( - timeoffData: DesunifyReturnType, - linkedUserId: string, - ): Promise>; - - sync(data: SyncParam): Promise>; -} - -export interface ITimeoffMapper { - desunify( - source: UnifiedHrisTimeoffInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalTimeoffOutput | OriginalTimeoffOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise; -} diff --git a/packages/api/src/hris/timeoff/types/model.unified.ts b/packages/api/src/hris/timeoff/types/model.unified.ts deleted file mode 100644 index 8af5a3284..000000000 --- a/packages/api/src/hris/timeoff/types/model.unified.ts +++ /dev/null @@ -1,216 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsNumber, - IsDateString, - IsBoolean, -} from 'class-validator'; - -export type Status = - | 'REQUESTED' - | 'APPROVED' - | 'DECLINED' - | 'CANCELLED' - | 'DELETED'; - -export type RequestType = - | 'VACATION' - | 'SICK' - | 'PERSONAL' - | 'JURY_DUTY' - | 'VOLUNTEER' - | 'BEREAVEMENT'; -export class UnifiedHrisTimeoffInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the employee taking time off', - }) - @IsUUID() - @IsOptional() - employee?: string; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the approver for the time off request', - }) - @IsUUID() - @IsOptional() - approver?: string; - - @ApiPropertyOptional({ - type: String, - example: 'REQUESTED', - // enum: ['REQUESTED', 'APPROVED', 'DECLINED', 'CANCELLED', 'DELETED'], - nullable: true, - description: 'The status of the time off request', - }) - @IsString() - @IsOptional() - status?: Status | string; - - @ApiPropertyOptional({ - type: String, - example: 'Annual vacation', - nullable: true, - description: 'A note from the employee about the time off request', - }) - @IsString() - @IsOptional() - employee_note?: string; - - @ApiPropertyOptional({ - type: String, - example: 'DAYS', - // enum: ['HOURS', 'DAYS'], - nullable: true, - description: 'The units used for the time off (e.g., Days, Hours)', - }) - @IsString() - @IsOptional() - units?: 'HOURS' | 'DAYS' | string; - - @ApiPropertyOptional({ - type: Number, - example: 5, - nullable: true, - description: 'The amount of time off requested', - }) - @IsNumber() - @IsOptional() - amount?: number; - - @ApiPropertyOptional({ - type: String, - example: 'VACATION', - /* enum: [ - 'VACATION', - 'SICK', - 'PERSONAL', - 'JURY_DUTY', - 'VOLUNTEER', - 'BEREAVEMENT', - ],*/ - nullable: true, - description: 'The type of time off request', - }) - @IsString() - @IsOptional() - request_type?: RequestType | string; - - @ApiPropertyOptional({ - type: Date, - example: '2024-07-01T09:00:00Z', - nullable: true, - description: 'The start time of the time off', - }) - @IsDateString() - @IsOptional() - start_time?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-07-05T17:00:00Z', - nullable: true, - description: 'The end time of the time off', - }) - @IsDateString() - @IsOptional() - end_time?: Date; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisTimeoffOutput extends UnifiedHrisTimeoffInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the time off record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'timeoff_1234', - nullable: true, - description: - 'The remote ID of the time off in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the time off in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: Date, - example: '2024-06-15T12:00:00Z', - nullable: true, - description: - 'The date when the time off was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-06-15T12:00:00Z', - nullable: true, - description: 'The created date of the time off record', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-06-15T12:00:00Z', - nullable: true, - description: 'The last modified date of the time off record', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: 'Indicates if the time off was deleted in the remote system', - }) - @IsBoolean() - @IsOptional() - remote_was_deleted?: boolean; -} diff --git a/packages/api/src/hris/timeoff/utils/index.ts b/packages/api/src/hris/timeoff/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/timeoff/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/timeoffbalance/services/registry.service.ts b/packages/api/src/hris/timeoffbalance/services/registry.service.ts deleted file mode 100644 index 1fb90d6ab..000000000 --- a/packages/api/src/hris/timeoffbalance/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ITimeoffBalanceService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: ITimeoffBalanceService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): ITimeoffBalanceService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/timeoffbalance/services/sage/index.ts b/packages/api/src/hris/timeoffbalance/services/sage/index.ts deleted file mode 100644 index 61f928029..000000000 --- a/packages/api/src/hris/timeoffbalance/services/sage/index.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; -import { HrisObject } from '@hris/@lib/@types'; -import { ITimeoffBalanceService } from '@hris/timeoffbalance/types'; -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { ServiceRegistry } from '../registry.service'; -import { SageTimeoffbalanceOutput } from './types'; - -@Injectable() -export class SageService implements ITimeoffBalanceService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private env: EnvironmentService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - HrisObject.timeoffbalance.toUpperCase() + ':' + SageService.name, - ); - this.registry.registerService('sage', this); - } - - async sync( - data: SyncParam, - ): Promise> { - try { - const { linkedUserId, id_employee } = data; - - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'sage', - vertical: 'hris', - }, - }); - - const employee = await this.prisma.hris_employees.findUnique({ - where: { - id_hris_employee: id_employee as string, - }, - select: { - remote_id: true, - }, - }); - - const resp = await axios.get( - `${connection.account_url}/api/employees/${employee.remote_id}/leave-management/balances`, - { - headers: { - 'Content-Type': 'application/json', - 'X-Auth-Token': this.cryptoService.decrypt(connection.access_token), - }, - }, - ); - this.logger.log(`Synced sage timeoffbalances !`); - - return { - data: resp.data.data, - message: 'Sage timeoffbalances retrieved', - statusCode: 200, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/timeoffbalance/services/sage/mappers.ts b/packages/api/src/hris/timeoffbalance/services/sage/mappers.ts deleted file mode 100644 index 448a08afd..000000000 --- a/packages/api/src/hris/timeoffbalance/services/sage/mappers.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { Utils } from '@hris/@lib/@utils'; -import { ITimeoffBalanceMapper } from '@hris/timeoffbalance/types'; -import { - UnifiedHrisTimeoffbalanceInput, - UnifiedHrisTimeoffbalanceOutput, -} from '@hris/timeoffbalance/types/model.unified'; -import { Injectable } from '@nestjs/common'; -import { SageTimeoffbalanceOutput } from './types'; - -@Injectable() -export class SageTimeoffbalanceMapper implements ITimeoffBalanceMapper { - constructor( - private mappersRegistry: MappersRegistry, - private utils: Utils, - private ingestService: IngestDataService, - private coreUnificationService: CoreUnification, - ) { - this.mappersRegistry.registerService( - 'hris', - 'timeoffbalance', - 'sage', - this, - ); - } - - async desunify( - source: UnifiedHrisTimeoffbalanceInput, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return; - } - - async unify( - source: SageTimeoffbalanceOutput | SageTimeoffbalanceOutput[], - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise< - UnifiedHrisTimeoffbalanceOutput | UnifiedHrisTimeoffbalanceOutput[] - > { - if (!Array.isArray(source)) { - return this.mapSingleTimeoffbalanceToUnified( - source, - connectionId, - customFieldMappings, - ); - } - return Promise.all( - source.map((timeoffbalance) => - this.mapSingleTimeoffbalanceToUnified( - timeoffbalance, - connectionId, - customFieldMappings, - ), - ), - ); - } - - private async mapSingleTimeoffbalanceToUnified( - timeoffbalance: SageTimeoffbalanceOutput, - connectionId: string, - customFieldMappings?: { slug: string; remote_id: string }[], - ): Promise { - return { - remote_id: null, - remote_data: timeoffbalance, - balance: timeoffbalance.available, - used: timeoffbalance.used, - }; - } -} diff --git a/packages/api/src/hris/timeoffbalance/services/sage/types.ts b/packages/api/src/hris/timeoffbalance/services/sage/types.ts deleted file mode 100644 index b22f8a766..000000000 --- a/packages/api/src/hris/timeoffbalance/services/sage/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type SageTimeoffbalanceOutput = Partial<{ - policy_id: number; - used: number; - available: number; -}>; diff --git a/packages/api/src/hris/timeoffbalance/services/timeoffbalance.service.ts b/packages/api/src/hris/timeoffbalance/services/timeoffbalance.service.ts deleted file mode 100644 index 33d58c218..000000000 --- a/packages/api/src/hris/timeoffbalance/services/timeoffbalance.service.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { Injectable } from '@nestjs/common'; -import { v4 as uuidv4 } from 'uuid'; -import { UnifiedHrisTimeoffbalanceOutput } from '../types/model.unified'; -import { ServiceRegistry } from './registry.service'; - -@Injectable() -export class TimeoffBalanceService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(TimeoffBalanceService.name); - } - - async getTimeoffBalance( - id_hris_time_off_balance: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const timeOffBalance = - await this.prisma.hris_time_off_balances.findUnique({ - where: { id_hris_time_off_balance: id_hris_time_off_balance }, - }); - - if (!timeOffBalance) { - throw new Error( - `Time off balance with ID ${id_hris_time_off_balance} not found.`, - ); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: timeOffBalance.id_hris_time_off_balance, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedTimeOffBalance: UnifiedHrisTimeoffbalanceOutput = { - id: timeOffBalance.id_hris_time_off_balance, - balance: timeOffBalance.balance - ? Number(timeOffBalance.balance) - : undefined, - employee_id: timeOffBalance.id_hris_employee, - used: timeOffBalance.used ? Number(timeOffBalance.used) : undefined, - policy_type: timeOffBalance.policy_type, - field_mappings: field_mappings, - remote_id: timeOffBalance.remote_id, - remote_created_at: timeOffBalance.remote_created_at, - created_at: timeOffBalance.created_at, - modified_at: timeOffBalance.modified_at, - remote_was_deleted: timeOffBalance.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: timeOffBalance.id_hris_time_off_balance, - }, - }); - unifiedTimeOffBalance.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.time_off_balance.pull', - method: 'GET', - url: '/hris/time_off_balance', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedTimeOffBalance; - } catch (error) { - throw error; - } - } - - async getTimeoffBalances( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisTimeoffbalanceOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const timeOffBalances = await this.prisma.hris_time_off_balances.findMany( - { - take: limit + 1, - cursor: cursor ? { id_hris_time_off_balance: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }, - ); - - const hasNextPage = timeOffBalances.length > limit; - if (hasNextPage) timeOffBalances.pop(); - - const unifiedTimeOffBalances = await Promise.all( - timeOffBalances.map(async (timeOffBalance) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: timeOffBalance.id_hris_time_off_balance, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedTimeOffBalance: UnifiedHrisTimeoffbalanceOutput = { - id: timeOffBalance.id_hris_time_off_balance, - balance: timeOffBalance.balance - ? Number(timeOffBalance.balance) - : undefined, - employee_id: timeOffBalance.id_hris_employee, - used: timeOffBalance.used ? Number(timeOffBalance.used) : undefined, - policy_type: timeOffBalance.policy_type, - field_mappings: field_mappings, - remote_id: timeOffBalance.remote_id, - remote_created_at: timeOffBalance.remote_created_at, - created_at: timeOffBalance.created_at, - modified_at: timeOffBalance.modified_at, - remote_was_deleted: timeOffBalance.remote_was_deleted, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: timeOffBalance.id_hris_time_off_balance, - }, - }); - unifiedTimeOffBalance.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedTimeOffBalance; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.time_off_balance.pull', - method: 'GET', - url: '/hris/time_off_balances', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedTimeOffBalances, - next_cursor: hasNextPage - ? timeOffBalances[timeOffBalances.length - 1].id_hris_time_off_balance - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/timeoffbalance/sync/sync.service.ts b/packages/api/src/hris/timeoffbalance/sync/sync.service.ts deleted file mode 100644 index ee33ee7e2..000000000 --- a/packages/api/src/hris/timeoffbalance/sync/sync.service.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { ApiResponse } from '@@core/utils/types'; -import { v4 as uuidv4 } from 'uuid'; -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 { UnifiedHrisTimeoffbalanceOutput } from '../types/model.unified'; -import { ITimeoffBalanceService } from '../types'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_time_off_balances as HrisTimeOffBalance } from '@prisma/client'; -import { OriginalTimeoffBalanceOutput } from '@@core/utils/types/original/original.hris'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'timeoffbalance', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: ITimeoffBalanceService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisTimeoffbalanceOutput, - OriginalTimeoffBalanceOutput, - ITimeoffBalanceService - >(integrationId, linkedUserId, 'hris', 'timeoffbalance', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - timeOffBalances: UnifiedHrisTimeoffbalanceOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const timeOffBalanceResults: HrisTimeOffBalance[] = []; - - for (let i = 0; i < timeOffBalances.length; i++) { - const timeOffBalance = timeOffBalances[i]; - const originId = timeOffBalance.remote_id; - - let existingTimeOffBalance = - await this.prisma.hris_time_off_balances.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const timeOffBalanceData = { - balance: timeOffBalance.balance - ? BigInt(timeOffBalance.balance) - : null, - id_hris_employee: timeOffBalance.employee_id, - used: timeOffBalance.used ? BigInt(timeOffBalance.used) : null, - policy_type: timeOffBalance.policy_type, - remote_id: originId, - remote_created_at: timeOffBalance.remote_created_at - ? new Date(timeOffBalance.remote_created_at) - : null, - modified_at: new Date(), - remote_was_deleted: timeOffBalance.remote_was_deleted || false, - }; - - if (existingTimeOffBalance) { - existingTimeOffBalance = - await this.prisma.hris_time_off_balances.update({ - where: { - id_hris_time_off_balance: - existingTimeOffBalance.id_hris_time_off_balance, - }, - data: timeOffBalanceData, - }); - } else { - existingTimeOffBalance = - await this.prisma.hris_time_off_balances.create({ - data: { - ...timeOffBalanceData, - id_hris_time_off_balance: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - timeOffBalanceResults.push(existingTimeOffBalance); - - // Process field mappings - await this.ingestService.processFieldMappings( - timeOffBalance.field_mappings, - existingTimeOffBalance.id_hris_time_off_balance, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingTimeOffBalance.id_hris_time_off_balance, - remote_data[i], - ); - } - - return timeOffBalanceResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/timeoffbalance/timeoffbalance.controller.ts b/packages/api/src/hris/timeoffbalance/timeoffbalance.controller.ts deleted file mode 100644 index 3e99ab567..000000000 --- a/packages/api/src/hris/timeoffbalance/timeoffbalance.controller.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { TimeoffBalanceService } from './services/timeoffbalance.service'; -import { - UnifiedHrisTimeoffbalanceInput, - UnifiedHrisTimeoffbalanceOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('hris/timeoffbalances') -@Controller('hris/timeoffbalances') -export class TimeoffBalanceController { - constructor( - private readonly timeoffbalanceService: TimeoffBalanceService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(TimeoffBalanceController.name); - } - - @ApiOperation({ - operationId: 'listHrisTimeoffbalances', - summary: 'List TimeoffBalances', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisTimeoffbalanceOutput) - @UseGuards(ApiKeyAuthGuard) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @Get() - async getTimeoffBalances( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.timeoffbalanceService.getTimeoffBalances( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisTimeoffbalance', - summary: 'Retrieve Time off Balances', - description: 'Retrieve Time off Balances from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the timeoffbalance you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisTimeoffbalanceOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.timeoffbalanceService.getTimeoffBalance( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } -} diff --git a/packages/api/src/hris/timeoffbalance/timeoffbalance.module.ts b/packages/api/src/hris/timeoffbalance/timeoffbalance.module.ts deleted file mode 100644 index 12f5213b3..000000000 --- a/packages/api/src/hris/timeoffbalance/timeoffbalance.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ServiceRegistry } from './services/registry.service'; -import { TimeoffBalanceService } from './services/timeoffbalance.service'; -import { SyncService } from './sync/sync.service'; -import { TimeoffBalanceController } from './timeoffbalance.controller'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Utils } from '@hris/@lib/@utils'; -import { SageTimeoffbalanceMapper } from './services/sage/mappers'; -import { SageService } from './services/sage'; -@Module({ - controllers: [TimeoffBalanceController], - providers: [ - TimeoffBalanceService, - CoreUnification, - Utils, - SyncService, - WebhookService, - ServiceRegistry, - IngestDataService, - SageTimeoffbalanceMapper, - /* PROVIDERS SERVICES */ - SageService, - ], - exports: [SyncService], -}) -export class TimeoffBalanceModule {} diff --git a/packages/api/src/hris/timeoffbalance/types/index.ts b/packages/api/src/hris/timeoffbalance/types/index.ts deleted file mode 100644 index aef0aaf01..000000000 --- a/packages/api/src/hris/timeoffbalance/types/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisTimeoffbalanceInput, - UnifiedHrisTimeoffbalanceOutput, -} from './model.unified'; -import { OriginalTimeoffBalanceOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface ITimeoffBalanceService { - sync(data: SyncParam): Promise>; -} - -export interface ITimeoffBalanceMapper { - desunify( - source: UnifiedHrisTimeoffbalanceInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalTimeoffBalanceOutput | OriginalTimeoffBalanceOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise< - UnifiedHrisTimeoffbalanceOutput | UnifiedHrisTimeoffbalanceOutput[] - >; -} diff --git a/packages/api/src/hris/timeoffbalance/types/model.unified.ts b/packages/api/src/hris/timeoffbalance/types/model.unified.ts deleted file mode 100644 index 941825f82..000000000 --- a/packages/api/src/hris/timeoffbalance/types/model.unified.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsNumber, - IsDateString, - IsBoolean, -} from 'class-validator'; - -export type PolicyType = - | 'VACATION' - | 'SICK' - | 'PERSONAL' - | 'JURY_DUTY' - | 'VOLUNTEER' - | 'BEREAVEMENT'; - -export class UnifiedHrisTimeoffbalanceInput { - @ApiPropertyOptional({ - type: Number, - example: 80, - nullable: true, - description: 'The current balance of time off', - }) - @IsNumber() - @IsOptional() - balance?: number; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the associated employee', - }) - @IsUUID() - @IsOptional() - employee_id?: string; - - @ApiPropertyOptional({ - type: Number, - example: 40, - nullable: true, - description: 'The amount of time off used', - }) - @IsNumber() - @IsOptional() - used?: number; - - @ApiPropertyOptional({ - type: String, - example: 'VACATION', - /*enum: [ - 'VACATION', - 'SICK', - 'PERSONAL', - 'JURY_DUTY', - 'VOLUNTEER', - 'BEREAVEMENT', - ],*/ - nullable: true, - description: 'The type of time off policy', - }) - @IsString() - @IsOptional() - policy_type?: PolicyType | string; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisTimeoffbalanceOutput extends UnifiedHrisTimeoffbalanceInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the time off balance record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'timeoff_balance_1234', - nullable: true, - description: - 'The remote ID of the time off balance in the context of the 3rd Party', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the time off balance in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; - - @ApiPropertyOptional({ - type: String, - example: '2024-06-15T12:00:00Z', - nullable: true, - description: - 'The date when the time off balance was created in the 3rd party system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: String, - example: '2024-06-15T12:00:00Z', - nullable: true, - description: 'The created date of the time off balance record', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: String, - example: '2024-06-15T12:00:00Z', - nullable: true, - description: 'The last modified date of the time off balance record', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - nullable: true, - description: - 'Indicates if the time off balance was deleted in the remote system', - }) - @IsBoolean() - @IsOptional() - remote_was_deleted?: boolean; -} diff --git a/packages/api/src/hris/timeoffbalance/utils/index.ts b/packages/api/src/hris/timeoffbalance/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/timeoffbalance/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/hris/timesheetentry/services/registry.service.ts b/packages/api/src/hris/timesheetentry/services/registry.service.ts deleted file mode 100644 index fea3c00cd..000000000 --- a/packages/api/src/hris/timesheetentry/services/registry.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ITimesheetentryService } from '../types'; - -@Injectable() -export class ServiceRegistry { - private serviceMap: Map; - - constructor() { - this.serviceMap = new Map(); - } - - registerService(serviceKey: string, service: ITimesheetentryService) { - this.serviceMap.set(serviceKey, service); - } - - getService(integrationId: string): ITimesheetentryService { - const service = this.serviceMap.get(integrationId); - if (!service) { - return null; - } - return service; - } -} diff --git a/packages/api/src/hris/timesheetentry/services/timesheetentry.service.ts b/packages/api/src/hris/timesheetentry/services/timesheetentry.service.ts deleted file mode 100644 index aa7661e54..000000000 --- a/packages/api/src/hris/timesheetentry/services/timesheetentry.service.ts +++ /dev/null @@ -1,370 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { ApiResponse } from '@@core/utils/types'; -import { throwTypedError } from '@@core/utils/errors'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { - UnifiedHrisTimesheetEntryInput, - UnifiedHrisTimesheetEntryOutput, -} from '../types/model.unified'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ServiceRegistry } from './registry.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { OriginalTimesheetentryOutput } from '@@core/utils/types/original/original.hris'; -import { HrisObject } from '@panora/shared'; -import { ITimesheetentryService } from '../types'; - -@Injectable() -export class TimesheetentryService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private coreUnification: CoreUnification, - private ingestService: IngestDataService, - private serviceRegistry: ServiceRegistry, - ) { - this.logger.setContext(TimesheetentryService.name); - } - - async validateLinkedUser(linkedUserId: string) { - const linkedUser = await this.prisma.linked_users.findUnique({ - where: { id_linked_user: linkedUserId }, - }); - if (!linkedUser) throw new ReferenceError('Linked User Not Found'); - return linkedUser; - } - - async addTimesheetentry( - unifiedTimesheetentryData: UnifiedHrisTimesheetEntryInput, - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - remote_data?: boolean, - ): Promise { - try { - const linkedUser = await this.validateLinkedUser(linkedUserId); - // Add any necessary validations here, e.g., validateEmployeeId if needed - - const desunifiedObject = - await this.coreUnification.desunify({ - sourceObject: unifiedTimesheetentryData, - targetType: HrisObject.timesheetentry, - providerName: integrationId, - vertical: 'hris', - customFieldMappings: [], - }); - - const service: ITimesheetentryService = - this.serviceRegistry.getService(integrationId); - const resp: ApiResponse = - await service.addTimesheetentry(desunifiedObject, linkedUserId); - - const unifiedObject = (await this.coreUnification.unify< - OriginalTimesheetentryOutput[] - >({ - sourceObject: [resp.data], - targetType: HrisObject.timesheetentry, - providerName: integrationId, - vertical: 'hris', - connectionId: connectionId, - customFieldMappings: [], - })) as UnifiedHrisTimesheetEntryOutput[]; - - const source_timesheetentry = resp.data; - const target_timesheetentry = unifiedObject[0]; - - const unique_hris_timesheetentry_id = - await this.saveOrUpdateTimesheetentry( - target_timesheetentry, - connectionId, - ); - - await this.ingestService.processRemoteData( - unique_hris_timesheetentry_id, - source_timesheetentry, - ); - - const result_timesheetentry = await this.getTimesheetentry( - unique_hris_timesheetentry_id, - undefined, - undefined, - connectionId, - projectId, - remote_data, - ); - - const status_resp = resp.statusCode === 201 ? 'success' : 'fail'; - const event = await this.prisma.events.create({ - data: { - id_connection: connectionId, - id_project: projectId, - id_event: uuidv4(), - status: status_resp, - type: 'hris.timesheetentry.push', - method: 'POST', - url: '/hris/timesheetentries', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - }, - }); - - await this.webhook.dispatchWebhook( - result_timesheetentry, - 'hris.timesheetentry.created', - linkedUser.id_project, - event.id_event, - ); - - return result_timesheetentry; - } catch (error) { - throw error; - } - } - - private async saveOrUpdateTimesheetentry( - timesheetentry: UnifiedHrisTimesheetEntryOutput, - connectionId: string, - ): Promise { - const existingTimesheetentry = - await this.prisma.hris_timesheet_entries.findFirst({ - where: { - remote_id: timesheetentry.remote_id, - id_connection: connectionId, - }, - }); - - const data: any = { - hours_worked: timesheetentry.hours_worked - ? BigInt(timesheetentry.hours_worked) - : null, - start_time: timesheetentry.start_time - ? new Date(timesheetentry.start_time) - : null, - end_time: timesheetentry.end_time - ? new Date(timesheetentry.end_time) - : null, - id_hris_employee: timesheetentry.employee_id, - remote_was_deleted: timesheetentry.remote_was_deleted ?? false, - modified_at: new Date(), - }; - - // Only include field_mappings if it exists in the input - if (timesheetentry.field_mappings) { - data.field_mappings = timesheetentry.field_mappings; - } - - if (existingTimesheetentry) { - const res = await this.prisma.hris_timesheet_entries.update({ - where: { - id_hris_timesheet_entry: - existingTimesheetentry.id_hris_timesheet_entry, - }, - data: data, - }); - - return res.id_hris_timesheet_entry; - } else { - data.created_at = new Date(); - data.remote_id = timesheetentry.remote_id; - data.id_connection = connectionId; - data.id_hris_timesheet_entry = uuidv4(); - data.remote_created_at = timesheetentry.remote_created_at - ? new Date(timesheetentry.remote_created_at) - : null; - - const newTimesheetentry = await this.prisma.hris_timesheet_entries.create( - { data: data }, - ); - - return newTimesheetentry.id_hris_timesheet_entry; - } - } - - async getTimesheetentry( - id_hris_timesheet_entry: string, - linkedUserId: string, - integrationId: string, - connectionId: string, - projectId: string, - remote_data?: boolean, - ): Promise { - try { - const timesheetEntry = - await this.prisma.hris_timesheet_entries.findUnique({ - where: { id_hris_timesheet_entry: id_hris_timesheet_entry }, - }); - - if (!timesheetEntry) { - throw new Error( - `Timesheet entry with ID ${id_hris_timesheet_entry} not found.`, - ); - } - - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: timesheetEntry.id_hris_timesheet_entry, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedTimesheetEntry: UnifiedHrisTimesheetEntryOutput = { - id: timesheetEntry.id_hris_timesheet_entry, - hours_worked: timesheetEntry.hours_worked - ? Number(timesheetEntry.hours_worked) - : undefined, - start_time: timesheetEntry.start_time, - end_time: timesheetEntry.end_time, - employee_id: timesheetEntry.id_hris_employee, - remote_id: timesheetEntry.remote_id, - remote_created_at: timesheetEntry.remote_created_at, - created_at: timesheetEntry.created_at, - modified_at: timesheetEntry.modified_at, - remote_was_deleted: timesheetEntry.remote_was_deleted, - field_mappings: field_mappings, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { ressource_owner_id: timesheetEntry.id_hris_timesheet_entry }, - }); - unifiedTimesheetEntry.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.timesheetentry.pull', - method: 'GET', - url: '/hris/timesheetentry', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return unifiedTimesheetEntry; - } catch (error) { - throw error; - } - } - - async getTimesheetentrys( - connectionId: string, - projectId: string, - integrationId: string, - linkedUserId: string, - limit: number, - remote_data?: boolean, - cursor?: string, - ): Promise<{ - data: UnifiedHrisTimesheetEntryOutput[]; - next_cursor: string | null; - previous_cursor: string | null; - }> { - try { - const timesheetEntries = - await this.prisma.hris_timesheet_entries.findMany({ - take: limit + 1, - cursor: cursor ? { id_hris_timesheet_entry: cursor } : undefined, - where: { id_connection: connectionId }, - orderBy: { created_at: 'asc' }, - }); - - const hasNextPage = timesheetEntries.length > limit; - if (hasNextPage) timesheetEntries.pop(); - - const unifiedTimesheetEntries = await Promise.all( - timesheetEntries.map(async (timesheetEntry) => { - const values = await this.prisma.value.findMany({ - where: { - entity: { - ressource_owner_id: timesheetEntry.id_hris_timesheet_entry, - }, - }, - include: { attribute: true }, - }); - - const field_mappings = Object.fromEntries( - values.map((value) => [value.attribute.slug, value.data]), - ); - - const unifiedTimesheetEntry: UnifiedHrisTimesheetEntryOutput = { - id: timesheetEntry.id_hris_timesheet_entry, - hours_worked: timesheetEntry.hours_worked - ? Number(timesheetEntry.hours_worked) - : undefined, - start_time: timesheetEntry.start_time, - end_time: timesheetEntry.end_time, - employee_id: timesheetEntry.id_hris_employee, - remote_id: timesheetEntry.remote_id, - remote_created_at: timesheetEntry.remote_created_at, - created_at: timesheetEntry.created_at, - modified_at: timesheetEntry.modified_at, - remote_was_deleted: timesheetEntry.remote_was_deleted, - field_mappings: field_mappings, - }; - - if (remote_data) { - const remoteDataRecord = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: timesheetEntry.id_hris_timesheet_entry, - }, - }); - unifiedTimesheetEntry.remote_data = remoteDataRecord - ? JSON.parse(remoteDataRecord.data) - : null; - } - - return unifiedTimesheetEntry; - }), - ); - - await this.prisma.events.create({ - data: { - id_event: uuidv4(), - status: 'success', - type: 'hris.timesheetentry.pull', - method: 'GET', - url: '/hris/timesheetentrys', - provider: integrationId, - direction: '0', - timestamp: new Date(), - id_linked_user: linkedUserId, - id_project: projectId, - id_connection: connectionId, - }, - }); - - return { - data: unifiedTimesheetEntries, - next_cursor: hasNextPage - ? timesheetEntries[timesheetEntries.length - 1] - .id_hris_timesheet_entry - : null, - previous_cursor: cursor ?? null, - }; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/timesheetentry/sync/sync.service.ts b/packages/api/src/hris/timesheetentry/sync/sync.service.ts deleted file mode 100644 index 453777a34..000000000 --- a/packages/api/src/hris/timesheetentry/sync/sync.service.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { OriginalTimesheetentryOutput } from '@@core/utils/types/original/original.hris'; -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; -import { HRIS_PROVIDERS } from '@panora/shared'; -import { hris_timesheet_entries as HrisTimesheetEntry } from '@prisma/client'; -import { v4 as uuidv4 } from 'uuid'; -import { ServiceRegistry } from '../services/registry.service'; -import { ITimesheetentryService } from '../types'; -import { UnifiedHrisTimesheetEntryOutput } from '../types/model.unified'; - -@Injectable() -export class SyncService implements OnModuleInit, IBaseSync { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private webhook: WebhookService, - private fieldMappingService: FieldMappingService, - private serviceRegistry: ServiceRegistry, - private coreUnification: CoreUnification, - private registry: CoreSyncRegistry, - private ingestService: IngestDataService, - ) { - this.logger.setContext(SyncService.name); - this.registry.registerService('hris', 'timesheetentry', this); - } - - async onModuleInit() { - // Initialization logic if needed - } - - @Cron('0 */8 * * *') // every 8 hours - async kickstartSync(id_project?: string) { - try { - const linkedUsers = await this.prisma.linked_users.findMany({ - where: { - id_project: id_project, - }, - }); - linkedUsers.map(async (linkedUser) => { - try { - const providers = HRIS_PROVIDERS; - for (const provider of providers) { - try { - await this.syncForLinkedUser({ - integrationId: provider, - linkedUserId: linkedUser.id_linked_user, - }); - } catch (error) { - throw error; - } - } - } catch (error) { - throw error; - } - }); - } catch (error) { - throw error; - } - } - - async syncForLinkedUser(param: SyncLinkedUserType) { - try { - const { integrationId, linkedUserId } = param; - const service: ITimesheetentryService = - this.serviceRegistry.getService(integrationId); - if (!service) return; - - await this.ingestService.syncForLinkedUser< - UnifiedHrisTimesheetEntryOutput, - OriginalTimesheetentryOutput, - ITimesheetentryService - >(integrationId, linkedUserId, 'hris', 'timesheetentry', service, []); - } catch (error) { - throw error; - } - } - - async saveToDb( - connection_id: string, - linkedUserId: string, - timesheetEntries: UnifiedHrisTimesheetEntryOutput[], - originSource: string, - remote_data: Record[], - ): Promise { - try { - const timesheetEntryResults: HrisTimesheetEntry[] = []; - - for (let i = 0; i < timesheetEntries.length; i++) { - const timesheetEntry = timesheetEntries[i]; - const originId = timesheetEntry.remote_id; - - let existingTimesheetEntry = - await this.prisma.hris_timesheet_entries.findFirst({ - where: { - remote_id: originId, - id_connection: connection_id, - }, - }); - - const timesheetEntryData = { - hours_worked: timesheetEntry.hours_worked - ? BigInt(timesheetEntry.hours_worked) - : null, - start_time: timesheetEntry.start_time - ? new Date(timesheetEntry.start_time) - : null, - end_time: timesheetEntry.end_time - ? new Date(timesheetEntry.end_time) - : null, - id_hris_employee: timesheetEntry.employee_id, - remote_id: originId, - remote_created_at: timesheetEntry.remote_created_at - ? new Date(timesheetEntry.remote_created_at) - : null, - modified_at: new Date(), - remote_was_deleted: timesheetEntry.remote_was_deleted || false, - }; - - if (existingTimesheetEntry) { - existingTimesheetEntry = - await this.prisma.hris_timesheet_entries.update({ - where: { - id_hris_timesheet_entry: - existingTimesheetEntry.id_hris_timesheet_entry, - }, - data: timesheetEntryData, - }); - } else { - existingTimesheetEntry = - await this.prisma.hris_timesheet_entries.create({ - data: { - ...timesheetEntryData, - id_hris_timesheet_entry: uuidv4(), - created_at: new Date(), - id_connection: connection_id, - }, - }); - } - - timesheetEntryResults.push(existingTimesheetEntry); - - // Process field mappings - await this.ingestService.processFieldMappings( - timesheetEntry.field_mappings, - existingTimesheetEntry.id_hris_timesheet_entry, - originSource, - linkedUserId, - ); - - // Process remote data - await this.ingestService.processRemoteData( - existingTimesheetEntry.id_hris_timesheet_entry, - remote_data[i], - ); - } - - return timesheetEntryResults; - } catch (error) { - throw error; - } - } -} diff --git a/packages/api/src/hris/timesheetentry/timesheetentry.controller.ts b/packages/api/src/hris/timesheetentry/timesheetentry.controller.ts deleted file mode 100644 index 7732a3d73..000000000 --- a/packages/api/src/hris/timesheetentry/timesheetentry.controller.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Patch, - Param, - Headers, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { - ApiBody, - ApiOperation, - ApiParam, - ApiQuery, - ApiTags, - ApiHeader, - //ApiKeyAuth, -} from '@nestjs/swagger'; - -import { TimesheetentryService } from './services/timesheetentry.service'; -import { - UnifiedHrisTimesheetEntryInput, - UnifiedHrisTimesheetEntryOutput, -} from './types/model.unified'; -import { ConnectionUtils } from '@@core/connections/@utils'; -import { ApiKeyAuthGuard } from '@@core/auth/guards/api-key.guard'; -import { QueryDto } from '@@core/utils/dtos/query.dto'; -import { - ApiGetCustomResponse, - ApiPaginatedResponse, - ApiPostCustomResponse, -} from '@@core/utils/dtos/openapi.respone.dto'; - -@ApiTags('hris/timesheetentries') -@Controller('hris/timesheetentries') -export class TimesheetentryController { - constructor( - private readonly timesheetentryService: TimesheetentryService, - private logger: LoggerService, - private connectionUtils: ConnectionUtils, - ) { - this.logger.setContext(TimesheetentryController.name); - } - - @ApiOperation({ - operationId: 'listHrisTimesheetentries', - summary: 'List Timesheetentries', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiPaginatedResponse(UnifiedHrisTimesheetEntryOutput) - @UseGuards(ApiKeyAuthGuard) - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) - @Get() - async getTimesheetentrys( - @Headers('x-connection-token') connection_token: string, - @Query() query: QueryDto, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, limit, cursor } = query; - return this.timesheetentryService.getTimesheetentrys( - connectionId, - projectId, - remoteSource, - linkedUserId, - limit, - remote_data, - cursor, - ); - } catch (error) { - throw new Error(error); - } - } - - @ApiOperation({ - operationId: 'retrieveHrisTimesheetentry', - summary: 'Retrieve Timesheetentry', - description: 'Retrieve an Timesheetentry from any connected Hris software', - }) - @ApiParam({ - name: 'id', - required: true, - type: String, - description: 'id of the timesheetentry you want to retrieve.', - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - example: false, - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiGetCustomResponse(UnifiedHrisTimesheetEntryOutput) - @UseGuards(ApiKeyAuthGuard) - @Get(':id') - async retrieve( - @Headers('x-connection-token') connection_token: string, - @Param('id') id: string, - @Query('remote_data') remote_data?: boolean, - ) { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.timesheetentryService.getTimesheetentry( - id, - linkedUserId, - remoteSource, - connectionId, - projectId, - remote_data, - ); - } - - @ApiOperation({ - operationId: 'createHrisTimesheetentry', - summary: 'Create Timesheetentrys', - description: 'Create Timesheetentrys in any supported Hris software', - }) - @ApiHeader({ - name: 'x-connection-token', - required: true, - description: 'The connection token', - example: 'b008e199-eda9-4629-bd41-a01b6195864a', - }) - @ApiQuery({ - name: 'remote_data', - required: false, - type: Boolean, - description: 'Set to true to include data from the original Hris software.', - }) - @ApiBody({ type: UnifiedHrisTimesheetEntryInput }) - @ApiPostCustomResponse(UnifiedHrisTimesheetEntryOutput) - @UseGuards(ApiKeyAuthGuard) - @Post() - async addTimesheetentry( - @Body() unifiedTimesheetentryData: UnifiedHrisTimesheetEntryInput, - @Headers('x-connection-token') connection_token: string, - @Query('remote_data') remote_data?: boolean, - ) { - try { - const { linkedUserId, remoteSource, connectionId, projectId } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - return this.timesheetentryService.addTimesheetentry( - unifiedTimesheetentryData, - connectionId, - projectId, - remoteSource, - linkedUserId, - remote_data, - ); - } catch (error) { - throw new Error(error); - } - } -} diff --git a/packages/api/src/hris/timesheetentry/timesheetentry.module.ts b/packages/api/src/hris/timesheetentry/timesheetentry.module.ts deleted file mode 100644 index 5df86b0a2..000000000 --- a/packages/api/src/hris/timesheetentry/timesheetentry.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TimesheetentryController } from './timesheetentry.controller'; -import { ServiceRegistry } from './services/registry.service'; -import { TimesheetentryService } from './services/timesheetentry.service'; -import { SyncService } from './sync/sync.service'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; -import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { Utils } from '@hris/@lib/@utils'; -@Module({ - controllers: [TimesheetentryController], - providers: [ - TimesheetentryService, - CoreUnification, - Utils, - SyncService, - WebhookService, - ServiceRegistry, - IngestDataService, - /* PROVIDERS SERVICES */ - ], - exports: [SyncService], -}) -export class TimesheetentryModule {} diff --git a/packages/api/src/hris/timesheetentry/types/index.ts b/packages/api/src/hris/timesheetentry/types/index.ts deleted file mode 100644 index 136dff390..000000000 --- a/packages/api/src/hris/timesheetentry/types/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; -import { - UnifiedHrisTimesheetEntryInput, - UnifiedHrisTimesheetEntryOutput, -} from './model.unified'; -import { OriginalTimesheetentryOutput } from '@@core/utils/types/original/original.hris'; -import { ApiResponse } from '@@core/utils/types'; -import { SyncParam } from '@@core/utils/types/interface'; - -export interface ITimesheetentryService { - addTimesheetentry( - timesheetentryData: DesunifyReturnType, - linkedUserId: string, - ): Promise>; - - sync(data: SyncParam): Promise>; -} - -export interface ITimesheetentryMapper { - desunify( - source: UnifiedHrisTimesheetEntryInput, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): DesunifyReturnType; - - unify( - source: OriginalTimesheetentryOutput | OriginalTimesheetentryOutput[], - connectionId: string, - customFieldMappings?: { - slug: string; - remote_id: string; - }[], - ): Promise< - UnifiedHrisTimesheetEntryOutput | UnifiedHrisTimesheetEntryOutput[] - >; -} diff --git a/packages/api/src/hris/timesheetentry/types/model.unified.ts b/packages/api/src/hris/timesheetentry/types/model.unified.ts deleted file mode 100644 index 5003c64f2..000000000 --- a/packages/api/src/hris/timesheetentry/types/model.unified.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsUUID, - IsOptional, - IsString, - IsDateString, - IsBoolean, - IsNumber, -} from 'class-validator'; - -export class UnifiedHrisTimesheetEntryInput { - @ApiPropertyOptional({ - type: Number, - example: 40, - nullable: true, - description: 'The number of hours worked', - }) - @IsNumber() - @IsOptional() - hours_worked?: number; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T08:00:00Z', - nullable: true, - description: 'The start time of the timesheet entry', - }) - @IsOptional() - start_time?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T16:00:00Z', - nullable: true, - description: 'The end time of the timesheet entry', - }) - @IsOptional() - end_time?: Date; - - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the associated employee', - }) - @IsUUID() - @IsOptional() - employee_id?: string; - - @ApiPropertyOptional({ - type: Boolean, - example: false, - description: - 'Indicates if the timesheet entry was deleted in the remote system', - }) - @IsBoolean() - @IsOptional() - remote_was_deleted?: boolean; - - @ApiPropertyOptional({ - type: Object, - example: { - custom_field_1: 'value1', - custom_field_2: 'value2', - }, - nullable: true, - description: - 'The custom field mappings of the object between the remote 3rd party & Panora', - }) - @IsOptional() - field_mappings?: Record; -} - -export class UnifiedHrisTimesheetEntryOutput extends UnifiedHrisTimesheetEntryInput { - @ApiPropertyOptional({ - type: String, - example: '801f9ede-c698-4e66-a7fc-48d19eebaa4f', - nullable: true, - description: 'The UUID of the timesheet entry record', - }) - @IsUUID() - @IsOptional() - id?: string; - - @ApiPropertyOptional({ - type: String, - example: 'id_1', - nullable: true, - description: 'The remote ID of the timesheet entry', - }) - @IsString() - @IsOptional() - remote_id?: string; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - nullable: true, - description: - 'The date when the timesheet entry was created in the remote system', - }) - @IsDateString() - @IsOptional() - remote_created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - description: 'The created date of the timesheet entry', - }) - @IsDateString() - @IsOptional() - created_at?: Date; - - @ApiPropertyOptional({ - type: Date, - example: '2024-10-01T12:00:00Z', - description: 'The last modified date of the timesheet entry', - }) - @IsDateString() - @IsOptional() - modified_at?: Date; - - @ApiPropertyOptional({ - type: Object, - example: { - raw_data: { - additional_field: 'some value', - }, - }, - nullable: true, - description: - 'The remote data of the timesheet entry in the context of the 3rd Party', - }) - @IsOptional() - remote_data?: Record; -} diff --git a/packages/api/src/hris/timesheetentry/utils/index.ts b/packages/api/src/hris/timesheetentry/utils/index.ts deleted file mode 100644 index f849788c1..000000000 --- a/packages/api/src/hris/timesheetentry/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -/* PUT ALL UTILS FUNCTIONS USED IN YOUR OBJECT METHODS HERE */ diff --git a/packages/api/src/ticketing/ticket/services/front/mappers.ts b/packages/api/src/ticketing/ticket/services/front/mappers.ts index 8719b179b..f7d312362 100644 --- a/packages/api/src/ticketing/ticket/services/front/mappers.ts +++ b/packages/api/src/ticketing/ticket/services/front/mappers.ts @@ -1,6 +1,7 @@ import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { OriginalTagOutput } from '@@core/utils/types/original/original.ats'; +// The following line is commented because it uses code from the ATS Module, which was removed from the project +//import { OriginalTagOutput } from '@@core/utils/types/original/original.ats'; import { UnifiedTicketingTagOutput } from '@ticketing/tag/types/model.unified'; import { Injectable } from '@nestjs/common'; import { TicketingObject } from '@ticketing/@lib/@types'; @@ -153,22 +154,24 @@ export class FrontTicketMapper implements ITicketMapper { }; } } - if (ticket.tags) { - const tags = (await this.coreUnificationService.unify< - OriginalTagOutput[] - >({ - sourceObject: ticket.tags, - targetType: TicketingObject.tag, - providerName: 'front', - vertical: 'ticketing', - connectionId: connectionId, - customFieldMappings: [], - })) as UnifiedTicketingTagOutput[]; - opts = { - ...opts, - tags: tags, - }; - } + // The following code is commented because it uses code from the ATS Module, which was removed from the project + + // if (ticket.tags) { + // const tags = (await this.coreUnificationService.unify< + // OriginalTagOutput[] + // >({ + // sourceObject: ticket.tags, + // targetType: TicketingObject.tag, + // providerName: 'front', + // vertical: 'ticketing', + // connectionId: connectionId, + // customFieldMappings: [], + // })) as UnifiedTicketingTagOutput[]; + // opts = { + // ...opts, + // tags: tags, + // }; + // } const unifiedTicket: UnifiedTicketingTicketOutput = { remote_id: ticket.id, remote_data: ticket, diff --git a/packages/api/swagger/swagger-spec.yaml b/packages/api/swagger/swagger-spec.yaml index 5b36d95dd..b8b36b094 100644 --- a/packages/api/swagger/swagger-spec.yaml +++ b/packages/api/swagger/swagger-spec.yaml @@ -77,7 +77,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -260,8 +259,6 @@ paths: schema: type: string responses: - '200': - description: '' '201': description: '' content: @@ -282,8 +279,6 @@ paths: schema: type: string responses: - '200': - description: '' '201': description: '' content: @@ -338,7 +333,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -463,7 +457,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -555,7 +548,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -645,7 +637,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -732,8 +723,6 @@ paths: - marketingautomation - crm - filestorage - - ats - - hris - accounting - ecommerce type: string @@ -771,12 +760,6 @@ paths: responses: '200': description: Pull frequency updated successfully - '201': - description: '' - content: - application/json: - schema: - type: object tags: *ref_6 x-speakeasy-group: sync get: @@ -816,7 +799,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -941,7 +923,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -1066,7 +1047,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -1190,7 +1170,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -1315,7 +1294,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -1440,7 +1418,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -1532,7 +1509,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -1656,7 +1632,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -1748,7 +1723,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -1841,7 +1815,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -1969,7 +1942,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -2061,7 +2033,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -2361,15 +2332,11 @@ paths: required: false in: query schema: - minimum: 1 - default: 1 type: number - name: limit required: false in: query schema: - minimum: 1 - default: 10 type: number responses: '200': @@ -2405,12 +2372,6 @@ paths: application/json: schema: type: object - '201': - description: '' - content: - application/json: - schema: - type: object tags: &ref_22 - passthrough x-speakeasy-group: passthrough @@ -2432,10 +2393,10 @@ paths: description: '' tags: *ref_22 x-speakeasy-group: passthrough.{retryid} - /hris/bankinfos: + /marketingautomation/actions: get: - operationId: listHrisBankInfo - summary: List Bank Info + operationId: listMarketingautomationAction + summary: List Actions parameters: - name: x-connection-token required: true @@ -2456,7 +2417,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -2477,10 +2437,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedHrisBankinfoOutput' + $ref: >- + #/components/schemas/UnifiedMarketingautomationActionOutput tags: &ref_23 - - hris/bankinfos - x-speakeasy-group: hris.bankinfos + - marketingautomation/actions + x-speakeasy-group: marketingautomation.actions x-speakeasy-pagination: type: cursor inputs: @@ -2489,11 +2450,46 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /hris/bankinfos/{id}: + post: + operationId: createMarketingautomationAction + summary: Create Action + description: Create a action in any supported Marketingautomation software + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: remote_data + required: false + in: query + description: >- + Set to true to include data from the original Marketingautomation + software. + example: false + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedMarketingautomationActionInput' + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedMarketingautomationActionOutput' + tags: *ref_23 + x-speakeasy-group: marketingautomation.actions + /marketingautomation/actions/{id}: get: - operationId: retrieveHrisBankInfo - summary: Retrieve Bank Info - description: Retrieve Bank Info from any connected Hris software + operationId: retrieveMarketingautomationAction + summary: Retrieve Actions + description: Retrieve Actions from any connected Marketingautomation software parameters: - name: x-connection-token required: true @@ -2504,14 +2500,16 @@ paths: - name: id required: true in: path - description: id of the bank info you want to retrieve. + description: id of the action you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. + description: >- + Set to true to include data from the original Marketingautomation + software. example: false schema: type: boolean @@ -2521,13 +2519,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisBankinfoOutput' + $ref: '#/components/schemas/UnifiedMarketingautomationActionOutput' tags: *ref_23 - x-speakeasy-group: hris.bankinfos - /hris/benefits: + x-speakeasy-group: marketingautomation.actions + /marketingautomation/automations: get: - operationId: listHrisBenefits - summary: List Benefits + operationId: listMarketingautomationAutomations + summary: List Automations parameters: - name: x-connection-token required: true @@ -2548,7 +2546,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -2569,10 +2566,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedHrisBenefitOutput' + $ref: >- + #/components/schemas/UnifiedMarketingautomationAutomationOutput tags: &ref_24 - - hris/benefits - x-speakeasy-group: hris.benefits + - marketingautomation/automations + x-speakeasy-group: marketingautomation.automations x-speakeasy-pagination: type: cursor inputs: @@ -2581,11 +2579,47 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /hris/benefits/{id}: + post: + operationId: createMarketingautomationAutomation + summary: Create Automation + description: Create a automation in any supported Marketingautomation software + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: remote_data + required: false + in: query + description: >- + Set to true to include data from the original Marketingautomation + software. + example: false + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedMarketingautomationAutomationInput' + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: >- + #/components/schemas/UnifiedMarketingautomationAutomationOutput + tags: *ref_24 + x-speakeasy-group: marketingautomation.automations + /marketingautomation/automations/{id}: get: - operationId: retrieveHrisBenefit - summary: Retrieve Benefit - description: Retrieve a Benefit from any connected Hris software + operationId: retrieveMarketingautomationAutomation + summary: Retrieve Automation + description: Retrieve an Automation from any connected Marketingautomation software parameters: - name: x-connection-token required: true @@ -2596,14 +2630,16 @@ paths: - name: id required: true in: path - description: id of the benefit you want to retrieve. + description: id of the automation you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. + description: >- + Set to true to include data from the original Marketingautomation + software. example: false schema: type: boolean @@ -2613,13 +2649,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisBenefitOutput' + $ref: >- + #/components/schemas/UnifiedMarketingautomationAutomationOutput tags: *ref_24 - x-speakeasy-group: hris.benefits - /hris/companies: + x-speakeasy-group: marketingautomation.automations + /marketingautomation/campaigns: get: - operationId: listHrisCompanies - summary: List Companies + operationId: listMarketingautomationCampaigns + summary: List Campaigns parameters: - name: x-connection-token required: true @@ -2640,7 +2677,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -2661,10 +2697,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedHrisCompanyOutput' + $ref: >- + #/components/schemas/UnifiedMarketingautomationCampaignOutput tags: &ref_25 - - hris/companies - x-speakeasy-group: hris.companies + - marketingautomation/campaigns + x-speakeasy-group: marketingautomation.campaigns x-speakeasy-pagination: type: cursor inputs: @@ -2673,11 +2710,46 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /hris/companies/{id}: + post: + operationId: createMarketingautomationCampaign + summary: Create Campaign + description: Create a campaign in any supported Marketingautomation software + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: remote_data + required: false + in: query + description: >- + Set to true to include data from the original Marketingautomation + software. + example: false + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedMarketingautomationCampaignInput' + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedMarketingautomationCampaignOutput' + tags: *ref_25 + x-speakeasy-group: marketingautomation.campaigns + /marketingautomation/campaigns/{id}: get: - operationId: retrieveHrisCompany - summary: Retrieve Company - description: Retrieve a Company from any connected Hris software + operationId: retrieveMarketingautomationCampaign + summary: Retrieve Campaign + description: Retrieve a Campaign from any connected Marketingautomation software parameters: - name: x-connection-token required: true @@ -2688,14 +2760,16 @@ paths: - name: id required: true in: path - description: id of the company you want to retrieve. + description: id of the campaign you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. + description: >- + Set to true to include data from the original Marketingautomation + software. example: false schema: type: boolean @@ -2705,13 +2779,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisCompanyOutput' + $ref: '#/components/schemas/UnifiedMarketingautomationCampaignOutput' tags: *ref_25 - x-speakeasy-group: hris.companies - /hris/dependents: + x-speakeasy-group: marketingautomation.campaigns + /marketingautomation/contacts: get: - operationId: listHrisDependents - summary: List Dependents + operationId: listMarketingAutomationContacts + summary: List Contacts parameters: - name: x-connection-token required: true @@ -2732,7 +2806,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -2753,10 +2826,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedHrisDependentOutput' + $ref: >- + #/components/schemas/UnifiedMarketingautomationContactOutput tags: &ref_26 - - hris/dependents - x-speakeasy-group: hris.dependents + - marketingautomation/contacts + x-speakeasy-group: marketingautomation.contacts x-speakeasy-pagination: type: cursor inputs: @@ -2765,11 +2839,46 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /hris/dependents/{id}: + post: + operationId: createMarketingAutomationContact + summary: Create Contact + description: Create a contact in any supported Marketingautomation software + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: remote_data + required: false + in: query + description: >- + Set to true to include data from the original Marketingautomation + software. + example: false + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedMarketingautomationContactInput' + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedMarketingautomationContactOutput' + tags: *ref_26 + x-speakeasy-group: marketingautomation.contacts + /marketingautomation/contacts/{id}: get: - operationId: retrieveHrisDependent - summary: Retrieve Dependent - description: Retrieve a Dependent from any connected Hris software + operationId: retrieveMarketingAutomationContact + summary: Retrieve Contacts + description: Retrieve Contacts from any connected Marketingautomation software parameters: - name: x-connection-token required: true @@ -2780,14 +2889,16 @@ paths: - name: id required: true in: path - description: id of the dependent you want to retrieve. + description: id of the contact you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. + description: >- + Set to true to include data from the original Marketingautomation + software. example: false schema: type: boolean @@ -2797,13 +2908,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisDependentOutput' + $ref: '#/components/schemas/UnifiedMarketingautomationContactOutput' tags: *ref_26 - x-speakeasy-group: hris.dependents - /hris/employeepayrollruns: + x-speakeasy-group: marketingautomation.contacts + /marketingautomation/emails: get: - operationId: listHrisEmployeePayrollRun - summary: List Employee Payroll Runs + operationId: listMarketingautomationEmails + summary: List Emails parameters: - name: x-connection-token required: true @@ -2824,7 +2935,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -2846,10 +2956,10 @@ paths: type: array items: $ref: >- - #/components/schemas/UnifiedHrisEmployeepayrollrunOutput + #/components/schemas/UnifiedMarketingautomationEmailOutput tags: &ref_27 - - hris/employeepayrollruns - x-speakeasy-group: hris.employeepayrollruns + - marketingautomation/emails + x-speakeasy-group: marketingautomation.emails x-speakeasy-pagination: type: cursor inputs: @@ -2858,11 +2968,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /hris/employeepayrollruns/{id}: + /marketingautomation/emails/{id}: get: - operationId: retrieveHrisEmployeePayrollRun - summary: Retrieve Employee Payroll Run - description: Retrieve Employee Payroll Run from any connected Hris software + operationId: retrieveMarketingautomationEmail + summary: Retrieve Email + description: Retrieve an Email from any connected Marketingautomation software parameters: - name: x-connection-token required: true @@ -2873,14 +2983,16 @@ paths: - name: id required: true in: path - description: id of the employeepayrollrun you want to retrieve. + description: id of the email you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. + description: >- + Set to true to include data from the original Marketingautomation + software. example: false schema: type: boolean @@ -2890,13 +3002,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisEmployeepayrollrunOutput' + $ref: '#/components/schemas/UnifiedMarketingautomationEmailOutput' tags: *ref_27 - x-speakeasy-group: hris.employeepayrollruns - /hris/employees: + x-speakeasy-group: marketingautomation.emails + /marketingautomation/events: get: - operationId: listHrisEmployees - summary: List Employees + operationId: listMarketingAutomationEvents + summary: List Events parameters: - name: x-connection-token required: true @@ -2917,7 +3029,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -2938,10 +3049,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedHrisEmployeeOutput' + $ref: >- + #/components/schemas/UnifiedMarketingautomationEventOutput tags: &ref_28 - - hris/employees - x-speakeasy-group: hris.employees + - marketingautomation/events + x-speakeasy-group: marketingautomation.events x-speakeasy-pagination: type: cursor inputs: @@ -2950,43 +3062,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - post: - operationId: createHrisEmployee - summary: Create Employees - description: Create Employees in any supported Hris software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original Hris software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedHrisEmployeeInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedHrisEmployeeOutput' - tags: *ref_28 - x-speakeasy-group: hris.employees - /hris/employees/{id}: + /marketingautomation/events/{id}: get: - operationId: retrieveHrisEmployee - summary: Retrieve Employee - description: Retrieve an Employee from any connected Hris software + operationId: retrieveMarketingautomationEvent + summary: Retrieve Event + description: Retrieve an Event from any connected Marketingautomation software parameters: - name: x-connection-token required: true @@ -2997,14 +3077,16 @@ paths: - name: id required: true in: path - description: id of the employee you want to retrieve. + description: id of the event you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. + description: >- + Set to true to include data from the original Marketingautomation + software. example: false schema: type: boolean @@ -3014,13 +3096,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisEmployeeOutput' + $ref: '#/components/schemas/UnifiedMarketingautomationEventOutput' tags: *ref_28 - x-speakeasy-group: hris.employees - /hris/employerbenefits: + x-speakeasy-group: marketingautomation.events + /marketingautomation/lists: get: - operationId: listHrisEmployerBenefits - summary: List Employer Benefits + operationId: listMarketingautomationLists + summary: List Lists parameters: - name: x-connection-token required: true @@ -3041,7 +3123,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -3063,10 +3144,10 @@ paths: type: array items: $ref: >- - #/components/schemas/UnifiedHrisEmployerbenefitOutput + #/components/schemas/UnifiedMarketingautomationListOutput tags: &ref_29 - - hris/employerbenefits - x-speakeasy-group: hris.employerbenefits + - marketingautomation/lists + x-speakeasy-group: marketingautomation.lists x-speakeasy-pagination: type: cursor inputs: @@ -3075,11 +3156,45 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /hris/employerbenefits/{id}: + post: + operationId: createMarketingautomationList + summary: Create Lists + description: Create Lists in any supported Marketingautomation software + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: remote_data + required: false + in: query + description: >- + Set to true to include data from the original Marketingautomation + software. + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedMarketingautomationListInput' + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedMarketingautomationListOutput' + tags: *ref_29 + x-speakeasy-group: marketingautomation.lists + /marketingautomation/lists/{id}: get: - operationId: retrieveHrisEmployerBenefit - summary: Retrieve Employer Benefit - description: Retrieve an Employer Benefit from any connected Hris software + operationId: retrieveMarketingautomationList + summary: Retrieve List + description: Retrieve a List from any connected Marketingautomation software parameters: - name: x-connection-token required: true @@ -3090,14 +3205,16 @@ paths: - name: id required: true in: path - description: id of the employer benefit you want to retrieve. + description: id of the list you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. + description: >- + Set to true to include data from the original Marketingautomation + software. example: false schema: type: boolean @@ -3107,13 +3224,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisEmployerbenefitOutput' + $ref: '#/components/schemas/UnifiedMarketingautomationListOutput' tags: *ref_29 - x-speakeasy-group: hris.employerbenefits - /hris/employments: + x-speakeasy-group: marketingautomation.lists + /marketingautomation/messages: get: - operationId: listHrisEmployments - summary: List Employments + operationId: listMarketingautomationMessages + summary: List Messages parameters: - name: x-connection-token required: true @@ -3134,7 +3251,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -3155,10 +3271,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedHrisEmploymentOutput' + $ref: >- + #/components/schemas/UnifiedMarketingautomationMessageOutput tags: &ref_30 - - hris/employments - x-speakeasy-group: hris.employments + - marketingautomation/messages + x-speakeasy-group: marketingautomation.messages x-speakeasy-pagination: type: cursor inputs: @@ -3167,11 +3284,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /hris/employments/{id}: + /marketingautomation/messages/{id}: get: - operationId: retrieveHrisEmployment - summary: Retrieve Employment - description: Retrieve an Employment from any connected Hris software + operationId: retrieveMarketingautomationMessage + summary: Retrieve Messages + description: Retrieve Messages from any connected Marketingautomation software parameters: - name: x-connection-token required: true @@ -3182,14 +3299,16 @@ paths: - name: id required: true in: path - description: id of the employment you want to retrieve. + description: id of the message you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. + description: >- + Set to true to include data from the original Marketingautomation + software. example: false schema: type: boolean @@ -3199,13 +3318,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisEmploymentOutput' + $ref: '#/components/schemas/UnifiedMarketingautomationMessageOutput' tags: *ref_30 - x-speakeasy-group: hris.employments - /hris/groups: + x-speakeasy-group: marketingautomation.messages + /marketingautomation/templates: get: - operationId: listHrisGroups - summary: List Groups + operationId: listMarketingautomationTemplates + summary: List Templates parameters: - name: x-connection-token required: true @@ -3226,7 +3345,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -3247,10 +3365,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedHrisGroupOutput' + $ref: >- + #/components/schemas/UnifiedMarketingautomationTemplateOutput tags: &ref_31 - - hris/groups - x-speakeasy-group: hris.groups + - marketingautomation/templates + x-speakeasy-group: marketingautomation.templates x-speakeasy-pagination: type: cursor inputs: @@ -3259,11 +3378,45 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /hris/groups/{id}: + post: + operationId: createMarketingautomationTemplate + summary: Create Template + description: Create a template in any supported Marketingautomation software + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: remote_data + required: false + in: query + description: >- + Set to true to include data from the original Marketingautomation + software. + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedMarketingautomationTemplateInput' + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedMarketingautomationTemplateOutput' + tags: *ref_31 + x-speakeasy-group: marketingautomation.templates + /marketingautomation/templates/{id}: get: - operationId: retrieveHrisGroup - summary: Retrieve Group - description: Retrieve a Group from any connected Hris software + operationId: retrieveMarketingautomationTemplate + summary: Retrieve Template + description: Retrieve a Template from any connected Marketingautomation software parameters: - name: x-connection-token required: true @@ -3274,14 +3427,16 @@ paths: - name: id required: true in: path - description: id of the group you want to retrieve. + description: id of the template you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. + description: >- + Set to true to include data from the original Marketingautomation + software. example: false schema: type: boolean @@ -3291,13 +3446,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisGroupOutput' + $ref: '#/components/schemas/UnifiedMarketingautomationTemplateOutput' tags: *ref_31 - x-speakeasy-group: hris.groups - /hris/locations: + x-speakeasy-group: marketingautomation.templates + /marketingautomation/users: get: - operationId: listHrisLocations - summary: List Locations + operationId: listMarketingAutomationUsers + summary: List Users parameters: - name: x-connection-token required: true @@ -3318,7 +3473,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -3339,10 +3493,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedHrisLocationOutput' + $ref: >- + #/components/schemas/UnifiedMarketingautomationUserOutput tags: &ref_32 - - hris/locations - x-speakeasy-group: hris.locations + - marketingautomation/users + x-speakeasy-group: marketingautomation.users x-speakeasy-pagination: type: cursor inputs: @@ -3351,11 +3506,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /hris/locations/{id}: + /marketingautomation/users/{id}: get: - operationId: retrieveHrisLocation - summary: Retrieve Location - description: Retrieve a Location from any connected Hris software + operationId: retrieveMarketingAutomationUser + summary: Retrieve Users + description: Retrieve Users from any connected Marketingautomation software parameters: - name: x-connection-token required: true @@ -3366,14 +3521,16 @@ paths: - name: id required: true in: path - description: id of the location you want to retrieve. + description: id of the user you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. + description: >- + Set to true to include data from the original Marketingautomation + software. example: false schema: type: boolean @@ -3383,13 +3540,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisLocationOutput' + $ref: '#/components/schemas/UnifiedMarketingautomationUserOutput' tags: *ref_32 - x-speakeasy-group: hris.locations - /hris/paygroups: + x-speakeasy-group: marketingautomation.users + /accounting/accounts: get: - operationId: listHrisPaygroups - summary: List Pay Groups + operationId: listAccountingAccounts + summary: List Accounts parameters: - name: x-connection-token required: true @@ -3410,7 +3567,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -3431,10 +3587,10 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedHrisPaygroupOutput' + $ref: '#/components/schemas/UnifiedAccountingAccountOutput' tags: &ref_33 - - hris/paygroups - x-speakeasy-group: hris.paygroups + - accounting/accounts + x-speakeasy-group: accounting.accounts x-speakeasy-pagination: type: cursor inputs: @@ -3443,11 +3599,44 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /hris/paygroups/{id}: + post: + operationId: createAccountingAccount + summary: Create Accounts + description: Create accounts in any supported Accounting software + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: remote_data + required: false + in: query + example: false + description: Set to true to include data from the original Accounting software. + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedAccountingAccountInput' + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedAccountingAccountOutput' + tags: *ref_33 + x-speakeasy-group: accounting.accounts + /accounting/accounts/{id}: get: - operationId: retrieveHrisPaygroup - summary: Retrieve Pay Group - description: Retrieve a Pay Group from any connected Hris software + operationId: retrieveAccountingAccount + summary: Retrieve Accounts + description: Retrieve Accounts from any connected Accounting software parameters: - name: x-connection-token required: true @@ -3458,15 +3647,15 @@ paths: - name: id required: true in: path - description: id of the paygroup you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the account you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -3475,13 +3664,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisPaygroupOutput' + $ref: '#/components/schemas/UnifiedAccountingAccountOutput' tags: *ref_33 - x-speakeasy-group: hris.paygroups - /hris/payrollruns: + x-speakeasy-group: accounting.accounts + /accounting/addresses: get: - operationId: listHrisPayrollRuns - summary: List Payroll Runs + operationId: listAccountingAddress + summary: List Addresss parameters: - name: x-connection-token required: true @@ -3502,7 +3691,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -3523,10 +3711,10 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedHrisPayrollrunOutput' + $ref: '#/components/schemas/UnifiedAccountingAddressOutput' tags: &ref_34 - - hris/payrollruns - x-speakeasy-group: hris.payrollruns + - accounting/addresses + x-speakeasy-group: accounting.addresses x-speakeasy-pagination: type: cursor inputs: @@ -3535,11 +3723,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /hris/payrollruns/{id}: + /accounting/addresses/{id}: get: - operationId: retrieveHrisPayrollRun - summary: Retrieve Payroll Run - description: Retrieve a Payroll Run from any connected Hris software + operationId: retrieveAccountingAddress + summary: Retrieve Addresses + description: Retrieve Addresses from any connected Accounting software parameters: - name: x-connection-token required: true @@ -3550,15 +3738,15 @@ paths: - name: id required: true in: path - description: id of the payroll run you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the address you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -3567,13 +3755,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisPayrollrunOutput' + $ref: '#/components/schemas/UnifiedAccountingAddressOutput' tags: *ref_34 - x-speakeasy-group: hris.payrollruns - /hris/timeoffs: + x-speakeasy-group: accounting.addresses + /accounting/attachments: get: - operationId: listHrisTimeoffs - summary: List Time Offs + operationId: listAccountingAttachments + summary: List Attachments parameters: - name: x-connection-token required: true @@ -3594,7 +3782,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -3615,10 +3802,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedHrisTimeoffOutput' + $ref: >- + #/components/schemas/UnifiedAccountingAttachmentOutput tags: &ref_35 - - hris/timeoffs - x-speakeasy-group: hris.timeoffs + - accounting/attachments + x-speakeasy-group: accounting.attachments x-speakeasy-pagination: type: cursor inputs: @@ -3628,9 +3816,9 @@ paths: outputs: nextCursor: $.next_cursor post: - operationId: createHrisTimeoff - summary: Create Timeoffs - description: Create Timeoffs in any supported Hris software + operationId: createAccountingAttachment + summary: Create Attachments + description: Create attachments in any supported Accounting software parameters: - name: x-connection-token required: true @@ -3641,7 +3829,8 @@ paths: - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. + example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean requestBody: @@ -3649,21 +3838,21 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisTimeoffInput' + $ref: '#/components/schemas/UnifiedAccountingAttachmentInput' responses: '201': description: '' content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisTimeoffOutput' + $ref: '#/components/schemas/UnifiedAccountingAttachmentOutput' tags: *ref_35 - x-speakeasy-group: hris.timeoffs - /hris/timeoffs/{id}: + x-speakeasy-group: accounting.attachments + /accounting/attachments/{id}: get: - operationId: retrieveHrisTimeoff - summary: Retrieve Time Off - description: Retrieve a Time Off from any connected Hris software + operationId: retrieveAccountingAttachment + summary: Retrieve Attachments + description: Retrieve attachments from any connected Accounting software parameters: - name: x-connection-token required: true @@ -3674,15 +3863,15 @@ paths: - name: id required: true in: path - description: id of the time off you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the attachment you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -3691,13 +3880,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisTimeoffOutput' + $ref: '#/components/schemas/UnifiedAccountingAttachmentOutput' tags: *ref_35 - x-speakeasy-group: hris.timeoffs - /hris/timeoffbalances: + x-speakeasy-group: accounting.attachments + /accounting/balancesheets: get: - operationId: listHrisTimeoffbalances - summary: List TimeoffBalances + operationId: listAccountingBalanceSheets + summary: List BalanceSheets parameters: - name: x-connection-token required: true @@ -3718,7 +3907,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -3739,10 +3927,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedHrisTimeoffbalanceOutput' + $ref: >- + #/components/schemas/UnifiedAccountingBalancesheetOutput tags: &ref_36 - - hris/timeoffbalances - x-speakeasy-group: hris.timeoffbalances + - accounting/balancesheets + x-speakeasy-group: accounting.balancesheets x-speakeasy-pagination: type: cursor inputs: @@ -3751,11 +3940,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /hris/timeoffbalances/{id}: + /accounting/balancesheets/{id}: get: - operationId: retrieveHrisTimeoffbalance - summary: Retrieve Time off Balances - description: Retrieve Time off Balances from any connected Hris software + operationId: retrieveAccountingBalanceSheet + summary: Retrieve BalanceSheets + description: Retrieve BalanceSheets from any connected Accounting software parameters: - name: x-connection-token required: true @@ -3766,15 +3955,15 @@ paths: - name: id required: true in: path - description: id of the timeoffbalance you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the balancesheet you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -3783,13 +3972,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisTimeoffbalanceOutput' + $ref: '#/components/schemas/UnifiedAccountingBalancesheetOutput' tags: *ref_36 - x-speakeasy-group: hris.timeoffbalances - /hris/timesheetentries: + x-speakeasy-group: accounting.balancesheets + /accounting/cashflowstatements: get: - operationId: listHrisTimesheetentries - summary: List Timesheetentries + operationId: listAccountingCashflowStatement + summary: List CashflowStatements parameters: - name: x-connection-token required: true @@ -3810,7 +3999,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -3831,10 +4019,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedHrisTimesheetEntryOutput' + $ref: >- + #/components/schemas/UnifiedAccountingCashflowstatementOutput tags: &ref_37 - - hris/timesheetentries - x-speakeasy-group: hris.timesheetentries + - accounting/cashflowstatements + x-speakeasy-group: accounting.cashflowstatements x-speakeasy-pagination: type: cursor inputs: @@ -3843,43 +4032,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - post: - operationId: createHrisTimesheetentry - summary: Create Timesheetentrys - description: Create Timesheetentrys in any supported Hris software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original Hris software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedHrisTimesheetEntryInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedHrisTimesheetEntryOutput' - tags: *ref_37 - x-speakeasy-group: hris.timesheetentries - /hris/timesheetentries/{id}: + /accounting/cashflowstatements/{id}: get: - operationId: retrieveHrisTimesheetentry - summary: Retrieve Timesheetentry - description: Retrieve an Timesheetentry from any connected Hris software + operationId: retrieveAccountingCashflowStatement + summary: Retrieve Cashflow Statements + description: Retrieve Cashflow Statements from any connected Accounting software parameters: - name: x-connection-token required: true @@ -3890,15 +4047,15 @@ paths: - name: id required: true in: path - description: id of the timesheetentry you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the cashflowstatement you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Hris software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -3907,13 +4064,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedHrisTimesheetEntryOutput' + $ref: '#/components/schemas/UnifiedAccountingCashflowstatementOutput' tags: *ref_37 - x-speakeasy-group: hris.timesheetentries - /marketingautomation/actions: + x-speakeasy-group: accounting.cashflowstatements + /accounting/companyinfos: get: - operationId: listMarketingautomationAction - summary: List Actions + operationId: listAccountingCompanyInfos + summary: List CompanyInfos parameters: - name: x-connection-token required: true @@ -3934,7 +4091,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -3956,10 +4112,10 @@ paths: type: array items: $ref: >- - #/components/schemas/UnifiedMarketingautomationActionOutput + #/components/schemas/UnifiedAccountingCompanyinfoOutput tags: &ref_38 - - marketingautomation/actions - x-speakeasy-group: marketingautomation.actions + - accounting/companyinfos + x-speakeasy-group: accounting.companyinfos x-speakeasy-pagination: type: cursor inputs: @@ -3968,46 +4124,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - post: - operationId: createMarketingautomationAction - summary: Create Action - description: Create a action in any supported Marketingautomation software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - description: >- - Set to true to include data from the original Marketingautomation - software. - example: false - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedMarketingautomationActionInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedMarketingautomationActionOutput' - tags: *ref_38 - x-speakeasy-group: marketingautomation.actions - /marketingautomation/actions/{id}: + /accounting/companyinfos/{id}: get: - operationId: retrieveMarketingautomationAction - summary: Retrieve Actions - description: Retrieve Actions from any connected Marketingautomation software + operationId: retrieveAccountingCompanyInfo + summary: Retrieve Company Infos + description: Retrieve Company Infos from any connected Accounting software parameters: - name: x-connection-token required: true @@ -4018,17 +4139,15 @@ paths: - name: id required: true in: path - description: id of the action you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the companyinfo you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: >- - Set to true to include data from the original Marketingautomation - software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -4037,13 +4156,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationActionOutput' + $ref: '#/components/schemas/UnifiedAccountingCompanyinfoOutput' tags: *ref_38 - x-speakeasy-group: marketingautomation.actions - /marketingautomation/automations: + x-speakeasy-group: accounting.companyinfos + /accounting/contacts: get: - operationId: listMarketingautomationAutomations - summary: List Automations + operationId: listAccountingContacts + summary: List Contacts parameters: - name: x-connection-token required: true @@ -4064,7 +4183,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -4085,11 +4203,10 @@ paths: data: type: array items: - $ref: >- - #/components/schemas/UnifiedMarketingautomationAutomationOutput + $ref: '#/components/schemas/UnifiedAccountingContactOutput' tags: &ref_39 - - marketingautomation/automations - x-speakeasy-group: marketingautomation.automations + - accounting/contacts + x-speakeasy-group: accounting.contacts x-speakeasy-pagination: type: cursor inputs: @@ -4099,9 +4216,9 @@ paths: outputs: nextCursor: $.next_cursor post: - operationId: createMarketingautomationAutomation - summary: Create Automation - description: Create a automation in any supported Marketingautomation software + operationId: createAccountingContact + summary: Create Contacts + description: Create contacts in any supported Accounting software parameters: - name: x-connection-token required: true @@ -4112,10 +4229,8 @@ paths: - name: remote_data required: false in: query - description: >- - Set to true to include data from the original Marketingautomation - software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean requestBody: @@ -4123,22 +4238,21 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationAutomationInput' + $ref: '#/components/schemas/UnifiedAccountingContactInput' responses: '201': description: '' content: application/json: schema: - $ref: >- - #/components/schemas/UnifiedMarketingautomationAutomationOutput + $ref: '#/components/schemas/UnifiedAccountingContactOutput' tags: *ref_39 - x-speakeasy-group: marketingautomation.automations - /marketingautomation/automations/{id}: + x-speakeasy-group: accounting.contacts + /accounting/contacts/{id}: get: - operationId: retrieveMarketingautomationAutomation - summary: Retrieve Automation - description: Retrieve an Automation from any connected Marketingautomation software + operationId: retrieveAccountingContact + summary: Retrieve Contacts + description: Retrieve Contacts from any connected Accounting software parameters: - name: x-connection-token required: true @@ -4149,17 +4263,15 @@ paths: - name: id required: true in: path - description: id of the automation you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the contact you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: >- - Set to true to include data from the original Marketingautomation - software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -4168,14 +4280,13 @@ paths: content: application/json: schema: - $ref: >- - #/components/schemas/UnifiedMarketingautomationAutomationOutput + $ref: '#/components/schemas/UnifiedAccountingContactOutput' tags: *ref_39 - x-speakeasy-group: marketingautomation.automations - /marketingautomation/campaigns: + x-speakeasy-group: accounting.contacts + /accounting/creditnotes: get: - operationId: listMarketingautomationCampaigns - summary: List Campaigns + operationId: listAccountingCreditNote + summary: List CreditNotes parameters: - name: x-connection-token required: true @@ -4196,7 +4307,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -4218,10 +4328,10 @@ paths: type: array items: $ref: >- - #/components/schemas/UnifiedMarketingautomationCampaignOutput + #/components/schemas/UnifiedAccountingCreditnoteOutput tags: &ref_40 - - marketingautomation/campaigns - x-speakeasy-group: marketingautomation.campaigns + - accounting/creditnotes + x-speakeasy-group: accounting.creditnotes x-speakeasy-pagination: type: cursor inputs: @@ -4230,46 +4340,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - post: - operationId: createMarketingautomationCampaign - summary: Create Campaign - description: Create a campaign in any supported Marketingautomation software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - description: >- - Set to true to include data from the original Marketingautomation - software. - example: false - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedMarketingautomationCampaignInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedMarketingautomationCampaignOutput' - tags: *ref_40 - x-speakeasy-group: marketingautomation.campaigns - /marketingautomation/campaigns/{id}: + /accounting/creditnotes/{id}: get: - operationId: retrieveMarketingautomationCampaign - summary: Retrieve Campaign - description: Retrieve a Campaign from any connected Marketingautomation software + operationId: retrieveAccountingCreditNote + summary: Retrieve Credit Notes + description: Retrieve Credit Notes from any connected Accounting software parameters: - name: x-connection-token required: true @@ -4280,17 +4355,15 @@ paths: - name: id required: true in: path - description: id of the campaign you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the creditnote you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: >- - Set to true to include data from the original Marketingautomation - software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -4299,13 +4372,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationCampaignOutput' + $ref: '#/components/schemas/UnifiedAccountingCreditnoteOutput' tags: *ref_40 - x-speakeasy-group: marketingautomation.campaigns - /marketingautomation/contacts: + x-speakeasy-group: accounting.creditnotes + /accounting/expenses: get: - operationId: listMarketingAutomationContacts - summary: List Contacts + operationId: listAccountingExpense + summary: List Expenses parameters: - name: x-connection-token required: true @@ -4326,7 +4399,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -4347,11 +4419,10 @@ paths: data: type: array items: - $ref: >- - #/components/schemas/UnifiedMarketingautomationContactOutput + $ref: '#/components/schemas/UnifiedAccountingExpenseOutput' tags: &ref_41 - - marketingautomation/contacts - x-speakeasy-group: marketingautomation.contacts + - accounting/expenses + x-speakeasy-group: accounting.expenses x-speakeasy-pagination: type: cursor inputs: @@ -4361,9 +4432,9 @@ paths: outputs: nextCursor: $.next_cursor post: - operationId: createMarketingAutomationContact - summary: Create Contact - description: Create a contact in any supported Marketingautomation software + operationId: createAccountingExpense + summary: Create Expenses + description: Create Expenses in any supported Accounting software parameters: - name: x-connection-token required: true @@ -4374,10 +4445,8 @@ paths: - name: remote_data required: false in: query - description: >- - Set to true to include data from the original Marketingautomation - software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean requestBody: @@ -4385,21 +4454,21 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationContactInput' + $ref: '#/components/schemas/UnifiedAccountingExpenseInput' responses: '201': description: '' content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationContactOutput' + $ref: '#/components/schemas/UnifiedAccountingExpenseOutput' tags: *ref_41 - x-speakeasy-group: marketingautomation.contacts - /marketingautomation/contacts/{id}: + x-speakeasy-group: accounting.expenses + /accounting/expenses/{id}: get: - operationId: retrieveMarketingAutomationContact - summary: Retrieve Contacts - description: Retrieve Contacts from any connected Marketingautomation software + operationId: retrieveAccountingExpense + summary: Retrieve Expenses + description: Retrieve Expenses from any connected Accounting software parameters: - name: x-connection-token required: true @@ -4410,17 +4479,15 @@ paths: - name: id required: true in: path - description: id of the contact you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the expense you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: >- - Set to true to include data from the original Marketingautomation - software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -4429,13 +4496,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationContactOutput' + $ref: '#/components/schemas/UnifiedAccountingExpenseOutput' tags: *ref_41 - x-speakeasy-group: marketingautomation.contacts - /marketingautomation/emails: + x-speakeasy-group: accounting.expenses + /accounting/incomestatements: get: - operationId: listMarketingautomationEmails - summary: List Emails + operationId: listAccountingIncomeStatement + summary: List IncomeStatements parameters: - name: x-connection-token required: true @@ -4456,7 +4523,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -4478,10 +4544,10 @@ paths: type: array items: $ref: >- - #/components/schemas/UnifiedMarketingautomationEmailOutput + #/components/schemas/UnifiedAccountingIncomestatementOutput tags: &ref_42 - - marketingautomation/emails - x-speakeasy-group: marketingautomation.emails + - accounting/incomestatements + x-speakeasy-group: accounting.incomestatements x-speakeasy-pagination: type: cursor inputs: @@ -4490,11 +4556,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /marketingautomation/emails/{id}: + /accounting/incomestatements/{id}: get: - operationId: retrieveMarketingautomationEmail - summary: Retrieve Email - description: Retrieve an Email from any connected Marketingautomation software + operationId: retrieveAccountingIncomeStatement + summary: Retrieve Income Statements + description: Retrieve Income Statements from any connected Accounting software parameters: - name: x-connection-token required: true @@ -4505,17 +4571,15 @@ paths: - name: id required: true in: path - description: id of the email you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the incomestatement you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: >- - Set to true to include data from the original Marketingautomation - software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -4524,13 +4588,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationEmailOutput' + $ref: '#/components/schemas/UnifiedAccountingIncomestatementOutput' tags: *ref_42 - x-speakeasy-group: marketingautomation.emails - /marketingautomation/events: + x-speakeasy-group: accounting.incomestatements + /accounting/invoices: get: - operationId: listMarketingAutomationEvents - summary: List Events + operationId: listAccountingInvoice + summary: List Invoices parameters: - name: x-connection-token required: true @@ -4551,7 +4615,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -4572,11 +4635,10 @@ paths: data: type: array items: - $ref: >- - #/components/schemas/UnifiedMarketingautomationEventOutput + $ref: '#/components/schemas/UnifiedAccountingInvoiceOutput' tags: &ref_43 - - marketingautomation/events - x-speakeasy-group: marketingautomation.events + - accounting/invoices + x-speakeasy-group: accounting.invoices x-speakeasy-pagination: type: cursor inputs: @@ -4585,11 +4647,44 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /marketingautomation/events/{id}: + post: + operationId: createAccountingInvoice + summary: Create Invoices + description: Create invoices in any supported Accounting software + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: remote_data + required: false + in: query + example: false + description: Set to true to include data from the original Accounting software. + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedAccountingInvoiceInput' + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedAccountingInvoiceOutput' + tags: *ref_43 + x-speakeasy-group: accounting.invoices + /accounting/invoices/{id}: get: - operationId: retrieveMarketingautomationEvent - summary: Retrieve Event - description: Retrieve an Event from any connected Marketingautomation software + operationId: retrieveAccountingInvoice + summary: Retrieve Invoices + description: Retrieve Invoices from any connected Accounting software parameters: - name: x-connection-token required: true @@ -4600,17 +4695,15 @@ paths: - name: id required: true in: path - description: id of the event you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the invoice you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: >- - Set to true to include data from the original Marketingautomation - software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -4619,13 +4712,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationEventOutput' + $ref: '#/components/schemas/UnifiedAccountingInvoiceOutput' tags: *ref_43 - x-speakeasy-group: marketingautomation.events - /marketingautomation/lists: + x-speakeasy-group: accounting.invoices + /accounting/items: get: - operationId: listMarketingautomationLists - summary: List Lists + operationId: listAccountingItem + summary: List Items parameters: - name: x-connection-token required: true @@ -4646,7 +4739,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -4667,11 +4759,10 @@ paths: data: type: array items: - $ref: >- - #/components/schemas/UnifiedMarketingautomationListOutput + $ref: '#/components/schemas/UnifiedAccountingItemOutput' tags: &ref_44 - - marketingautomation/lists - x-speakeasy-group: marketingautomation.lists + - accounting/items + x-speakeasy-group: accounting.items x-speakeasy-pagination: type: cursor inputs: @@ -4680,45 +4771,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - post: - operationId: createMarketingautomationList - summary: Create Lists - description: Create Lists in any supported Marketingautomation software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - description: >- - Set to true to include data from the original Marketingautomation - software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedMarketingautomationListInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedMarketingautomationListOutput' - tags: *ref_44 - x-speakeasy-group: marketingautomation.lists - /marketingautomation/lists/{id}: + /accounting/items/{id}: get: - operationId: retrieveMarketingautomationList - summary: Retrieve List - description: Retrieve a List from any connected Marketingautomation software + operationId: retrieveAccountingItem + summary: Retrieve Items + description: Retrieve Items from any connected Accounting software parameters: - name: x-connection-token required: true @@ -4729,17 +4786,15 @@ paths: - name: id required: true in: path - description: id of the list you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the item you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: >- - Set to true to include data from the original Marketingautomation - software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -4748,13 +4803,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationListOutput' + $ref: '#/components/schemas/UnifiedAccountingItemOutput' tags: *ref_44 - x-speakeasy-group: marketingautomation.lists - /marketingautomation/messages: + x-speakeasy-group: accounting.items + /accounting/journalentries: get: - operationId: listMarketingautomationMessages - summary: List Messages + operationId: listAccountingJournalEntry + summary: List JournalEntrys parameters: - name: x-connection-token required: true @@ -4775,7 +4830,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -4797,10 +4851,10 @@ paths: type: array items: $ref: >- - #/components/schemas/UnifiedMarketingautomationMessageOutput + #/components/schemas/UnifiedAccountingJournalentryOutput tags: &ref_45 - - marketingautomation/messages - x-speakeasy-group: marketingautomation.messages + - accounting/journalentries + x-speakeasy-group: accounting.journalentries x-speakeasy-pagination: type: cursor inputs: @@ -4809,11 +4863,44 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /marketingautomation/messages/{id}: + post: + operationId: createAccountingJournalEntry + summary: Create Journal Entries + description: Create Journal Entries in any supported Accounting software + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: remote_data + required: false + in: query + example: false + description: Set to true to include data from the original Accounting software. + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedAccountingJournalentryInput' + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedAccountingJournalentryOutput' + tags: *ref_45 + x-speakeasy-group: accounting.journalentries + /accounting/journalentries/{id}: get: - operationId: retrieveMarketingautomationMessage - summary: Retrieve Messages - description: Retrieve Messages from any connected Marketingautomation software + operationId: retrieveAccountingJournalEntry + summary: Retrieve Journal Entries + description: Retrieve Journal Entries from any connected Accounting software parameters: - name: x-connection-token required: true @@ -4824,17 +4911,15 @@ paths: - name: id required: true in: path - description: id of the message you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the journalentry you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: >- - Set to true to include data from the original Marketingautomation - software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -4843,13 +4928,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationMessageOutput' + $ref: '#/components/schemas/UnifiedAccountingJournalentryOutput' tags: *ref_45 - x-speakeasy-group: marketingautomation.messages - /marketingautomation/templates: + x-speakeasy-group: accounting.journalentries + /accounting/payments: get: - operationId: listMarketingautomationTemplates - summary: List Templates + operationId: listAccountingPayment + summary: List Payments parameters: - name: x-connection-token required: true @@ -4870,7 +4955,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -4891,11 +4975,10 @@ paths: data: type: array items: - $ref: >- - #/components/schemas/UnifiedMarketingautomationTemplateOutput + $ref: '#/components/schemas/UnifiedAccountingPaymentOutput' tags: &ref_46 - - marketingautomation/templates - x-speakeasy-group: marketingautomation.templates + - accounting/payments + x-speakeasy-group: accounting.payments x-speakeasy-pagination: type: cursor inputs: @@ -4905,9 +4988,9 @@ paths: outputs: nextCursor: $.next_cursor post: - operationId: createMarketingautomationTemplate - summary: Create Template - description: Create a template in any supported Marketingautomation software + operationId: createAccountingPayment + summary: Create Payments + description: Create Payments in any supported Accounting software parameters: - name: x-connection-token required: true @@ -4918,9 +5001,8 @@ paths: - name: remote_data required: false in: query - description: >- - Set to true to include data from the original Marketingautomation - software. + example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean requestBody: @@ -4928,21 +5010,21 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationTemplateInput' + $ref: '#/components/schemas/UnifiedAccountingPaymentInput' responses: '201': description: '' content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationTemplateOutput' + $ref: '#/components/schemas/UnifiedAccountingPaymentOutput' tags: *ref_46 - x-speakeasy-group: marketingautomation.templates - /marketingautomation/templates/{id}: + x-speakeasy-group: accounting.payments + /accounting/payments/{id}: get: - operationId: retrieveMarketingautomationTemplate - summary: Retrieve Template - description: Retrieve a Template from any connected Marketingautomation software + operationId: retrieveAccountingPayment + summary: Retrieve Payments + description: Retrieve Payments from any connected Accounting software parameters: - name: x-connection-token required: true @@ -4953,17 +5035,15 @@ paths: - name: id required: true in: path - description: id of the template you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the payment you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: >- - Set to true to include data from the original Marketingautomation - software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -4972,13 +5052,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationTemplateOutput' + $ref: '#/components/schemas/UnifiedAccountingPaymentOutput' tags: *ref_46 - x-speakeasy-group: marketingautomation.templates - /marketingautomation/users: + x-speakeasy-group: accounting.payments + /accounting/phonenumbers: get: - operationId: listMarketingAutomationUsers - summary: List Users + operationId: listAccountingPhonenumber + summary: List PhoneNumbers parameters: - name: x-connection-token required: true @@ -4999,7 +5079,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -5021,10 +5100,10 @@ paths: type: array items: $ref: >- - #/components/schemas/UnifiedMarketingautomationUserOutput + #/components/schemas/UnifiedAccountingPhonenumberOutput tags: &ref_47 - - marketingautomation/users - x-speakeasy-group: marketingautomation.users + - accounting/phonenumbers + x-speakeasy-group: accounting.phonenumbers x-speakeasy-pagination: type: cursor inputs: @@ -5033,11 +5112,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /marketingautomation/users/{id}: + /accounting/phonenumbers/{id}: get: - operationId: retrieveMarketingAutomationUser - summary: Retrieve Users - description: Retrieve Users from any connected Marketingautomation software + operationId: retrieveAccountingPhonenumber + summary: Retrieve Phone Numbers + description: Retrieve Phone Numbers from any connected Accounting software parameters: - name: x-connection-token required: true @@ -5048,17 +5127,15 @@ paths: - name: id required: true in: path - description: id of the user you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the phonenumber you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: >- - Set to true to include data from the original Marketingautomation - software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -5067,13 +5144,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedMarketingautomationUserOutput' + $ref: '#/components/schemas/UnifiedAccountingPhonenumberOutput' tags: *ref_47 - x-speakeasy-group: marketingautomation.users - /ats/activities: + x-speakeasy-group: accounting.phonenumbers + /accounting/purchaseorders: get: - operationId: listAtsActivity - summary: List Activities + operationId: listAccountingPurchaseOrder + summary: List PurchaseOrders parameters: - name: x-connection-token required: true @@ -5094,7 +5171,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -5115,10 +5191,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedAtsActivityOutput' + $ref: >- + #/components/schemas/UnifiedAccountingPurchaseorderOutput tags: &ref_48 - - ats/activities - x-speakeasy-group: ats.activities + - accounting/purchaseorders + x-speakeasy-group: accounting.purchaseorders x-speakeasy-pagination: type: cursor inputs: @@ -5128,9 +5205,9 @@ paths: outputs: nextCursor: $.next_cursor post: - operationId: createAtsActivity - summary: Create Activities - description: Create Activities in any supported Ats software + operationId: createAccountingPurchaseOrder + summary: Create Purchase Orders + description: Create Purchase Orders in any supported Accounting software parameters: - name: x-connection-token required: true @@ -5141,8 +5218,8 @@ paths: - name: remote_data required: false in: query - description: Set to true to include data from the original Ats software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean requestBody: @@ -5150,21 +5227,21 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsActivityInput' + $ref: '#/components/schemas/UnifiedAccountingPurchaseorderInput' responses: '201': description: '' content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsActivityOutput' + $ref: '#/components/schemas/UnifiedAccountingPurchaseorderOutput' tags: *ref_48 - x-speakeasy-group: ats.activities - /ats/activities/{id}: + x-speakeasy-group: accounting.purchaseorders + /accounting/purchaseorders/{id}: get: - operationId: retrieveAtsActivity - summary: Retrieve Activities - description: Retrieve Activities from any connected Ats software + operationId: retrieveAccountingPurchaseOrder + summary: Retrieve Purchase Orders + description: Retrieve Purchase Orders from any connected Accounting software parameters: - name: x-connection-token required: true @@ -5175,15 +5252,15 @@ paths: - name: id required: true in: path - description: id of the activity you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the purchaseorder you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Ats software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -5192,13 +5269,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsActivityOutput' + $ref: '#/components/schemas/UnifiedAccountingPurchaseorderOutput' tags: *ref_48 - x-speakeasy-group: ats.activities - /ats/applications: + x-speakeasy-group: accounting.purchaseorders + /accounting/taxrates: get: - operationId: listAtsApplication - summary: List Applications + operationId: listAccountingTaxRate + summary: List TaxRates parameters: - name: x-connection-token required: true @@ -5219,7 +5296,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -5240,10 +5316,10 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedAtsApplicationOutput' + $ref: '#/components/schemas/UnifiedAccountingTaxrateOutput' tags: &ref_49 - - ats/applications - x-speakeasy-group: ats.applications + - accounting/taxrates + x-speakeasy-group: accounting.taxrates x-speakeasy-pagination: type: cursor inputs: @@ -5252,44 +5328,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - post: - operationId: createAtsApplication - summary: Create Applications - description: Create Applications in any supported Ats software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original Ats software. - example: false - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAtsApplicationInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAtsApplicationOutput' - tags: *ref_49 - x-speakeasy-group: ats.applications - /ats/applications/{id}: + /accounting/taxrates/{id}: get: - operationId: retrieveAtsApplication - summary: Retrieve Applications - description: Retrieve Applications from any connected Ats software + operationId: retrieveAccountingTaxRate + summary: Retrieve Tax Rates + description: Retrieve Tax Rates from any connected Accounting software parameters: - name: x-connection-token required: true @@ -5300,15 +5343,15 @@ paths: - name: id required: true in: path - description: id of the application you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the taxrate you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Ats software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -5317,13 +5360,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsApplicationOutput' + $ref: '#/components/schemas/UnifiedAccountingTaxrateOutput' tags: *ref_49 - x-speakeasy-group: ats.applications - /ats/attachments: + x-speakeasy-group: accounting.taxrates + /accounting/trackingcategories: get: - operationId: listAtsAttachment - summary: List Attachments + operationId: listAccountingTrackingCategorys + summary: List TrackingCategorys parameters: - name: x-connection-token required: true @@ -5344,7 +5387,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -5365,10 +5407,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedAtsAttachmentOutput' + $ref: >- + #/components/schemas/UnifiedAccountingTrackingcategoryOutput tags: &ref_50 - - ats/attachments - x-speakeasy-group: ats.attachments + - accounting/trackingcategories + x-speakeasy-group: accounting.trackingcategories x-speakeasy-pagination: type: cursor inputs: @@ -5377,44 +5420,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - post: - operationId: createAtsAttachment - summary: Create Attachments - description: Create Attachments in any supported ATS software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original Ats software. - example: false - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAtsAttachmentInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAtsAttachmentOutput' - tags: *ref_50 - x-speakeasy-group: ats.attachments - /ats/attachments/{id}: + /accounting/trackingcategories/{id}: get: - operationId: retrieveAtsAttachment - summary: Retrieve Attachments - description: Retrieve Attachments from any connected Ats software + operationId: retrieveAccountingTrackingCategory + summary: Retrieve Tracking Categories + description: Retrieve Tracking Categories from any connected Accounting software parameters: - name: x-connection-token required: true @@ -5425,15 +5435,15 @@ paths: - name: id required: true in: path - description: id of the attachment you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the trackingcategory you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Ats software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -5442,13 +5452,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsAttachmentOutput' + $ref: '#/components/schemas/UnifiedAccountingTrackingcategoryOutput' tags: *ref_50 - x-speakeasy-group: ats.attachments - /ats/candidates: + x-speakeasy-group: accounting.trackingcategories + /accounting/transactions: get: - operationId: listAtsCandidate - summary: List Candidates + operationId: listAccountingTransaction + summary: List Transactions parameters: - name: x-connection-token required: true @@ -5469,7 +5479,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -5490,10 +5499,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedAtsCandidateOutput' + $ref: >- + #/components/schemas/UnifiedAccountingTransactionOutput tags: &ref_51 - - ats/candidates - x-speakeasy-group: ats.candidates + - accounting/transactions + x-speakeasy-group: accounting.transactions x-speakeasy-pagination: type: cursor inputs: @@ -5502,44 +5512,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - post: - operationId: createAtsCandidate - summary: Create Candidates - description: Create Candidates in any supported Ats software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original Ats software. - example: false - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAtsCandidateInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAtsCandidateOutput' - tags: *ref_51 - x-speakeasy-group: ats.candidates - /ats/candidates/{id}: + /accounting/transactions/{id}: get: - operationId: retrieveAtsCandidate - summary: Retrieve Candidates - description: Retrieve Candidates from any connected Ats software + operationId: retrieveAccountingTransaction + summary: Retrieve Transactions + description: Retrieve Transactions from any connected Accounting software parameters: - name: x-connection-token required: true @@ -5550,15 +5527,15 @@ paths: - name: id required: true in: path - description: id of the candidate you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the transaction you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Ats software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -5567,13 +5544,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsCandidateOutput' + $ref: '#/components/schemas/UnifiedAccountingTransactionOutput' tags: *ref_51 - x-speakeasy-group: ats.candidates - /ats/departments: + x-speakeasy-group: accounting.transactions + /accounting/vendorcredits: get: - operationId: listAtsDepartments - summary: List Departments + operationId: listAccountingVendorCredit + summary: List VendorCredits parameters: - name: x-connection-token required: true @@ -5594,7 +5571,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -5615,10 +5591,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedAtsDepartmentOutput' + $ref: >- + #/components/schemas/UnifiedAccountingVendorcreditOutput tags: &ref_52 - - ats/departments - x-speakeasy-group: ats.departments + - accounting/vendorcredits + x-speakeasy-group: accounting.vendorcredits x-speakeasy-pagination: type: cursor inputs: @@ -5627,11 +5604,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /ats/departments/{id}: + /accounting/vendorcredits/{id}: get: - operationId: retrieveAtsDepartment - summary: Retrieve Departments - description: Retrieve Departments from any connected Ats software + operationId: retrieveAccountingVendorCredit + summary: Retrieve Vendor Credits + description: Retrieve Vendor Credits from any connected Accounting software parameters: - name: x-connection-token required: true @@ -5642,15 +5619,15 @@ paths: - name: id required: true in: path - description: id of the department you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the vendorcredit you want to retrieve. schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Ats software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean responses: @@ -5659,13 +5636,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsDepartmentOutput' + $ref: '#/components/schemas/UnifiedAccountingVendorcreditOutput' tags: *ref_52 - x-speakeasy-group: ats.departments - /ats/interviews: + x-speakeasy-group: accounting.vendorcredits + /filestorage/folders: get: - operationId: listAtsInterview - summary: List Interviews + operationId: listFilestorageFolder + summary: List Folders parameters: - name: x-connection-token required: true @@ -5686,7 +5663,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -5707,10 +5683,10 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedAtsInterviewOutput' + $ref: '#/components/schemas/UnifiedFilestorageFolderOutput' tags: &ref_53 - - ats/interviews - x-speakeasy-group: ats.interviews + - filestorage/folders + x-speakeasy-group: filestorage.folders x-speakeasy-pagination: type: cursor inputs: @@ -5720,9 +5696,9 @@ paths: outputs: nextCursor: $.next_cursor post: - operationId: createAtsInterview - summary: Create Interviews - description: Create Interviews in any supported Ats software + operationId: createFilestorageFolder + summary: Create Folders + description: Create Folders in any supported Filestorage software parameters: - name: x-connection-token required: true @@ -5733,8 +5709,8 @@ paths: - name: remote_data required: false in: query - description: Set to true to include data from the original Ats software. example: false + description: Set to true to include data from the original Accounting software. schema: type: boolean requestBody: @@ -5742,21 +5718,21 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsInterviewInput' + $ref: '#/components/schemas/UnifiedFilestorageFolderInput' responses: '201': description: '' content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsInterviewOutput' + $ref: '#/components/schemas/UnifiedFilestorageFolderOutput' tags: *ref_53 - x-speakeasy-group: ats.interviews - /ats/interviews/{id}: + x-speakeasy-group: filestorage.folders + /filestorage/folders/{id}: get: - operationId: retrieveAtsInterview - summary: Retrieve Interviews - description: Retrieve Interviews from any connected Ats software + operationId: retrieveFilestorageFolder + summary: Retrieve Folders + description: Retrieve Folders from any connected Filestorage software parameters: - name: x-connection-token required: true @@ -5767,14 +5743,14 @@ paths: - name: id required: true in: path - description: id of the interview you want to retrieve. + description: id of the folder you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Ats software. + description: Set to true to include data from the original File Storage software. example: false schema: type: boolean @@ -5784,13 +5760,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsInterviewOutput' + $ref: '#/components/schemas/UnifiedFilestorageFolderOutput' tags: *ref_53 - x-speakeasy-group: ats.interviews - /ats/jobinterviewstages: + x-speakeasy-group: filestorage.folders + /filestorage/groups: get: - operationId: listAtsJobInterviewStage - summary: List JobInterviewStages + operationId: listFilestorageGroup + summary: List Groups parameters: - name: x-connection-token required: true @@ -5811,7 +5787,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -5832,11 +5807,10 @@ paths: data: type: array items: - $ref: >- - #/components/schemas/UnifiedAtsJobinterviewstageOutput + $ref: '#/components/schemas/UnifiedFilestorageGroupOutput' tags: &ref_54 - - ats/jobinterviewstages - x-speakeasy-group: ats.jobinterviewstages + - filestorage/groups + x-speakeasy-group: filestorage.groups x-speakeasy-pagination: type: cursor inputs: @@ -5845,11 +5819,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /ats/jobinterviewstages/{id}: + /filestorage/groups/{id}: get: - operationId: retrieveAtsJobInterviewStage - summary: Retrieve Job Interview Stages - description: Retrieve Job Interview Stages from any connected Ats software + operationId: retrieveFilestorageGroup + summary: Retrieve Groups + description: Retrieve Groups from any connected Filestorage software parameters: - name: x-connection-token required: true @@ -5860,14 +5834,14 @@ paths: - name: id required: true in: path - description: id of the jobinterviewstage you want to retrieve. + description: id of the permission you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Ats software. + description: Set to true to include data from the original File Storage software. example: false schema: type: boolean @@ -5877,13 +5851,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsJobinterviewstageOutput' + $ref: '#/components/schemas/UnifiedFilestorageGroupOutput' tags: *ref_54 - x-speakeasy-group: ats.jobinterviewstages - /ats/jobs: + x-speakeasy-group: filestorage.groups + /filestorage/users: get: - operationId: listAtsJob - summary: List Jobs + operationId: listFilestorageUsers + summary: List Users parameters: - name: x-connection-token required: true @@ -5904,7 +5878,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -5925,10 +5898,10 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedAtsJobOutput' + $ref: '#/components/schemas/UnifiedFilestorageUserOutput' tags: &ref_55 - - ats/jobs - x-speakeasy-group: ats.jobs + - filestorage/users + x-speakeasy-group: filestorage.users x-speakeasy-pagination: type: cursor inputs: @@ -5937,11 +5910,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /ats/jobs/{id}: + /filestorage/users/{id}: get: - operationId: retrieveAtsJob - summary: Retrieve Jobs - description: Retrieve Jobs from any connected Ats software + operationId: retrieveFilestorageUser + summary: Retrieve Users + description: Retrieve Users from any connected Filestorage software parameters: - name: x-connection-token required: true @@ -5952,14 +5925,14 @@ paths: - name: id required: true in: path - description: id of the job you want to retrieve. + description: id of the permission you want to retrieve. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Ats software. + description: Set to true to include data from the original File Storage software. example: false schema: type: boolean @@ -5969,13 +5942,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsJobOutput' + $ref: '#/components/schemas/UnifiedFilestorageUserOutput' tags: *ref_55 - x-speakeasy-group: ats.jobs - /ats/offers: + x-speakeasy-group: filestorage.users + /ecommerce/products: get: - operationId: listAtsOffer - summary: List Offers + operationId: listEcommerceProducts + summary: List Products parameters: - name: x-connection-token required: true @@ -5996,7 +5969,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -6017,10 +5989,10 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedAtsOfferOutput' + $ref: '#/components/schemas/UnifiedEcommerceProductOutput' tags: &ref_56 - - ats/offers - x-speakeasy-group: ats.offers + - ecommerce/products + x-speakeasy-group: ecommerce.products x-speakeasy-pagination: type: cursor inputs: @@ -6029,11 +6001,44 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /ats/offers/{id}: + post: + operationId: createEcommerceProduct + summary: Create Products + description: Create Products in any supported Ecommerce software + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: remote_data + required: false + in: query + example: false + description: Set to true to include data from the original Accounting software. + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedEcommerceProductInput' + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedEcommerceProductOutput' + tags: *ref_56 + x-speakeasy-group: ecommerce.products + /ecommerce/products/{id}: get: - operationId: retrieveAtsOffer - summary: Retrieve Offers - description: Retrieve Offers from any connected Ats software + operationId: retrieveEcommerceProduct + summary: Retrieve Products + description: Retrieve products from any connected Ats software parameters: - name: x-connection-token required: true @@ -6044,15 +6049,13 @@ paths: - name: id required: true in: path - description: id of the offer you want to retrieve. - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the product you want to retrieve. schema: type: string - name: remote_data required: false in: query description: Set to true to include data from the original Ats software. - example: false schema: type: boolean responses: @@ -6061,13 +6064,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsOfferOutput' + $ref: '#/components/schemas/UnifiedEcommerceProductOutput' tags: *ref_56 - x-speakeasy-group: ats.offers - /ats/offices: + x-speakeasy-group: ecommerce.products + /ecommerce/orders: get: - operationId: listAtsOffice - summary: List Offices + operationId: listEcommerceOrders + summary: List Orders parameters: - name: x-connection-token required: true @@ -6088,7 +6091,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -6109,10 +6111,10 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedAtsOfficeOutput' + $ref: '#/components/schemas/UnifiedEcommerceOrderOutput' tags: &ref_57 - - ats/offices - x-speakeasy-group: ats.offices + - ecommerce/orders + x-speakeasy-group: ecommerce.orders x-speakeasy-pagination: type: cursor inputs: @@ -6121,11 +6123,44 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /ats/offices/{id}: + post: + operationId: createEcommerceOrder + summary: Create Orders + description: Create Orders in any supported Ecommerce software + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: remote_data + required: false + in: query + example: false + description: Set to true to include data from the original Accounting software. + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedEcommerceOrderInput' + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedEcommerceOrderOutput' + tags: *ref_57 + x-speakeasy-group: ecommerce.orders + /ecommerce/orders/{id}: get: - operationId: retrieveAtsOffice - summary: Retrieve Offices - description: Retrieve Offices from any connected Ats software + operationId: retrieveEcommerceOrder + summary: Retrieve Orders + description: Retrieve orders from any connected Ats software parameters: - name: x-connection-token required: true @@ -6136,15 +6171,13 @@ paths: - name: id required: true in: path - description: id of the office you want to retrieve. - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the order you want to retrieve. schema: type: string - name: remote_data required: false in: query description: Set to true to include data from the original Ats software. - example: false schema: type: boolean responses: @@ -6153,13 +6186,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsOfficeOutput' + $ref: '#/components/schemas/UnifiedEcommerceOrderOutput' tags: *ref_57 - x-speakeasy-group: ats.offices - /ats/rejectreasons: + x-speakeasy-group: ecommerce.orders + /ecommerce/customers: get: - operationId: listAtsRejectReasons - summary: List RejectReasons + operationId: listEcommerceCustomers + summary: List Customers parameters: - name: x-connection-token required: true @@ -6180,7 +6213,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -6201,10 +6233,10 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedAtsRejectreasonOutput' + $ref: '#/components/schemas/UnifiedEcommerceCustomerOutput' tags: &ref_58 - - ats/rejectreasons - x-speakeasy-group: ats.rejectreasons + - ecommerce/customers + x-speakeasy-group: ecommerce.customers x-speakeasy-pagination: type: cursor inputs: @@ -6213,11 +6245,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /ats/rejectreasons/{id}: + /ecommerce/customers/{id}: get: - operationId: retrieveAtsRejectReason - summary: Retrieve Reject Reasons - description: Retrieve Reject Reasons from any connected Ats software + operationId: retrieveEcommerceCustomer + summary: Retrieve Customers + description: Retrieve customers from any connected Ats software parameters: - name: x-connection-token required: true @@ -6228,15 +6260,13 @@ paths: - name: id required: true in: path - description: id of the rejectreason you want to retrieve. - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the customer you want to retrieve. schema: type: string - name: remote_data required: false in: query description: Set to true to include data from the original Ats software. - example: false schema: type: boolean responses: @@ -6245,13 +6275,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsRejectreasonOutput' + $ref: '#/components/schemas/UnifiedEcommerceCustomerOutput' tags: *ref_58 - x-speakeasy-group: ats.rejectreasons - /ats/scorecards: + x-speakeasy-group: ecommerce.customers + /ecommerce/fulfillments: get: - operationId: listAtsScorecard - summary: List ScoreCards + operationId: listEcommerceFulfillments + summary: List Fulfillments parameters: - name: x-connection-token required: true @@ -6272,7 +6302,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -6293,10 +6322,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedAtsScorecardOutput' + $ref: >- + #/components/schemas/UnifiedEcommerceFulfillmentOutput tags: &ref_59 - - ats/scorecards - x-speakeasy-group: ats.scorecards + - ecommerce/fulfillments + x-speakeasy-group: ecommerce.fulfillments x-speakeasy-pagination: type: cursor inputs: @@ -6305,11 +6335,11 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /ats/scorecards/{id}: + /ecommerce/fulfillments/{id}: get: - operationId: retrieveAtsScorecard - summary: Retrieve Score Cards - description: Retrieve Score Cards from any connected Ats software + operationId: retrieveEcommerceFulfillment + summary: Retrieve Fulfillments + description: Retrieve fulfillments from any connected Ats software parameters: - name: x-connection-token required: true @@ -6320,15 +6350,13 @@ paths: - name: id required: true in: path - description: id of the scorecard you want to retrieve. - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: id of the fulfillment you want to retrieve. schema: type: string - name: remote_data required: false in: query description: Set to true to include data from the original Ats software. - example: false schema: type: boolean responses: @@ -6337,13 +6365,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsScorecardOutput' + $ref: '#/components/schemas/UnifiedEcommerceFulfillmentOutput' tags: *ref_59 - x-speakeasy-group: ats.scorecards - /ats/tags: + x-speakeasy-group: ecommerce.fulfillments + /ticketing/attachments: get: - operationId: listAtsTags - summary: List Tags + operationId: listTicketingAttachments + summary: List Attachments parameters: - name: x-connection-token required: true @@ -6364,7 +6392,6 @@ paths: example: 10 description: Set to get the number of records. schema: - default: 50 type: number - name: cursor required: false @@ -6385,10 +6412,11 @@ paths: data: type: array items: - $ref: '#/components/schemas/UnifiedAtsTagOutput' + $ref: >- + #/components/schemas/UnifiedTicketingAttachmentOutput tags: &ref_60 - - ats/tags - x-speakeasy-group: ats.tags + - ticketing/attachments + x-speakeasy-group: ticketing.attachments x-speakeasy-pagination: type: cursor inputs: @@ -6397,11 +6425,10 @@ paths: type: cursor outputs: nextCursor: $.next_cursor - /ats/tags/{id}: - get: - operationId: retrieveAtsTag - summary: Retrieve Tags - description: Retrieve Tags from any connected Ats software + post: + operationId: createTicketingAttachment + summary: Create Attachments + description: Create Attachments in any supported Ticketing software parameters: - name: x-connection-token required: true @@ -6409,91 +6436,32 @@ paths: description: The connection token schema: type: string - - name: id - required: true - in: path - description: id of the tag you want to retrieve. - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - schema: - type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Ats software. - example: false + description: Set to true to include data from the original Ticketing software. schema: type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedTicketingAttachmentInput' responses: - '200': + '201': description: '' content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsTagOutput' + $ref: '#/components/schemas/UnifiedTicketingAttachmentOutput' tags: *ref_60 - x-speakeasy-group: ats.tags - /ats/users: - get: - operationId: listAtsUsers - summary: List Users - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedAtsUserOutput' - tags: &ref_61 - - ats/users - x-speakeasy-group: ats.users - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /ats/users/{id}: + x-speakeasy-group: ticketing.attachments + /ticketing/attachments/{id}: get: - operationId: retrieveAtsUser - summary: Retrieve Users - description: Retrieve Users from any connected Ats software + operationId: retrieveTicketingAttachment + summary: Retrieve Attachments + description: Retrieve Attachments from any connected Ticketing software parameters: - name: x-connection-token required: true @@ -6504,14 +6472,14 @@ paths: - name: id required: true in: path - description: id of the user you want to retrieve. + description: id of the attachment you want to retrive. example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f schema: type: string - name: remote_data required: false in: query - description: Set to true to include data from the original Ats software. + description: Set to true to include data from the original Ticketing software. example: false schema: type: boolean @@ -6521,7897 +6489,1725 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnifiedAtsUserOutput' - tags: *ref_61 - x-speakeasy-group: ats.users - /ats/eeocs: - get: - operationId: listAtsEeocs - summary: List Eeocss - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedAtsEeocsOutput' - tags: &ref_62 - - ats/eeocs - x-speakeasy-group: ats.eeocs - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /ats/eeocs/{id}: - get: - operationId: retrieveAtsEeocs - summary: Retrieve Eeocs - description: Retrieve a eeocs from any connected Ats software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - description: id of the eeocs you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original Ats software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAtsEeocsOutput' - tags: *ref_62 - x-speakeasy-group: ats.eeocs - /accounting/accounts: - get: - operationId: listAccountingAccounts - summary: List Accounts - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedAccountingAccountOutput' - tags: &ref_63 - - accounting/accounts - x-speakeasy-group: accounting.accounts - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - post: - operationId: createAccountingAccount - summary: Create Accounts - description: Create accounts in any supported Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingAccountInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingAccountOutput' - tags: *ref_63 - x-speakeasy-group: accounting.accounts - /accounting/accounts/{id}: - get: - operationId: retrieveAccountingAccount - summary: Retrieve Accounts - description: Retrieve Accounts from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the account you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingAccountOutput' - tags: *ref_63 - x-speakeasy-group: accounting.accounts - /accounting/addresses: - get: - operationId: listAccountingAddress - summary: List Addresss - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedAccountingAddressOutput' - tags: &ref_64 - - accounting/addresses - x-speakeasy-group: accounting.addresses - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /accounting/addresses/{id}: - get: - operationId: retrieveAccountingAddress - summary: Retrieve Addresses - description: Retrieve Addresses from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the address you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingAddressOutput' - tags: *ref_64 - x-speakeasy-group: accounting.addresses - /accounting/attachments: - get: - operationId: listAccountingAttachments - summary: List Attachments - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedAccountingAttachmentOutput - tags: &ref_65 - - accounting/attachments - x-speakeasy-group: accounting.attachments - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - post: - operationId: createAccountingAttachment - summary: Create Attachments - description: Create attachments in any supported Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingAttachmentInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingAttachmentOutput' - tags: *ref_65 - x-speakeasy-group: accounting.attachments - /accounting/attachments/{id}: - get: - operationId: retrieveAccountingAttachment - summary: Retrieve Attachments - description: Retrieve attachments from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the attachment you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingAttachmentOutput' - tags: *ref_65 - x-speakeasy-group: accounting.attachments - /accounting/balancesheets: - get: - operationId: listAccountingBalanceSheets - summary: List BalanceSheets - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedAccountingBalancesheetOutput - tags: &ref_66 - - accounting/balancesheets - x-speakeasy-group: accounting.balancesheets - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /accounting/balancesheets/{id}: - get: - operationId: retrieveAccountingBalanceSheet - summary: Retrieve BalanceSheets - description: Retrieve BalanceSheets from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the balancesheet you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingBalancesheetOutput' - tags: *ref_66 - x-speakeasy-group: accounting.balancesheets - /accounting/cashflowstatements: - get: - operationId: listAccountingCashflowStatement - summary: List CashflowStatements - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedAccountingCashflowstatementOutput - tags: &ref_67 - - accounting/cashflowstatements - x-speakeasy-group: accounting.cashflowstatements - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /accounting/cashflowstatements/{id}: - get: - operationId: retrieveAccountingCashflowStatement - summary: Retrieve Cashflow Statements - description: Retrieve Cashflow Statements from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the cashflowstatement you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingCashflowstatementOutput' - tags: *ref_67 - x-speakeasy-group: accounting.cashflowstatements - /accounting/companyinfos: - get: - operationId: listAccountingCompanyInfos - summary: List CompanyInfos - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedAccountingCompanyinfoOutput - tags: &ref_68 - - accounting/companyinfos - x-speakeasy-group: accounting.companyinfos - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /accounting/companyinfos/{id}: - get: - operationId: retrieveAccountingCompanyInfo - summary: Retrieve Company Infos - description: Retrieve Company Infos from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the companyinfo you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingCompanyinfoOutput' - tags: *ref_68 - x-speakeasy-group: accounting.companyinfos - /accounting/contacts: - get: - operationId: listAccountingContacts - summary: List Contacts - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedAccountingContactOutput' - tags: &ref_69 - - accounting/contacts - x-speakeasy-group: accounting.contacts - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - post: - operationId: createAccountingContact - summary: Create Contacts - description: Create contacts in any supported Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingContactInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingContactOutput' - tags: *ref_69 - x-speakeasy-group: accounting.contacts - /accounting/contacts/{id}: - get: - operationId: retrieveAccountingContact - summary: Retrieve Contacts - description: Retrieve Contacts from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the contact you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingContactOutput' - tags: *ref_69 - x-speakeasy-group: accounting.contacts - /accounting/creditnotes: - get: - operationId: listAccountingCreditNote - summary: List CreditNotes - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedAccountingCreditnoteOutput - tags: &ref_70 - - accounting/creditnotes - x-speakeasy-group: accounting.creditnotes - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /accounting/creditnotes/{id}: - get: - operationId: retrieveAccountingCreditNote - summary: Retrieve Credit Notes - description: Retrieve Credit Notes from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the creditnote you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingCreditnoteOutput' - tags: *ref_70 - x-speakeasy-group: accounting.creditnotes - /accounting/expenses: - get: - operationId: listAccountingExpense - summary: List Expenses - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedAccountingExpenseOutput' - tags: &ref_71 - - accounting/expenses - x-speakeasy-group: accounting.expenses - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - post: - operationId: createAccountingExpense - summary: Create Expenses - description: Create Expenses in any supported Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingExpenseInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingExpenseOutput' - tags: *ref_71 - x-speakeasy-group: accounting.expenses - /accounting/expenses/{id}: - get: - operationId: retrieveAccountingExpense - summary: Retrieve Expenses - description: Retrieve Expenses from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the expense you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingExpenseOutput' - tags: *ref_71 - x-speakeasy-group: accounting.expenses - /accounting/incomestatements: - get: - operationId: listAccountingIncomeStatement - summary: List IncomeStatements - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedAccountingIncomestatementOutput - tags: &ref_72 - - accounting/incomestatements - x-speakeasy-group: accounting.incomestatements - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /accounting/incomestatements/{id}: - get: - operationId: retrieveAccountingIncomeStatement - summary: Retrieve Income Statements - description: Retrieve Income Statements from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the incomestatement you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingIncomestatementOutput' - tags: *ref_72 - x-speakeasy-group: accounting.incomestatements - /accounting/invoices: - get: - operationId: listAccountingInvoice - summary: List Invoices - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedAccountingInvoiceOutput' - tags: &ref_73 - - accounting/invoices - x-speakeasy-group: accounting.invoices - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - post: - operationId: createAccountingInvoice - summary: Create Invoices - description: Create invoices in any supported Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingInvoiceInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingInvoiceOutput' - tags: *ref_73 - x-speakeasy-group: accounting.invoices - /accounting/invoices/{id}: - get: - operationId: retrieveAccountingInvoice - summary: Retrieve Invoices - description: Retrieve Invoices from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the invoice you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingInvoiceOutput' - tags: *ref_73 - x-speakeasy-group: accounting.invoices - /accounting/items: - get: - operationId: listAccountingItem - summary: List Items - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedAccountingItemOutput' - tags: &ref_74 - - accounting/items - x-speakeasy-group: accounting.items - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /accounting/items/{id}: - get: - operationId: retrieveAccountingItem - summary: Retrieve Items - description: Retrieve Items from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the item you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingItemOutput' - tags: *ref_74 - x-speakeasy-group: accounting.items - /accounting/journalentries: - get: - operationId: listAccountingJournalEntry - summary: List JournalEntrys - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedAccountingJournalentryOutput - tags: &ref_75 - - accounting/journalentries - x-speakeasy-group: accounting.journalentries - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - post: - operationId: createAccountingJournalEntry - summary: Create Journal Entries - description: Create Journal Entries in any supported Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingJournalentryInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingJournalentryOutput' - tags: *ref_75 - x-speakeasy-group: accounting.journalentries - /accounting/journalentries/{id}: - get: - operationId: retrieveAccountingJournalEntry - summary: Retrieve Journal Entries - description: Retrieve Journal Entries from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the journalentry you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingJournalentryOutput' - tags: *ref_75 - x-speakeasy-group: accounting.journalentries - /accounting/payments: - get: - operationId: listAccountingPayment - summary: List Payments - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedAccountingPaymentOutput' - tags: &ref_76 - - accounting/payments - x-speakeasy-group: accounting.payments - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - post: - operationId: createAccountingPayment - summary: Create Payments - description: Create Payments in any supported Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingPaymentInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingPaymentOutput' - tags: *ref_76 - x-speakeasy-group: accounting.payments - /accounting/payments/{id}: - get: - operationId: retrieveAccountingPayment - summary: Retrieve Payments - description: Retrieve Payments from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the payment you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingPaymentOutput' - tags: *ref_76 - x-speakeasy-group: accounting.payments - /accounting/phonenumbers: - get: - operationId: listAccountingPhonenumber - summary: List PhoneNumbers - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedAccountingPhonenumberOutput - tags: &ref_77 - - accounting/phonenumbers - x-speakeasy-group: accounting.phonenumbers - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /accounting/phonenumbers/{id}: - get: - operationId: retrieveAccountingPhonenumber - summary: Retrieve Phone Numbers - description: Retrieve Phone Numbers from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the phonenumber you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingPhonenumberOutput' - tags: *ref_77 - x-speakeasy-group: accounting.phonenumbers - /accounting/purchaseorders: - get: - operationId: listAccountingPurchaseOrder - summary: List PurchaseOrders - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedAccountingPurchaseorderOutput - tags: &ref_78 - - accounting/purchaseorders - x-speakeasy-group: accounting.purchaseorders - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - post: - operationId: createAccountingPurchaseOrder - summary: Create Purchase Orders - description: Create Purchase Orders in any supported Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingPurchaseorderInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingPurchaseorderOutput' - tags: *ref_78 - x-speakeasy-group: accounting.purchaseorders - /accounting/purchaseorders/{id}: - get: - operationId: retrieveAccountingPurchaseOrder - summary: Retrieve Purchase Orders - description: Retrieve Purchase Orders from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the purchaseorder you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingPurchaseorderOutput' - tags: *ref_78 - x-speakeasy-group: accounting.purchaseorders - /accounting/taxrates: - get: - operationId: listAccountingTaxRate - summary: List TaxRates - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedAccountingTaxrateOutput' - tags: &ref_79 - - accounting/taxrates - x-speakeasy-group: accounting.taxrates - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /accounting/taxrates/{id}: - get: - operationId: retrieveAccountingTaxRate - summary: Retrieve Tax Rates - description: Retrieve Tax Rates from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the taxrate you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingTaxrateOutput' - tags: *ref_79 - x-speakeasy-group: accounting.taxrates - /accounting/trackingcategories: - get: - operationId: listAccountingTrackingCategorys - summary: List TrackingCategorys - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedAccountingTrackingcategoryOutput - tags: &ref_80 - - accounting/trackingcategories - x-speakeasy-group: accounting.trackingcategories - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /accounting/trackingcategories/{id}: - get: - operationId: retrieveAccountingTrackingCategory - summary: Retrieve Tracking Categories - description: Retrieve Tracking Categories from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the trackingcategory you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingTrackingcategoryOutput' - tags: *ref_80 - x-speakeasy-group: accounting.trackingcategories - /accounting/transactions: - get: - operationId: listAccountingTransaction - summary: List Transactions - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedAccountingTransactionOutput - tags: &ref_81 - - accounting/transactions - x-speakeasy-group: accounting.transactions - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /accounting/transactions/{id}: - get: - operationId: retrieveAccountingTransaction - summary: Retrieve Transactions - description: Retrieve Transactions from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the transaction you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingTransactionOutput' - tags: *ref_81 - x-speakeasy-group: accounting.transactions - /accounting/vendorcredits: - get: - operationId: listAccountingVendorCredit - summary: List VendorCredits - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedAccountingVendorcreditOutput - tags: &ref_82 - - accounting/vendorcredits - x-speakeasy-group: accounting.vendorcredits - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /accounting/vendorcredits/{id}: - get: - operationId: retrieveAccountingVendorCredit - summary: Retrieve Vendor Credits - description: Retrieve Vendor Credits from any connected Accounting software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: id of the vendorcredit you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedAccountingVendorcreditOutput' - tags: *ref_82 - x-speakeasy-group: accounting.vendorcredits - /filestorage/folders: - get: - operationId: listFilestorageFolder - summary: List Folders - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedFilestorageFolderOutput' - tags: &ref_83 - - filestorage/folders - x-speakeasy-group: filestorage.folders - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - post: - operationId: createFilestorageFolder - summary: Create Folders - description: Create Folders in any supported Filestorage software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedFilestorageFolderInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedFilestorageFolderOutput' - tags: *ref_83 - x-speakeasy-group: filestorage.folders - /filestorage/folders/{id}: - get: - operationId: retrieveFilestorageFolder - summary: Retrieve Folders - description: Retrieve Folders from any connected Filestorage software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - description: id of the folder you want to retrieve. - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original File Storage software. - example: false - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedFilestorageFolderOutput' - tags: *ref_83 - x-speakeasy-group: filestorage.folders - /filestorage/groups: - get: - operationId: listFilestorageGroup - summary: List Groups - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedFilestorageGroupOutput' - tags: &ref_84 - - filestorage/groups - x-speakeasy-group: filestorage.groups - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /filestorage/groups/{id}: - get: - operationId: retrieveFilestorageGroup - summary: Retrieve Groups - description: Retrieve Groups from any connected Filestorage software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - description: id of the permission you want to retrieve. - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original File Storage software. - example: false - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedFilestorageGroupOutput' - tags: *ref_84 - x-speakeasy-group: filestorage.groups - /filestorage/users: - get: - operationId: listFilestorageUsers - summary: List Users - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedFilestorageUserOutput' - tags: &ref_85 - - filestorage/users - x-speakeasy-group: filestorage.users - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /filestorage/users/{id}: - get: - operationId: retrieveFilestorageUser - summary: Retrieve Users - description: Retrieve Users from any connected Filestorage software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - description: id of the permission you want to retrieve. - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original File Storage software. - example: false - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedFilestorageUserOutput' - tags: *ref_85 - x-speakeasy-group: filestorage.users - /ecommerce/products: - get: - operationId: listEcommerceProducts - summary: List Products - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedEcommerceProductOutput' - tags: &ref_86 - - ecommerce/products - x-speakeasy-group: ecommerce.products - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - post: - operationId: createEcommerceProduct - summary: Create Products - description: Create Products in any supported Ecommerce software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedEcommerceProductInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedEcommerceProductOutput' - tags: *ref_86 - x-speakeasy-group: ecommerce.products - /ecommerce/products/{id}: - get: - operationId: retrieveEcommerceProduct - summary: Retrieve Products - description: Retrieve products from any connected Ats software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - description: id of the product you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original Ats software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedEcommerceProductOutput' - tags: *ref_86 - x-speakeasy-group: ecommerce.products - /ecommerce/orders: - get: - operationId: listEcommerceOrders - summary: List Orders - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedEcommerceOrderOutput' - tags: &ref_87 - - ecommerce/orders - x-speakeasy-group: ecommerce.orders - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - post: - operationId: createEcommerceOrder - summary: Create Orders - description: Create Orders in any supported Ecommerce software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: false - description: Set to true to include data from the original Accounting software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedEcommerceOrderInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedEcommerceOrderOutput' - tags: *ref_87 - x-speakeasy-group: ecommerce.orders - /ecommerce/orders/{id}: - get: - operationId: retrieveEcommerceOrder - summary: Retrieve Orders - description: Retrieve orders from any connected Ats software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - description: id of the order you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original Ats software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedEcommerceOrderOutput' - tags: *ref_87 - x-speakeasy-group: ecommerce.orders - /ecommerce/customers: - get: - operationId: listEcommerceCustomers - summary: List Customers - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: '#/components/schemas/UnifiedEcommerceCustomerOutput' - tags: &ref_88 - - ecommerce/customers - x-speakeasy-group: ecommerce.customers - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /ecommerce/customers/{id}: - get: - operationId: retrieveEcommerceCustomer - summary: Retrieve Customers - description: Retrieve customers from any connected Ats software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - description: id of the customer you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original Ats software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedEcommerceCustomerOutput' - tags: *ref_88 - x-speakeasy-group: ecommerce.customers - /ecommerce/fulfillments: - get: - operationId: listEcommerceFulfillments - summary: List Fulfillments - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedEcommerceFulfillmentOutput - tags: &ref_89 - - ecommerce/fulfillments - x-speakeasy-group: ecommerce.fulfillments - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - /ecommerce/fulfillments/{id}: - get: - operationId: retrieveEcommerceFulfillment - summary: Retrieve Fulfillments - description: Retrieve fulfillments from any connected Ats software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - description: id of the fulfillment you want to retrieve. - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original Ats software. - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedEcommerceFulfillmentOutput' - tags: *ref_89 - x-speakeasy-group: ecommerce.fulfillments - /ticketing/attachments: - get: - operationId: listTicketingAttachments - summary: List Attachments - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - example: true - description: Set to true to include data from the original software. - schema: - type: boolean - - name: limit - required: false - in: query - example: 10 - description: Set to get the number of records. - schema: - default: 50 - type: number - - name: cursor - required: false - in: query - example: 1b8b05bb-5273-4012-b520-8657b0b90874 - description: Set to get the number of records after this cursor. - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/PaginatedDto' - - properties: - data: - type: array - items: - $ref: >- - #/components/schemas/UnifiedTicketingAttachmentOutput - tags: &ref_90 - - ticketing/attachments - x-speakeasy-group: ticketing.attachments - x-speakeasy-pagination: - type: cursor - inputs: - - name: cursor - in: parameters - type: cursor - outputs: - nextCursor: $.next_cursor - post: - operationId: createTicketingAttachment - summary: Create Attachments - description: Create Attachments in any supported Ticketing software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original Ticketing software. - schema: - type: boolean - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedTicketingAttachmentInput' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedTicketingAttachmentOutput' - tags: *ref_90 - x-speakeasy-group: ticketing.attachments - /ticketing/attachments/{id}: - get: - operationId: retrieveTicketingAttachment - summary: Retrieve Attachments - description: Retrieve Attachments from any connected Ticketing software - parameters: - - name: x-connection-token - required: true - in: header - description: The connection token - schema: - type: string - - name: id - required: true - in: path - description: id of the attachment you want to retrive. - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - schema: - type: string - - name: remote_data - required: false - in: query - description: Set to true to include data from the original Ticketing software. - example: false - schema: - type: boolean - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UnifiedTicketingAttachmentOutput' - tags: *ref_90 - x-speakeasy-group: ticketing.attachments -info: - title: Panora API - description: A unified API to ship integrations - version: '1.0' - contact: {} -tags: [] -servers: - - url: https://api.panora.dev - description: Production server - - url: https://api-sandbox.panora.dev - description: Sandbox server - - url: https://api-dev.panora.dev - description: Development server -components: - securitySchemes: - api_key: - type: apiKey - in: header - name: x-api-key - schemas: - RagQueryOutput: - type: object - properties: - chunk: - type: string - example: 'Date : 06/07/2023' - nullable: false - description: The chunk which matches the embed query - metadata: - type: object - example: - blobType: '' - text: ATTESTATION - additionalProperties: true - nullable: true - description: The metadata tied to the chunk - score: - type: number - example: 0.87 - nullable: true - description: The score - embedding: - example: - - -0.00442447886 - - -0.00116857514 - - 0.00869117491 - - -0.0361584462 - - -0.00220073434 - - 0.00946036354 - - -0.0101112155 - nullable: true - description: The embedding of the relevant chunk - type: array - items: - type: number - required: - - chunk - - metadata - - score - - embedding - QueryBody: - type: object - properties: - query: - type: string - example: When does Panora incorporated? - nullable: false - description: The query you want to received embeddings and chunks for - topK: - type: number - example: '3' - nullable: true - description: The number of most appropriate documents for your query. - required: - - query - PaginatedDto: - type: object - properties: - prev_cursor: - type: string - nullable: true - next_cursor: - type: string - nullable: true - data: - type: array - items: - type: object - required: - - prev_cursor - - next_cursor - - data - UnifiedFilestorageFileOutput: - type: object - properties: - name: - type: string - example: my_paris_photo.png - description: The name of the file - nullable: true - file_url: - type: string - example: https://example.com/my_paris_photo.png - description: The url of the file - nullable: true - mime_type: - type: string - example: application/pdf - description: The mime type of the file - nullable: true - size: - type: string - example: '1024' - description: The size of the file - nullable: true - folder_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the folder tied to the file - nullable: true - permission: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the permission tied to the file - nullable: true - shared_link: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the shared link tied to the file - nullable: true - field_mappings: - type: object - example: &ref_91 - fav_dish: broccoli - fav_color: red - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - nullable: true - additionalProperties: true - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the file - nullable: true - remote_id: - type: string - example: id_1 - description: The id of the file in the context of the 3rd Party - nullable: true - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - description: The remote data of the file in the context of the 3rd Party - nullable: true - additionalProperties: true - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The created date of the object - nullable: true - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The modified date of the object - nullable: true - required: - - name - - file_url - - mime_type - - size - - folder_id - - permission - - shared_link - UnifiedFilestorageFileInput: - type: object - properties: - name: - type: string - example: my_paris_photo.png - description: The name of the file - nullable: true - file_url: - type: string - example: https://example.com/my_paris_photo.png - description: The url of the file - nullable: true - mime_type: - type: string - example: application/pdf - description: The mime type of the file - nullable: true - size: - type: string - example: '1024' - description: The size of the file - nullable: true - folder_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the folder tied to the file - nullable: true - permission: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the permission tied to the file - nullable: true - shared_link: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the shared link tied to the file - nullable: true - field_mappings: - type: object - example: *ref_91 - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - nullable: true - additionalProperties: true - required: - - name - - file_url - - mime_type - - size - - folder_id - - permission - - shared_link - LoginDto: - type: object - properties: - id_user: - type: string - email: - type: string - password_hash: - type: string - required: - - email - - password_hash - Connection: - type: object - properties: - id_connection: - type: string - example: 123e4567-e89b-12d3-a456-426614174000 - description: Unique identifier for the connection - status: - type: string - example: active - description: Status of the connection - provider_slug: - type: string - example: hubspot - description: Slug for the provider - vertical: - type: string - example: crm - description: Vertical category of the connection - account_url: - type: string - example: https://example.com/account - description: URL of the account - token_type: - type: string - example: oauth2 - enum: - - oauth2 - - apikey - - basic - description: Strategy type - access_token: - type: string - example: access_token_example - description: Access token for the connection - refresh_token: - type: string - example: refresh_token_example - description: Refresh token for the connection - expiration_timestamp: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: Expiration timestamp of the access token - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: Timestamp when the connection was created - connection_token: - type: string - example: 123e4567-e89b-12d3-a456-426614174000 - description: UUID Token for the connection - id_project: - type: string - example: 123e4567-e89b-12d3-a456-426614174001 - description: Project ID associated with the connection - id_linked_user: - type: string - example: 123e4567-e89b-12d3-a456-426614174002 - description: Linked user ID associated with the connection - required: - - id_connection - - status - - provider_slug - - vertical - - account_url - - token_type - - access_token - - refresh_token - - expiration_timestamp - - created_at - - connection_token - - id_project - - id_linked_user - WebhookResponse: - type: object - properties: - id_webhook_endpoint: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The unique UUID of the webhook. - endpoint_description: - type: string - nullable: true - example: Webhook to receive connection events - description: The description of the webhook. - url: - type: string - example: https://acme.com/webhook_receiver - nullable: true - description: The endpoint url of the webhook. - secret: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The secret of the webhook. - active: - type: boolean - example: true - nullable: true - description: The status of the webhook. - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The created date of the webhook. - nullable: true - scope: - example: - - connection.created - nullable: true - description: The events that the webhook listen to. - type: array - items: - type: string - id_project: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The project id tied to the webhook. - last_update: - format: date-time - type: string - nullable: true - example: '2024-10-01T12:00:00Z' - description: The last update date of the webhook. - required: - - id_webhook_endpoint - - endpoint_description - - url - - secret - - active - - created_at - - scope - - id_project - - last_update - WebhookDto: - type: object - properties: - url: - type: string - example: https://acme.com/webhook_receiver - nullable: true - description: The endpoint url of the webhook. - description: - type: string - example: Webhook to receive connection events - nullable: true - description: The description of the webhook. - scope: - example: - - connection.created - nullable: true - description: The events that the webhook listen to. - type: array - items: - type: string - required: - - url - - scope - SignatureVerificationDto: - type: object - properties: - payload: - type: object - additionalProperties: true - nullable: true - description: The payload event of the webhook. - signature: - type: string - nullable: true - description: The signature of the webhook. - secret: - type: string - nullable: true - description: The secret of the webhook. - required: - - payload - - signature - - secret - UnifiedTicketingCommentInput: - type: object - properties: - body: - type: string - nullable: true - example: Assigned to Eric ! - description: The body of the comment - html_body: - type: string - nullable: true - example:

Assigned to Eric !

- description: The html body of the comment - is_private: - type: boolean - nullable: true - example: false - description: The public status of the comment - creator_type: - type: string - nullable: true - example: USER - description: >- - The creator type of the comment. Authorized values are either USER - or CONTACT - ticket_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the ticket the comment is tied to - contact_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: >- - The UUID of the contact which the comment belongs to (if no user_id - specified) - user_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: >- - The UUID of the user which the comment belongs to (if no contact_id - specified) - attachments: - type: array - items: &ref_114 - oneOf: - - type: string - - $ref: '#/components/schemas/UnifiedTicketingAttachmentOutput' - nullable: true - example: &ref_115 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The attachements UUIDs tied to the comment - required: - - body - UnifiedTicketingTicketOutput: - type: object - properties: - name: - type: string - example: Customer Service Inquiry - nullable: true - description: The name of the ticket - status: - type: string - example: OPEN - nullable: true - description: The status of the ticket. Authorized values are OPEN or CLOSED. - description: - type: string - example: Help customer - nullable: true - description: The description of the ticket - due_date: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The date the ticket is due - type: - type: string - example: BUG - nullable: true - description: >- - The type of the ticket. Authorized values are PROBLEM, QUESTION, or - TASK - parent_ticket: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the parent ticket - collections: - type: array - items: &ref_92 - oneOf: - - type: string - - $ref: '#/components/schemas/UnifiedTicketingCollectionOutput' - example: &ref_93 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The collection UUIDs the ticket belongs to - tags: - type: array - items: &ref_94 - oneOf: - - type: string - - $ref: '#/components/schemas/UnifiedTicketingTagOutput' - example: &ref_95 - - my_tag - - urgent_tag - nullable: true - description: The tags names of the ticket - completed_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The date the ticket has been completed - priority: - type: string - example: HIGH - nullable: true - description: >- - The priority of the ticket. Authorized values are HIGH, MEDIUM or - LOW. - assigned_to: - example: &ref_96 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The users UUIDs the ticket is assigned to - type: array - items: - type: string - comment: - example: &ref_97 - content: Assigned the issue ! - nullable: true - description: The comment of the ticket - allOf: - - $ref: '#/components/schemas/UnifiedTicketingCommentInput' - account_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the account which the ticket belongs to - contact_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the contact which the ticket belongs to - attachments: - type: array - items: &ref_98 - oneOf: - - type: string - - $ref: '#/components/schemas/UnifiedTicketingAttachmentInput' - example: &ref_99 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The attachements UUIDs tied to the ticket - nullable: true - field_mappings: - type: object - example: &ref_100 - fav_dish: broccoli - fav_color: red - nullable: true - description: >- - The custom field mappings of the ticket between the remote 3rd party - & Panora - additionalProperties: true - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the ticket - remote_id: - type: string - example: id_1 - nullable: true - description: The id of the ticket in the context of the 3rd Party - remote_data: - type: object - example: - key1: value1 - key2: 42 - key3: true - nullable: true - additionalProperties: true - description: The remote data of the ticket in the context of the 3rd Party - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the object - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The modified date of the object - required: - - name - - description - UnifiedTicketingTicketInput: - type: object - properties: - name: - type: string - example: Customer Service Inquiry - nullable: true - description: The name of the ticket - status: - type: string - example: OPEN - nullable: true - description: The status of the ticket. Authorized values are OPEN or CLOSED. - description: - type: string - example: Help customer - nullable: true - description: The description of the ticket - due_date: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The date the ticket is due - type: - type: string - example: BUG - nullable: true - description: >- - The type of the ticket. Authorized values are PROBLEM, QUESTION, or - TASK - parent_ticket: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the parent ticket - collections: - type: array - items: *ref_92 - example: *ref_93 - nullable: true - description: The collection UUIDs the ticket belongs to - tags: - type: array - items: *ref_94 - example: *ref_95 - nullable: true - description: The tags names of the ticket - completed_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The date the ticket has been completed - priority: - type: string - example: HIGH - nullable: true - description: >- - The priority of the ticket. Authorized values are HIGH, MEDIUM or - LOW. - assigned_to: - example: *ref_96 - nullable: true - description: The users UUIDs the ticket is assigned to - type: array - items: - type: string - comment: - example: *ref_97 - nullable: true - description: The comment of the ticket - allOf: - - $ref: '#/components/schemas/UnifiedTicketingCommentInput' - account_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the account which the ticket belongs to - contact_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the contact which the ticket belongs to - attachments: - type: array - items: *ref_98 - example: *ref_99 - description: The attachements UUIDs tied to the ticket - nullable: true - field_mappings: - type: object - example: *ref_100 - nullable: true - description: >- - The custom field mappings of the ticket between the remote 3rd party - & Panora - additionalProperties: true - required: - - name - - description - UnifiedTicketingUserOutput: - type: object - properties: - name: - type: string - nullable: true - description: The name of the user - example: John Doe - email_address: - type: string - nullable: true - description: The email address of the user - example: john.doe@example.com - teams: - nullable: true - description: The teams whose the user is part of - example: - - team1 - - team2 - type: array - items: - type: string - account_id: - type: string - nullable: true - description: The account or organization the user is part of - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true - description: >- - The custom field mappings of the user between the remote 3rd party & - Panora - additionalProperties: true - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the user - remote_id: - type: string - example: id_1 - nullable: true - description: The id of the user in the context of the 3rd Party - remote_data: - type: object - example: - key1: value1 - key2: 42 - key3: true - nullable: true - additionalProperties: true - description: The remote data of the user in the context of the 3rd Party - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the object - modified_at: - format: date-time - type: string - example: '2023-10-01T12:00:00Z' - nullable: true - description: The modified date of the object - required: - - name - - email_address - UnifiedTicketingAccountOutput: - type: object - properties: - name: - type: string - example: My Personal Account - nullable: true - description: The name of the account - domains: - example: - - acme.com - - acme-test.com - nullable: true - description: The domains of the account - type: array - items: - type: string - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true - description: >- - The custom field mappings of the account between the remote 3rd - party & Panora - additionalProperties: true - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the account - remote_id: - type: string - example: id_1 - description: The remote ID of the account in the context of the 3rd Party - nullable: true - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true - additionalProperties: true - description: The remote data of the account in the context of the 3rd Party - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The created date of the account - nullable: true - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The modified date of the account - nullable: true - required: - - name - UnifiedTicketingContactOutput: - type: object - properties: - name: - type: string - example: Joe - nullable: true - description: The name of the contact - email_address: - type: string - example: joedoe@acme.org - nullable: true - description: The email address of the contact - phone_number: - type: string - example: +33 6 50 11 11 10 - nullable: true - description: The phone number of the contact - details: - type: string - example: Contact Details - nullable: true - description: The details of the contact - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true - description: >- - The custom field mappings of the contact between the remote 3rd - party & Panora - additionalProperties: true - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the contact - remote_id: - type: string - example: id_1 - description: The remote ID of the contact in the context of the 3rd Party - nullable: true - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true - additionalProperties: true - description: The remote data of the contact in the context of the 3rd Party - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the object - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The modified date of the object - required: - - name - - email_address - ResyncStatusDto: - type: object - properties: - timestamp: - format: date-time - type: string - example: '' - nullable: true - vertical: - type: string - example: ticketing - enum: - - ticketing - - ats - - accounting - - hris - - crm - - filestorage - - ecommerce - - marketingautomation - nullable: true - provider: - type: string - example: gitlab - nullable: true - status: - type: string - example: success - enum: - - success - - fail - nullable: true - required: - - timestamp - - vertical - - provider - - status - UpdatePullFrequencyDto: - type: object - properties: - crm: - type: number - example: 1800 - description: Frequency in seconds - ats: - type: number - example: 3600 - description: Frequency in seconds - hris: - type: number - example: 7200 - description: Frequency in seconds - accounting: - type: number - example: 14400 - description: Frequency in seconds - filestorage: - type: number - example: 28800 - description: Frequency in seconds - ecommerce: - type: number - example: 43200 - description: Frequency in seconds - ticketing: - type: number - example: 86400 - description: Frequency in seconds - required: - - crm - - ats - - hris - - accounting - - filestorage - - ecommerce - - ticketing - Email: - type: object - properties: - email_address: - type: string - nullable: true - description: The email address - email_address_type: - type: string - nullable: true - description: >- - The email address type. Authorized values are either PERSONAL or - WORK. - owner_type: - type: string - enum: - - COMPANY - - CONTACT - nullable: true - description: The owner type of an email - required: - - email_address - - email_address_type - Address: - type: object - properties: - street_1: - type: string - nullable: true - example: 5th Avenue - description: The street - street_2: - type: string - nullable: true - example: Street 2 - description: 'More information about the street ' - city: - type: string - nullable: true - example: New York - description: The city - state: - type: string - example: New York - nullable: true - description: The state - postal_code: - type: string - example: '10001' - nullable: true - description: The postal code - country: - type: string - example: United States of America - nullable: true - description: The country - address_type: - type: string - nullable: true - example: PERSONAL - description: The address type. Authorized values are either PERSONAL or WORK. - owner_type: - type: string - nullable: true - description: The owner type of the address - required: - - street_1 - - street_2 - - city - - state - - postal_code - - country - - address_type - - owner_type - Phone: - type: object - properties: - phone_number: - type: string - nullable: true - description: >- - The phone number starting with a plus (+) followed by the country - code (e.g +336676778890 for France) - phone_type: - type: string - nullable: true - description: The phone type. Authorized values are either MOBILE or WORK - owner_type: - type: string - nullable: true - description: The owner type of a phone number - required: - - phone_number - - phone_type - UnifiedCrmCompanyOutput: - type: object - properties: - name: - type: string - example: Acme - description: The name of the company - nullable: true - industry: - type: string - example: ACCOUNTING - description: >- - The industry of the company. Authorized values can be found in the - Industry enum. - nullable: true - number_of_employees: - type: number - example: 10 - description: The number of employees of the company - nullable: true - user_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the user who owns the company - nullable: true - email_addresses: - description: The email addresses of the company - example: &ref_101 - - email_address: acme@gmail.com - email_address_type: WORK - nullable: true - type: array - items: - $ref: '#/components/schemas/Email' - addresses: - description: The addresses of the company - example: &ref_102 - - street_1: 5th Avenue - city: New York - state: NY - country: USA - address_type: WORK - nullable: true - type: array - items: - $ref: '#/components/schemas/Address' - phone_numbers: - description: The phone numbers of the company - example: &ref_103 - - phone_number: '+33660606067' - phone_type: WORK - nullable: true - type: array - items: - $ref: '#/components/schemas/Phone' - field_mappings: - type: object - example: &ref_104 - fav_dish: broccoli - fav_color: red - description: >- - The custom field mappings of the company between the remote 3rd - party & Panora - nullable: true - additionalProperties: true - id: - type: string - description: The UUID of the company - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - remote_id: - type: string - example: id_1 - description: The id of the company in the context of the Crm 3rd Party - nullable: true - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - description: The remote data of the company in the context of the Crm 3rd Party - nullable: true - additionalProperties: true - created_at: - type: object - example: '2024-10-01T12:00:00Z' - description: The created date of the object - nullable: true - modified_at: - type: object - example: '2024-10-01T12:00:00Z' - description: The modified date of the object - nullable: true - required: - - name - UnifiedCrmCompanyInput: - type: object - properties: - name: - type: string - example: Acme - description: The name of the company - nullable: true - industry: - type: string - example: ACCOUNTING - description: >- - The industry of the company. Authorized values can be found in the - Industry enum. - nullable: true - number_of_employees: - type: number - example: 10 - description: The number of employees of the company - nullable: true - user_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the user who owns the company - nullable: true - email_addresses: - description: The email addresses of the company - example: *ref_101 - nullable: true - type: array - items: - $ref: '#/components/schemas/Email' - addresses: - description: The addresses of the company - example: *ref_102 - nullable: true - type: array - items: - $ref: '#/components/schemas/Address' - phone_numbers: - description: The phone numbers of the company - example: *ref_103 - nullable: true - type: array - items: - $ref: '#/components/schemas/Phone' - field_mappings: - type: object - example: *ref_104 - description: >- - The custom field mappings of the company between the remote 3rd - party & Panora - nullable: true - additionalProperties: true - required: - - name - UnifiedCrmContactOutput: - type: object - properties: - first_name: - type: string - description: The first name of the contact - example: John - nullable: true - last_name: - type: string - description: The last name of the contact - example: Doe - nullable: true - email_addresses: - nullable: true - description: The email addresses of the contact - example: &ref_105 - - email: john.doe@example.com - type: WORK - type: array - items: - $ref: '#/components/schemas/Email' - phone_numbers: - nullable: true - description: The phone numbers of the contact - example: &ref_106 - - phone: '1234567890' - type: WORK - type: array - items: - $ref: '#/components/schemas/Phone' - addresses: - nullable: true - description: The addresses of the contact - example: &ref_107 - - street: 123 Main St - city: Anytown - state: CA - zip: '12345' - country: USA - type: WORK - type: array - items: - $ref: '#/components/schemas/Address' - user_id: - type: string - nullable: true - description: The UUID of the user who owns the contact - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - field_mappings: - type: object - example: &ref_108 - fav_dish: broccoli - fav_color: red - nullable: true - description: >- - The custom field mappings of the contact between the remote 3rd - party & Panora - additionalProperties: true - id: - type: string - description: The UUID of the contact - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - remote_id: - type: string - example: id_1 - nullable: true - description: The id of the contact in the context of the Crm 3rd Party - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true - additionalProperties: true - description: The remote data of the contact in the context of the Crm 3rd Party - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the object - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The modified date of the object - required: - - first_name - - last_name - UnifiedCrmContactInput: - type: object - properties: - first_name: - type: string - description: The first name of the contact - example: John - nullable: true - last_name: - type: string - description: The last name of the contact - example: Doe - nullable: true - email_addresses: - nullable: true - description: The email addresses of the contact - example: *ref_105 - type: array - items: - $ref: '#/components/schemas/Email' - phone_numbers: - nullable: true - description: The phone numbers of the contact - example: *ref_106 - type: array - items: - $ref: '#/components/schemas/Phone' - addresses: - nullable: true - description: The addresses of the contact - example: *ref_107 - type: array - items: - $ref: '#/components/schemas/Address' - user_id: - type: string - nullable: true - description: The UUID of the user who owns the contact - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - field_mappings: - type: object - example: *ref_108 - nullable: true - description: >- - The custom field mappings of the contact between the remote 3rd - party & Panora - additionalProperties: true - required: - - first_name - - last_name - UnifiedCrmDealOutput: - type: object - properties: - name: - type: string - example: Huge Contract with Acme - description: The name of the deal - nullable: true - description: - type: string - example: Contract with Sales Operations Team - description: The description of the deal - nullable: true - amount: - type: number - example: 1000 - description: The amount of the deal - nullable: true - user_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the user who is on the deal - stage_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the stage of the deal - company_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the company tied to the deal - field_mappings: - type: object - nullable: true - example: &ref_109 - fav_dish: broccoli - fav_color: red - description: >- - The custom field mappings of the company between the remote 3rd - party & Panora - additionalProperties: true - id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the deal - remote_id: - type: string - nullable: true - example: id_1 - description: The id of the deal in the context of the Crm 3rd Party - remote_data: - type: object - nullable: true - example: - fav_dish: broccoli - fav_color: red - additionalProperties: true - description: The remote data of the deal in the context of the Crm 3rd Party - created_at: - format: date-time - type: string - nullable: true - example: '2024-10-01T12:00:00Z' - description: The created date of the object - modified_at: - format: date-time - type: string - nullable: true - example: '2024-10-01T12:00:00Z' - description: The modified date of the object - required: - - name - - description - - amount - UnifiedCrmDealInput: - type: object - properties: - name: - type: string - example: Huge Contract with Acme - description: The name of the deal - nullable: true - description: - type: string - example: Contract with Sales Operations Team - description: The description of the deal - nullable: true - amount: - type: number - example: 1000 - description: The amount of the deal - nullable: true - user_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the user who is on the deal - stage_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the stage of the deal - company_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the company tied to the deal - field_mappings: - type: object - nullable: true - example: *ref_109 - description: >- - The custom field mappings of the company between the remote 3rd - party & Panora - additionalProperties: true - required: - - name - - description - - amount - UnifiedCrmEngagementOutput: - type: object - properties: - content: - type: string - nullable: true - example: Meeting call with CTO - description: The content of the engagement - direction: - type: string - nullable: true - example: INBOUND - description: >- - The direction of the engagement. Authorized values are INBOUND or - OUTBOUND - subject: - type: string - example: Technical features planning - nullable: true - description: The subject of the engagement - start_at: - format: date-time - type: string - nullable: true - example: '2024-10-01T12:00:00Z' - description: The start time of the engagement - end_time: - format: date-time - type: string - nullable: true - example: '2024-10-01T22:00:00Z' - description: The end time of the engagement - type: - type: string - nullable: true - example: MEETING - description: >- - The type of the engagement. Authorized values are EMAIL, CALL or - MEETING - user_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the user tied to the engagement - company_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the company tied to the engagement - contacts: - nullable: true - example: &ref_110 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUIDs of contacts tied to the engagement object - type: array - items: - type: string - field_mappings: - type: object - nullable: true - example: &ref_111 - fav_dish: broccoli - fav_color: red - description: >- - The custom field mappings of the engagement between the remote 3rd - party & Panora - additionalProperties: true - id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the engagement - remote_id: - type: string - nullable: true - example: id_1 - description: The id of the engagement in the context of the Crm 3rd Party - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - additionalProperties: true - nullable: true - description: >- - The remote data of the engagement in the context of the Crm 3rd - Party - created_at: - format: date-time - type: string - nullable: true - example: '2024-10-01T12:00:00Z' - description: The created date of the object - modified_at: - format: date-time - type: string - nullable: true - example: '2024-10-01T12:00:00Z' - description: The modified date of the object - required: - - type - UnifiedCrmEngagementInput: - type: object - properties: - content: - type: string - nullable: true - example: Meeting call with CTO - description: The content of the engagement - direction: - type: string - nullable: true - example: INBOUND - description: >- - The direction of the engagement. Authorized values are INBOUND or - OUTBOUND - subject: - type: string - example: Technical features planning - nullable: true - description: The subject of the engagement - start_at: - format: date-time - type: string - nullable: true - example: '2024-10-01T12:00:00Z' - description: The start time of the engagement - end_time: - format: date-time - type: string - nullable: true - example: '2024-10-01T22:00:00Z' - description: The end time of the engagement - type: - type: string - nullable: true - example: MEETING - description: >- - The type of the engagement. Authorized values are EMAIL, CALL or - MEETING - user_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the user tied to the engagement - company_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the company tied to the engagement - contacts: - nullable: true - example: *ref_110 - description: The UUIDs of contacts tied to the engagement object - type: array - items: - type: string - field_mappings: - type: object - nullable: true - example: *ref_111 - description: >- - The custom field mappings of the engagement between the remote 3rd - party & Panora - additionalProperties: true - required: - - type - UnifiedCrmNoteOutput: - type: object - properties: - content: - type: string - example: My notes taken during the meeting - description: The content of the note - nullable: true - user_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the user tied to the note - nullable: true - company_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the company tied to the note - contact_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the contact tied to the note - nullable: true - deal_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the deal tied to the note - field_mappings: - type: object - example: &ref_112 - fav_dish: broccoli - fav_color: red - nullable: true - description: >- - The custom field mappings of the note between the remote 3rd party & - Panora - additionalProperties: true - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the note - remote_id: - type: string - example: id_1 - description: The ID of the note in the context of the Crm 3rd Party - nullable: true - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true - additionalProperties: true - description: The remote data of the note in the context of the Crm 3rd Party - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the object - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The modified date of the object - required: - - content - UnifiedCrmNoteInput: - type: object - properties: - content: - type: string - example: My notes taken during the meeting - description: The content of the note - nullable: true - user_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the user tied to the note - nullable: true - company_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the company tied to the note - contact_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the contact tied to the note - nullable: true - deal_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the deal tied to the note - field_mappings: - type: object - example: *ref_112 - nullable: true - description: >- - The custom field mappings of the note between the remote 3rd party & - Panora - additionalProperties: true - required: - - content - UnifiedCrmStageOutput: - type: object - properties: - stage_name: - type: string - example: Qualified - description: The name of the stage - nullable: true - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - description: >- - The custom field mappings of the stage between the remote 3rd party - & Panora - nullable: true - additionalProperties: true - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the stage - nullable: true - remote_id: - type: string - example: id_1 - description: The ID of the stage in the context of the Crm 3rd Party - nullable: true - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - description: The remote data of the stage in the context of the Crm 3rd Party - nullable: true - additionalProperties: true - created_at: - type: object - example: '2024-10-01T12:00:00Z' - description: The created date of the object - nullable: true - modified_at: - type: object - example: '2024-10-01T12:00:00Z' - description: The modified date of the object - nullable: true - required: - - stage_name - UnifiedCrmTaskOutput: - type: object - properties: - subject: - type: string - example: Answer customers - description: The subject of the task - nullable: true - content: - type: string - example: Prepare email campaign - description: The content of the task - nullable: true - status: - type: string - example: PENDING - description: The status of the task. Authorized values are PENDING, COMPLETED. - nullable: true - due_date: - type: string - example: '2024-10-01T12:00:00Z' - description: The due date of the task - nullable: true - finished_date: - type: string - example: '2024-10-01T12:00:00Z' - description: The finished date of the task - nullable: true - user_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the user tied to the task - nullable: true - company_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the company tied to the task - nullable: true - deal_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the deal tied to the task - nullable: true - field_mappings: - type: object - example: &ref_113 - fav_dish: broccoli - fav_color: red - description: >- - The custom field mappings of the task between the remote 3rd party & - Panora - nullable: true - additionalProperties: true - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the task - nullable: true - remote_id: - type: string - example: id_1 - description: The ID of the task in the context of the Crm 3rd Party - nullable: true - remote_data: - type: object - example: - key1: value1 - key2: 42 - key3: true - description: The remote data of the task in the context of the Crm 3rd Party - nullable: true - additionalProperties: true - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The created date of the object - nullable: true - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The modified date of the object - nullable: true - required: - - subject - - content - - status - UnifiedCrmTaskInput: - type: object - properties: - subject: - type: string - example: Answer customers - description: The subject of the task - nullable: true - content: - type: string - example: Prepare email campaign - description: The content of the task - nullable: true - status: - type: string - example: PENDING - description: The status of the task. Authorized values are PENDING, COMPLETED. - nullable: true - due_date: - type: string - example: '2024-10-01T12:00:00Z' - description: The due date of the task - nullable: true - finished_date: - type: string - example: '2024-10-01T12:00:00Z' - description: The finished date of the task - nullable: true - user_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the user tied to the task - nullable: true - company_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the company tied to the task - nullable: true - deal_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the deal tied to the task - nullable: true - field_mappings: - type: object - example: *ref_113 - description: >- - The custom field mappings of the task between the remote 3rd party & - Panora - nullable: true - additionalProperties: true - required: - - subject - - content - - status - UnifiedCrmUserOutput: - type: object - properties: - name: - type: string - example: Jane Doe - description: The name of the user - nullable: true - email: - type: string - example: jane.doe@example.com - description: The email of the user - nullable: true - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - description: >- - The custom field mappings of the user between the remote 3rd party & - Panora - nullable: true - additionalProperties: true - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the user - nullable: true - remote_id: - type: string - example: id_1 - description: The id of the user in the context of the Crm 3rd Party - nullable: true - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - description: The remote data of the user in the context of the Crm 3rd Party - nullable: true - additionalProperties: true - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The created date of the object - nullable: true - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The modified date of the object - nullable: true - required: - - name - - email - UnifiedTicketingCollectionOutput: - type: object - properties: - name: - type: string - example: My Personal Collection - nullable: true - description: The name of the collection - description: - type: string - example: Collect issues - nullable: true - description: The description of the collection - collection_type: - type: string - example: PROJECT - nullable: true - description: >- - The type of the collection. Authorized values are either PROJECT or - LIST - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the collection - remote_id: - type: string - example: id_1 - nullable: true - description: The id of the collection in the context of the 3rd Party - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true - additionalProperties: true - description: The remote data of the collection in the context of the 3rd Party - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the object - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The modified date of the object - required: - - name - UnifiedTicketingCommentOutput: - type: object - properties: - body: - type: string - nullable: true - example: Assigned to Eric ! - description: The body of the comment - html_body: - type: string - nullable: true - example:

Assigned to Eric !

- description: The html body of the comment - is_private: - type: boolean - nullable: true - example: false - description: The public status of the comment - creator_type: - type: string - nullable: true - example: USER - description: >- - The creator type of the comment. Authorized values are either USER - or CONTACT - ticket_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the ticket the comment is tied to - contact_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: >- - The UUID of the contact which the comment belongs to (if no user_id - specified) - user_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: >- - The UUID of the user which the comment belongs to (if no contact_id - specified) - attachments: - type: array - items: *ref_114 - nullable: true - example: *ref_115 - description: The attachements UUIDs tied to the comment - id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the comment - remote_id: - type: string - nullable: true - example: id_1 - description: The id of the comment in the context of the 3rd Party - remote_data: - type: object - nullable: true - example: - fav_dish: broccoli - fav_color: red - additionalProperties: true - description: The remote data of the comment in the context of the 3rd Party - created_at: - format: date-time - type: string - nullable: true - example: '2024-10-01T12:00:00Z' - description: The created date of the object - modified_at: - format: date-time - type: string - nullable: true - example: '2024-10-01T12:00:00Z' - description: The modified date of the object - required: - - body - UnifiedTicketingTagOutput: - type: object - properties: - name: - type: string - example: urgent_tag - nullable: true - description: The name of the tag - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true - description: >- - The custom field mappings of the tag between the remote 3rd party & - Panora - additionalProperties: true - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the tag - remote_id: - type: string - example: id_1 - description: The remote ID of the tag in the context of the 3rd Party - nullable: true - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true - additionalProperties: true - description: The remote data of the tag in the context of the 3rd Party - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The created date of the tag - nullable: true - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The modified date of the tag - nullable: true - required: - - name - UnifiedTicketingTeamOutput: - type: object - properties: - name: - type: string - example: My team - nullable: true - description: The name of the team - description: - type: string - example: Internal members - nullable: true - description: The description of the team - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true - description: >- - The custom field mappings of the team between the remote 3rd party & - Panora - additionalProperties: true - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the team - remote_id: - type: string - example: id_1 - nullable: true - description: The id of the team in the context of the 3rd Party - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true - additionalProperties: true - description: The remote data of the team in the context of the 3rd Party - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the object - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The modified date of the object - required: - - name - LinkedUserResponse: - type: object - properties: - id_linked_user: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - linked_user_origin_id: - type: string - example: id_1 - nullable: true - alias: - type: string - example: acme - nullable: true - id_project: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - required: - - id_linked_user - - linked_user_origin_id - - alias - - id_project - CreateLinkedUserDto: - type: object - properties: - linked_user_origin_id: - type: string - description: The id of the user in the context of your own software - example: id_1 - alias: - type: string - nullable: true - description: Your company alias - example: acme - required: - - linked_user_origin_id - - alias - CreateBatchLinkedUserDto: - type: object - properties: - linked_user_origin_ids: - nullable: true - description: The ids of the users in the context of your own software - example: - - id_1 - type: array - items: - type: string - alias: - type: string - nullable: true - description: Your company alias - example: acme - required: - - linked_user_origin_ids - - alias - ProjectResponse: - type: object - properties: - id_project: - type: string - example: 123e4567-e89b-12d3-a456-426614174000 - description: Unique identifier for the project - name: - type: string - example: My Project - description: Name of the project - sync_mode: - type: string - example: automatic - description: Synchronization mode of the project - pull_frequency: - type: number - example: 3600 - description: Frequency of pulling data in seconds - redirect_url: - type: string - example: https://example.com/redirect - description: Redirect URL for the project - id_user: - type: string - example: 123e4567-e89b-12d3-a456-426614174001 - description: User ID associated with the project - id_connector_set: - type: string - example: 123e4567-e89b-12d3-a456-426614174002 - description: Connector set ID associated with the project - required: - - id_project - - name - - sync_mode - - id_user - - id_connector_set - CreateProjectDto: - type: object - properties: - name: - type: string - example: Project Name - description: The name of the project - id_organization: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The organization ID - id_user: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The user ID - required: - - name - - id_user - CustomFieldResponse: - type: object - properties: - id_attribute: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: Attribute Id - status: - type: string - nullable: true - example: '' - description: Attribute Status - ressource_owner_type: - type: string - example: '' - nullable: true - description: Attribute Ressource Owner Type - slug: - type: string - nullable: true - example: fav_dish - description: Attribute Slug - description: - type: string - nullable: true - example: My favorite dish - description: Attribute Description - data_type: - type: string - nullable: true - example: string - enum: - - string - - number - description: Attribute Data Type - remote_id: - type: string - nullable: true - example: id_1 - description: Attribute Remote Id - source: - type: string - nullable: true - example: hubspot - description: Attribute Source - id_entity: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: Attribute Entity Id - id_project: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: Attribute Project Id - scope: - type: string - nullable: true - example: '' - description: Attribute Scope - id_consumer: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: Attribute Consumer Id - created_at: - format: date-time - type: string - nullable: true - example: '2024-10-01T12:00:00Z' - description: Attribute Created Date - modified_at: - format: date-time - type: string - nullable: true - example: '2024-10-01T12:00:00Z' - description: Attribute Modified Date - required: - - id_attribute - - status - - ressource_owner_type - - slug - - description - - data_type - - remote_id - - source - - id_entity - - id_project - - scope - - id_consumer - - created_at - - modified_at - DefineTargetFieldDto: - type: object - properties: - object_type_owner: - type: string - example: company - enum: - - company - - contact - - deal - - lead - - note - - task - - engagement - - stage - - user - nullable: true - name: - type: string - nullable: true - example: fav_dish - description: The name of the target field - description: - type: string - nullable: true - example: My favorite dish - description: The description of the target field - data_type: - type: string - nullable: true - example: string - enum: - - string - - number - description: The data type of the target field - required: - - object_type_owner - - name - - description - - data_type - CustomFieldCreateDto: - type: object - properties: - object_type_owner: - type: string - example: company - enum: - - company - - contact - - deal - - lead - - note - - task - - engagement - - stage - - user - nullable: true - name: - type: string - nullable: true - example: my_favorite_dish - description: The name of the custom field - description: - type: string - nullable: true - example: Favorite Dish - description: The description of the custom field - data_type: - type: string - example: string - nullable: true - enum: - - string - - number - description: The data type of the custom field - source_custom_field_id: - type: string - nullable: true - example: id_1 - description: The source custom field ID - source_provider: - type: string - nullable: true - example: hubspot - description: The name of the source software/provider - linked_user_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The linked user ID - required: - - object_type_owner - - name - - description - - data_type - - source_custom_field_id - - source_provider - - linked_user_id - MapFieldToProviderDto: - type: object - properties: - attributeId: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The attribute ID - source_custom_field_id: - type: string - nullable: true - example: id_1 - description: The source custom field ID - source_provider: - type: string - nullable: true - example: hubspot - description: The source provider - linked_user_id: - type: string - nullable: true - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The linked user ID - required: - - attributeId - - source_custom_field_id - - source_provider - - linked_user_id - EventResponse: - type: object - properties: - id_event: - type: string - example: 123e4567-e89b-12d3-a456-426614174000 - description: Unique identifier for the event - id_connection: - type: string - example: 123e4567-e89b-12d3-a456-426614174001 - description: Connection ID associated with the event - id_project: - type: string - example: 123e4567-e89b-12d3-a456-426614174002 - description: Project ID associated with the event - type: - type: string - example: connection.created - enum: - - crm.contact.created - - crm.contact.pulled - - crm.company.created - - crm.company.pulled - - crm.deal.created - - crm.deal.pulled - - crm.engagement.created - - crm.engagement.pulled - - crm.note.created - - crm.note.pulled - - crm.stage.pulled - - crm.task.pulled - - crm.task.created - - crm.user.pulled - - ticketing.ticket.created - - ticketing.ticket.pulled - - ticketing.comment.created - - ticketing.comment.pulled - - ticketing.attachment.created - - ticketing.attachment.pulled - - ticketing.collection.pulled - - ticketing.account.pulled - - ticketing.contact.pulled - - ticketing.tag.pulled - - ticketing.team.pulled - - ticketing.user.pulled - - ats.activity.created - - ats.activity.pulled - - ats.application.created - - ats.application.pulled - - ats.attachment.created - - ats.attachment.pulled - - ats.candidate.created - - ats.candidate.pulled - - ats.department.pulled - - ats.eecos.pulled - - ats.interview.created - - ats.interview.pulled - - ats.job.pulled - - ats.jobinterviewstage.pulled - - ats.offer.created - - ats.office.pulled - - ats.rejectreason.pulled - - ats.scorecard.pulled - - ats.tag.pulled - - ats.user.pulled - - filestorage.file.created - - filestorage.file.pulled - - filestorage.folder.created - - filestorage.folder.pulled - - filestorage.group.pulled - - filestorage.user.pulled - - filestorage.drive.pulled - - filestorage.permission.pulled - - filestorage.sharedlink.pulled - - connection.created - description: Scope of the event - status: - type: string - example: success - enum: - - success - - fail - description: Status of the event - direction: - type: string - example: '0' - description: Direction of the event - method: - type: string - example: POST - enum: - - GET - - POST - - PUT - - DELETE - description: HTTP method used for the event - url: - type: string - example: /crm/companies - description: URL associated with the event - provider: - type: string - example: hubspot - description: Provider associated with the event - timestamp: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: Timestamp of the event - id_linked_user: - type: string - example: 123e4567-e89b-12d3-a456-426614174003 - description: Linked user ID associated with the event - required: - - id_event - - id_connection - - id_project - - type - - status - - direction - - method - - url - - provider - - timestamp - - id_linked_user - PassThroughRequestDto: - type: object - properties: - method: - enum: - - GET - - POST - type: string - path: - type: string - nullable: true - data: - type: object - request_format: - oneOf: - - type: object - additionalProperties: true - - type: array - items: - type: object - additionalProperties: true - nullable: true - overrideBaseUrl: - type: object - additionalProperties: true - nullable: true - headers: - type: object - required: - - method - - path - UnifiedHrisBankinfoOutput: - type: object - properties: - account_type: - type: string - example: CHECKING - nullable: true - description: The type of the bank account - bank_name: - type: string - example: Bank of America - nullable: true - description: The name of the bank - account_number: - type: string - example: '1234567890' - nullable: true - description: The account number - routing_number: - type: string - example: '021000021' - nullable: true - description: The routing number of the bank - employee_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the associated employee - field_mappings: - type: object - example: - custom_field_1: value1 - custom_field_2: value2 - nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the bank info record - remote_id: - type: string - example: id_1 - nullable: true - description: The remote ID of the bank info in the context of the 3rd Party - remote_data: - type: object - example: - raw_data: - additional_field: some value - nullable: true - description: The remote data of the bank info in the context of the 3rd Party - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The date when the bank info was created in the 3rd party system - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the bank info record - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The last modified date of the bank info record - remote_was_deleted: - type: boolean - example: false - nullable: true - description: Indicates if the bank info was deleted in the remote system - required: - - id - - created_at - - modified_at - - remote_was_deleted - UnifiedHrisBenefitOutput: - type: object - properties: - provider_name: - type: string - example: Health Insurance Provider - nullable: true - description: The name of the benefit provider - employee_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the associated employee - employee_contribution: - type: number - example: 100 - nullable: true - description: The employee contribution amount - company_contribution: - type: number - example: 200 - nullable: true - description: The company contribution amount - start_date: - format: date-time - type: string - example: '2024-01-01T00:00:00Z' - nullable: true - description: The start date of the benefit - end_date: - format: date-time - type: string - example: '2024-12-31T23:59:59Z' - nullable: true - description: The end date of the benefit - employer_benefit_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the associated employer benefit - field_mappings: - type: object - example: - custom_field_1: value1 - custom_field_2: value2 - nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the benefit record - remote_id: - type: string - example: benefit_1234 - nullable: true - description: The remote ID of the benefit in the context of the 3rd Party - remote_data: - type: object - example: - raw_data: - additional_field: some value - nullable: true - description: The remote data of the benefit in the context of the 3rd Party - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The date when the benefit was created in the 3rd party system - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the benefit record - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The last modified date of the benefit record - remote_was_deleted: - type: boolean - example: false - nullable: true - description: Indicates if the benefit was deleted in the remote system - UnifiedHrisCompanyOutput: - type: object - properties: - legal_name: - type: string - example: Acme Corporation - nullable: true - description: The legal name of the company - locations: - example: - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: UUIDs of the of the Location associated with the company - type: array - items: - type: string - display_name: - type: string - example: Acme Corp - nullable: true - description: The display name of the company - eins: - example: - - 12-3456789 - - 98-7654321 - nullable: true - description: The Employer Identification Numbers (EINs) of the company - type: array - items: - type: string - field_mappings: - type: object - example: - custom_field_1: value1 - custom_field_2: value2 - nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the company record - remote_id: - type: string - example: company_1234 - nullable: true - description: The remote ID of the company in the context of the 3rd Party - remote_data: - type: object - example: - raw_data: - additional_field: some value - nullable: true - description: The remote data of the company in the context of the 3rd Party - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The date when the company was created in the 3rd party system - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the company record - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The last modified date of the company record - remote_was_deleted: - type: boolean - example: false - nullable: true - description: Indicates if the company was deleted in the remote system - UnifiedHrisDependentOutput: + $ref: '#/components/schemas/UnifiedTicketingAttachmentOutput' + tags: *ref_60 + x-speakeasy-group: ticketing.attachments +info: + title: Panora API + description: A unified API to ship integrations + version: '1.0' + contact: {} +tags: [] +servers: + - url: https://api.panora.dev + description: Production server + - url: https://api-sandbox.panora.dev + description: Sandbox server + - url: https://api-dev.panora.dev + description: Development server +components: + securitySchemes: + api_key: + type: apiKey + in: header + name: x-api-key + schemas: + RagQueryOutput: type: object properties: - first_name: - type: string - example: John - nullable: true - description: The first name of the dependent - last_name: - type: string - example: Doe - nullable: true - description: The last name of the dependent - middle_name: - type: string - example: Michael - nullable: true - description: The middle name of the dependent - relationship: - type: string - example: CHILD - nullable: true - description: The relationship of the dependent to the employee - date_of_birth: - format: date-time - type: string - example: '2020-01-01' - nullable: true - description: The date of birth of the dependent - gender: - type: string - example: MALE - nullable: true - description: The gender of the dependent - phone_number: - type: string - example: '+1234567890' - nullable: true - description: The phone number of the dependent - home_location: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the home location - is_student: - type: boolean - example: true - nullable: true - description: Indicates if the dependent is a student - ssn: - type: string - example: 123-45-6789 - nullable: true - description: The Social Security Number of the dependent - employee_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the associated employee - field_mappings: - type: object - example: - custom_field_1: value1 - custom_field_2: value2 - nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the dependent record - remote_id: + chunk: type: string - example: dependent_1234 - nullable: true - description: The remote ID of the dependent in the context of the 3rd Party - remote_data: + example: 'Date : 06/07/2023' + nullable: false + description: The chunk which matches the embed query + metadata: type: object example: - raw_data: - additional_field: some value - nullable: true - description: The remote data of the dependent in the context of the 3rd Party - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The date when the dependent was created in the 3rd party system - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the dependent record - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The last modified date of the dependent record - remote_was_deleted: - type: boolean - example: false - nullable: true - description: Indicates if the dependent was deleted in the remote system - DeductionItem: - type: object - properties: - name: - type: string - example: Health Insurance - nullable: true - description: The name of the deduction - employee_deduction: - type: number - example: 100 - nullable: true - description: The amount of employee deduction - company_deduction: - type: number - example: 200 - nullable: true - description: The amount of company deduction - EarningItem: - type: object - properties: - amount: - type: number - example: 1000 - nullable: true - description: The amount of the earning - type: - type: string - example: Salary - nullable: true - description: The type of the earning - TaxItem: - type: object - properties: - name: - type: string - example: Federal Income Tax - nullable: true - description: The name of the tax - amount: - type: number - example: 250 - nullable: true - description: The amount of the tax - employer_tax: - type: boolean - example: true - nullable: true - description: Indicates if this is an employer tax - UnifiedHrisEmployeepayrollrunOutput: - type: object - properties: - employee_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the associated employee - payroll_run_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the associated payroll run - gross_pay: - type: number - example: 5000 + blobType: '' + text: ATTESTATION + additionalProperties: true nullable: true - description: The gross pay amount - net_pay: + description: The metadata tied to the chunk + score: type: number - example: 4000 - nullable: true - description: The net pay amount - start_date: - format: date-time - type: string - example: '2023-01-01T00:00:00Z' - nullable: true - description: The start date of the pay period - end_date: - format: date-time - type: string - example: '2023-01-15T23:59:59Z' - nullable: true - description: The end date of the pay period - check_date: - format: date-time - type: string - example: '2023-01-20T00:00:00Z' - nullable: true - description: The date the check was issued - deductions: - nullable: true - description: The list of deductions for this payroll run - type: array - items: - $ref: '#/components/schemas/DeductionItem' - earnings: - nullable: true - description: The list of earnings for this payroll run - type: array - items: - $ref: '#/components/schemas/EarningItem' - taxes: - nullable: true - description: The list of taxes for this payroll run - type: array - items: - $ref: '#/components/schemas/TaxItem' - field_mappings: - type: object - example: - custom_field_1: value1 - custom_field_2: value2 - nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the employee payroll run record - remote_id: - type: string - example: payroll_run_1234 + example: 0.87 nullable: true - description: >- - The remote ID of the employee payroll run in the context of the 3rd - Party - remote_data: - type: object + description: The score + embedding: example: - raw_data: - additional_field: some value - nullable: true - description: >- - The remote data of the employee payroll run in the context of the - 3rd Party - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: >- - The date when the employee payroll run was created in the 3rd party - system - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the employee payroll run record - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The last modified date of the employee payroll run record - remote_was_deleted: - type: boolean - example: false - nullable: true - description: >- - Indicates if the employee payroll run was deleted in the remote - system - UnifiedHrisEmployeeOutput: - type: object - properties: - groups: - example: &ref_116 - - Group1 - - Group2 - nullable: true - description: The groups the employee belongs to - type: array - items: - type: string - locations: - example: &ref_117 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: UUIDs of the of the Location associated with the company - type: array - items: - type: string - employee_number: - type: string - example: EMP001 - nullable: true - description: The employee number - company_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the associated company - first_name: - type: string - example: John - nullable: true - description: The first name of the employee - last_name: - type: string - example: Doe - nullable: true - description: The last name of the employee - preferred_name: - type: string - example: Johnny - nullable: true - description: The preferred name of the employee - display_full_name: - type: string - example: John Doe - nullable: true - description: The full display name of the employee - username: - type: string - example: johndoe - nullable: true - description: The username of the employee - work_email: - type: string - example: john.doe@company.com - nullable: true - description: The work email of the employee - personal_email: - type: string - example: john.doe@personal.com - nullable: true - description: The personal email of the employee - mobile_phone_number: - type: string - example: '+1234567890' - nullable: true - description: The mobile phone number of the employee - employments: - example: &ref_118 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + - -0.00442447886 + - -0.00116857514 + - 0.00869117491 + - -0.0361584462 + - -0.00220073434 + - 0.00946036354 + - -0.0101112155 nullable: true - description: The employments of the employee + description: The embedding of the relevant chunk type: array items: - type: string - ssn: + type: number + required: + - chunk + - metadata + - score + - embedding + QueryBody: + type: object + properties: + query: type: string - example: 123-45-6789 + example: When does Panora incorporated? + nullable: false + description: The query you want to received embeddings and chunks for + topK: + type: number + example: '3' nullable: true - description: The Social Security Number of the employee - gender: + description: The number of most appropriate documents for your query. + required: + - query + PaginatedDto: + type: object + properties: + prev_cursor: type: string - example: MALE nullable: true - description: The gender of the employee - ethnicity: + next_cursor: type: string - example: AMERICAN_INDIAN_OR_ALASKA_NATIVE nullable: true - description: The ethnicity of the employee - marital_status: + data: + type: array + items: + type: object + required: + - prev_cursor + - next_cursor + - data + UnifiedFilestorageFileOutput: + type: object + properties: + name: type: string - example: Married + example: my_paris_photo.png + description: The name of the file nullable: true - description: The marital status of the employee - date_of_birth: - format: date-time + file_url: type: string - example: '1990-01-01' + example: https://example.com/my_paris_photo.png + description: The url of the file nullable: true - description: The date of birth of the employee - start_date: - format: date-time + mime_type: type: string - example: '2020-01-01' + example: application/pdf + description: The mime type of the file nullable: true - description: The start date of the employee - employment_status: + size: type: string - example: ACTIVE + example: '1024' + description: The size of the file nullable: true - description: The employment status of the employee - termination_date: - format: date-time + folder_id: type: string - example: '2025-01-01' + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the folder tied to the file nullable: true - description: The termination date of the employee - avatar_url: + permission: type: string - example: https://example.com/avatar.jpg + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the permission tied to the file nullable: true - description: The URL of the employee's avatar - manager_id: + shared_link: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the shared link tied to the file nullable: true - description: UUID of the manager (employee) of the employee field_mappings: type: object - example: &ref_119 - custom_field_1: value1 - custom_field_2: value2 - nullable: true + example: &ref_61 + fav_dish: broccoli + fav_color: red description: >- The custom field mappings of the object between the remote 3rd party & Panora + nullable: true + additionalProperties: true id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the file nullable: true - description: The UUID of the employee record remote_id: type: string - example: employee_1234 + example: id_1 + description: The id of the file in the context of the 3rd Party nullable: true - description: The remote ID of the employee in the context of the 3rd Party remote_data: type: object example: - raw_data: - additional_field: some value - nullable: true - description: The remote data of the employee in the context of the 3rd Party - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' + fav_dish: broccoli + fav_color: red + description: The remote data of the file in the context of the 3rd Party nullable: true - description: The date when the employee was created in the 3rd party system + additionalProperties: true created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' + description: The created date of the object nullable: true - description: The created date of the employee record modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' + description: The modified date of the object nullable: true - description: The last modified date of the employee record - remote_was_deleted: - type: boolean - example: false - nullable: true - description: Indicates if the employee was deleted in the remote system - UnifiedHrisEmployeeInput: + required: + - name + - file_url + - mime_type + - size + - folder_id + - permission + - shared_link + UnifiedFilestorageFileInput: type: object properties: - groups: - example: *ref_116 + name: + type: string + example: my_paris_photo.png + description: The name of the file nullable: true - description: The groups the employee belongs to - type: array - items: - type: string - locations: - example: *ref_117 + file_url: + type: string + example: https://example.com/my_paris_photo.png + description: The url of the file nullable: true - description: UUIDs of the of the Location associated with the company - type: array - items: - type: string - employee_number: + mime_type: type: string - example: EMP001 + example: application/pdf + description: The mime type of the file nullable: true - description: The employee number - company_id: + size: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: '1024' + description: The size of the file nullable: true - description: The UUID of the associated company - first_name: + folder_id: type: string - example: John + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the folder tied to the file nullable: true - description: The first name of the employee - last_name: + permission: type: string - example: Doe + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the permission tied to the file nullable: true - description: The last name of the employee - preferred_name: + shared_link: type: string - example: Johnny + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the shared link tied to the file + nullable: true + field_mappings: + type: object + example: *ref_61 + description: >- + The custom field mappings of the object between the remote 3rd party + & Panora nullable: true - description: The preferred name of the employee - display_full_name: + additionalProperties: true + required: + - name + - file_url + - mime_type + - size + - folder_id + - permission + - shared_link + LoginDto: + type: object + properties: + id_user: type: string - example: John Doe + email: + type: string + password_hash: + type: string + required: + - id_user + - email + - password_hash + Connection: + type: object + properties: + id_connection: + type: string + example: 123e4567-e89b-12d3-a456-426614174000 + description: Unique identifier for the connection + status: + type: string + example: active + description: Status of the connection + provider_slug: + type: string + example: hubspot + description: Slug for the provider + vertical: + type: string + example: crm + description: Vertical category of the connection + account_url: + type: string + example: https://example.com/account + description: URL of the account + token_type: + type: string + example: oauth2 + enum: + - oauth2 + - apikey + - basic + description: Strategy type + access_token: + type: string + example: access_token_example + description: Access token for the connection + refresh_token: + type: string + example: refresh_token_example + description: Refresh token for the connection + expiration_timestamp: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + description: Expiration timestamp of the access token + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + description: Timestamp when the connection was created + connection_token: + type: string + example: 123e4567-e89b-12d3-a456-426614174000 + description: UUID Token for the connection + id_project: + type: string + example: 123e4567-e89b-12d3-a456-426614174001 + description: Project ID associated with the connection + id_linked_user: + type: string + example: 123e4567-e89b-12d3-a456-426614174002 + description: Linked user ID associated with the connection + required: + - id_connection + - status + - provider_slug + - vertical + - account_url + - token_type + - access_token + - refresh_token + - expiration_timestamp + - created_at + - connection_token + - id_project + - id_linked_user + WebhookResponse: + type: object + properties: + id_webhook_endpoint: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The full display name of the employee - username: + description: The unique UUID of the webhook. + endpoint_description: type: string - example: johndoe + example: Webhook to receive connection events nullable: true - description: The username of the employee - work_email: + description: The description of the webhook. + url: type: string - example: john.doe@company.com + example: https://acme.com/webhook_receiver nullable: true - description: The work email of the employee - personal_email: + description: The endpoint url of the webhook. + secret: type: string - example: john.doe@personal.com + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The secret of the webhook. + active: + type: boolean + example: true nullable: true - description: The personal email of the employee - mobile_phone_number: + description: The status of the webhook. + created_at: + format: date-time type: string - example: '+1234567890' + example: '2024-10-01T12:00:00Z' + description: The created date of the webhook. nullable: true - description: The mobile phone number of the employee - employments: - example: *ref_118 + scope: + example: + - connection.created nullable: true - description: The employments of the employee + description: The events that the webhook listen to. type: array items: type: string - ssn: - type: string - example: 123-45-6789 - nullable: true - description: The Social Security Number of the employee - gender: - type: string - example: MALE - nullable: true - description: The gender of the employee - ethnicity: - type: string - example: AMERICAN_INDIAN_OR_ALASKA_NATIVE - nullable: true - description: The ethnicity of the employee - marital_status: + id_project: type: string - example: Married + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The marital status of the employee - date_of_birth: + description: The project id tied to the webhook. + last_update: format: date-time type: string - example: '1990-01-01' + example: '2024-10-01T12:00:00Z' nullable: true - description: The date of birth of the employee - start_date: - format: date-time + description: The last update date of the webhook. + required: + - id_webhook_endpoint + - endpoint_description + - url + - secret + - active + - created_at + - scope + - id_project + - last_update + WebhookDto: + type: object + properties: + url: type: string - example: '2020-01-01' + example: https://acme.com/webhook_receiver nullable: true - description: The start date of the employee - employment_status: + description: The endpoint url of the webhook. + description: type: string - example: ACTIVE + example: Webhook to receive connection events nullable: true - description: The employment status of the employee - termination_date: - format: date-time - type: string - example: '2025-01-01' + description: The description of the webhook. + scope: + example: + - connection.created nullable: true - description: The termination date of the employee - avatar_url: - type: string - example: https://example.com/avatar.jpg + description: The events that the webhook listen to. + type: array + items: + type: string + required: + - url + - description + - scope + SignatureVerificationDto: + type: object + properties: + payload: + type: object + additionalProperties: true nullable: true - description: The URL of the employee's avatar - manager_id: + description: The payload event of the webhook. + signature: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: UUID of the manager (employee) of the employee - field_mappings: - type: object - example: *ref_119 + description: The signature of the webhook. + secret: + type: string nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - UnifiedHrisEmployerbenefitOutput: + description: The secret of the webhook. + required: + - payload + - signature + - secret + UnifiedTicketingCommentInput: type: object properties: - benefit_plan_type: + body: type: string - example: Health Insurance nullable: true - description: The type of the benefit plan - name: + example: Assigned to Eric ! + description: The body of the comment + html_body: type: string - example: Company Health Plan nullable: true - description: The name of the employer benefit - description: - type: string - example: Comprehensive health insurance coverage for employees + example:

Assigned to Eric !

+ description: The html body of the comment + is_private: + type: boolean nullable: true - description: The description of the employer benefit - deduction_code: + example: false + description: The public status of the comment + creator_type: type: string - example: HEALTH-001 - nullable: true - description: The deduction code for the employer benefit - field_mappings: - type: object - example: - custom_field_1: value1 - custom_field_2: value2 nullable: true + example: USER description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - id: + The creator type of the comment. Authorized values are either USER + or CONTACT + ticket_id: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the employer benefit record - remote_id: + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the ticket the comment is tied to + contact_id: type: string - example: benefit_1234 - nullable: true - description: >- - The remote ID of the employer benefit in the context of the 3rd - Party - remote_data: - type: object - example: - raw_data: - additional_field: some value nullable: true + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f description: >- - The remote data of the employer benefit in the context of the 3rd - Party - remote_created_at: - format: date-time + The UUID of the contact which the comment belongs to (if no user_id + specified) + user_id: type: string - example: '2024-10-01T12:00:00Z' nullable: true + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f description: >- - The date when the employer benefit was created in the 3rd party - system - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the employer benefit record - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The last modified date of the employer benefit record - remote_was_deleted: - type: boolean - example: false + The UUID of the user which the comment belongs to (if no contact_id + specified) + attachments: + type: array + items: &ref_84 + oneOf: + - type: string + - $ref: '#/components/schemas/UnifiedTicketingAttachmentOutput' nullable: true - description: Indicates if the employer benefit was deleted in the remote system - UnifiedHrisEmploymentOutput: + example: &ref_85 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The attachements UUIDs tied to the comment + required: + - body + UnifiedTicketingTicketOutput: type: object properties: - job_title: - type: string - example: Software Engineer - nullable: true - description: The job title of the employment - pay_rate: - type: number - example: 100000 - nullable: true - description: The pay rate of the employment - pay_period: - type: string - example: MONTHLY - nullable: true - description: The pay period of the employment - pay_frequency: + name: type: string - example: WEEKLY + example: Customer Service Inquiry nullable: true - description: The pay frequency of the employment - pay_currency: + description: The name of the ticket + status: type: string - example: USD + example: OPEN nullable: true - description: The currency of the pay - flsa_status: + description: The status of the ticket. Authorized values are OPEN or CLOSED. + description: type: string - example: EXEMPT + example: Help customer nullable: true - description: The FLSA status of the employment - effective_date: + description: The description of the ticket + due_date: format: date-time type: string - example: '2023-01-01' - nullable: true - description: The effective date of the employment - employment_type: - type: string - example: FULL_TIME - nullable: true - description: The type of employment - pay_group_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: '2024-10-01T12:00:00Z' nullable: true - description: The UUID of the associated pay group - employee_id: + description: The date the ticket is due + type: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the associated employee - field_mappings: - type: object - example: - custom_field_1: value1 - custom_field_2: value2 + example: BUG nullable: true description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - id: + The type of the ticket. Authorized values are PROBLEM, QUESTION, or + TASK + parent_ticket: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the employment record - remote_id: - type: string - example: employment_1234 + description: The UUID of the parent ticket + collections: + type: array + items: &ref_62 + oneOf: + - type: string + - $ref: '#/components/schemas/UnifiedTicketingCollectionOutput' + example: &ref_63 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The remote ID of the employment in the context of the 3rd Party - remote_data: - type: object - example: - raw_data: - additional_field: some value + description: The collection UUIDs the ticket belongs to + tags: + type: array + items: &ref_64 + oneOf: + - type: string + - $ref: '#/components/schemas/UnifiedTicketingTagOutput' + example: &ref_65 + - my_tag + - urgent_tag nullable: true - description: The remote data of the employment in the context of the 3rd Party - remote_created_at: + description: The tags names of the ticket + completed_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The date when the employment was created in the 3rd party system - created_at: - format: date-time + description: The date the ticket has been completed + priority: type: string - example: '2024-10-01T12:00:00Z' + example: HIGH nullable: true - description: The created date of the employment record - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' + description: >- + The priority of the ticket. Authorized values are HIGH, MEDIUM or + LOW. + assigned_to: + example: &ref_66 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The last modified date of the employment record - remote_was_deleted: - type: boolean - example: false + description: The users UUIDs the ticket is assigned to + type: array + items: + type: string + comment: + example: &ref_67 + content: Assigned the issue ! nullable: true - description: Indicates if the employment was deleted in the remote system - UnifiedHrisGroupOutput: - type: object - properties: - parent_group: + description: The comment of the ticket + allOf: + - $ref: '#/components/schemas/UnifiedTicketingCommentInput' + account_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the account which the ticket belongs to + contact_id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the parent group - name: - type: string - example: Engineering Team - nullable: true - description: The name of the group - type: - type: string - example: DEPARTMENT + description: The UUID of the contact which the ticket belongs to + attachments: + type: array + items: &ref_68 + oneOf: + - type: string + - $ref: '#/components/schemas/UnifiedTicketingAttachmentInput' + example: &ref_69 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The attachements UUIDs tied to the ticket nullable: true - description: The type of the group field_mappings: type: object - example: - custom_field_1: value1 - custom_field_2: value2 + example: &ref_70 + fav_dish: broccoli + fav_color: red nullable: true description: >- - The custom field mappings of the object between the remote 3rd party + The custom field mappings of the ticket between the remote 3rd party & Panora + additionalProperties: true id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the group record + description: The UUID of the ticket remote_id: type: string - example: group_1234 + example: id_1 nullable: true - description: The remote ID of the group in the context of the 3rd Party + description: The id of the ticket in the context of the 3rd Party remote_data: type: object example: - raw_data: - additional_field: some value - nullable: true - description: The remote data of the group in the context of the 3rd Party - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' + key1: value1 + key2: 42 + key3: true nullable: true - description: The date when the group was created in the 3rd party system + additionalProperties: true + description: The remote data of the ticket in the context of the 3rd Party created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the group record + description: The created date of the object modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The last modified date of the group record - remote_was_deleted: - type: boolean - example: false - nullable: true - description: Indicates if the group was deleted in the remote system - UnifiedHrisLocationOutput: + description: The modified date of the object + required: + - name + - description + UnifiedTicketingTicketInput: type: object properties: name: type: string - example: Headquarters + example: Customer Service Inquiry nullable: true - description: The name of the location - phone_number: + description: The name of the ticket + status: type: string - example: '+1234567890' + example: OPEN nullable: true - description: The phone number of the location - street_1: + description: The status of the ticket. Authorized values are OPEN or CLOSED. + description: type: string - example: 123 Main St + example: Help customer nullable: true - description: The first line of the street address - street_2: + description: The description of the ticket + due_date: + format: date-time type: string - example: Suite 456 + example: '2024-10-01T12:00:00Z' nullable: true - description: The second line of the street address - city: + description: The date the ticket is due + type: type: string - example: San Francisco + example: BUG nullable: true - description: The city of the location - state: + description: >- + The type of the ticket. Authorized values are PROBLEM, QUESTION, or + TASK + parent_ticket: type: string - example: CA + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The state or region of the location - zip_code: - type: string - example: '94105' + description: The UUID of the parent ticket + collections: + type: array + items: *ref_62 + example: *ref_63 nullable: true - description: The zip or postal code of the location - country: + description: The collection UUIDs the ticket belongs to + tags: + type: array + items: *ref_64 + example: *ref_65 + nullable: true + description: The tags names of the ticket + completed_at: + format: date-time type: string - example: USA + example: '2024-10-01T12:00:00Z' nullable: true - description: The country of the location - location_type: + description: The date the ticket has been completed + priority: type: string - example: WORK + example: HIGH nullable: true - description: The type of the location - company_id: + description: >- + The priority of the ticket. Authorized values are HIGH, MEDIUM or + LOW. + assigned_to: + example: *ref_66 + nullable: true + description: The users UUIDs the ticket is assigned to + type: array + items: + type: string + comment: + example: *ref_67 + nullable: true + description: The comment of the ticket + allOf: + - $ref: '#/components/schemas/UnifiedTicketingCommentInput' + account_id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the company associated with the location - employee_id: + description: The UUID of the account which the ticket belongs to + contact_id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the employee associated with the location + description: The UUID of the contact which the ticket belongs to + attachments: + type: array + items: *ref_68 + example: *ref_69 + description: The attachements UUIDs tied to the ticket + nullable: true field_mappings: type: object - example: - custom_field_1: value1 - custom_field_2: value2 + example: *ref_70 nullable: true description: >- - The custom field mappings of the object between the remote 3rd party + The custom field mappings of the ticket between the remote 3rd party & Panora + additionalProperties: true + required: + - name + - description + UnifiedTicketingUserOutput: + type: object + properties: + name: + type: string + nullable: true + description: The name of the user + example: John Doe + email_address: + type: string + nullable: true + description: The email address of the user + example: john.doe@example.com + teams: + nullable: true + description: The teams whose the user is part of + example: + - team1 + - team2 + type: array + items: + type: string + account_id: + type: string + nullable: true + description: The account or organization the user is part of + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + field_mappings: + type: object + example: + fav_dish: broccoli + fav_color: red + nullable: true + description: >- + The custom field mappings of the user between the remote 3rd party & + Panora + additionalProperties: true id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the location record + description: The UUID of the user remote_id: type: string - example: location_1234 + example: id_1 nullable: true - description: The remote ID of the location in the context of the 3rd Party + description: The id of the user in the context of the 3rd Party remote_data: type: object example: - raw_data: - additional_field: some value - nullable: true - description: The remote data of the location in the context of the 3rd Party - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' + key1: value1 + key2: 42 + key3: true nullable: true - description: The date when the location was created in the 3rd party system + additionalProperties: true + description: The remote data of the user in the context of the 3rd Party created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the location record + description: The created date of the object modified_at: format: date-time type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The last modified date of the location record - remote_was_deleted: - type: boolean - example: false + example: '2023-10-01T12:00:00Z' nullable: true - description: Indicates if the location was deleted in the remote system - UnifiedHrisPaygroupOutput: + description: The modified date of the object + required: + - name + - email_address + UnifiedTicketingAccountOutput: type: object properties: - pay_group_name: + name: type: string - example: Monthly Salaried + example: My Personal Account + nullable: true + description: The name of the account + domains: + example: + - acme.com + - acme-test.com nullable: true - description: The name of the pay group + description: The domains of the account + type: array + items: + type: string field_mappings: type: object example: - custom_field_1: value1 - custom_field_2: value2 + fav_dish: broccoli + fav_color: red nullable: true description: >- - The custom field mappings of the object between the remote 3rd party - & Panora + The custom field mappings of the account between the remote 3rd + party & Panora + additionalProperties: true id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the pay group record + description: The UUID of the account remote_id: type: string - example: paygroup_1234 + example: id_1 + description: The remote ID of the account in the context of the 3rd Party nullable: true - description: The remote ID of the pay group in the context of the 3rd Party remote_data: type: object example: - raw_data: - additional_field: some value - nullable: true - description: The remote data of the pay group in the context of the 3rd Party - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' + fav_dish: broccoli + fav_color: red nullable: true - description: The date when the pay group was created in the 3rd party system + additionalProperties: true + description: The remote data of the account in the context of the 3rd Party created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' + description: The created date of the account nullable: true - description: The created date of the pay group record modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' + description: The modified date of the account nullable: true - description: The last modified date of the pay group record - remote_was_deleted: - type: boolean - example: false - nullable: true - description: Indicates if the pay group was deleted in the remote system - UnifiedHrisPayrollrunOutput: + required: + - name + UnifiedTicketingContactOutput: type: object properties: - run_state: - type: string - example: PAID - nullable: true - description: The state of the payroll run - run_type: + name: type: string - example: REGULAR + example: Joe nullable: true - description: The type of the payroll run - start_date: - format: date-time + description: The name of the contact + email_address: type: string - example: '2024-01-01T00:00:00Z' + example: joedoe@acme.org nullable: true - description: The start date of the payroll run - end_date: - format: date-time + description: The email address of the contact + phone_number: type: string - example: '2024-01-15T23:59:59Z' + example: +33 6 50 11 11 10 nullable: true - description: The end date of the payroll run - check_date: - format: date-time + description: The phone number of the contact + details: type: string - example: '2024-01-20T00:00:00Z' + example: Contact Details nullable: true - description: The check date of the payroll run + description: The details of the contact field_mappings: type: object example: - custom_field_1: value1 - custom_field_2: value2 + fav_dish: broccoli + fav_color: red nullable: true description: >- - The custom field mappings of the object between the remote 3rd party - & Panora + The custom field mappings of the contact between the remote 3rd + party & Panora + additionalProperties: true id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the payroll run record + description: The UUID of the contact remote_id: type: string - example: payroll_run_1234 + example: id_1 + description: The remote ID of the contact in the context of the 3rd Party nullable: true - description: The remote ID of the payroll run in the context of the 3rd Party remote_data: type: object example: - raw_data: - additional_field: some value - nullable: true - description: The remote data of the payroll run in the context of the 3rd Party - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' + fav_dish: broccoli + fav_color: red nullable: true - description: The date when the payroll run was created in the 3rd party system + additionalProperties: true + description: The remote data of the contact in the context of the 3rd Party created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the payroll run record + description: The created date of the object modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The last modified date of the payroll run record - remote_was_deleted: - type: boolean - example: false - nullable: true - description: Indicates if the payroll run was deleted in the remote system - employee_payroll_runs: - example: - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: >- - The UUIDs of the employee payroll runs associated with this payroll - run - type: array - items: - type: string - UnifiedHrisTimeoffOutput: + description: The modified date of the object + required: + - name + - email_address + ResyncStatusDto: type: object properties: - employee: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the employee taking time off - approver: + timestamp: + format: date-time type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: '' nullable: true - description: The UUID of the approver for the time off request - status: + vertical: type: string - example: REQUESTED + example: ticketing + enum: + - ticketing + - accounting + - crm + - filestorage + - ecommerce + - marketingautomation nullable: true - description: The status of the time off request - employee_note: + provider: type: string - example: Annual vacation + example: gitlab nullable: true - description: A note from the employee about the time off request - units: + status: type: string - example: DAYS + example: success + enum: + - success + - fail nullable: true - description: The units used for the time off (e.g., Days, Hours) - amount: + required: + - timestamp + - vertical + - provider + - status + UpdatePullFrequencyDto: + type: object + properties: + crm: type: number - example: 5 - nullable: true - description: The amount of time off requested - request_type: + example: 1800 + description: Frequency in seconds + accounting: + type: number + example: 14400 + description: Frequency in seconds + filestorage: + type: number + example: 28800 + description: Frequency in seconds + ecommerce: + type: number + example: 43200 + description: Frequency in seconds + ticketing: + type: number + example: 86400 + description: Frequency in seconds + required: + - crm + - accounting + - filestorage + - ecommerce + - ticketing + Email: + type: object + properties: + email_address: type: string - example: VACATION nullable: true - description: The type of time off request - start_time: - format: date-time + description: The email address + email_address_type: type: string - example: '2024-07-01T09:00:00Z' nullable: true - description: The start time of the time off - end_time: - format: date-time + description: >- + The email address type. Authorized values are either PERSONAL or + WORK. + owner_type: type: string - example: '2024-07-05T17:00:00Z' + enum: + - COMPANY + - CONTACT nullable: true - description: The end time of the time off - field_mappings: - type: object - example: &ref_120 - custom_field_1: value1 - custom_field_2: value2 + description: The owner type of an email + required: + - email_address + - email_address_type + Address: + type: object + properties: + street_1: + type: string nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - id: + example: 5th Avenue + description: The street + street_2: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the time off record - remote_id: + example: Street 2 + description: 'More information about the street ' + city: type: string - example: timeoff_1234 nullable: true - description: The remote ID of the time off in the context of the 3rd Party - remote_data: - type: object - example: - raw_data: - additional_field: some value + example: New York + description: The city + state: + type: string + example: New York nullable: true - description: The remote data of the time off in the context of the 3rd Party - remote_created_at: - format: date-time + description: The state + postal_code: type: string - example: '2024-06-15T12:00:00Z' + example: '10001' nullable: true - description: The date when the time off was created in the 3rd party system - created_at: - format: date-time + description: The postal code + country: type: string - example: '2024-06-15T12:00:00Z' + example: United States of America nullable: true - description: The created date of the time off record - modified_at: - format: date-time + description: The country + address_type: type: string - example: '2024-06-15T12:00:00Z' nullable: true - description: The last modified date of the time off record - remote_was_deleted: - type: boolean - example: false + example: PERSONAL + description: The address type. Authorized values are either PERSONAL or WORK. + owner_type: + type: string nullable: true - description: Indicates if the time off was deleted in the remote system - UnifiedHrisTimeoffInput: + description: The owner type of the address + required: + - street_1 + - street_2 + - city + - state + - postal_code + - country + - address_type + - owner_type + Phone: type: object properties: - employee: + phone_number: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the employee taking time off - approver: + description: >- + The phone number starting with a plus (+) followed by the country + code (e.g +336676778890 for France) + phone_type: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the approver for the time off request - status: + description: The phone type. Authorized values are either MOBILE or WORK + owner_type: type: string - example: REQUESTED nullable: true - description: The status of the time off request - employee_note: + description: The owner type of a phone number + required: + - phone_number + - phone_type + UnifiedCrmCompanyOutput: + type: object + properties: + name: type: string - example: Annual vacation + example: Acme + description: The name of the company nullable: true - description: A note from the employee about the time off request - units: + industry: type: string - example: DAYS + example: ACCOUNTING + description: >- + The industry of the company. Authorized values can be found in the + Industry enum. nullable: true - description: The units used for the time off (e.g., Days, Hours) - amount: + number_of_employees: type: number - example: 5 + example: 10 + description: The number of employees of the company + nullable: true + user_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the user who owns the company + nullable: true + email_addresses: + description: The email addresses of the company + example: &ref_71 + - email_address: acme@gmail.com + email_address_type: WORK + nullable: true + type: array + items: + $ref: '#/components/schemas/Email' + addresses: + description: The addresses of the company + example: &ref_72 + - street_1: 5th Avenue + city: New York + state: NY + country: USA + address_type: WORK + nullable: true + type: array + items: + $ref: '#/components/schemas/Address' + phone_numbers: + description: The phone numbers of the company + example: &ref_73 + - phone_number: '+33660606067' + phone_type: WORK + nullable: true + type: array + items: + $ref: '#/components/schemas/Phone' + field_mappings: + type: object + example: &ref_74 + fav_dish: broccoli + fav_color: red + description: >- + The custom field mappings of the company between the remote 3rd + party & Panora + nullable: true + additionalProperties: true + id: + type: string + description: The UUID of the company + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The amount of time off requested - request_type: + remote_id: type: string - example: VACATION + example: id_1 + description: The id of the company in the context of the Crm 3rd Party nullable: true - description: The type of time off request - start_time: - format: date-time - type: string - example: '2024-07-01T09:00:00Z' + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + description: The remote data of the company in the context of the Crm 3rd Party nullable: true - description: The start time of the time off - end_time: - format: date-time - type: string - example: '2024-07-05T17:00:00Z' + additionalProperties: true + created_at: + type: object + example: '2024-10-01T12:00:00Z' + description: The created date of the object nullable: true - description: The end time of the time off - field_mappings: + modified_at: type: object - example: *ref_120 + example: '2024-10-01T12:00:00Z' + description: The modified date of the object nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - UnifiedHrisTimeoffbalanceOutput: + required: + - name + UnifiedCrmCompanyInput: type: object properties: - balance: - type: number - example: 80 + name: + type: string + example: Acme + description: The name of the company nullable: true - description: The current balance of time off - employee_id: + industry: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: ACCOUNTING + description: >- + The industry of the company. Authorized values can be found in the + Industry enum. nullable: true - description: The UUID of the associated employee - used: + number_of_employees: type: number - example: 40 + example: 10 + description: The number of employees of the company nullable: true - description: The amount of time off used - policy_type: + user_id: type: string - example: VACATION + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the user who owns the company nullable: true - description: The type of time off policy - field_mappings: - type: object - example: - custom_field_1: value1 - custom_field_2: value2 + email_addresses: + description: The email addresses of the company + example: *ref_71 nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + type: array + items: + $ref: '#/components/schemas/Email' + addresses: + description: The addresses of the company + example: *ref_72 nullable: true - description: The UUID of the time off balance record - remote_id: - type: string - example: timeoff_balance_1234 + type: array + items: + $ref: '#/components/schemas/Address' + phone_numbers: + description: The phone numbers of the company + example: *ref_73 nullable: true - description: >- - The remote ID of the time off balance in the context of the 3rd - Party - remote_data: + type: array + items: + $ref: '#/components/schemas/Phone' + field_mappings: type: object - example: - raw_data: - additional_field: some value - nullable: true + example: *ref_74 description: >- - The remote data of the time off balance in the context of the 3rd - Party - remote_created_at: - type: string - example: '2024-06-15T12:00:00Z' + The custom field mappings of the company between the remote 3rd + party & Panora nullable: true - description: >- - The date when the time off balance was created in the 3rd party - system - created_at: + additionalProperties: true + required: + - name + UnifiedCrmContactOutput: + type: object + properties: + first_name: type: string - example: '2024-06-15T12:00:00Z' + description: The first name of the contact + example: John nullable: true - description: The created date of the time off balance record - modified_at: + last_name: type: string - example: '2024-06-15T12:00:00Z' + description: The last name of the contact + example: Doe nullable: true - description: The last modified date of the time off balance record - remote_was_deleted: - type: boolean - example: false + email_addresses: nullable: true - description: Indicates if the time off balance was deleted in the remote system - UnifiedHrisTimesheetEntryOutput: - type: object - properties: - hours_worked: - type: number - example: 40 + description: The email addresses of the contact + example: &ref_75 + - email: john.doe@example.com + type: WORK + type: array + items: + $ref: '#/components/schemas/Email' + phone_numbers: nullable: true - description: The number of hours worked - start_time: - format: date-time - type: string - example: '2024-10-01T08:00:00Z' + description: The phone numbers of the contact + example: &ref_76 + - phone: '1234567890' + type: WORK + type: array + items: + $ref: '#/components/schemas/Phone' + addresses: nullable: true - description: The start time of the timesheet entry - end_time: - format: date-time + description: The addresses of the contact + example: &ref_77 + - street: 123 Main St + city: Anytown + state: CA + zip: '12345' + country: USA + type: WORK + type: array + items: + $ref: '#/components/schemas/Address' + user_id: type: string - example: '2024-10-01T16:00:00Z' nullable: true - description: The end time of the timesheet entry - employee_id: - type: string + description: The UUID of the user who owns the contact example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the associated employee - remote_was_deleted: - type: boolean - example: false - description: Indicates if the timesheet entry was deleted in the remote system field_mappings: type: object - example: &ref_121 - custom_field_1: value1 - custom_field_2: value2 + example: &ref_78 + fav_dish: broccoli + fav_color: red nullable: true description: >- - The custom field mappings of the object between the remote 3rd party - & Panora + The custom field mappings of the contact between the remote 3rd + party & Panora + additionalProperties: true id: type: string + description: The UUID of the contact example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the timesheet entry record remote_id: type: string example: id_1 nullable: true - description: The remote ID of the timesheet entry - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' + description: The id of the contact in the context of the Crm 3rd Party + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red nullable: true - description: The date when the timesheet entry was created in the remote system + additionalProperties: true + description: The remote data of the contact in the context of the Crm 3rd Party created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' - description: The created date of the timesheet entry + nullable: true + description: The created date of the object modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' - description: The last modified date of the timesheet entry - remote_data: - type: object - example: - raw_data: - additional_field: some value nullable: true - description: >- - The remote data of the timesheet entry in the context of the 3rd - Party - UnifiedHrisTimesheetEntryInput: + description: The modified date of the object + required: + - first_name + - last_name + UnifiedCrmContactInput: type: object properties: - hours_worked: - type: number - example: 40 - nullable: true - description: The number of hours worked - start_time: - format: date-time - type: string - example: '2024-10-01T08:00:00Z' - nullable: true - description: The start time of the timesheet entry - end_time: - format: date-time - type: string - example: '2024-10-01T16:00:00Z' - nullable: true - description: The end time of the timesheet entry - employee_id: + first_name: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the associated employee - remote_was_deleted: - type: boolean - example: false - description: Indicates if the timesheet entry was deleted in the remote system - field_mappings: - type: object - example: *ref_121 + description: The first name of the contact + example: John nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - UnifiedMarketingautomationActionOutput: - type: object - properties: {} - UnifiedMarketingautomationActionInput: - type: object - properties: {} - UnifiedMarketingautomationAutomationOutput: - type: object - properties: {} - UnifiedMarketingautomationAutomationInput: - type: object - properties: {} - UnifiedMarketingautomationCampaignOutput: - type: object - properties: {} - UnifiedMarketingautomationCampaignInput: - type: object - properties: {} - UnifiedMarketingautomationContactOutput: - type: object - properties: {} - UnifiedMarketingautomationContactInput: - type: object - properties: {} - UnifiedMarketingautomationEmailOutput: - type: object - properties: {} - UnifiedMarketingautomationEventOutput: - type: object - properties: {} - UnifiedMarketingautomationListOutput: - type: object - properties: {} - UnifiedMarketingautomationListInput: - type: object - properties: {} - UnifiedMarketingautomationMessageOutput: - type: object - properties: {} - UnifiedMarketingautomationTemplateOutput: - type: object - properties: {} - UnifiedMarketingautomationTemplateInput: - type: object - properties: {} - UnifiedMarketingautomationUserOutput: - type: object - properties: {} - UnifiedAtsActivityOutput: - type: object - properties: - activity_type: + last_name: type: string - example: NOTE + description: The last name of the contact + example: Doe nullable: true - description: The type of activity. NOTE, EMAIL or OTHER - subject: + email_addresses: + nullable: true + description: The email addresses of the contact + example: *ref_75 + type: array + items: + $ref: '#/components/schemas/Email' + phone_numbers: + nullable: true + description: The phone numbers of the contact + example: *ref_76 + type: array + items: + $ref: '#/components/schemas/Phone' + addresses: + nullable: true + description: The addresses of the contact + example: *ref_77 + type: array + items: + $ref: '#/components/schemas/Address' + user_id: type: string - example: Email subject nullable: true - description: The subject of the activity - body: + description: The UUID of the user who owns the contact + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + field_mappings: + type: object + example: *ref_78 + nullable: true + description: >- + The custom field mappings of the contact between the remote 3rd + party & Panora + additionalProperties: true + required: + - first_name + - last_name + UnifiedCrmDealOutput: + type: object + properties: + name: type: string - example: Dear Diana, I love you + example: Huge Contract with Acme + description: The name of the deal nullable: true - description: The body of the activity - visibility: + description: type: string - example: PUBLIC + example: Contract with Sales Operations Team + description: The description of the deal + nullable: true + amount: + type: number + example: 1000 + description: The amount of the deal nullable: true - description: The visibility of the activity. ADMIN_ONLY, PUBLIC or PRIVATE - candidate_id: + user_id: type: string + nullable: true example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the user who is on the deal + stage_id: + type: string nullable: true - description: The UUID of the candidate - remote_created_at: + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the stage of the deal + company_id: type: string - format: date-time - example: '2024-10-01T12:00:00Z' nullable: true - description: The remote creation date of the activity + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the company tied to the deal field_mappings: type: object - example: &ref_122 + nullable: true + example: &ref_79 fav_dish: broccoli fav_color: red - additionalProperties: true - nullable: true description: >- - The custom field mappings of the object between the remote 3rd party - & Panora + The custom field mappings of the company between the remote 3rd + party & Panora + additionalProperties: true id: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the activity + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the deal remote_id: type: string - example: id_1 nullable: true - description: The remote ID of the activity in the context of the 3rd Party + example: id_1 + description: The id of the deal in the context of the Crm 3rd Party remote_data: type: object + nullable: true example: fav_dish: broccoli fav_color: red - nullable: true additionalProperties: true - description: The remote data of the activity in the context of the 3rd Party + description: The remote data of the deal in the context of the Crm 3rd Party created_at: format: date-time type: string - example: '2024-10-01T12:00:00Z' nullable: true + example: '2024-10-01T12:00:00Z' description: The created date of the object modified_at: format: date-time type: string - example: '2024-10-01T12:00:00Z' nullable: true + example: '2024-10-01T12:00:00Z' description: The modified date of the object - UnifiedAtsActivityInput: + required: + - name + - description + - amount + UnifiedCrmDealInput: type: object properties: - activity_type: + name: type: string - example: NOTE + example: Huge Contract with Acme + description: The name of the deal nullable: true - description: The type of activity. NOTE, EMAIL or OTHER - subject: + description: type: string - example: Email subject + example: Contract with Sales Operations Team + description: The description of the deal nullable: true - description: The subject of the activity - body: - type: string - example: Dear Diana, I love you + amount: + type: number + example: 1000 + description: The amount of the deal nullable: true - description: The body of the activity - visibility: + user_id: type: string - example: PUBLIC nullable: true - description: The visibility of the activity. ADMIN_ONLY, PUBLIC or PRIVATE - candidate_id: - type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the user who is on the deal + stage_id: + type: string nullable: true - description: The UUID of the candidate - remote_created_at: + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the stage of the deal + company_id: type: string - format: date-time - example: '2024-10-01T12:00:00Z' nullable: true - description: The remote creation date of the activity + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the company tied to the deal field_mappings: type: object - example: *ref_122 - additionalProperties: true nullable: true + example: *ref_79 description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - UnifiedAtsApplicationOutput: + The custom field mappings of the company between the remote 3rd + party & Panora + additionalProperties: true + required: + - name + - description + - amount + UnifiedCrmEngagementOutput: type: object properties: - applied_at: - format: date-time + content: type: string nullable: true - description: The application date - example: '2024-10-01T12:00:00Z' - rejected_at: - format: date-time + example: Meeting call with CTO + description: The content of the engagement + direction: type: string nullable: true - description: The rejection date - example: '2024-10-01T12:00:00Z' - offers: - nullable: true - description: The offers UUIDs for the application - example: &ref_123 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - - 12345678-1234-1234-1234-123456789012 - type: array - items: - type: string - source: + example: INBOUND + description: >- + The direction of the engagement. Authorized values are INBOUND or + OUTBOUND + subject: type: string + example: Technical features planning nullable: true - description: The source of the application - example: Source Name - credited_to: + description: The subject of the engagement + start_at: + format: date-time type: string nullable: true - description: The UUID of the person credited for the application - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - current_stage: + example: '2024-10-01T12:00:00Z' + description: The start time of the engagement + end_time: + format: date-time type: string nullable: true - description: The UUID of the current stage of the application - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - reject_reason: + example: '2024-10-01T22:00:00Z' + description: The end time of the engagement + type: type: string nullable: true - description: The rejection reason for the application - example: Candidate not experienced enough - candidate_id: + example: MEETING + description: >- + The type of the engagement. Authorized values are EMAIL, CALL or + MEETING + user_id: type: string nullable: true - description: The UUID of the candidate example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - job_id: + description: The UUID of the user tied to the engagement + company_id: type: string - description: The UUID of the job + nullable: true example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the company tied to the engagement + contacts: + nullable: true + example: &ref_80 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUIDs of contacts tied to the engagement object + type: array + items: + type: string field_mappings: type: object - example: &ref_124 + nullable: true + example: &ref_81 fav_dish: broccoli fav_color: red - additionalProperties: true - nullable: true description: >- - The custom field mappings of the object between the remote 3rd party - & Panora + The custom field mappings of the engagement between the remote 3rd + party & Panora + additionalProperties: true id: type: string nullable: true - description: The UUID of the application example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the engagement remote_id: type: string nullable: true - description: The remote ID of the application in the context of the 3rd Party example: id_1 + description: The id of the engagement in the context of the Crm 3rd Party remote_data: type: object example: fav_dish: broccoli fav_color: red - nullable: true additionalProperties: true - description: The remote data of the application in the context of the 3rd Party + nullable: true + description: >- + The remote data of the engagement in the context of the Crm 3rd + Party created_at: format: date-time type: string - example: '2024-10-01T12:00:00Z' nullable: true + example: '2024-10-01T12:00:00Z' description: The created date of the object modified_at: format: date-time type: string - example: '2024-10-01T12:00:00Z' nullable: true + example: '2024-10-01T12:00:00Z' description: The modified date of the object - remote_created_at: - format: date-time + required: + - type + UnifiedCrmEngagementInput: + type: object + properties: + content: type: string nullable: true - description: The remote created date of the object - remote_modified_at: - format: date-time + example: Meeting call with CTO + description: The content of the engagement + direction: type: string nullable: true - description: The remote modified date of the object - UnifiedAtsApplicationInput: - type: object - properties: - applied_at: - format: date-time + example: INBOUND + description: >- + The direction of the engagement. Authorized values are INBOUND or + OUTBOUND + subject: type: string + example: Technical features planning nullable: true - description: The application date - example: '2024-10-01T12:00:00Z' - rejected_at: + description: The subject of the engagement + start_at: format: date-time type: string nullable: true - description: The rejection date example: '2024-10-01T12:00:00Z' - offers: - nullable: true - description: The offers UUIDs for the application - example: *ref_123 - type: array - items: - type: string - source: + description: The start time of the engagement + end_time: + format: date-time type: string nullable: true - description: The source of the application - example: Source Name - credited_to: + example: '2024-10-01T22:00:00Z' + description: The end time of the engagement + type: type: string nullable: true - description: The UUID of the person credited for the application - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - current_stage: + example: MEETING + description: >- + The type of the engagement. Authorized values are EMAIL, CALL or + MEETING + user_id: type: string nullable: true - description: The UUID of the current stage of the application example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - reject_reason: - type: string - nullable: true - description: The rejection reason for the application - example: Candidate not experienced enough - candidate_id: + description: The UUID of the user tied to the engagement + company_id: type: string nullable: true - description: The UUID of the candidate - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - job_id: - type: string - description: The UUID of the job example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the company tied to the engagement + contacts: + nullable: true + example: *ref_80 + description: The UUIDs of contacts tied to the engagement object + type: array + items: + type: string field_mappings: type: object - example: *ref_124 - additionalProperties: true nullable: true + example: *ref_81 description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - UnifiedAtsAttachmentOutput: + The custom field mappings of the engagement between the remote 3rd + party & Panora + additionalProperties: true + required: + - type + UnifiedCrmNoteOutput: type: object properties: - file_url: - type: string - example: https://example.com/file.pdf - nullable: true - description: The URL of the file - file_name: + content: type: string - example: file.pdf + example: My notes taken during the meeting + description: The content of the note nullable: true - description: The name of the file - attachment_type: + user_id: type: string - example: RESUME + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the user tied to the note nullable: true - description: The type of the file - remote_created_at: + company_id: type: string - example: '2024-10-01T12:00:00Z' - format: date-time + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The remote creation date of the attachment - remote_modified_at: + description: The UUID of the company tied to the note + contact_id: type: string - example: '2024-10-01T12:00:00Z' - format: date-time + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the contact tied to the note nullable: true - description: The remote modification date of the attachment - candidate_id: + deal_id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the candidate + description: The UUID of the deal tied to the note field_mappings: type: object - example: &ref_125 + example: &ref_82 fav_dish: broccoli fav_color: red - additionalProperties: true nullable: true description: >- - The custom field mappings of the object between the remote 3rd party - & Panora + The custom field mappings of the note between the remote 3rd party & + Panora + additionalProperties: true id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the attachment + description: The UUID of the note remote_id: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: id_1 + description: The ID of the note in the context of the Crm 3rd Party nullable: true - description: The remote ID of the attachment remote_data: type: object example: @@ -14419,7 +8215,7 @@ components: fav_color: red nullable: true additionalProperties: true - description: The remote data of the attachment in the context of the 3rd Party + description: The remote data of the note in the context of the Crm 3rd Party created_at: format: date-time type: string @@ -14432,580 +8228,323 @@ components: example: '2024-10-01T12:00:00Z' nullable: true description: The modified date of the object - UnifiedAtsAttachmentInput: + required: + - content + UnifiedCrmNoteInput: type: object properties: - file_url: - type: string - example: https://example.com/file.pdf - nullable: true - description: The URL of the file - file_name: + content: type: string - example: file.pdf + example: My notes taken during the meeting + description: The content of the note nullable: true - description: The name of the file - attachment_type: + user_id: type: string - example: RESUME + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the user tied to the note nullable: true - description: The type of the file - remote_created_at: + company_id: type: string - example: '2024-10-01T12:00:00Z' - format: date-time + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The remote creation date of the attachment - remote_modified_at: + description: The UUID of the company tied to the note + contact_id: type: string - example: '2024-10-01T12:00:00Z' - format: date-time + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the contact tied to the note nullable: true - description: The remote modification date of the attachment - candidate_id: + deal_id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the candidate + description: The UUID of the deal tied to the note field_mappings: type: object - example: *ref_125 - additionalProperties: true + example: *ref_82 nullable: true description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - Url: - type: object - properties: - url: - type: string - nullable: true - description: The url. - url_type: - type: string - nullable: true - description: The url type. It takes [WEBSITE | BLOG | LINKEDIN | GITHUB | OTHER] + The custom field mappings of the note between the remote 3rd party & + Panora + additionalProperties: true required: - - url - - url_type - UnifiedAtsCandidateOutput: + - content + UnifiedCrmStageOutput: type: object properties: - first_name: - type: string - example: Joe - nullable: true - description: The first name of the candidate - last_name: - type: string - example: Doe - nullable: true - description: The last name of the candidate - company: - type: string - example: Acme - nullable: true - description: The company of the candidate - title: - type: string - example: Analyst - nullable: true - description: The title of the candidate - locations: - type: string - example: New York - nullable: true - description: The locations of the candidate - is_private: - type: boolean - example: false - nullable: true - description: Whether the candidate is private - email_reachable: - type: boolean - example: true - nullable: true - description: Whether the candidate is reachable by email - remote_created_at: - type: string - example: '2024-10-01T12:00:00Z' - format: date-time - nullable: true - description: The remote creation date of the candidate - remote_modified_at: - type: string - example: '2024-10-01T12:00:00Z' - format: date-time - nullable: true - description: The remote modification date of the candidate - last_interaction_at: + stage_name: type: string - example: '2024-10-01T12:00:00Z' - format: date-time - nullable: true - description: The last interaction date with the candidate - attachments: - type: array - items: &ref_126 - oneOf: - - type: string - - $ref: '#/components/schemas/UnifiedAtsAttachmentOutput' - example: &ref_127 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The attachments UUIDs of the candidate - applications: - type: array - items: &ref_128 - oneOf: - - type: string - - $ref: '#/components/schemas/UnifiedAtsApplicationOutput' - example: &ref_129 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The applications UUIDs of the candidate - tags: - type: array - items: &ref_130 - oneOf: - - type: string - - $ref: '#/components/schemas/UnifiedAtsTagOutput' - example: &ref_131 - - tag_1 - - tag_2 - nullable: true - description: The tags of the candidate - urls: - example: &ref_132 - - url: mywebsite.com - url_type: WEBSITE - nullable: true - description: >- - The urls of the candidate, possible values for Url type are WEBSITE, - BLOG, LINKEDIN, GITHUB, or OTHER - type: array - items: - $ref: '#/components/schemas/Url' - phone_numbers: - example: &ref_133 - - phone_number: '+33660688899' - phone_type: WORK - nullable: true - description: The phone numbers of the candidate - type: array - items: - $ref: '#/components/schemas/Phone' - email_addresses: - example: &ref_134 - - email_address: joedoe@gmail.com - email_address_type: WORK + example: Qualified + description: The name of the stage nullable: true - description: The email addresses of the candidate - type: array - items: - $ref: '#/components/schemas/Email' field_mappings: type: object - example: &ref_135 + example: fav_dish: broccoli fav_color: red - additionalProperties: true - nullable: true description: >- - The custom field mappings of the object between the remote 3rd party + The custom field mappings of the stage between the remote 3rd party & Panora + nullable: true + additionalProperties: true id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the stage nullable: true - description: The UUID of the candidate remote_id: type: string example: id_1 + description: The ID of the stage in the context of the Crm 3rd Party nullable: true - description: The id of the candidate in the context of the 3rd Party remote_data: type: object example: fav_dish: broccoli fav_color: red + description: The remote data of the stage in the context of the Crm 3rd Party nullable: true additionalProperties: true - description: The remote data of the candidate in the context of the 3rd Party created_at: - format: date-time - type: string + type: object example: '2024-10-01T12:00:00Z' - nullable: true description: The created date of the object + nullable: true modified_at: - format: date-time - type: string + type: object example: '2024-10-01T12:00:00Z' - nullable: true description: The modified date of the object - UnifiedAtsCandidateInput: + nullable: true + required: + - stage_name + UnifiedCrmTaskOutput: type: object properties: - first_name: - type: string - example: Joe - nullable: true - description: The first name of the candidate - last_name: - type: string - example: Doe - nullable: true - description: The last name of the candidate - company: + subject: type: string - example: Acme + example: Answer customers + description: The subject of the task nullable: true - description: The company of the candidate - title: + content: type: string - example: Analyst + example: Prepare email campaign + description: The content of the task nullable: true - description: The title of the candidate - locations: + status: type: string - example: New York - nullable: true - description: The locations of the candidate - is_private: - type: boolean - example: false - nullable: true - description: Whether the candidate is private - email_reachable: - type: boolean - example: true + example: PENDING + description: The status of the task. Authorized values are PENDING, COMPLETED. nullable: true - description: Whether the candidate is reachable by email - remote_created_at: + due_date: type: string example: '2024-10-01T12:00:00Z' - format: date-time + description: The due date of the task nullable: true - description: The remote creation date of the candidate - remote_modified_at: + finished_date: type: string example: '2024-10-01T12:00:00Z' - format: date-time + description: The finished date of the task nullable: true - description: The remote modification date of the candidate - last_interaction_at: + user_id: type: string - example: '2024-10-01T12:00:00Z' - format: date-time - nullable: true - description: The last interaction date with the candidate - attachments: - type: array - items: *ref_126 - example: *ref_127 - nullable: true - description: The attachments UUIDs of the candidate - applications: - type: array - items: *ref_128 - example: *ref_129 - nullable: true - description: The applications UUIDs of the candidate - tags: - type: array - items: *ref_130 - example: *ref_131 - nullable: true - description: The tags of the candidate - urls: - example: *ref_132 - nullable: true - description: >- - The urls of the candidate, possible values for Url type are WEBSITE, - BLOG, LINKEDIN, GITHUB, or OTHER - type: array - items: - $ref: '#/components/schemas/Url' - phone_numbers: - example: *ref_133 - nullable: true - description: The phone numbers of the candidate - type: array - items: - $ref: '#/components/schemas/Phone' - email_addresses: - example: *ref_134 + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the user tied to the task nullable: true - description: The email addresses of the candidate - type: array - items: - $ref: '#/components/schemas/Email' - field_mappings: - type: object - example: *ref_135 - additionalProperties: true + company_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the company tied to the task nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - UnifiedAtsDepartmentOutput: - type: object - properties: - name: + deal_id: type: string - example: Sales + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the deal tied to the task nullable: true - description: The name of the department field_mappings: type: object - example: + example: &ref_83 fav_dish: broccoli - fav_color: red - additionalProperties: true - nullable: true + fav_color: red description: >- - The custom field mappings of the object between the remote 3rd party - & Panora + The custom field mappings of the task between the remote 3rd party & + Panora + nullable: true + additionalProperties: true id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the task nullable: true - description: The UUID of the department remote_id: type: string example: id_1 + description: The ID of the task in the context of the Crm 3rd Party nullable: true - description: The remote ID of the department in the context of the 3rd Party remote_data: type: object example: key1: value1 key2: 42 key3: true + description: The remote data of the task in the context of the Crm 3rd Party nullable: true additionalProperties: true - description: The remote data of the department in the context of the 3rd Party created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' - nullable: true description: The created date of the object + nullable: true modified_at: format: date-time type: string - example: '2023-10-01T12:00:00Z' - nullable: true + example: '2024-10-01T12:00:00Z' description: The modified date of the object - UnifiedAtsInterviewOutput: + nullable: true + required: + - subject + - content + - status + UnifiedCrmTaskInput: type: object properties: - status: + subject: type: string - example: SCHEDULED + example: Answer customers + description: The subject of the task nullable: true - description: The status of the interview - application_id: + content: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: Prepare email campaign + description: The content of the task nullable: true - description: The UUID of the application - job_interview_stage_id: + status: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: PENDING + description: The status of the task. Authorized values are PENDING, COMPLETED. nullable: true - description: The UUID of the job interview stage - organized_by: + due_date: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: '2024-10-01T12:00:00Z' + description: The due date of the task nullable: true - description: The UUID of the organizer - interviewers: - example: &ref_136 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + finished_date: + type: string + example: '2024-10-01T12:00:00Z' + description: The finished date of the task nullable: true - description: The UUIDs of the interviewers - type: array - items: - type: string - location: + user_id: type: string - example: San Francisco + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the user tied to the task nullable: true - description: The location of the interview - start_at: - format: date-time + company_id: type: string - example: '2024-10-01T12:00:00Z' + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the company tied to the task nullable: true - description: The start date and time of the interview - end_at: - format: date-time + deal_id: type: string - example: '2024-10-01T12:00:00Z' + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the deal tied to the task nullable: true - description: The end date and time of the interview - remote_created_at: - format: date-time + field_mappings: + type: object + example: *ref_83 + description: >- + The custom field mappings of the task between the remote 3rd party & + Panora + nullable: true + additionalProperties: true + required: + - subject + - content + - status + UnifiedCrmUserOutput: + type: object + properties: + name: type: string - example: '2024-10-01T12:00:00Z' + example: Jane Doe + description: The name of the user nullable: true - description: The remote creation date of the interview - remote_updated_at: - format: date-time + email: type: string - example: '2024-10-01T12:00:00Z' + example: jane.doe@example.com + description: The email of the user nullable: true - description: The remote modification date of the interview field_mappings: type: object - example: &ref_137 + example: fav_dish: broccoli fav_color: red - additionalProperties: true - nullable: true description: >- - The custom field mappings of the object between the remote 3rd party - & Panora + The custom field mappings of the user between the remote 3rd party & + Panora + nullable: true + additionalProperties: true id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the user nullable: true - description: The UUID of the interview remote_id: type: string example: id_1 + description: The id of the user in the context of the Crm 3rd Party nullable: true - description: The remote ID of the interview in the context of the 3rd Party remote_data: type: object example: fav_dish: broccoli fav_color: red + description: The remote data of the user in the context of the Crm 3rd Party nullable: true additionalProperties: true - description: The remote data of the interview in the context of the 3rd Party created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' - nullable: true description: The created date of the object + nullable: true modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' - nullable: true description: The modified date of the object - UnifiedAtsInterviewInput: - type: object - properties: - status: - type: string - example: SCHEDULED - nullable: true - description: The status of the interview - application_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the application - job_interview_stage_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the job interview stage - organized_by: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the organizer - interviewers: - example: *ref_136 - nullable: true - description: The UUIDs of the interviewers - type: array - items: - type: string - location: - type: string - example: San Francisco - nullable: true - description: The location of the interview - start_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The start date and time of the interview - end_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' nullable: true - description: The end date and time of the interview - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The remote creation date of the interview - remote_updated_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The remote modification date of the interview - field_mappings: - type: object - example: *ref_137 - additionalProperties: true - nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - UnifiedAtsJobinterviewstageOutput: + required: + - name + - email + UnifiedTicketingCollectionOutput: type: object properties: name: type: string - example: Second Call - nullable: true - description: The name of the job interview stage - stage_order: - type: number - example: 1 + example: My Personal Collection nullable: true - description: The order of the stage - job_id: + description: The name of the collection + description: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: Collect issues nullable: true - description: The UUID of the job - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - additionalProperties: true + description: The description of the collection + collection_type: + type: string + example: PROJECT nullable: true description: >- - The custom field mappings of the object between the remote 3rd party - & Panora + The type of the collection. Authorized values are either PROJECT or + LIST id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the job interview stage + description: The UUID of the collection remote_id: type: string example: id_1 nullable: true - description: >- - The remote ID of the job interview stage in the context of the 3rd - Party + description: The id of the collection in the context of the 3rd Party remote_data: type: object example: @@ -15013,9 +8552,7 @@ components: fav_color: red nullable: true additionalProperties: true - description: >- - The remote data of the job interview stage in the context of the 3rd - Party + description: The remote data of the collection in the context of the 3rd Party created_at: format: date-time type: string @@ -15028,236 +8565,173 @@ components: example: '2024-10-01T12:00:00Z' nullable: true description: The modified date of the object - UnifiedAtsJobOutput: + required: + - name + UnifiedTicketingCommentOutput: type: object properties: - name: + body: type: string - example: Financial Analyst nullable: true - description: The name of the job - description: + example: Assigned to Eric ! + description: The body of the comment + html_body: type: string - example: Extract financial data and write detailed investment thesis nullable: true - description: The description of the job - code: - type: string - example: JOB123 + example:

Assigned to Eric !

+ description: The html body of the comment + is_private: + type: boolean nullable: true - description: The code of the job - status: + example: false + description: The public status of the comment + creator_type: type: string - example: OPEN nullable: true - description: The status of the job - type: + example: USER + description: >- + The creator type of the comment. Authorized values are either USER + or CONTACT + ticket_id: type: string - example: POSTING - nullable: true - description: The type of the job - confidential: - type: boolean - example: true - nullable: true - description: Whether the job is confidential - departments: - example: - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The departments UUIDs associated with the job - type: array - items: - type: string - offices: - example: - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The offices UUIDs associated with the job - type: array - items: - type: string - managers: - example: - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The managers UUIDs associated with the job - type: array - items: - type: string - recruiters: - example: - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The recruiters UUIDs associated with the job - type: array - items: - type: string - remote_created_at: + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the ticket the comment is tied to + contact_id: type: string - example: '2024-10-01T12:00:00Z' - format: date-time nullable: true - description: The remote creation date of the job - remote_updated_at: + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: >- + The UUID of the contact which the comment belongs to (if no user_id + specified) + user_id: type: string - example: '2024-10-01T12:00:00Z' - format: date-time - nullable: true - description: The remote modification date of the job - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - additionalProperties: true nullable: true + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f description: >- - The custom field mappings of the object between the remote 3rd party - & Panora + The UUID of the user which the comment belongs to (if no contact_id + specified) + attachments: + type: array + items: *ref_84 + nullable: true + example: *ref_85 + description: The attachements UUIDs tied to the comment id: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the job + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the comment remote_id: type: string - example: id_1 nullable: true - description: The remote ID of the job in the context of the 3rd Party + example: id_1 + description: The id of the comment in the context of the 3rd Party remote_data: type: object - example: - key1: value1 - key2: 42 - key3: true nullable: true + example: + fav_dish: broccoli + fav_color: red additionalProperties: true - description: The remote data of the job in the context of the 3rd Party + description: The remote data of the comment in the context of the 3rd Party created_at: format: date-time type: string - example: '2024-10-01T12:00:00Z' nullable: true + example: '2024-10-01T12:00:00Z' description: The created date of the object modified_at: format: date-time type: string - example: '2023-10-01T12:00:00Z' nullable: true + example: '2024-10-01T12:00:00Z' description: The modified date of the object - UnifiedAtsOfferOutput: + required: + - body + UnifiedTicketingTagOutput: type: object properties: - created_by: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the creator - nullable: true - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The remote creation date of the offer - nullable: true - closed_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The closing date of the offer - nullable: true - sent_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The sending date of the offer - nullable: true - start_date: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The start date of the offer - nullable: true - status: - type: string - example: DRAFT - description: The status of the offer - nullable: true - application_id: + name: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the application + example: urgent_tag nullable: true + description: The name of the tag field_mappings: type: object example: fav_dish: broccoli fav_color: red - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora nullable: true + description: >- + The custom field mappings of the tag between the remote 3rd party & + Panora additionalProperties: true id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the offer nullable: true + description: The UUID of the tag remote_id: type: string example: id_1 - description: The remote ID of the offer in the context of the 3rd Party + description: The remote ID of the tag in the context of the 3rd Party nullable: true remote_data: type: object example: fav_dish: broccoli fav_color: red - description: The remote data of the offer in the context of the 3rd Party nullable: true additionalProperties: true + description: The remote data of the tag in the context of the 3rd Party created_at: - type: object + format: date-time + type: string example: '2024-10-01T12:00:00Z' - description: The created date of the object + description: The created date of the tag nullable: true modified_at: - type: object + format: date-time + type: string example: '2024-10-01T12:00:00Z' - description: The modified date of the object + description: The modified date of the tag nullable: true - UnifiedAtsOfficeOutput: + required: + - name + UnifiedTicketingTeamOutput: type: object properties: name: type: string - example: Condo Office 5th + example: My team nullable: true - description: The name of the office - location: + description: The name of the team + description: type: string - example: New York + example: Internal members nullable: true - description: The location of the office + description: The description of the team field_mappings: type: object example: fav_dish: broccoli fav_color: red - additionalProperties: true nullable: true description: >- - The custom field mappings of the object between the remote 3rd party - & Panora + The custom field mappings of the team between the remote 3rd party & + Panora + additionalProperties: true id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the office + nullable: true + description: The UUID of the team remote_id: type: string example: id_1 nullable: true - description: The remote ID of the office in the context of the 3rd Party + description: The id of the team in the context of the 3rd Party remote_data: type: object example: @@ -15265,7 +8739,7 @@ components: fav_color: red nullable: true additionalProperties: true - description: The remote data of the office in the context of the 3rd Party + description: The remote data of the team in the context of the 3rd Party created_at: format: date-time type: string @@ -15278,330 +8752,547 @@ components: example: '2024-10-01T12:00:00Z' nullable: true description: The modified date of the object - UnifiedAtsRejectreasonOutput: + required: + - name + LinkedUserResponse: type: object properties: - name: + id_linked_user: type: string - example: Candidate inexperienced + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The name of the reject reason - field_mappings: - type: object + linked_user_origin_id: + type: string + example: id_1 + nullable: true + alias: + type: string + example: acme + nullable: true + id_project: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + required: + - id_linked_user + - linked_user_origin_id + - alias + - id_project + CreateLinkedUserDto: + type: object + properties: + linked_user_origin_id: + type: string + description: The id of the user in the context of your own software + example: id_1 + alias: + type: string + nullable: true + description: Your company alias + example: acme + required: + - linked_user_origin_id + - alias + CreateBatchLinkedUserDto: + type: object + properties: + linked_user_origin_ids: + nullable: true + description: The ids of the users in the context of your own software example: - fav_dish: broccoli - fav_color: red - additionalProperties: true + - id_1 + type: array + items: + type: string + alias: + type: string nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - id: + description: Your company alias + example: acme + required: + - linked_user_origin_ids + - alias + ProjectResponse: + type: object + properties: + id_project: + type: string + example: 123e4567-e89b-12d3-a456-426614174000 + description: Unique identifier for the project + name: + type: string + example: My Project + description: Name of the project + sync_mode: + type: string + example: automatic + description: Synchronization mode of the project + pull_frequency: + type: number + example: 3600 + description: Frequency of pulling data in seconds + redirect_url: + type: string + example: https://example.com/redirect + description: Redirect URL for the project + id_user: + type: string + example: 123e4567-e89b-12d3-a456-426614174001 + description: User ID associated with the project + id_connector_set: + type: string + example: 123e4567-e89b-12d3-a456-426614174002 + description: Connector set ID associated with the project + required: + - id_project + - name + - sync_mode + - pull_frequency + - redirect_url + - id_user + - id_connector_set + CreateProjectDto: + type: object + properties: + name: + type: string + example: Project Name + description: The name of the project + id_organization: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The organization ID + id_user: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The user ID + required: + - name + - id_user + CustomFieldResponse: + type: object + properties: + id_attribute: + type: string + nullable: true + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: Attribute Id + status: + type: string + nullable: true + example: '' + description: Attribute Status + ressource_owner_type: + type: string + example: '' + nullable: true + description: Attribute Ressource Owner Type + slug: + type: string + nullable: true + example: fav_dish + description: Attribute Slug + description: + type: string + nullable: true + example: My favorite dish + description: Attribute Description + data_type: + type: string + nullable: true + example: string + enum: + - string + - number + description: Attribute Data Type + remote_id: + type: string + nullable: true + example: id_1 + description: Attribute Remote Id + source: + type: string + nullable: true + example: hubspot + description: Attribute Source + id_entity: + type: string + nullable: true + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: Attribute Entity Id + id_project: type: string nullable: true - description: The UUID of the reject reason example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - remote_id: + description: Attribute Project Id + scope: type: string nullable: true - description: The remote ID of the reject reason in the context of the 3rd Party - example: id_1 - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red + example: '' + description: Attribute Scope + id_consumer: + type: string nullable: true - additionalProperties: true - description: The remote data of the reject reason in the context of the 3rd Party + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: Attribute Consumer Id created_at: format: date-time type: string - example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the object + example: '2024-10-01T12:00:00Z' + description: Attribute Created Date modified_at: format: date-time type: string - example: '2024-10-01T12:00:00Z' nullable: true - description: The modified date of the object - UnifiedAtsScorecardOutput: + example: '2024-10-01T12:00:00Z' + description: Attribute Modified Date + required: + - id_attribute + - status + - ressource_owner_type + - slug + - description + - data_type + - remote_id + - source + - id_entity + - id_project + - scope + - id_consumer + - created_at + - modified_at + DefineTargetFieldDto: type: object properties: - overall_recommendation: + object_type_owner: type: string - example: 'YES' + example: company + enum: + - company + - contact + - deal + - lead + - note + - task + - engagement + - stage + - user nullable: true - description: The overall recommendation - application_id: + name: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the application - interview_id: + example: fav_dish + description: The name of the target field + description: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the interview - remote_created_at: + example: My favorite dish + description: The description of the target field + data_type: type: string - example: '2024-10-01T12:00:00Z' - format: date-time nullable: true - description: The remote creation date of the scorecard - submitted_at: + example: string + enum: + - string + - number + description: The data type of the target field + required: + - object_type_owner + - name + - description + - data_type + CustomFieldCreateDto: + type: object + properties: + object_type_owner: type: string - example: '2024-10-01T12:00:00Z' - format: date-time + example: company + enum: + - company + - contact + - deal + - lead + - note + - task + - engagement + - stage + - user nullable: true - description: The submission date of the scorecard - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - additionalProperties: true + name: + type: string nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - id: + example: my_favorite_dish + description: The name of the custom field + description: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the scorecard - remote_id: + nullable: true + example: Favorite Dish + description: The description of the custom field + data_type: type: string - example: id_1 + example: string nullable: true - description: The remote ID of the scorecard in the context of the 3rd Party - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red + enum: + - string + - number + description: The data type of the custom field + source_custom_field_id: + type: string nullable: true - additionalProperties: true - description: The remote data of the scorecard in the context of the 3rd Party - created_at: - format: date-time + example: id_1 + description: The source custom field ID + source_provider: type: string - example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the object - modified_at: - format: date-time + example: hubspot + description: The name of the source software/provider + linked_user_id: type: string - example: '2024-10-01T12:00:00Z' nullable: true - description: The modified date of the object - UnifiedAtsTagOutput: + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The linked user ID + required: + - object_type_owner + - name + - description + - data_type + - source_custom_field_id + - source_provider + - linked_user_id + MapFieldToProviderDto: type: object properties: - name: + attributeId: type: string - example: Important nullable: true - description: The name of the tag - id_ats_candidate: - type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the candidate - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - additionalProperties: true - nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - id: + description: The attribute ID + source_custom_field_id: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the tag - remote_id: - type: string example: id_1 - nullable: true - description: The remote ID of the tag in the context of the 3rd Party - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true - additionalProperties: true - description: The remote data of the tag in the context of the 3rd Party - created_at: - format: date-time + description: The source custom field ID + source_provider: type: string nullable: true - example: '2024-10-01T12:00:00Z' - description: The creation date of the tag - modified_at: - format: date-time + example: hubspot + description: The source provider + linked_user_id: type: string nullable: true - example: '2024-10-01T12:00:00Z' - description: The modification date of the tag - UnifiedAtsUserOutput: + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The linked user ID + required: + - attributeId + - source_custom_field_id + - source_provider + - linked_user_id + EventResponse: type: object properties: - first_name: + id_event: type: string - example: John - description: The first name of the user - nullable: true - last_name: + example: 123e4567-e89b-12d3-a456-426614174000 + description: Unique identifier for the event + id_connection: type: string - example: Doe - description: The last name of the user - nullable: true - email: + example: 123e4567-e89b-12d3-a456-426614174001 + description: Connection ID associated with the event + id_project: type: string - example: john.doe@example.com - description: The email of the user - nullable: true - disabled: - type: boolean - example: false - description: Whether the user is disabled - nullable: true - access_role: + example: 123e4567-e89b-12d3-a456-426614174002 + description: Project ID associated with the event + type: + type: string + example: connection.created + enum: + - crm.contact.created + - crm.contact.pulled + - crm.company.created + - crm.company.pulled + - crm.deal.created + - crm.deal.pulled + - crm.engagement.created + - crm.engagement.pulled + - crm.note.created + - crm.note.pulled + - crm.stage.pulled + - crm.task.pulled + - crm.task.created + - crm.user.pulled + - ticketing.ticket.created + - ticketing.ticket.pulled + - ticketing.comment.created + - ticketing.comment.pulled + - ticketing.attachment.created + - ticketing.attachment.pulled + - ticketing.collection.pulled + - ticketing.account.pulled + - ticketing.contact.pulled + - ticketing.tag.pulled + - ticketing.team.pulled + - ticketing.user.pulled + - ats.activity.created + - ats.activity.pulled + - ats.application.created + - ats.application.pulled + - ats.attachment.created + - ats.attachment.pulled + - ats.candidate.created + - ats.candidate.pulled + - ats.department.pulled + - ats.eecos.pulled + - ats.interview.created + - ats.interview.pulled + - ats.job.pulled + - ats.jobinterviewstage.pulled + - ats.offer.created + - ats.office.pulled + - ats.rejectreason.pulled + - ats.scorecard.pulled + - ats.tag.pulled + - ats.user.pulled + - filestorage.file.created + - filestorage.file.pulled + - filestorage.folder.created + - filestorage.folder.pulled + - filestorage.group.pulled + - filestorage.user.pulled + - filestorage.drive.pulled + - filestorage.permission.pulled + - filestorage.sharedlink.pulled + - connection.created + description: Scope of the event + status: type: string - example: ADMIN - description: The access role of the user - nullable: true - remote_created_at: - format: date-time + example: success + enum: + - success + - fail + description: Status of the event + direction: type: string - example: '2024-10-01T12:00:00Z' - description: The remote creation date of the user - nullable: true - remote_modified_at: - format: date-time + example: '0' + description: Direction of the event + method: type: string - example: '2024-10-01T12:00:00Z' - description: The remote modification date of the user - nullable: true - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - nullable: true - additionalProperties: true - id: + example: POST + enum: + - GET + - POST + - PUT + - DELETE + description: HTTP method used for the event + url: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the user - nullable: true - remote_id: + example: /crm/companies + description: URL associated with the event + provider: type: string - example: id_1 - description: The remote ID of the user in the context of the 3rd Party - nullable: true - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - description: The remote data of the user in the context of the 3rd Party - nullable: true - additionalProperties: true - created_at: + example: hubspot + description: Provider associated with the event + timestamp: format: date-time type: string example: '2024-10-01T12:00:00Z' - description: The created date of the object - nullable: true - modified_at: - format: date-time + description: Timestamp of the event + id_linked_user: type: string - example: '2024-10-01T12:00:00Z' - description: The modified date of the object - nullable: true - UnifiedAtsEeocsOutput: + example: 123e4567-e89b-12d3-a456-426614174003 + description: Linked user ID associated with the event + required: + - id_event + - id_connection + - id_project + - type + - status + - direction + - method + - url + - provider + - timestamp + - id_linked_user + PassThroughRequestDto: type: object properties: - candidate_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the candidate - submitted_at: - type: string - example: '2024-10-01T12:00:00Z' - format: date-time - nullable: true - description: The submission date of the EEOC - race: - type: string - example: AMERICAN_INDIAN_OR_ALASKAN_NATIVE - nullable: true - description: The race of the candidate - gender: - type: string - example: MALE - nullable: true - description: The gender of the candidate - veteran_status: + method: type: string - example: I_AM_NOT_A_PROTECTED_VETERAN - nullable: true - description: The veteran status of the candidate - disability_status: + enum: + - GET + - POST + path: type: string - example: YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY nullable: true - description: The disability status of the candidate - field_mappings: + data: type: object - example: - fav_dish: broccoli - fav_color: red - additionalProperties: true - nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party - & Panora - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the EEOC - remote_id: - type: string - example: id_1 + request_format: + oneOf: + - type: object + additionalProperties: true + - type: array + items: + type: object + additionalProperties: true nullable: true - description: The remote ID of the EEOC in the context of the 3rd Party - remote_data: + overrideBaseUrl: type: object - example: - fav_dish: broccoli - fav_color: red - nullable: true additionalProperties: true - description: The remote data of the EEOC in the context of the 3rd Party - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The created date of the object - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' nullable: true - description: The modified date of the object + required: + - method + - path + - data + - request_format + - overrideBaseUrl + UnifiedMarketingautomationActionOutput: + type: object + properties: {} + UnifiedMarketingautomationActionInput: + type: object + properties: {} + UnifiedMarketingautomationAutomationOutput: + type: object + properties: {} + UnifiedMarketingautomationAutomationInput: + type: object + properties: {} + UnifiedMarketingautomationCampaignOutput: + type: object + properties: {} + UnifiedMarketingautomationCampaignInput: + type: object + properties: {} + UnifiedMarketingautomationContactOutput: + type: object + properties: {} + UnifiedMarketingautomationContactInput: + type: object + properties: {} + UnifiedMarketingautomationEmailOutput: + type: object + properties: {} + UnifiedMarketingautomationEventOutput: + type: object + properties: {} + UnifiedMarketingautomationListOutput: + type: object + properties: {} + UnifiedMarketingautomationListInput: + type: object + properties: {} + UnifiedMarketingautomationMessageOutput: + type: object + properties: {} + UnifiedMarketingautomationTemplateOutput: + type: object + properties: {} + UnifiedMarketingautomationTemplateInput: + type: object + properties: {} + UnifiedMarketingautomationUserOutput: + type: object + properties: {} UnifiedAccountingAccountOutput: type: object properties: @@ -15657,7 +9348,7 @@ components: description: The UUID of the associated company info field_mappings: type: object - example: &ref_138 + example: &ref_86 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -15748,7 +9439,7 @@ components: description: The UUID of the associated company info field_mappings: type: object - example: *ref_138 + example: *ref_86 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -15864,7 +9555,7 @@ components: description: The UUID of the associated account field_mappings: type: object - example: &ref_139 + example: &ref_87 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -15920,7 +9611,7 @@ components: description: The UUID of the associated account field_mappings: type: object - example: *ref_139 + example: *ref_87 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -16320,7 +10011,7 @@ components: description: The UUID of the associated company info field_mappings: type: object - example: &ref_140 + example: &ref_88 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -16406,7 +10097,7 @@ components: description: The UUID of the associated company info field_mappings: type: object - example: *ref_140 + example: *ref_88 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -16596,7 +10287,7 @@ components: nullable: true description: The UUID of the associated company info tracking_categories: - example: &ref_141 + example: &ref_89 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: The UUIDs of the tracking categories associated with the expense @@ -16610,7 +10301,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: &ref_142 + example: &ref_90 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -16707,7 +10398,7 @@ components: nullable: true description: The UUID of the associated company info tracking_categories: - example: *ref_141 + example: *ref_89 nullable: true description: The UUIDs of the tracking categories associated with the expense type: array @@ -16720,7 +10411,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: *ref_142 + example: *ref_90 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -16894,7 +10585,7 @@ components: nullable: true description: The UUID of the associated accounting period tracking_categories: - example: &ref_143 + example: &ref_91 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true @@ -16909,7 +10600,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: &ref_144 + example: &ref_92 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -17038,7 +10729,7 @@ components: nullable: true description: The UUID of the associated accounting period tracking_categories: - example: *ref_143 + example: *ref_91 nullable: true description: The UUIDs of the tracking categories associated with the invoice type: array @@ -17051,7 +10742,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: *ref_144 + example: *ref_92 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -17148,7 +10839,7 @@ components: nullable: true description: The date of the transaction payments: - example: &ref_145 + example: &ref_93 - payment1 - payment2 nullable: true @@ -17157,7 +10848,7 @@ components: items: type: string applied_payments: - example: &ref_146 + example: &ref_94 - appliedPayment1 - appliedPayment2 nullable: true @@ -17191,7 +10882,7 @@ components: nullable: true description: The journal number tracking_categories: - example: &ref_147 + example: &ref_95 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: >- @@ -17217,7 +10908,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: &ref_148 + example: &ref_96 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -17277,14 +10968,14 @@ components: nullable: true description: The date of the transaction payments: - example: *ref_145 + example: *ref_93 nullable: true description: The payments associated with the journal entry type: array items: type: string applied_payments: - example: *ref_146 + example: *ref_94 nullable: true description: The applied payments for the journal entry type: array @@ -17316,7 +11007,7 @@ components: nullable: true description: The journal number tracking_categories: - example: *ref_147 + example: *ref_95 nullable: true description: >- The UUIDs of the tracking categories associated with the journal @@ -17341,7 +11032,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: *ref_148 + example: *ref_96 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -17401,7 +11092,7 @@ components: nullable: true description: The UUID of the associated accounting period tracking_categories: - example: &ref_149 + example: &ref_97 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: The UUIDs of the tracking categories associated with the payment @@ -17415,7 +11106,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: &ref_150 + example: &ref_98 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -17512,7 +11203,7 @@ components: nullable: true description: The UUID of the associated accounting period tracking_categories: - example: *ref_149 + example: *ref_97 nullable: true description: The UUIDs of the tracking categories associated with the payment type: array @@ -17525,7 +11216,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: *ref_150 + example: *ref_98 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -17657,7 +11348,7 @@ components: nullable: true description: The exchange rate applied to the purchase order tracking_categories: - example: &ref_151 + example: &ref_99 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: >- @@ -17678,7 +11369,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: &ref_152 + example: &ref_100 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -17796,7 +11487,7 @@ components: nullable: true description: The exchange rate applied to the purchase order tracking_categories: - example: *ref_151 + example: *ref_99 nullable: true description: >- The UUIDs of the tracking categories associated with the purchase @@ -17816,7 +11507,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: *ref_152 + example: *ref_100 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -18209,7 +11900,7 @@ components: description: The UUID of the permission tied to the folder field_mappings: type: object - example: &ref_153 + example: &ref_101 fav_dish: broccoli fav_color: red additionalProperties: true @@ -18300,7 +11991,7 @@ components: description: The UUID of the permission tied to the folder field_mappings: type: object - example: *ref_153 + example: *ref_101 additionalProperties: true nullable: true description: >- @@ -18467,7 +12158,7 @@ components: nullable: true description: The status of the product. Either ACTIVE, DRAFT OR ARCHIVED. images_urls: - example: &ref_154 + example: &ref_102 - https://myproduct/image nullable: true description: The URLs of the product images @@ -18485,7 +12176,7 @@ components: nullable: true description: The vendor of the product variants: - example: &ref_155 + example: &ref_103 - title: teeshirt price: 20 sku: '3' @@ -18497,7 +12188,7 @@ components: items: $ref: '#/components/schemas/Variant' tags: - example: &ref_156 + example: &ref_104 - tag_1 nullable: true description: The tags associated with the product @@ -18506,7 +12197,7 @@ components: type: string field_mappings: type: object - example: &ref_157 + example: &ref_105 fav_dish: broccoli fav_color: red nullable: true @@ -18559,7 +12250,7 @@ components: nullable: true description: The status of the product. Either ACTIVE, DRAFT OR ARCHIVED. images_urls: - example: *ref_154 + example: *ref_102 nullable: true description: The URLs of the product images type: array @@ -18576,13 +12267,13 @@ components: nullable: true description: The vendor of the product variants: - example: *ref_155 + example: *ref_103 description: The variants of the product type: array items: $ref: '#/components/schemas/Variant' tags: - example: *ref_156 + example: *ref_104 nullable: true description: The tags associated with the product type: array @@ -18590,7 +12281,7 @@ components: type: string field_mappings: type: object - example: *ref_157 + example: *ref_105 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -18652,7 +12343,7 @@ components: description: The UUID of the customer associated with the order items: nullable: true - example: &ref_158 + example: &ref_106 - remote_id: '12345' product_id: prod_001 variant_id: var_001 @@ -18683,7 +12374,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: &ref_159 + example: &ref_107 fav_dish: broccoli fav_color: red nullable: true @@ -18774,14 +12465,14 @@ components: description: The UUID of the customer associated with the order items: nullable: true - example: *ref_158 + example: *ref_106 description: The items in the order type: array items: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: *ref_159 + example: *ref_107 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -18958,7 +12649,7 @@ components: field_mappings: type: object nullable: true - example: &ref_160 + example: &ref_108 fav_dish: broccoli fav_color: red description: >- @@ -19030,7 +12721,7 @@ components: field_mappings: type: object nullable: true - example: *ref_160 + example: *ref_108 description: >- The custom field mappings of the attachment between the remote 3rd party & Panora diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index 3425ce69e..42be8e10e 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -14,11 +14,9 @@ "paths": { "@@core/*": ["src/@core/*"], "@crm/*": ["src/crm/*"], - "@hris/*": ["src/hris/*"], "@ticketing/*": ["src/ticketing/*"], "@filestorage/*": ["src/filestorage/*"], "@marketingautomation/*": ["src/marketingautomation/*"], - "@ats/*": ["src/ats/*"], "@accounting/*": ["src/accounting/*"], "@ecommerce/*": ["src/ecommerce/*"] }, diff --git a/packages/api/variables.MD b/packages/api/variables.MD index 6ca21ab42..cdcf62e9b 100644 --- a/packages/api/variables.MD +++ b/packages/api/variables.MD @@ -86,17 +86,6 @@ | QUICKBOOKS_ACCOUNTING_CLOUD_CLIENT_SECRET | | | | XERO_ACCOUNTING_CLOUD_CLIENT_ID | | | | XERO_ACCOUNTING_CLOUD_CLIENT_SECRET | | | -| DEEL_HRIS_CLOUD_CLIENT_ID | | | -| DEEL_HRIS_CLOUD_CLIENT_SECRET | | | -| RIPPLING_HRIS_CLOUD_CLIENT_ID | | | -| RIPPLING_HRIS_CLOUD_CLIENT_SECRET | | | -| GUSTO_HRIS_CLOUD_CLIENT_ID | | | -| GUSTO_HRIS_CLOUD_CLIENT_SECRET | | | -| FACTORIAL_HRIS_CLOUD_CLIENT_ID | | | -| FACTORIAL_HRIS_CLOUD_CLIENT_SECRET | | | -| NAMELY_HRIS_CLOUD_CLIENT_ID | | | -| NAMELY_HRIS_CLOUD_CLIENT_SECRET | | | -| NAMELY_HRIS_CLOUD_SUBDOMAIN | | | | GOOGLEDRIVE_FILESTORAGE_CLOUD_CLIENT_ID | | | | GOOGLEDRIVE_FILESTORAGE_CLOUD_CLIENT_SECRET | | | | ONEDRIVE_FILESTORAGE_CLOUD_CLIENT_ID | | | diff --git a/packages/shared/src/categories.ts b/packages/shared/src/categories.ts index 00369f15b..3987c45e9 100644 --- a/packages/shared/src/categories.ts +++ b/packages/shared/src/categories.ts @@ -1,14 +1,11 @@ export enum ConnectorCategory { Crm = 'crm', - Hris = 'hris', - Ats = 'ats', Accounting = 'accounting', Ticketing = 'ticketing', MarketingAutomation = 'marketingautomation', FileStorage = 'filestorage', Productivity = 'productivity', - Ecommerce = 'ecommerce', - Cybersecurity = 'cybersecurity' + Ecommerce = 'ecommerce' } export const categoriesVerticals: ConnectorCategory[] = Object.values(ConnectorCategory); diff --git a/packages/shared/src/connectors/index.ts b/packages/shared/src/connectors/index.ts index 92b7df549..11a05171e 100644 --- a/packages/shared/src/connectors/index.ts +++ b/packages/shared/src/connectors/index.ts @@ -1,6 +1,4 @@ export const CRM_PROVIDERS = ['zoho', 'zendesk', 'hubspot', 'pipedrive', 'attio', 'close', 'microsoftdynamicssales']; -export const HRIS_PROVIDERS = []; -export const ATS_PROVIDERS = ['ashby']; export const ACCOUNTING_PROVIDERS = []; export const TICKETING_PROVIDERS = ['zendesk', 'front', 'jira', 'gorgias', 'gitlab', 'github', 'linear']; export const MARKETINGAUTOMATION_PROVIDERS = []; diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index 128cacfaf..2d555b0e5 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -1240,1468 +1240,6 @@ export const CONNECTORS_METADATA: ProvidersConfig = { } }, }, - 'ats': { - 'applicantstack': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'ashby': { - urls: { - docsUrl: 'https://developers.ashbyhq.com', - apiUrl: 'https://api.ashbyhq.com' - }, - logoPath: 'https://images.ctfassets.net/p03bi75xct27/2tVvkghDdMJxzkMca2QLnr/31b520c5e07db0103948af171fb54e99/ashby_logo_square.jpeg?q=80&fm=webp&w=2048', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, - primaryColor: '#4a3ead', - authStrategy: { - strategy: AuthStrategy.basic, - properties: ['api_key'] - } - }, - 'bamboohr': { - scopes: 'openid+email', - urls: { - docsUrl: 'https://documentation.bamboohr.com/docs/getting-started', - apiUrl: (subdomain) => `https://api.bamboohr.com/api/gateway.php/${subdomain}`, - }, - logoPath: 'https://play-lh.googleusercontent.com/c4BW9wr_QAiIeVBYHhP7rs06w99xJzxgLvmL5I1mkucC3_ATMyL1t7Doz0_LQ0X-qS0', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - primaryColor: '#599D16', - authStrategy: { - strategy: AuthStrategy.basic, - properties: ['subdomain', 'api_key'] - }, - }, - 'breezy': { - scopes: '', - urls: { - docsUrl: 'https://developer.breezy.hr/reference/overview', - apiUrl: 'https://api.breezy.hr/v3' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - // todo - 'bullhorn': { - scopes: '', - urls: { - docsUrl: 'https://bullhorn.github.io/rest-api-docs/', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'cats': { - scopes: '', - urls: { - docsUrl: 'https://docs.catsone.com/api/v3/', - apiUrl: 'https://api.catsone.com/v3' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'clayhr': { - scopes: '', - urls: { - docsUrl: 'https://clayhr.readme.io/', - apiUrl: '/rm/api/v3' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key, - properties: ['user_id'] - } - }, - // todo - 'clockwork': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - // todo - 'comeet': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'cornerstone_talentlink': { - scopes: '', - urls: { - docsUrl: 'https://developer.lumesse-talenthub.com/rest-api-developers-guide/1.21.33/index.html?page=rest-api&subpage=introduction', - apiUrl: 'https://apiproxy.shared.lumessetalentlink.com/tlk/rest' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'engage_ats': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'eploy': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.oauth2 - } - }, - 'fountain': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'freshteam': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'greenhouse': { - scopes: 'candidates.create candidates.view jobs.view', - urls: { - docsUrl: 'https://developers.greenhouse.io/harvest.html', - apiUrl: 'https://api.greenhouse.io/v1/partner', - authBaseUrl: 'https://api.greenhouse.io/oauth/authorize' - }, - logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSQRGdTs2tatWjail4b3hpHLIyI-6gXZLxhvw&s', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.oauth2 - } - }, - 'greenhouse_job_boards': { - scopes: '', - urls: { - docsUrl: 'https://developers.greenhouse.io/job-board.html', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.basic - } - }, - 'harbour_ats': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'homerun': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'hrcloud': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'icims': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://advancedcommunities.com/wp-content/uploads/2023/03/group-1928.png', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.oauth2 - } - }, - 'infinite_brassring': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'jazzhr': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'jobadder': { - scopes: 'offline_access read write', - urls: { - docsUrl: 'https://api.jobadder.com/v2/docs#section/Getting-Started/Authentication', - apiUrl: '', - authBaseUrl: 'https://id.jobadder.com/connect/authorize' - }, - logoPath: 'https://images.saasworthy.com/jobadder_5399_logo_1586769331_xj0dn.jpg', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.oauth2 - } - }, - 'jobscore': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'jobvite': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.oauth2 - } - }, - 'lano': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'lever': { - scopes: 'offline_access applications:read:admin opportunities:read:admin opportunities:write:admin', - urls: { - docsUrl: 'https://hire.lever.co/developer/documentation#introduction', - apiUrl: 'https://api.lever.co/v1', - authBaseUrl: 'https://auth.lever.co/authorize' // or https://sandbox-lever.auth0.com/authorize - }, - logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQbR9XSB1lbZnYlLWyqMe5Px80ghtEOUqHeqw&s', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.oauth2 - } - }, - 'occupop': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'oracle_fusion': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'oracle_taleo': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'personio_recruiting': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'pinpoint': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'polymer': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'recruiterflow': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'recruitive': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'sage': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'sap_successfactors': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'smartrecruiters': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'talentlyft': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'talentreef': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'teamtailor': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'tellent': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'tribepad': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'ukg_pro_recruiting': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'workable': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'workday': { - urls: { - docsUrl: 'https://apidocs.workdayspend.com/services/legacy/v3.html#tag/support', - apiUrl: 'https://api.us.workdayspend.com/services' // todo other locations - }, - logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSZTX2h9yFQ0u4ziDqvfQ224wW4N1s5JvJ5nA&s', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - primaryColor: '#0167AB', - authStrategy: { - strategy: AuthStrategy.api_key, - properties: ['api_key'] - } - }, - 'zoho_recruit': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - }, - 'hris': { - '7shifts': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'adp_workforce_now': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'alexishr': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'alliancehcm': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'altera_payroll': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'breathe': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'ceridian_dayforce': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'charlie': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.basic - } - - }, - 'charthop': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'clayhr': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'cyberark': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'deel': { - scopes: 'contracts:read contracts:write invoice-adjustments:read invoice-adjustments:write accounting:read candidates:read candidates:write Users:read Users:write tasks:read tasks:write organizations:read organizations:write', - urls: { - docsUrl: 'https://developer.deel.com/docs/welcome#/', - apiUrl: 'https://api.letsdeel.com/rest/v2', - authBaseUrl: 'https://app.deel.com/oauth2/authorize' - }, - logoPath: 'https://asset.brandfetch.io/id4NSNrRnG/idXzwlo3iL.jpeg', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, - authStrategy: { - strategy: AuthStrategy.oauth2 - }, - options: { - local_redirect_uri_in_https: true - } - }, - 'rippling': { - urls: { - docsUrl: 'https://developer.rippling.com/docs/rippling-api/9rw6guf819r5f-introduction-for-customers', - apiUrl: 'https://api.rippling.com/platform/api', - authBaseUrl: (APPNAME) => `https://app.rippling.com/apps/PLATFORM/${APPNAME}/authorize` - }, - logoPath: 'https://avatars.githubusercontent.com/u/19614805?s=280&v=4', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.oauth2 - }, - options: { - company_subdomain: true, - local_redirect_uri_in_https: true - } - }, - 'employment_hero': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'factorial': { - urls: { - docsUrl: 'https://apidoc.factorialhr.com/docs/authentication-1', - apiUrl: 'https://api.factorialhr.com/api/v1', - authBaseUrl: 'https://api.factorialhr.com/oauth/authorize' - }, - logoPath: 'https://pbs.twimg.com/profile_images/1449821710853808130/N2nL_8z3_400x400.jpg', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.oauth2 - } - }, - 'freshteam': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'google_workspace': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'gusto': { - urls: { - docsUrl: 'https://docs.gusto.com/app-integrations/docs/introduction', - apiUrl: 'https://api.gusto.com', // api.gusto-demo.com - authBaseUrl: 'https://api.gusto-demo.com/oauth/authorize' - }, - logoPath: 'https://cdn.runalloy.com/landing/uploads-new/Gusto_Logo_67ca008403.png', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, - authStrategy: { - strategy: AuthStrategy.oauth2 - } - }, - 'hibob': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'hrcloud': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'hrpartner': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'humaans': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'humi': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'insperity_premier': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'active_campaign': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'intellli_hr': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'iris_cascade': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'jumpcloud': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'justworks': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'kallidus': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'keka': { - scopes: '', - urls: { - docsUrl: 'https://developers.keka.com/reference/getting-started-for-customers', - apiUrl: '' // todo https://{company}.{environment}.com/api/v1 - }, - logoPath: 'https://play-lh.googleusercontent.com/OiG-QsKPm--v-16z225UtUND2IIbinlLrnBhSRch9kL_ruBCIh4GDA0sD_BnUPF93Ew', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.oauth2 - } - }, - 'kenjo': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'lano': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'lucca': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'microsoft_entra_id': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'namely': { - urls: { - docsUrl: 'https://developers.namely.com/docs/namely-api/ZG9jOjE1NTkwMDU5-authentication', - apiUrl: 'https://stoplight.io/mocks/namely/namely-api/182542', // TODO - authBaseUrl: (myDomain) => `https://${myDomain}.namely.com/api/v1/oauth2/authorize` - }, - logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRZzUUYuH2sjtkUfh6BpOHoREyCe_ZV7DWIuQ&s', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - options: { - company_subdomain: true - }, - authStrategy: { - strategy: AuthStrategy.oauth2 - } - }, - 'nmbrs': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'officient': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.oauth2 - } - }, - 'okta': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'onelogin': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'oracle_hcm': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'oyster_hr': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'paycaptain': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'paychex': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'paycor': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://assets.wheelhouse.com/media/_solution_logo_04042023_58844144.png', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - primaryColor: '#F8A22D', - authStrategy: { - strategy: AuthStrategy.api_key, - properties: ['api_key'] - } - }, - 'payfit': { - scopes: '', - urls: { - docsUrl: 'https://developers.payfit.io/docs/cbb', - apiUrl: 'https://partner-api.payfit.com', - authBaseUrl: 'https://oauth.payfit.com/authorize' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.oauth2 - } - }, - 'paylocity': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '', - authBaseUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'people_hr': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'personio': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'pingone': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'proliant': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'remote': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'sage': { - urls: { - docsUrl: 'https://sagehr.docs.apiary.io/#reference', - apiUrl: (subdomain) => `https://${subdomain}.sage.hr` - }, - logoPath: 'https://appexchange.salesforce.com/partners/servlet/servlet.FileDownload?file=00P4V00000xPZsjUAG', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, - primaryColor: '#00d639', - authStrategy: { - strategy: AuthStrategy.api_key, - properties: ['api_key', 'subdomain'] - }, - }, - 'sap_successfactors': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'sesame': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'square_payroll': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'trinet': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'trinet_hr_platform': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'ukg_pro': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'ukg_pro_workforce': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'ukg_ready': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'workday': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - 'zoho_people': { - scopes: '', - urls: { - docsUrl: '', - apiUrl: '' - }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', - description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, - authStrategy: { - strategy: AuthStrategy.api_key - } - }, - }, 'filestorage': { 'googledrive': { scopes: 'https://www.googleapis.com/auth/drive', @@ -2960,124 +1498,5 @@ export const CONNECTORS_METADATA: ProvidersConfig = { properties: ['username', 'password', 'store_url'] } }, - }, - 'cybersecurity': { - 'semgrep': { - urls: { - docsUrl: 'https://semgrep.dev/api/v1/docs/#section/Introduction', - apiUrl: 'https://semgrep.dev/api', - }, - logoPath: 'https://yt3.googleusercontent.com/NWVXYvuzHDgJJsbda7eyyz21Ba2qnq5WmuGrt9ax1rs6PP-mlDl5LCJ4ZO0Z2ZbiCq4ZoxqiGg=s900-c-k-c0x00ffffff-no-rj', - description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, - primaryColor: '#10C096', - authStrategy: { - strategy: AuthStrategy.api_key, - properties: ['api_key'] - } - }, - 'snyk': { - scopes: 'org.read org.report.read org.project.read', - urls: { - docsUrl: 'https://docs.snyk.io/snyk-api/', - apiUrl: 'https://api.snyk.io', - authBaseUrl: 'https://app.snyk.io/oauth2/authorize' - }, - logoPath: 'https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1215%2Ffe4be452-1e68-444a-bf77-db21bf3a7bdc.png', - description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, - authStrategy: { - strategy: AuthStrategy.oauth2 - }, - options: { - local_redirect_uri_in_https: true - } - }, - 'tenable': { - urls: { - docsUrl: 'https://developer.tenable.com/reference/navigate', - apiUrl: 'https://cloud.tenable.com', - }, - logoPath: 'https://pbs.twimg.com/profile_images/1410604377757216768/ocEKYniC_400x400.jpg', - description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, - primaryColor: '#0D1E40', - authStrategy: { - strategy: AuthStrategy.basic, - properties: ['access_key', 'secret_key'] - } - }, - 'qualys': { - urls: { - docsUrl: 'https://docs.qualys.com/en/vm/api/scans/index.htm#t=get_started%2Fauthentication.htm', - apiUrl: (baseApi) => `https://${baseApi}/api` - }, - logoPath: 'https://companieslogo.com/img/orig/QLYS-68c2032c.png?t=1720244493', - description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, - primaryColor: '#ED2E28', - authStrategy: { - strategy: AuthStrategy.basic, - properties: ['username', 'password', 'api_url'] - } - }, - 'rapid7insightvm': { - urls: { - docsUrl: 'https://help.rapid7.com/insightvm/en-us/api/index.html', - apiUrl: (region) => `https://${region}.api.insight.rapid7.com`, - }, - logoPath: 'https://images.saasworthy.com/insightvm_9113_logo_1635748346_lc0gr.png', - description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, - primaryColor: '#E95722', - authStrategy: { - strategy: AuthStrategy.api_key, - properties: ['region', 'api_key'] - } - }, - 'crowdstrike': { - scopes: '', - urls: { - docsUrl: 'https://developer.crowdstrike.com/', - apiUrl: (dotHost) => `https://api${dotHost}.crowdstrike.com`, - authBaseUrl: '' - }, - logoPath: 'https://pbs.twimg.com/profile_images/1451022302578049024/6L-zG5oq_400x400.jpg', - description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, - primaryColor: '#FC0001', - authStrategy: { - strategy: AuthStrategy.oauth2, - } - }, - 'sentinelone': { - urls: { - docsUrl: 'https://www.postman.com/api-evangelist/sentinelone/overview', - apiUrl: (host) => `https://${host}.sentinelone.net`, - }, - logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTZHLye2za7fjLiggqC1upKhhM3T-laySJSLQ&s', - description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, - primaryColor: '#522E74', - authStrategy: { - strategy: AuthStrategy.api_key, - properties: ['host', 'api_key'] - } - }, - 'microsoftdefender': { - scopes: '', - urls: { - docsUrl: 'https://learn.microsoft.com/en-us/defender-endpoint/api/apis-intro', - apiUrl: '', - authBaseUrl: '' - }, - logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSuR4GZElDP7UNKhXS9jDGpElBTdchjg8hSsA&s', - description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, - primaryColor: '#0078D8', - authStrategy: { - strategy: AuthStrategy.oauth2, - } - }, } }; diff --git a/packages/shared/src/standardObjects.ts b/packages/shared/src/standardObjects.ts index aeba4720f..fc4ca8998 100644 --- a/packages/shared/src/standardObjects.ts +++ b/packages/shared/src/standardObjects.ts @@ -9,42 +9,6 @@ export enum CrmObject { user = 'user', } -export enum HrisObject { - bankinfo = 'bankinfo', - benefit = 'benefit', - company = 'company', - dependent = 'dependent', - employee = 'employee', - employeepayrollrun = 'employeepayrollrun', - employerbenefit = 'employerbenefit', - employment = 'employment', - group = 'group', - location = 'location', - paygroup = 'paygroup', - payrollrun = 'payrollrun', - timeoff = 'timeoff', - timeoffbalance = 'timeoffbalance', - timesheetentry = 'timesheetentry', -} - -export enum AtsObject { - activity = 'activity', - application = 'application', - attachment = 'attachment', - candidate = 'candidate', - department = 'department', - interview = 'interview', - jobinterviewstage = 'jobinterviewstage', - job = 'job', - offer = 'offer', - office = 'office', - rejectreason = 'rejectreason', - scorecard = 'scorecard', - tag = 'tag', - user = 'user', - eeocs = 'eeocs', -} - export enum AccountingObject { balancesheet = 'balancesheet', cashflowstatement = 'cashflowstatement', @@ -120,21 +84,12 @@ export const standardObjects = [ ...prependPrefixToEnumValues('crm', CrmObject), ...prependPrefixToEnumValues('ticketing', TicketingObject), ...prependPrefixToEnumValues('filestorage', FileStorageObject), - ...prependPrefixToEnumValues('ats', AtsObject), ...prependPrefixToEnumValues('ecommerce', EcommerceObject), ]; export function getCrmCommonObjects(): string[] { return Object.values(CrmObject); } - -export function getHrisCommonObjects(): string[] { - return Object.values(HrisObject); -} - -export function getAtsCommonObjects(): string[] { - return Object.values(AtsObject); -} export function getAccountingCommonObjects(): string[] { return Object.values(AccountingObject); @@ -161,10 +116,6 @@ export function getCommonObjectsForVertical(vertical: string): string[] { switch (vertical.toLowerCase()) { case 'crm': return getCrmCommonObjects(); - case 'hris': - return getHrisCommonObjects(); - case 'ats': - return getAtsCommonObjects(); case 'accounting': return getAccountingCommonObjects(); case 'ecommerce': diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index 19a2f4100..7581e197d 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -1,5 +1,5 @@ import { CONNECTORS_METADATA } from './connectors/metadata'; -import { ACCOUNTING_PROVIDERS, ATS_PROVIDERS, CRM_PROVIDERS, ECOMMERCE_PROVIDERS, FILESTORAGE_PROVIDERS, HRIS_PROVIDERS, MARKETINGAUTOMATION_PROVIDERS, TICKETING_PROVIDERS } from './connectors'; +import { ACCOUNTING_PROVIDERS, CRM_PROVIDERS, ECOMMERCE_PROVIDERS, FILESTORAGE_PROVIDERS, MARKETINGAUTOMATION_PROVIDERS, TICKETING_PROVIDERS } from './connectors'; import { AuthStrategy, AuthType, DynamicApiUrl, DynamicAuthorization, StaticApiUrl, StringAuthorization, VerticalConfig } from './types'; import { categoriesVerticals, ConnectorCategory } from './categories'; @@ -137,7 +137,7 @@ export function getLogoURL(providerName: string): string { export function mergeAllProviders(...arrays: string[][]): { vertical: string, value: string }[] { const result: { vertical: string, value: string }[] = []; arrays.forEach((arr, index) => { - const arrayName = Object.keys({ CRM_PROVIDERS, HRIS_PROVIDERS, ATS_PROVIDERS, ACCOUNTING_PROVIDERS, TICKETING_PROVIDERS, MARKETINGAUTOMATION_PROVIDERS, FILESTORAGE_PROVIDERS, ECOMMERCE_PROVIDERS})[index]; + const arrayName = Object.keys({ CRM_PROVIDERS, ACCOUNTING_PROVIDERS, TICKETING_PROVIDERS, MARKETINGAUTOMATION_PROVIDERS, FILESTORAGE_PROVIDERS, ECOMMERCE_PROVIDERS})[index]; arr.forEach(item => { if (item !== '') { result.push({ vertical: arrayName.split('_')[0], value: item }); @@ -147,16 +147,12 @@ export function mergeAllProviders(...arrays: string[][]): { vertical: string, va return result; } -export const ALL_PROVIDERS: { vertical: string, value: string }[] = mergeAllProviders(CRM_PROVIDERS, HRIS_PROVIDERS, ATS_PROVIDERS, ACCOUNTING_PROVIDERS, TICKETING_PROVIDERS, MARKETINGAUTOMATION_PROVIDERS, FILESTORAGE_PROVIDERS, ECOMMERCE_PROVIDERS) +export const ALL_PROVIDERS: { vertical: string, value: string }[] = mergeAllProviders(CRM_PROVIDERS, ACCOUNTING_PROVIDERS, TICKETING_PROVIDERS, MARKETINGAUTOMATION_PROVIDERS, FILESTORAGE_PROVIDERS, ECOMMERCE_PROVIDERS) export function slugFromCategory(category: ConnectorCategory) { switch(category) { case ConnectorCategory.Crm: return 'crm'; - case ConnectorCategory.Hris: - return 'hris'; - case ConnectorCategory.Ats: - return 'ats'; case ConnectorCategory.Ticketing: return 'tcg'; case ConnectorCategory.MarketingAutomation: @@ -176,10 +172,6 @@ export function categoryFromSlug(slug: string): ConnectorCategory | null { switch (slug) { case 'crm': return ConnectorCategory.Crm; - case 'hris': - return ConnectorCategory.Hris; - case 'ats': - return ConnectorCategory.Ats; case 'tcg': return ConnectorCategory.Ticketing; case 'mktg':