UNPKG

@hso/d365-cli

Version:

Dynamics 365 Command Line Interface for TypeScript projects for Dataverse

120 lines (111 loc) 5.93 kB
import {WebApi} from './WebApi'; export class Service { public static async parseCreateModel(entityLogicalName: string, model: Model): Promise<Model> { const entity = Service.parseModel(model), metadata = await Xrm.Utility.getEntityMetadata(entityLogicalName, Object.keys(entity)); Service.cleanCloneModel(entity, metadata); return WebApi.populateBindings(entity, metadata); } private static cleanCloneModel(model: Model, metadata: Xrm.Metadata.EntityMetadata): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any delete (model as any)[metadata.PrimaryIdAttribute]; delete model.statuscode; delete model.statecode; delete model.ownerid; // eslint-disable-next-line @typescript-eslint/no-explicit-any delete (model as any)['_ownerid_value@Microsoft.Dynamics.CRM.lookuplogicalname']; // delete model.modifiedon; // delete model.createdon; // delete model.versionnumber; } private static parseModel(model: Model): Model { const keys = Object.keys(model), // eslint-disable-next-line @typescript-eslint/no-explicit-any cleanModel: any = {...model}; for (const key of keys) { if (cleanModel[key] === null) { delete cleanModel[key]; } else if (key.startsWith('@')) { delete cleanModel[key]; } else if (key.endsWith('FormattedValue')) { delete cleanModel[key]; } else if (key.startsWith('_') && key.endsWith('_value') && !key.includes('@')) { const navigationProperty = cleanModel[`${key}@Microsoft.Dynamics.CRM.associatednavigationproperty`], trimmedKey = key.substr(1, key.length - 7); if (navigationProperty) { cleanModel[trimmedKey] = cleanModel[key]; delete cleanModel[`${key}@Microsoft.Dynamics.CRM.associatednavigationproperty`]; } else { delete cleanModel[`${key}@Microsoft.Dynamics.CRM.lookuplogicalname`]; } delete cleanModel[key]; delete cleanModel[`${trimmedKey}_LogicalName`]; // Added in WebApi#parseValues } } return cleanModel; } public static async validateRecord(entityLogicalName: string, model: Model): Promise<ModelValidation> { let validation = await Service.validateRequiredAttributes(entityLogicalName, model); if (validation.isValid) { validation = await Service.validateMesh(entityLogicalName, model); } return validation; } private static async validateMesh(entityLogicalName: string, model: Model): Promise<ModelValidation> { const keys = Object.keys(model), wrongKey = keys.find(key => key.startsWith('_') || key.endsWith('FormattedValue') || key.endsWith('LogicalName')); if (wrongKey) { return { isValid: false, attribute: wrongKey, category: 'Invalid key' }; } return {isValid: true}; } private static async validateRequiredAttributes(entityLogicalName: string, model: Model): Promise<ModelValidation> { const requiredAttributeLogicalNames = await Service.getRequiredAttributeLogicalNames(entityLogicalName), filteredAttributeMetadatas = await Xrm.Utility.getEntityMetadata(entityLogicalName, requiredAttributeLogicalNames), keys = Object.keys(model); for (const name of requiredAttributeLogicalNames) { const attributeDescriptor = Service.getAttributeDescriptor(name, filteredAttributeMetadatas), property = attributeDescriptor.LogicalName, bindings: string[] = []; const targets: string[] = attributeDescriptor.Targets || []; for (const target of targets) { const binding = await WebApi.getBinding(property, 'dummy', filteredAttributeMetadatas, target); bindings.push(Object.keys(binding)[0]); } if (!keys.includes(property) && !keys.includes(`${property}@odata.bind`) && !bindings.some(binding => keys.includes(binding))) { return { isValid: false, attribute: name, category: 'Mandatory' }; } } return {isValid: true}; } private static getAttributeDescriptor(attribute: string, entityMetadata: Xrm.Metadata.EntityMetadata): { AttributeOf?: string; LogicalName: string; Targets: string[]; } { const attributeMetadata = entityMetadata.Attributes.get(attribute); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore let attributeDescriptor = attributeMetadata.attributeDescriptor; if (attributeDescriptor.AttributeOf) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore attributeDescriptor = entityMetadata.Attributes.get(attributeDescriptor.AttributeOf).attributeDescriptor; } return attributeDescriptor; } private static async getRequiredAttributeLogicalNames(entityLogicalName: string): Promise<string[]> { const rawAttributesMetadatas = await WebApi.getAttributesMetadata(entityLogicalName, ['LogicalName', 'RequiredLevel']); return rawAttributesMetadatas .filter((attrMetadata: {RequiredLevel: {Value: string}}) => attrMetadata.RequiredLevel.Value === 'ApplicationRequired') .map((requiredAttributeMetadata: {LogicalName: string}) => requiredAttributeMetadata.LogicalName); } }