mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-10 18:01:59 +00:00
176 lines
5.2 KiB
TypeScript
176 lines
5.2 KiB
TypeScript
import bluebird from 'bluebird';
|
|
import * as R from 'ramda';
|
|
import { Inject, Service } from 'typedi';
|
|
import { first } from 'lodash';
|
|
import { ImportFileDataValidator } from './ImportFileDataValidator';
|
|
import { Knex } from 'knex';
|
|
import {
|
|
ImportInsertError,
|
|
ImportOperError,
|
|
ImportOperSuccess,
|
|
ImportableContext,
|
|
} from './interfaces';
|
|
import { ServiceError } from '@/exceptions';
|
|
import { getUniqueImportableValue, trimObject } from './_utils';
|
|
import { ImportableResources } from './ImportableResources';
|
|
import ResourceService from '../Resource/ResourceService';
|
|
import { Import } from '@/system/models';
|
|
|
|
@Service()
|
|
export class ImportFileCommon {
|
|
@Inject()
|
|
private importFileValidator: ImportFileDataValidator;
|
|
|
|
@Inject()
|
|
private importable: ImportableResources;
|
|
|
|
@Inject()
|
|
private resource: ResourceService;
|
|
|
|
/**
|
|
* Imports the given parsed data to the resource storage through registered importable service.
|
|
* @param {number} tenantId -
|
|
* @param {string} resourceName - Resource name.
|
|
* @param {Record<string, any>} parsedData - Parsed data.
|
|
* @param {Knex.Transaction} trx - Knex transaction.
|
|
* @returns {Promise<[ImportOperSuccess[], ImportOperError[]]>}
|
|
*/
|
|
public async import(
|
|
tenantId: number,
|
|
importFile: Import,
|
|
parsedData: Record<string, any>[],
|
|
trx?: Knex.Transaction
|
|
): Promise<[ImportOperSuccess[], ImportOperError[]]> {
|
|
const resourceFields = this.resource.getResourceFields2(
|
|
tenantId,
|
|
importFile.resource
|
|
);
|
|
const ImportableRegistry = this.importable.registry;
|
|
const importable = ImportableRegistry.getImportable(importFile.resource);
|
|
|
|
const concurrency = importable.concurrency || 10;
|
|
|
|
const success: ImportOperSuccess[] = [];
|
|
const failed: ImportOperError[] = [];
|
|
|
|
const importAsync = async (objectDTO, index: number): Promise<void> => {
|
|
const context: ImportableContext = {
|
|
rowIndex: index,
|
|
import: importFile,
|
|
};
|
|
const transformedDTO = importable.transform(objectDTO, context);
|
|
const rowNumber = index + 1;
|
|
const uniqueValue = getUniqueImportableValue(resourceFields, objectDTO);
|
|
const errorContext = {
|
|
rowNumber,
|
|
uniqueValue,
|
|
};
|
|
try {
|
|
// Validate the DTO object before passing it to the service layer.
|
|
await this.importFileValidator.validateData(
|
|
resourceFields,
|
|
transformedDTO
|
|
);
|
|
try {
|
|
// Run the importable function and listen to the errors.
|
|
const data = await importable.importable(
|
|
tenantId,
|
|
transformedDTO,
|
|
trx
|
|
);
|
|
success.push({ index, data });
|
|
} catch (err) {
|
|
if (err instanceof ServiceError) {
|
|
const error: ImportInsertError[] = [
|
|
{
|
|
errorCode: 'ServiceError',
|
|
errorMessage: err.message || err.errorType,
|
|
...errorContext,
|
|
},
|
|
];
|
|
failed.push({ index, error });
|
|
} else {
|
|
const error: ImportInsertError[] = [
|
|
{
|
|
errorCode: 'UnknownError',
|
|
errorMessage: 'Unknown error occurred',
|
|
...errorContext,
|
|
},
|
|
];
|
|
failed.push({ index, error });
|
|
}
|
|
}
|
|
} catch (errors) {
|
|
const error = errors.map((er) => ({ ...er, ...errorContext }));
|
|
failed.push({ index, error });
|
|
}
|
|
};
|
|
await bluebird.map(parsedData, importAsync, { concurrency });
|
|
|
|
return [success, failed];
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} resourceName
|
|
* @param {Record<string, any>} params
|
|
*/
|
|
public async validateParamsSchema(
|
|
resourceName: string,
|
|
params: Record<string, any>
|
|
) {
|
|
const ImportableRegistry = this.importable.registry;
|
|
const importable = ImportableRegistry.getImportable(resourceName);
|
|
|
|
const yupSchema = importable.paramsValidationSchema();
|
|
|
|
try {
|
|
await yupSchema.validate(params, { abortEarly: false });
|
|
} catch (validationError) {
|
|
const errors = validationError.inner.map((error) => ({
|
|
errorCode: 'ParamsValidationError',
|
|
errorMessage: error.errors,
|
|
}));
|
|
throw errors;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} resourceName
|
|
* @param {Record<string, any>} params
|
|
*/
|
|
public async validateParams(
|
|
tenantId: number,
|
|
resourceName: string,
|
|
params: Record<string, any>
|
|
) {
|
|
const ImportableRegistry = this.importable.registry;
|
|
const importable = ImportableRegistry.getImportable(resourceName);
|
|
|
|
await importable.validateParams(tenantId, params);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} resourceName
|
|
* @param {Record<string, any>} params
|
|
* @returns
|
|
*/
|
|
public transformParams(resourceName: string, params: Record<string, any>) {
|
|
const ImportableRegistry = this.importable.registry;
|
|
const importable = ImportableRegistry.getImportable(resourceName);
|
|
|
|
return importable.transformParams(params);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the sheet columns from the given sheet data.
|
|
* @param {unknown[]} json
|
|
* @returns {string[]}
|
|
*/
|
|
public parseSheetColumns(json: unknown[]): string[] {
|
|
return R.compose(Object.keys, trimObject, first)(json);
|
|
}
|
|
}
|