@salesforce/acu-pack
Version:
SFDX CLI Extensions
273 lines • 12 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = require("@salesforce/command");
const command_base_1 = require("../../../lib/command-base");
const options_factory_1 = require("../../../lib/options-factory");
const utils_1 = require("../../../lib/utils");
const scaffold_options_1 = require("../../../lib/scaffold-options");
const sfdx_project_1 = require("../../../lib/sfdx-project");
const sfdx_tasks_1 = require("../../../lib/sfdx-tasks");
class Scaffold extends command_base_1.CommandBase {
constructor() {
super(...arguments);
this.schemas = new Map();
this.index = 0;
}
async runInternal() {
let options;
// Read/Write the options file if it does not exist already
if (this.flags.options) {
options = await options_factory_1.OptionsFactory.get(scaffold_options_1.ScaffoldOptions, this.flags.options);
if (!options) {
this.raiseError(`Unable to read options file: ${this.flags.options}.`);
}
}
else {
options = new scaffold_options_1.ScaffoldOptions();
await options.loadDefaults();
}
if (this.flags.sobjects) {
options.sObjectTypes.push(...this.flags.sobjects.split(','));
}
this.ux.log('Retrieving Schemas...');
for (const sObjectType of options.sObjectTypes) {
await this.getSchema(sObjectType.replace(' ', ''));
}
this.ux.log('Reading ./sfdx-project.json file...');
const project = await sfdx_project_1.default.default();
const defaultFolder = project.getDefaultDirectory();
this.ux.log('Generating Apex cls & cls-meta files...');
const rootPath = `./${defaultFolder}/main/default/classes/`;
await utils_1.default.mkDirPath(rootPath);
for (const [schemaName, schema] of this.schemas) {
this.ux.log('\t' + schemaName);
const fileDetails = this.generateTestSetupCode(schemaName, schema, options);
await utils_1.default.writeFile(rootPath + `${fileDetails.name}.cls`, fileDetails.contents);
await utils_1.default.writeFile(rootPath + `${fileDetails.name}.cls-meta.xml`, Scaffold.META_XML.replace(/API_VERSION_TOKEN/, project.sourceApiVersion));
}
}
async getSchema(sObjectType) {
let schema = this.schemas.get(sObjectType);
if (!schema) {
schema = await sfdx_tasks_1.SfdxTasks.describeObject(this.orgAlias, sObjectType);
if (!schema) {
this.raiseError('The returned schema is null.');
}
if (!schema.fields) {
this.raiseError('The returned schema does not contain a fields member.');
}
this.schemas.set(schema.name.split('__')[0], schema);
}
/* eslint-disable-next-line @typescript-eslint/no-unsafe-return */
return schema;
}
generateTestSetupCode(simpleName, schema, options) {
// Don't exceed max class name length
const noUnderscoreName = simpleName.replace(/_/g, '');
const className = `${noUnderscoreName.substring(0, Scaffold.MAX_CLASS_NAME_LENGTH - 4)}Test`;
const varName = `${className.substring(0, 1).toLowerCase()}${className.substring(1)}`;
const pre = '\t\t\t';
const classLines = [
'// This class was generated by the acu-pack:apex:scaffold command.',
'@IsTest',
`public with sharing class ${className} {`,
'',
'\t@TestSetup',
'\tstatic void setupTestData() {',
'\t\t// Create instance',
`\t\t${schema.name} ${varName} = new ${schema.name}( `,
];
const codeLines = new Map();
for (const field of schema.fields) {
// Skip optional fields?
if (!options.includeOptionalFields && field.nillable) {
continue;
}
if (field.createable) {
const value = options.includeRandomValues ? this.generateFieldValue(field) : null;
codeLines.set(field.name, `${pre}${field.name} = ${value}`);
}
}
const sortedKeys = utils_1.default.sortArray(Array.from(codeLines.keys()));
for (const key of sortedKeys) {
let classLine = codeLines.get(key);
if (key !== sortedKeys[sortedKeys.length - 1]) {
classLine += ',';
}
classLines.push(classLine);
}
classLines.push(...['\t\t);', `\t\tinsert ${varName};`, '\t}', '}']);
return {
name: className,
contents: classLines.join('\n'),
};
}
generateFieldValue(field) {
// https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/pages_variables_global_objecttype_schema_fields_reference.htm
if (!field) {
this.raiseError('The field argument cannot be null.');
}
const noUnderscoreName = field.name.split('__')[0].replace(/_/g, '');
const getStr = (fld, maxLength) => {
if (!fld) {
this.raiseError('The fld argument cannot be null.');
}
const value = fld.name;
let strLen = fld.length;
if (!strLen || strLen === 0 || strLen > maxLength) {
strLen = maxLength;
}
// trim if we are too long
if (strLen && value.length > strLen) {
return value.substr(0, strLen);
}
return value;
};
const getDec = (fld, maxLength) => {
if (!fld) {
this.raiseError('The fld argument cannot be null.');
}
let num = '';
let numLen = fld.precision;
if (!numLen || numLen === 0 || numLen > maxLength) {
numLen = maxLength;
}
const scale = fld.scale ?? 0;
for (let index = 1; index <= numLen - scale; index++) {
num += get1Rand();
}
if (fld.scale > 0) {
num += '.';
for (let index = 1; index <= scale; index++) {
num += get1Rand();
}
}
return num;
};
const getRand = (min, max) => {
return Math.floor(Math.random() * (max - min) + min);
};
const get1Rand = () => {
return getRand(0, 9);
};
const getPicklist = (picklistValues, count) => {
const values = [];
const index = getRand(0, picklistValues.length);
for (const picklist of picklistValues.slice(index)) {
if (!picklist.active) {
continue;
}
values.push(picklist.value);
if (values.length === count) {
return values;
}
}
return null;
};
const getValue = (fld) => {
if (!fld) {
this.raiseError('The fld argument cannot be null.');
}
switch (fld.type) {
case 'anytype':
case 'string':
case 'encryptedString':
case 'textarea':
return `'${getStr(fld)}'`;
case 'base64':
return `'${Buffer.from(getStr(fld)).toString('base64')}'`;
case 'textarea1': {
const lineCount = 3;
// Calculate length of each line (subract for \n) then divide
const lineLength = Math.floor((fld.length - lineCount) / 3);
const lines = [];
for (let lineIndex = 0; lineIndex < lineCount; lineIndex++) {
lines.push(`${getStr(fld, lineLength)}`);
}
return lines.join('+\n');
}
case 'int':
case 'integer':
return `${getDec(fld, 10)}`;
case 'long':
return `${getDec(fld, 16)}L`;
case 'double':
case 'percent':
return `${getDec(fld, 10)}`;
case 'currency':
return `${getDec(fld)}`;
case 'address':
return `'123 ${fld.name} St.'`;
case 'boolean':
return `${Math.random() < 0.5 ? 'true' : 'false'}`;
case 'date':
return 'Date.today()';
case 'datetime':
return 'Datetime.now()';
case 'time':
return 'Datetime.getTime()';
case 'email':
return `'${fld.name}@${noUnderscoreName}.email.org'`;
case 'phone': {
const phone = `555-${getRand(100, 999)}-${getRand(1000, 9999)} ext ${++this.index}`;
// phone max is 40
return `'${phone.substr(0, 40)}'`;
}
case 'multipicklist': {
if (fld.picklistValues?.length === 0) {
this.ux.log(`Skipping: ${fld.name} (${fld.type}) - no picklist values.`);
}
const count = Math.floor(fld.picklistValues.length / 3);
const values = getPicklist(fld.picklistValues, count);
return values ? `'${values.join(';').replace(/'/g, "\\'")}'` : null;
}
case 'picklist': {
if (fld.picklistValues?.length === 0) {
this.ux.log(`Skipping: ${fld.name} (${fld.type}) - no picklist values.`);
}
const value = getPicklist(fld.picklistValues, 1);
return value ? `'${value.join(';').replace(/'/g, "\\'")}'` : null;
}
case 'url':
return `'https://www.${noUnderscoreName}.salesforce.com.${this.orgAlias}/index'`;
case 'id':
case 'reference':
case 'combobox':
case 'dataCategoryGroupReference':
default:
this.ux.log(`Skipping: ${fld.name} (${fld.type})`);
return null;
}
};
return getValue(field);
}
}
exports.default = Scaffold;
Scaffold.description = command_base_1.CommandBase.messages.getMessage('apex.scaffold.commandDescription');
Scaffold.examples = [
`$ sfdx acu-pack:apex:scaffold -u myOrgAlias -s Account,MyObject__c'
Generates AccountTest.cls & MyObjectTest.cls Apex test classes (and cls-meta files) for the Account & MyObject__c SObject types. Random values assigned to required fields by default`,
`$ sfdx acu-pack:apex:scaffold -u myOrgAlias -o scaffold-options.json
Generates Apex test classes (and cls-meta files) for specified CustomObjects. The specified options file is used.`,
];
Scaffold.flagsConfig = {
sobjects: command_1.flags.string({
char: 's',
description: command_base_1.CommandBase.messages.getMessage('apex.scaffold.sObjectsFlagDescription'),
}),
options: command_1.flags.string({
char: 'o',
description: command_base_1.CommandBase.messages.getMessage('apex.scaffold.optionsFlagDescription'),
}),
};
// Comment this out if your command does not require an org username
Scaffold.requiresUsername = true;
// Set this to true if your command requires a project workspace; 'requiresProject' is false by default
Scaffold.requiresProject = true;
Scaffold.META_XML = '<?xml version="1.0" encoding="UTF-8"?>\n' +
'<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">\n' +
'<apiVersion>API_VERSION_TOKEN</apiVersion>\n' +
'<status>Active</status>\n' +
'</ApexClass>';
Scaffold.MAX_CLASS_NAME_LENGTH = 40;
//# sourceMappingURL=scaffold.js.map