feat: linking relation with id in importing

This commit is contained in:
Ahmed Bouhuolia
2024-04-01 01:13:31 +02:00
parent 22a016b56e
commit 74da28b464
21 changed files with 394 additions and 84 deletions

View File

@@ -1,21 +1,32 @@
import { Service } from 'typedi';
import { Inject, Service } from 'typedi';
import * as R from 'ramda';
import { isUndefined, get, chain } from 'lodash';
import bluebird from 'bluebird';
import { isUndefined, get, chain, toArray, pickBy, castArray } from 'lodash';
import { ImportMappingAttr, ResourceMetaFieldsMap } from './interfaces';
import { parseBoolean } from '@/utils';
import { trimObject } from './_utils';
import { Account, Item } from '@/models';
import ResourceService from '../Resource/ResourceService';
import { Knex } from 'knex';
const CurrencyParsingDTOs = 10;
@Service()
export class ImportFileDataTransformer {
@Inject()
private resource: ResourceService;
/**
*
* @param {number} tenantId -
* @param {}
*/
public parseSheetData(
public async parseSheetData(
tenantId: number,
importFile: any,
importableFields: any,
data: Record<string, unknown>[]
data: Record<string, unknown>[],
trx?: Knex.Transaction
) {
// Sanitize the sheet data.
const sanitizedData = this.sanitizeSheetData(data);
@@ -25,10 +36,17 @@ export class ImportFileDataTransformer {
sanitizedData,
importFile.mappingParsed
);
const resourceModel = this.resource.getResourceModel(
tenantId,
importFile.resource
);
// Parse the mapped sheet values.
const parsedValues = this.parseExcelValues(importableFields, mappedDTOs);
return parsedValues;
return this.parseExcelValues(
importableFields,
mappedDTOs,
resourceModel,
trx
);
}
/**
@@ -67,35 +85,86 @@ export class ImportFileDataTransformer {
* @param {Record<string, any>} valueDTOS -
* @returns {Record<string, any>}
*/
public parseExcelValues(
public async parseExcelValues(
fields: ResourceMetaFieldsMap,
valueDTOs: Record<string, any>[]
): Record<string, any> {
const parser = (value, key) => {
valueDTOs: Record<string, any>[],
resourceModel: any,
trx?: Knex.Transaction
): Promise<Record<string, any>> {
// Prases the given object value based on the field key type.
const parser = async (value, key) => {
let _value = value;
const field = fields[key];
// Parses the boolean value.
if (fields[key].fieldType === 'boolean') {
_value = parseBoolean(value, false);
// Parses the enumeration value.
} else if (fields[key].fieldType === 'enumeration') {
} else if (field.fieldType === 'enumeration') {
const field = fields[key];
const option = get(field, 'options', []).find(
(option) => option.label === value
);
_value = get(option, 'key');
// Prases the numeric value.
// Parses the numeric value.
} else if (fields[key].fieldType === 'number') {
_value = parseFloat(value);
// Parses the relation value.
} else if (field.fieldType === 'relation') {
const relationModel = resourceModel.relationMappings[field.relationKey];
const RelationModel = relationModel?.modelClass;
if (!relationModel || !RelationModel) {
throw new Error(`The relation model of ${key} field is not exist.`);
}
const relationQuery = RelationModel.query(trx);
const relationKeys = field?.importableRelationLabel
? castArray(field?.importableRelationLabel)
: castArray(field?.relationEntityLabel);
relationQuery.where(function () {
relationKeys.forEach((relationKey: string) => {
this.orWhereRaw('LOWER(??) = LOWER(?)', [relationKey, value]);
});
});
const result = await relationQuery.first();
_value = get(result, 'id');
}
return _value;
};
return valueDTOs.map((DTO) => {
return chain(DTO)
.pickBy((value, key) => !isUndefined(fields[key]))
.mapValues(parser)
.value();
const parseKey = (key: string) => {
const field = fields[key];
let _objectTransferObjectKey = key;
if (field.fieldType === 'relation') {
_objectTransferObjectKey = `${key}Id`;
}
return _objectTransferObjectKey;
};
const parseAsync = async (valueDTO) => {
// Remove the undefined fields.
const _valueDTO = pickBy(
valueDTO,
(value, key) => !isUndefined(fields[key])
);
const keys = Object.keys(_valueDTO);
// Map the object values.
return bluebird.reduce(
keys,
async (acc, key) => {
const parsedValue = await parser(_valueDTO[key], key);
const parsedKey = await parseKey(key);
acc[parsedKey] = parsedValue;
return acc;
},
{}
);
};
return bluebird.map(valueDTOs, parseAsync, {
concurrency: CurrencyParsingDTOs,
});
}
}