@accounter/server
Version:
The test suite is split into three Vitest projects for efficiency and isolation:
264 lines (240 loc) • 7.43 kB
text/typescript
import DataLoader from 'dataloader';
import { Injectable, Scope } from 'graphql-modules';
import { sql } from '@pgtyped/runtime';
import { getCacheInstance } from '../../../shared/helpers/index.js';
import { DBProvider } from '../../app-providers/db.provider.js';
import { BusinessesProvider } from '../../financial-entities/providers/businesses.provider.js';
import type {
IDeleteContractQuery,
IGetAllOpenContractsQuery,
IGetAllOpenContractsResult,
IGetContractsByAdminBusinessIdsQuery,
IGetContractsByClientIdsQuery,
IGetContractsByIdsQuery,
IInsertContractParams,
IInsertContractQuery,
IUpdateContractParams,
IUpdateContractQuery,
} from '../types.js';
const getAllOpenContracts = sql<IGetAllOpenContractsQuery>`
SELECT *
FROM accounter_schema.clients_contracts
WHERE is_active IS TRUE;`;
const getContractsByIds = sql<IGetContractsByIdsQuery>`
SELECT *
FROM accounter_schema.clients_contracts
WHERE id In $$ids;`;
const getContractsByAdminBusinessIds = sql<IGetContractsByAdminBusinessIdsQuery>`
SELECT fe.owner_id, c.*
FROM accounter_schema.clients_contracts c
LEFT JOIN accounter_schema.financial_entities fe ON c.client_id = fe.id
WHERE owner_id IN $$adminBusinessIds;`;
const getContractsByClientIds = sql<IGetContractsByClientIdsQuery>`
SELECT *
FROM accounter_schema.clients_contracts
WHERE client_id IN $$clientIds;`;
const deleteContract = sql<IDeleteContractQuery>`
DELETE FROM accounter_schema.clients_contracts
WHERE id = $id;`;
const updateContract = sql<IUpdateContractQuery>`
UPDATE accounter_schema.clients_contracts
SET
client_id = COALESCE(
$clientId,
client_id
),
purchase_orders = COALESCE(
$purchaseOrders,
purchase_orders
),
start_date = COALESCE(
$startDate,
start_date
),
end_date = COALESCE(
$endDate,
end_date
),
remarks = COALESCE(
$remarks,
remarks
),
document_type = COALESCE(
$documentType,
document_type
),
amount = COALESCE(
$amount,
amount
),
currency = COALESCE(
$currency,
currency
),
billing_cycle = COALESCE(
$billingCycle,
billing_cycle
),
product = COALESCE(
$product,
product
),
plan = COALESCE(
$plan,
plan
),
is_active = COALESCE(
$isActive,
is_active
),
ms_cloud = COALESCE(
$msCloud,
ms_cloud
),
operations_count = COALESCE(
$operationsLimit,
operations_count
)
WHERE
id = $contractId
RETURNING *;
`;
const insertContract = sql<IInsertContractQuery>`
INSERT INTO accounter_schema.clients_contracts (
client_id,
purchase_orders,
start_date,
end_date,
remarks,
document_type,
amount,
currency,
billing_cycle,
product,
plan,
is_active,
ms_cloud,
operations_count
)
VALUES ($clientId,
$purchaseOrders,
$startDate,
$endDate,
$remarks,
$documentType,
$amount,
$currency,
$billingCycle,
$product,
$plan,
$isActive,
$msCloud,
$operationsLimit)
RETURNING *;`;
({
scope: Scope.Operation,
global: true,
})
export class ContractsProvider {
cache = getCacheInstance({
stdTTL: 60 * 60, // 1 hours
});
constructor(
private dbProvider: DBProvider,
private businessesProvider: BusinessesProvider,
) {}
public getAllOpenContracts() {
const cached = this.cache.get<IGetAllOpenContractsResult[]>('all-contracts');
if (cached) {
return Promise.resolve(cached);
}
return getAllOpenContracts.run(undefined, this.dbProvider).then(contracts => {
if (contracts) {
this.cache.set('all-contracts', contracts);
contracts.map(contract => {
this.cache.set(`contract-${contract.id}`, contract);
});
}
return contracts;
});
}
private async contractsByIds(ids: readonly string[]) {
const contracts = await getContractsByIds.run({ ids }, this.dbProvider);
return ids.map(id => contracts.find(contract => contract.id === id));
}
public getContractsByIdLoader = new DataLoader(
(ids: readonly string[]) => this.contractsByIds(ids),
{
cacheKeyFn: id => `contract-${id}`,
cacheMap: this.cache,
},
);
private async contractsByAdminBusinessIds(adminBusinessIds: readonly string[]) {
const contracts = await getContractsByAdminBusinessIds.run(
{ adminBusinessIds },
this.dbProvider,
);
return adminBusinessIds.map(adminBusinessId =>
contracts.filter(contract => contract.owner_id === adminBusinessId),
);
}
public getContractsByAdminBusinessIdLoader = new DataLoader(
(adminBusinessIds: readonly string[]) => this.contractsByAdminBusinessIds(adminBusinessIds),
{
cacheKeyFn: adminBusinessId => `admin-business-${adminBusinessId}-contracts`,
cacheMap: this.cache,
},
);
private async contractsByClients(clientIds: readonly string[]) {
const contracts = await getContractsByClientIds.run({ clientIds }, this.dbProvider);
return clientIds.map(clientId => contracts.filter(contract => contract.client_id === clientId));
}
public getContractsByClientIdLoader = new DataLoader(
(ids: readonly string[]) => this.contractsByClients(ids),
{
cacheKeyFn: id => `client-contracts-${id}`,
cacheMap: this.cache,
},
);
public async createContract(params: IInsertContractParams) {
const [newContract] = await insertContract.run(params, this.dbProvider);
this.cache.set(`contract-${newContract.id}`, newContract);
// Invalidate list caches
this.getContractsByClientIdLoader.clear(newContract.client_id);
const business = await this.businessesProvider.getBusinessByIdLoader.load(
newContract.client_id,
);
if (business?.owner_id) {
this.getContractsByAdminBusinessIdLoader.clear(business.owner_id);
}
return newContract;
}
public async updateContract(params: IUpdateContractParams) {
const [updatedContract] = await updateContract.run(params, this.dbProvider);
if (params.contractId) {
this.invalidateCacheForContract(params.contractId);
} else {
this.clearCache();
}
this.cache.set(`contract-${updatedContract.id}`, updatedContract);
return updatedContract;
}
public async deleteContract(contractId: string) {
await deleteContract.run({ id: contractId }, this.dbProvider);
this.invalidateCacheForContract(contractId);
return true;
}
public async invalidateCacheForContract(contractId: string) {
const contract = await this.getContractsByIdLoader.load(contractId);
if (contract) {
this.cache.delete(`client-contracts-${contract.client_id}`);
const business = await this.businessesProvider.getBusinessByIdLoader.load(contract.client_id);
if (business?.owner_id) {
this.cache.delete(`admin-business-${business.owner_id}-contracts`);
}
}
}
public clearCache() {
this.cache.clear();
}
}