@accounter/server
Version:
Accounter GraphQL server
309 lines (277 loc) • 9.18 kB
text/typescript
import DataLoader from 'dataloader';
import { Injectable, Scope } from 'graphql-modules';
import { sql } from '@pgtyped/runtime';
import { reassureOwnerIdExists } from '../../../shared/helpers/index.js';
import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import { TenantAwareDBClient } from '../../app-providers/tenant-db-client.js';
import type {
IDeleteIssuedDocumentParams,
IDeleteIssuedDocumentQuery,
IGetAllIssuedDocumentsParams,
IGetAllIssuedDocumentsQuery,
IGetAllIssuedDocumentsResult,
IGetIssuedDocumentsByClientIdsQuery,
IGetIssuedDocumentsByExternalIdsQuery,
IGetIssuedDocumentsByIdsQuery,
IGetIssuedDocumentsByTypeParams,
IGetIssuedDocumentsByTypeQuery,
IGetIssuedDocumentsStatusByChargeIdsQuery,
IInsertIssuedDocumentsParams,
IInsertIssuedDocumentsQuery,
IUpdateIssuedDocumentByExternalIdParams,
IUpdateIssuedDocumentByExternalIdQuery,
IUpdateIssuedDocumentParams,
IUpdateIssuedDocumentQuery,
} from '../types.js';
const getAllIssuedDocuments = sql<IGetAllIssuedDocumentsQuery>`
SELECT *
FROM accounter_schema.documents_issued;
`;
const getIssuedDocumentsByIds = sql<IGetIssuedDocumentsByIdsQuery>`
SELECT *
FROM accounter_schema.documents_issued
WHERE id IN $$Ids;
`;
const getIssuedDocumentsByExternalIds = sql<IGetIssuedDocumentsByExternalIdsQuery>`
SELECT *
FROM accounter_schema.documents_issued
WHERE external_id IN $$externalIds;
`;
const getIssuedDocumentsByClientIds = sql<IGetIssuedDocumentsByClientIdsQuery>`
SELECT d.*, di.external_id, di.status, di.linked_document_ids
FROM accounter_schema.documents_issued di
LEFT JOIN accounter_schema.documents d
ON di.id = d.id
WHERE d.debtor_id IN $$clientIds OR d.creditor_id IN $$clientIds;
`;
const getIssuedDocumentsStatusByChargeIds = sql<IGetIssuedDocumentsStatusByChargeIdsQuery>`
SELECT
documents.charge_id,
COALESCE(
bool_or(
documents_issued.status = 'OPEN'::accounter_schema.document_status
),
false
) AS open_docs_flag
FROM
accounter_schema.documents_issued
LEFT JOIN accounter_schema.documents ON documents.id = documents_issued.id
WHERE
documents.charge_id in $$chargeIds
GROUP BY
documents.charge_id
`;
const getIssuedDocumentsByType = sql<IGetIssuedDocumentsByTypeQuery>`
SELECT d.*, di.external_id, di.status, di.linked_document_ids
FROM accounter_schema.documents_issued di
LEFT JOIN accounter_schema.documents d
ON di.id = d.id
WHERE d.type = $type
ORDER BY d.serial_number DESC
LIMIT $limit;
`;
const updateIssuedDocument = sql<IUpdateIssuedDocumentQuery>`
UPDATE accounter_schema.documents_issued
SET
external_id = COALESCE(
$externalId,
external_id
),
status = COALESCE(
$status,
status
),
linked_document_ids = COALESCE(
$linkedDocumentIds,
linked_document_ids
)
WHERE
id = $documentId
RETURNING *;
`;
const updateIssuedDocumentByExternalId = sql<IUpdateIssuedDocumentByExternalIdQuery>`
UPDATE accounter_schema.documents_issued
SET
id = COALESCE(
$documentId,
id
),
status = COALESCE(
$status,
status
),
linked_document_ids = COALESCE(
$linkedDocumentIds,
linked_document_ids
)
WHERE
external_id = $externalId
RETURNING *;
`;
const deleteIssuedDocument = sql<IDeleteIssuedDocumentQuery>`
DELETE FROM accounter_schema.documents_issued
WHERE id = $documentId
RETURNING id;
`;
const insertIssuedDocuments = sql<IInsertIssuedDocumentsQuery>`
INSERT INTO accounter_schema.documents_issued (
id,
external_id,
status,
linked_document_ids,
owner_id
)
VALUES $$issuedDocuments(
id,
externalId,
status,
linkedDocumentIds,
ownerId
)
RETURNING *;`;
({
scope: Scope.Operation,
global: true,
})
export class IssuedDocumentsProvider {
constructor(
private db: TenantAwareDBClient,
private adminContextProvider: AdminContextProvider,
) {}
private allIssuedDocumentsCache: Promise<IGetAllIssuedDocumentsResult[]> | null = null;
public async getAllIssuedDocuments(params: IGetAllIssuedDocumentsParams) {
if (this.allIssuedDocumentsCache) {
return this.allIssuedDocumentsCache;
}
this.allIssuedDocumentsCache = getAllIssuedDocuments.run(params, this.db).then(docs => {
docs.map(doc => {
this.getIssuedDocumentsByIdLoader.prime(doc.id, doc);
});
return docs;
});
return this.allIssuedDocumentsCache;
}
private async batchIssuedDocumentsByIds(ids: readonly string[]) {
const uniqueIDs = [...new Set(ids)];
try {
const docs = await getIssuedDocumentsByIds.run({ Ids: uniqueIDs }, this.db);
return ids.map(id => docs.find(doc => doc.id === id));
} catch (e) {
console.error(e);
return ids.map(() => null);
}
}
public getIssuedDocumentsByIdLoader = new DataLoader((ids: readonly string[]) =>
this.batchIssuedDocumentsByIds(ids),
);
private async batchIssuedDocumentsByExternalIds(externalIds: readonly string[]) {
const uniqueExternalIDs = [...new Set(externalIds)];
try {
const docs = await getIssuedDocumentsByExternalIds.run(
{ externalIds: uniqueExternalIDs },
this.db,
);
return externalIds.map(id => docs.find(doc => doc.external_id === id));
} catch (e) {
console.error(e);
return externalIds.map(() => null);
}
}
public getIssuedDocumentsByExternalIdLoader = new DataLoader((externalIds: readonly string[]) =>
this.batchIssuedDocumentsByExternalIds(externalIds),
);
private async batchIssuedDocumentsByClientIds(clientIds: readonly string[]) {
const uniqueClientIDs = [...new Set(clientIds)];
try {
const docs = await getIssuedDocumentsByClientIds.run({ clientIds: uniqueClientIDs }, this.db);
return clientIds.map(id =>
docs.filter(doc => {
this.getIssuedDocumentsByIdLoader.prime(doc.id, doc);
return doc.creditor_id === id || doc.debtor_id === id;
}),
);
} catch (e) {
const message = 'Error fetching issued documents by client IDs';
console.error(message, e);
throw new Error(message, { cause: e });
}
}
public getIssuedDocumentsByClientIdLoader = new DataLoader(
(clientIds: readonly string[]) => this.batchIssuedDocumentsByClientIds(clientIds),
{
cache: false,
},
);
private async batchIssuedDocumentsStatusByChargeIds(chargeIds: readonly string[]) {
const uniqueClientIDs = [...new Set(chargeIds)];
try {
const statuses = await getIssuedDocumentsStatusByChargeIds.run(
{ chargeIds: uniqueClientIDs },
this.db,
);
return chargeIds.map(id =>
statuses.find(status => {
return status.charge_id === id;
}),
);
} catch (e) {
const message = 'Error fetching issued documents status by charge IDs';
console.error(message, e);
throw new Error(message, { cause: e });
}
}
public getIssuedDocumentsStatusByChargeIdLoader = new DataLoader(
(chargeIds: readonly string[]) => this.batchIssuedDocumentsStatusByChargeIds(chargeIds),
{
cache: false,
},
);
public async getIssuedDocumentsByType(params: IGetIssuedDocumentsByTypeParams) {
return getIssuedDocumentsByType.run(params, this.db).then(docs => {
docs.map(doc => {
this.getIssuedDocumentsByIdLoader.prime(doc.id, doc);
});
return docs;
});
}
public async updateIssuedDocument(params: IUpdateIssuedDocumentParams) {
if (params.documentId) {
this.invalidateById(params.documentId);
}
return updateIssuedDocument.run(params, this.db);
}
public async updateIssuedDocumentByExternalId(params: IUpdateIssuedDocumentByExternalIdParams) {
return updateIssuedDocumentByExternalId.run(params, this.db).then(res => {
if (res[0]?.id) {
this.invalidateById(res[0].id);
}
return res;
});
}
public async deleteIssuedDocument(params: IDeleteIssuedDocumentParams) {
if (params.documentId) {
this.invalidateById(params.documentId);
}
return deleteIssuedDocument.run(params, this.db);
}
public async insertIssuedDocuments(params: IInsertIssuedDocumentsParams) {
this.allIssuedDocumentsCache = null;
const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
const issuedDocumentsWithOwnerId = params.issuedDocuments.map(doc =>
reassureOwnerIdExists(doc, ownerId),
);
return insertIssuedDocuments.run({ issuedDocuments: issuedDocumentsWithOwnerId }, this.db);
}
public async invalidateById(id: string) {
this.getIssuedDocumentsByIdLoader.clear(id);
this.allIssuedDocumentsCache = null;
this.getIssuedDocumentsStatusByChargeIdLoader.clearAll();
}
public clearCache() {
this.getIssuedDocumentsByIdLoader.clearAll();
this.getIssuedDocumentsByExternalIdLoader.clearAll();
this.getIssuedDocumentsByClientIdLoader.clearAll();
this.getIssuedDocumentsStatusByChargeIdLoader.clearAll();
this.allIssuedDocumentsCache = null;
}
}