UNPKG

@kitmi/data

Version:

Jacaranda Framework Data Access Model

105 lines (84 loc) 3.82 kB
import typeSystem from '@kitmi/validators/allSync'; import { _ } from '@kitmi/utils'; import { ApplicationError } from '@kitmi/types'; // Activator will only be called when the field value is null // Activator signature: (entity, field, context, ...args) => value const activatorTable = typeSystem.handlers.activator; activatorTable['isEqual'] = (args) => args[0] === args[1]; activatorTable['isNotEqual'] = (args) => args[0] !== args[1]; activatorTable['setValueWhen'] = ([value, condition]) => (condition ? value : null); activatorTable['concat'] = ([sep = '', ...strs]) => strs.join(sep); activatorTable['sum'] = (args) => args.reduce((sum, v) => (sum += v), 0); activatorTable['multiply'] = ([multiplier, multiplicand]) => multiplier * multiplicand; activatorTable['timeOfValueSet'] = ([value]) => (value != null ? new Date() : null); export const _Activators = _.mapValues( activatorTable, (activator) => (entity, field, context, ...options) => activator(options, field, context) ); _Activators.fetch_ = async (entity, field, context, assoc, options) => { const parts = assoc.split('.'); const selectedField = parts.pop(); const remoteAssoc = parts.join('.'); const localAssoc = parts.shift(); let interAssoc; if (parts.length > 0) { interAssoc = parts.join('.'); } if (!(localAssoc in context.latest)) { return undefined; } const assocValue = context.latest[localAssoc]; if (assocValue == null) { throw new ApplicationError( `The value of referenced association "${localAssoc}" of entity "${entity.meta.name}" should not be null.` ); } const assocMeta = entity.meta.associations[localAssoc]; if (!assocMeta) { throw new ApplicationError(`"${localAssoc}" is not an association field of entity "${entity.meta.name}".`); } if (assocMeta.list) { throw new ApplicationError( `"${localAssoc}" entity "${entity.meta.name}" is a list-style association which is not supported by the built-in fetch_ Activators.` ); } // local cache in context, shared by other fields if any let remoteEntity = context.populated && context.populated[remoteAssoc]; if (!remoteEntity) { const findOptions = { $select: [selectedField], $where: { [assocMeta.key]: assocValue } }; if (interAssoc) { findOptions.$relation = [interAssoc]; } if (!context.options.$dryRun) { await entity.ensureTransaction_(context.options.$ctx, `${entity.meta.name}.${context.op}.activator.fetch_`); } remoteEntity = await entity.db.entity(assocMeta.entity).findOne_(findOptions); if (!remoteEntity) { throw new ApplicationError( `Unable to find the "${assocMeta.entity}" with [${assocMeta.key}=${assocValue}]. Entity: ${entity.meta.name}` ); } context.populated || (context.populated = {}); context.populated[localAssoc] = remoteEntity; let currentAssoc = localAssoc; while (parts.length > 0) { const nextAssoc = parts.shift(); // todo: nested accessor remoteEntity = remoteEntity[':' + nextAssoc]; if (Array.isArray(remoteEntity)) { throw new Error('Remote entity should not be an array.'); } currentAssoc = currentAssoc + '.' + nextAssoc; context.populated[currentAssoc] = remoteEntity; } } if (!(selectedField in remoteEntity)) { throw new ApplicationError( `"${selectedField}" is not a field of remote association "${remoteAssoc}" of entity "${entity.meta.name}".` ); } return remoteEntity[selectedField]; }; export default activatorTable;