UNPKG

@accounter/server

Version:
232 lines • 9.42 kB
import { __decorate, __metadata, __param } from "tslib"; import { addHours, subYears } from 'date-fns'; import { GraphQLError } from 'graphql'; import { Inject, Injectable, Scope } from 'graphql-modules'; import { dateToTimelessDateString } from '../../../shared/helpers/index.js'; import { ENVIRONMENT } from '../../../shared/tokens.js'; import { ProviderCredentialsProvider } from '../../provider-credentials/providers/provider-credentials.provider.js'; import { ContractSchema, downloadInvoicePdfSchema, retrieveInvoicesSchema, retrievePaymentBreakdownSchema, retrievePaymentReceiptsSchema, } from './schemas.js'; let DeelClientProvider = class DeelClientProvider { env; credentialsProvider; host = 'https://api.letsdeel.com/rest/v2'; tokenPromise = null; constructor(env, credentialsProvider) { this.env = env; this.credentialsProvider = credentialsProvider; } // This is a workaround for the Deel API returning PST dates as UTC dates. timeZoneFix(dateString) { const date = new Date(dateString); return addHours(date, 7).toUTCString(); } getApiToken() { this.tokenPromise ??= this._fetchToken(); return this.tokenPromise; } async _fetchToken() { const fromDb = await this.credentialsProvider.getDeelCredentials(); const token = fromDb?.apiToken ?? null; if (!token) { throw new GraphQLError('Deel credentials not configured for this tenant', { extensions: { code: 'PROVIDER_NOT_CONFIGURED' }, }); } return token; } async getPaymentReceipts() { try { const queryVars = { date_from: dateToTimelessDateString(subYears(new Date(), 1)), date_to: dateToTimelessDateString(new Date()), }; const url = new URL(`${this.host}/payments`); Object.entries(queryVars).map(([key, value]) => { url.searchParams.set(key, value); }); const res = await fetch(url, { headers: { accept: 'application/json', authorization: `Bearer ${await this.getApiToken()}`, }, }); if (!res.ok) { console.error(await res.text()); throw new Error(`Deel API returned status ${res.status}`); } const rawData = await res.json(); if ('errors' in rawData) { console.log(rawData); throw new Error('Deel API returned an error'); } const data = retrievePaymentReceiptsSchema.parse(rawData); return data; } catch (error) { const message = 'Failed to fetch Deel payment receipts'; console.error(`${message}: ${error}`); throw new Error(message, { cause: error }); } } async getPaymentBreakdown(paymentId) { try { const url = new URL(`${this.host}/payments/${paymentId}/breakdown`); const res = await fetch(url, { headers: { accept: 'application/json', authorization: `Bearer ${await this.getApiToken()}`, }, }); if (!res.ok) { console.error(await res.text()); throw new Error(`Deel API returned status ${res.status}`); } const rawData = await res.json(); if ('errors' in rawData) { console.log(rawData); throw new Error('Deel API returned an error'); } const data = retrievePaymentBreakdownSchema.parse(rawData); return data; } catch (error) { const message = 'Failed to fetch Deel payment breakdown'; console.error(`${message}: ${error}`); throw new Error(message, { cause: error }); } } async getSalaryInvoices() { const invoices = []; try { const apiToken = await this.getApiToken(); const queryVars = { limit: 50, offset: 0, issued_from_date: dateToTimelessDateString(subYears(new Date(), 1)), }; for (let i = 0; i < 10; i++) { const url = new URL(`${this.host}/invoices`); Object.entries({ ...queryVars, offset: i * 50 }).map(([key, value]) => { url.searchParams.set(key, value); }); const res = await fetch(url, { headers: { accept: 'application/json', authorization: `Bearer ${apiToken}`, }, }); if (!res.ok) { console.error(await res.text()); throw new Error(`Deel API returned status ${res.status}`); } const rawData = await res.json(); if ('errors' in rawData) { console.log(rawData); throw new Error('Deel API returned an error'); } const data = retrieveInvoicesSchema.parse(rawData); if (data.data.length) { data.data = data.data.map(invoice => { return { ...invoice, issued_at: this.timeZoneFix(invoice.issued_at), due_date: this.timeZoneFix(invoice.due_date), }; }); } else { break; } invoices.push(...data.data); } return invoices; } catch (error) { const message = 'Failed to fetch Deel salary invoices'; console.error(`${message}: ${error}`); throw new Error(message, { cause: error }); } } async getSalaryInvoiceFile(id) { try { const url = new URL(`${this.host}/invoices/${id}/download`); const res = await fetch(url, { headers: { accept: 'application/json', authorization: `Bearer ${await this.getApiToken()}`, }, }); if (!res.ok) { console.error(await res.text()); throw new Error(`Deel API returned status ${res.status}`); } const rawData = await res.json(); const data = downloadInvoicePdfSchema.parse(rawData); if (!data.data.url) { throw new Error('Deel invoice file download URL is missing'); } const invoiceFileRes = await fetch(data.data.url); if (!invoiceFileRes.ok) { throw new Error('Deel invoice file download failed'); } const invoiceFileBuffer = await invoiceFileRes.arrayBuffer(); const blob = new Blob([invoiceFileBuffer], { type: 'application/pdf' }); return blob; } catch (error) { const message = 'Failed to fetch Deel invoice file'; console.error(`${message}: ${error}`); throw new Error(message, { cause: error }); } } async getContractDetails(contractId) { try { const url = new URL(`${this.host}/contracts/${contractId}`); const res = await fetch(url, { headers: { accept: 'application/json', authorization: `Bearer ${await this.getApiToken()}`, }, }); if (!res.ok) { console.error(await res.text()); throw new Error(`Deel API returned status ${res.status}`); } const rawData = await res.json(); if ('errors' in rawData) { console.log(rawData); throw new Error('Deel API returned an error'); } const contract = ContractSchema.safeParse(rawData); if (!contract.success) { let errorMessage = 'Deel contract data validation failed'; if (contract.error.issues) { for (const issue of contract.error.issues) { let value = rawData; for (const pathPart of issue.path) { value = value ? value[pathPart] : undefined; } errorMessage += `\n - Path: ${issue.path.join('.')}, Message: ${issue.message}, value: ${value}`; } } throw new Error(errorMessage); } return contract.data; } catch (error) { const message = 'Failed to fetch Deel contract details'; console.error(`${message}: ${error}`); throw new Error(message, { cause: error }); } } }; DeelClientProvider = __decorate([ Injectable({ scope: Scope.Operation, global: true, }), __param(0, Inject(ENVIRONMENT)), __metadata("design:paramtypes", [Object, ProviderCredentialsProvider]) ], DeelClientProvider); export { DeelClientProvider }; //# sourceMappingURL=deel-client.provider.js.map