UNPKG

@reldens/storage

Version:
268 lines (248 loc) 8.8 kB
/** * * Reldens - PrismaRelationResolver * */ const { sc } = require('@reldens/utils'); class PrismaRelationResolver { constructor(props) { this.relationMetadata = sc.get(props, 'relationMetadata', {}); this.metadataLoader = sc.get(props, 'metadataLoader', null); this.allRelationMappings = sc.get(props, 'allRelationMappings', {}); this.foreignKeyMappings = sc.get(props, 'foreignKeyMappings', {}); this.foreignKeyTargetFields = sc.get(props, 'foreignKeyTargetFields', {}); this.typeCaster = sc.get(props, 'typeCaster', null); this.currentModelMappings = {}; this.reverseModelMappings = {}; } updateMetadata(metadata) { this.relationMetadata = sc.get(metadata, 'relationMetadata', this.relationMetadata); this.foreignKeyMappings = sc.get(metadata, 'foreignKeyMappings', this.foreignKeyMappings); this.foreignKeyTargetFields = sc.get(metadata, 'foreignKeyTargetFields', this.foreignKeyTargetFields); } setCurrentModelMappings(mappings) { this.currentModelMappings = mappings || {}; this.reverseModelMappings = this.buildReverseMappings(this.currentModelMappings); } buildReverseMappings(mappings) { let reverse = {}; for(let reldensName of Object.keys(mappings)){ let prismaName = mappings[reldensName]; reverse[prismaName] = reldensName; } return reverse; } getAllRelations() { return Object.keys(this.relationMetadata || {}); } mapToPrismaName(reldensName, modelName) { if(!modelName){ if(sc.hasOwn(this.relationMetadata, reldensName)){ return reldensName; } if(sc.hasOwn(this.currentModelMappings, reldensName)){ return this.currentModelMappings[reldensName]; } return null; } let modelMappings = sc.get(this.allRelationMappings, modelName.toLowerCase(), {}); if(sc.hasOwn(modelMappings, reldensName)){ return modelMappings[reldensName]; } return null; } mapToReldensName(prismaName) { if(sc.hasOwn(this.reverseModelMappings, prismaName)){ return this.reverseModelMappings[prismaName]; } return prismaName; } getRelatedModelName(prismaName, fromModelName) { if(!fromModelName){ let meta = sc.get(this.relationMetadata, prismaName, null); if(meta){ return meta.model; } return null; } if(!this.metadataLoader){ return null; } let dmmf = this.metadataLoader.getDmmf(); if(!dmmf){ return null; } let modelInfo = this.findModelInDmmf(dmmf, fromModelName); if(!modelInfo){ return null; } for(let field of modelInfo.fields){ if(field.name === prismaName && 'object' === field.kind){ return field.type; } } return null; } findModelInDmmf(dmmf, modelName) { let lowerName = modelName.toLowerCase(); if(sc.hasOwn(dmmf.models, lowerName)){ return dmmf.models[lowerName]; } if(sc.isArray(dmmf.models)){ for(let model of dmmf.models){ if(model.name.toLowerCase() === lowerName){ return model; } } } return null; } convertForeignKeysToRelations(params, isCreate = false) { let data = {}; let relations = {}; for(let key of Object.keys(params)){ if(!sc.hasOwn(this.foreignKeyMappings, key)){ data[key] = params[key]; continue; } let relationName = this.foreignKeyMappings[key]; let value = params[key]; let targetField = sc.get(this.foreignKeyTargetFields, key, 'id'); if(!this.typeCaster){ relations[relationName] = {connect: {[targetField]: value}}; continue; } if(this.typeCaster.isNullOrEmpty(value)){ if(isCreate){ continue; } relations[relationName] = {disconnect: true}; continue; } let fieldType = sc.get(this.typeCaster.fieldTypes, key, null); let connectValue = fieldType ? this.typeCaster.castValue(value, fieldType, key) : this.typeCaster.castToIdType(value); relations[relationName] = { connect: {[targetField]: connectValue} }; } return {...data, ...relations}; } buildIncludeObjectWithMapping(relations) { let include = {}; let mapping = {}; for(let relation of relations){ this.processRelationPath(relation, include, mapping); } return {include, mapping}; } processRelationPath(relationPath, include, mapping) { let parts = relationPath.split('.'); let currentInclude = include; let currentModelName = null; for(let i = 0; i < parts.length; i++){ let reldensName = parts[i]; let prismaName = this.mapToPrismaName(reldensName, currentModelName); if(!prismaName){ return; } if(reldensName !== prismaName){ mapping[prismaName] = reldensName; } let isLastPart = (i === parts.length - 1); if(isLastPart){ currentInclude[prismaName] = true; return; } if(!sc.hasOwn(currentInclude, prismaName) || true === currentInclude[prismaName]){ currentInclude[prismaName] = {include: {}}; } currentModelName = this.getRelatedModelName(prismaName, currentModelName); if(!currentModelName){ return; } currentInclude = currentInclude[prismaName].include; } } transformRelationResults(result, includeConfig, relationMapping) { if(!result || !includeConfig){ return result; } if(sc.isArray(result)){ return result.map(item => this.transformSingleResult(item, includeConfig, relationMapping)); } return this.transformSingleResult(result, includeConfig, relationMapping); } transformSingleResult(item, includeConfig, relationMapping) { if(!item || !sc.isObject(item)){ return item; } let transformed = {...item}; for(let prismaName of Object.keys(includeConfig)){ if(!sc.hasOwn(transformed, prismaName)){ continue; } let relationValue = transformed[prismaName]; let nestedConfig = includeConfig[prismaName]; if(sc.isObject(nestedConfig) && sc.hasOwn(nestedConfig, 'include')){ relationValue = this.transformRelationResults(relationValue, nestedConfig.include, relationMapping); } let reldensName = sc.get(relationMapping, prismaName, null); if(!reldensName){ reldensName = this.mapToReldensName(prismaName); } transformed[reldensName] = relationValue; if(reldensName !== prismaName){ delete transformed[prismaName]; } } return transformed; } buildCreateDataWithRelations(params, relations) { let createData = {}; for(let relation of relations){ let prismaName = this.mapToPrismaName(relation, null); if(!prismaName){ continue; } if(!sc.hasOwn(params, relation)){ continue; } let relationData = params[relation]; if(!this.typeCaster){ createData[prismaName] = sc.isArray(relationData) ? {connect: relationData.map(item => ({id: item.id}))} : {connect: {id: relationData.id}}; continue; } if(sc.isArray(relationData)){ createData[prismaName] = { connect: relationData.map(item => ({id: this.typeCaster.castToIdType(item.id)})) }; continue; } createData[prismaName] = { connect: {id: this.typeCaster.castToIdType(relationData.id)} }; } return createData; } } module.exports.PrismaRelationResolver = PrismaRelationResolver;