@accounter/server
Version:
Accounter GraphQL server
232 lines • 9.42 kB
JavaScript
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