Skip to content

Commit

Permalink
feat(scratch): add org-capitalize-record-types
Browse files Browse the repository at this point in the history
  • Loading branch information
cristiand391 committed Jan 16, 2024
1 parent 891318a commit 725bf67
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 16 deletions.
4 changes: 4 additions & 0 deletions messages/envVars.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,7 @@ Deprecated environment variable: %s. Please use %s instead.
Deprecated environment variable: %s. Please use %s instead.

Your environment has both variables populated, and with different values. The value from %s will be used.

# sfCapitalizeRecordTypes

Boolean indicating whether or not to capitalize object settings.
5 changes: 5 additions & 0 deletions src/config/envVars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export enum EnvironmentVariable {
'SF_UPDATE_INSTRUCTIONS' = 'SF_UPDATE_INSTRUCTIONS',
'SF_INSTALLER' = 'SF_INSTALLER',
'SF_ENV' = 'SF_ENV',
'SF_CAPITALIZE_RECORD_TYPES' = 'SF_CAPITALIZE_RECORD_TYPES',
}
type EnvMetaData = {
description: string;
Expand Down Expand Up @@ -417,6 +418,10 @@ export const SUPPORTED_ENV_VARS: EnvType = {
description: getMessage(EnvironmentVariable.SF_ENV),
synonymOf: null,
},
[EnvironmentVariable.SF_CAPITALIZE_RECORD_TYPES]: {
description: getMessage(EnvironmentVariable.SF_CAPITALIZE_RECORD_TYPES),
synonymOf: null,
},
};

export class EnvVars extends Env {
Expand Down
8 changes: 8 additions & 0 deletions src/org/orgConfigProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,17 @@ export enum OrgConfigProperties {
* The url for the debugger configuration.
*/
ORG_ISV_DEBUGGER_URL = 'org-isv-debugger-url',
/**
* Capitalize record types when deploying scratch org settings
*/
ORG_CAPITALIZE_RECORD_TYPES = 'org-capitalize-record-types',
}

export const ORG_CONFIG_ALLOWED_PROPERTIES = [
{
key: OrgConfigProperties.ORG_CAPITALIZE_RECORD_TYPES,
description: messages.getMessage(OrgConfigProperties.ORG_CUSTOM_METADATA_TEMPLATES),
},
{
key: OrgConfigProperties.ORG_CUSTOM_METADATA_TEMPLATES,
description: messages.getMessage(OrgConfigProperties.ORG_CUSTOM_METADATA_TEMPLATES),
Expand Down
9 changes: 7 additions & 2 deletions src/org/scratchOrgCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { Duration } from '@salesforce/kit';
import { Duration, toBoolean } from '@salesforce/kit';
import { ensureString } from '@salesforce/ts-types';
import { Messages } from '../messages';
import { Logger } from '../logger/logger';
Expand Down Expand Up @@ -229,7 +229,12 @@ export const scratchOrgCreate = async (options: ScratchOrgCreateOptions): Promis
});

// gets the scratch org settings (will use in both signup paths AND to deploy the settings)
const settingsGenerator = new SettingsGenerator();
const settingsGenerator = new SettingsGenerator({
capitalizeRecordTypes: toBoolean(
(await ConfigAggregator.create()).getInfo('org-capitalize-record-types').value ?? true
),
});

const settings = await settingsGenerator.extract(scratchOrgInfo);
logger.debug(`the scratch org def file has settings: ${settingsGenerator.hasSettings()}`);

Expand Down
45 changes: 34 additions & 11 deletions src/org/scratchOrgSettingsGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,19 @@ export const createObjectFileContent = ({
return { ...output, ...{ version: apiVersion } };
};

const calculateBusinessProcess = (objectName: string, defaultRecordType: string): Array<string | null> => {
const calculateBusinessProcess = (
objectName: string,
defaultRecordType: string,
capitalizeBusinessProcess: boolean
): Array<string | null> => {
let businessProcessName = null;
let businessProcessPicklistVal = null;
// These four objects require any record type to specify a "business process"--
// a restricted set of items from a standard picklist on the object.
if (['Case', 'Lead', 'Opportunity', 'Solution'].includes(objectName)) {
businessProcessName = upperFirst(defaultRecordType) + 'Process';
businessProcessName = capitalizeBusinessProcess
? `${upperFirst(defaultRecordType)}Process`
: `${defaultRecordType}Process`;
switch (objectName) {
case 'Case':
businessProcessPicklistVal = 'New';
Expand All @@ -110,7 +116,8 @@ export const createRecordTypeAndBusinessProcessFileContent = (
objectName: string,
json: Record<string, unknown>,
allRecordTypes: string[],
allBusinessProcesses: string[]
allBusinessProcesses: string[],
capitalizeRecordTypes: boolean
): JsonMap => {
let output = {
'@': {
Expand All @@ -126,15 +133,23 @@ export const createRecordTypeAndBusinessProcessFileContent = (
};
}

const defaultRecordType = json.defaultRecordType;
const defaultRecordType = capitalizeRecordTypes
? upperFirst(json.defaultRecordType as string)
: json.defaultRecordType;

if (typeof defaultRecordType === 'string') {
// We need to keep track of these globally for when we generate the package XML.
allRecordTypes.push(`${name}.${upperFirst(defaultRecordType)}`);
const [businessProcessName, businessProcessPicklistVal] = calculateBusinessProcess(name, defaultRecordType);
allRecordTypes.push(`${name}.${defaultRecordType}`);
const [businessProcessName, businessProcessPicklistVal] = calculateBusinessProcess(
name,
defaultRecordType,
capitalizeRecordTypes
);

// Create the record type
const recordTypes = {
fullName: upperFirst(defaultRecordType),
label: upperFirst(defaultRecordType),
fullName: defaultRecordType,
label: defaultRecordType,
active: true,
};

Expand Down Expand Up @@ -186,9 +201,16 @@ export default class SettingsGenerator {
private allBusinessProcesses: string[] = [];
private readonly shapeDirName: string;
private readonly packageFilePath: string;

public constructor(options?: { mdApiTmpDir?: string; shapeDirName?: string; asDirectory?: boolean }) {
private readonly capitalizeRecordTypes: boolean;

public constructor(options?: {
mdApiTmpDir?: string;
shapeDirName?: string;
asDirectory?: boolean;
capitalizeRecordTypes?: boolean;
}) {
this.logger = Logger.childFromRoot('SettingsGenerator');
this.capitalizeRecordTypes = options?.capitalizeRecordTypes ?? false;
// If SFDX_MDAPI_TEMP_DIR is set, copy settings to that dir for people to inspect.
const mdApiTmpDir = options?.mdApiTmpDir ?? env.getString('SFDX_MDAPI_TEMP_DIR');
this.shapeDirName = options?.shapeDirName ?? `shape_${Date.now()}`;
Expand Down Expand Up @@ -344,7 +366,8 @@ export default class SettingsGenerator {
item,
value,
allRecordTypes,
allbusinessProcesses
allbusinessProcesses,
this.capitalizeRecordTypes
);
const xml = js2xmlparser.parse('CustomObject', fileContent);
return this.writer.addToStore(xml, path.join(objectsDir, upperFirst(item) + '.object'));
Expand Down
71 changes: 68 additions & 3 deletions test/unit/org/scratchOrgSettingsGeneratorTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,8 @@ describe('scratchOrgSettingsGenerator', () => {
'account',
objectSettingsData.account,
allRecordTypes,
allbusinessProcesses
allbusinessProcesses,
true
);
expect(recordTypeAndBusinessProcessFileContent).to.deep.equal({
'@': { xmlns: 'http://soap.sforce.com/2006/04/metadata' },
Expand All @@ -649,14 +650,39 @@ describe('scratchOrgSettingsGenerator', () => {
expect(allbusinessProcesses).to.deep.equal([]);
});

it('createRecordTypeAndBusinessProcessFileContent with account type, not capitalized', () => {
const objectSettingsDataLowercaseRecordType = {
account: {
defaultRecordType: 'personAccount',
},
};

const allRecordTypes: string[] = [];
const allbusinessProcesses: string[] = [];
const recordTypeAndBusinessProcessFileContent = createRecordTypeAndBusinessProcessFileContent(
'account',
objectSettingsDataLowercaseRecordType.account,
allRecordTypes,
allbusinessProcesses,
false
);
expect(recordTypeAndBusinessProcessFileContent).to.deep.equal({
'@': { xmlns: 'http://soap.sforce.com/2006/04/metadata' },
recordTypes: { fullName: 'personAccount', label: 'personAccount', active: true },
});
expect(allRecordTypes).to.deep.equal(['Account.personAccount']);
expect(allbusinessProcesses).to.deep.equal([]);
});

it('createRecordTypeAndBusinessProcessFileContent with opportunity values', () => {
const allRecordTypes: string[] = [];
const allbusinessProcesses: string[] = [];
const recordTypeAndBusinessProcessFileContent = createRecordTypeAndBusinessProcessFileContent(
'opportunity',
objectSettingsData.opportunity,
allRecordTypes,
allbusinessProcesses
allbusinessProcesses,
true
);
expect(recordTypeAndBusinessProcessFileContent).to.deep.equal({
'@': { xmlns: 'http://soap.sforce.com/2006/04/metadata' },
Expand Down Expand Up @@ -686,7 +712,8 @@ describe('scratchOrgSettingsGenerator', () => {
'case',
objectSettingsData.case,
allRecordTypes,
allbusinessProcesses
allbusinessProcesses,
true
);
expect(recordTypeAndBusinessProcessFileContent).to.deep.equal({
'@': { xmlns: 'http://soap.sforce.com/2006/04/metadata' },
Expand All @@ -709,6 +736,44 @@ describe('scratchOrgSettingsGenerator', () => {
expect(allRecordTypes).to.deep.equal(['Case.Default']);
expect(allbusinessProcesses).to.deep.equal(['Case.DefaultProcess']);
});

it('createRecordTypeAndBusinessProcessFileContent with case values, not capitalized', () => {
const objectSettingsDataLowercaseRecordType = {
case: {
defaultRecordType: 'default',
sharingModel: 'private',
},
};
const allRecordTypes: string[] = [];
const allbusinessProcesses: string[] = [];
const recordTypeAndBusinessProcessFileContent = createRecordTypeAndBusinessProcessFileContent(
'case',
objectSettingsDataLowercaseRecordType.case,
allRecordTypes,
allbusinessProcesses,
false
);
expect(recordTypeAndBusinessProcessFileContent).to.deep.equal({
'@': { xmlns: 'http://soap.sforce.com/2006/04/metadata' },
sharingModel: 'Private',
recordTypes: {
fullName: 'default',
label: 'default',
active: true,
businessProcess: 'defaultProcess',
},
businessProcesses: {
fullName: 'defaultProcess',
isActive: true,
values: {
fullName: 'New',
default: true,
},
},
});
expect(allRecordTypes).to.deep.equal(['Case.default']);
expect(allbusinessProcesses).to.deep.equal(['Case.defaultProcess']);
});
});

describe('createObjectFileContent', () => {
Expand Down

2 comments on commit 725bf67

@svc-cli-bot
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logger Benchmarks - ubuntu-latest

Benchmark suite Current: 725bf67 Previous: 7fa714e Ratio
Child logger creation 481735 ops/sec (±1.11%) 483717 ops/sec (±0.70%) 1.00
Logging a string on root logger 802872 ops/sec (±8.88%) 759525 ops/sec (±6.34%) 0.95
Logging an object on root logger 594659 ops/sec (±8.05%) 604635 ops/sec (±7.63%) 1.02
Logging an object with a message on root logger 8845 ops/sec (±205.65%) 10311 ops/sec (±201.53%) 1.17
Logging an object with a redacted prop on root logger 496053 ops/sec (±10.19%) 418638 ops/sec (±7.98%) 0.84
Logging a nested 3-level object on root logger 402072 ops/sec (±8.94%) 351944 ops/sec (±9.59%) 0.88

This comment was automatically generated by workflow using github-action-benchmark.

@svc-cli-bot
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logger Benchmarks - windows-latest

Benchmark suite Current: 725bf67 Previous: 7fa714e Ratio
Child logger creation 335895 ops/sec (±0.60%) 341012 ops/sec (±3.72%) 1.02
Logging a string on root logger 836773 ops/sec (±6.06%) 827137 ops/sec (±11.71%) 0.99
Logging an object on root logger 605208 ops/sec (±6.60%) 638470 ops/sec (±6.22%) 1.05
Logging an object with a message on root logger 3809 ops/sec (±217.49%) 4975 ops/sec (±209.82%) 1.31
Logging an object with a redacted prop on root logger 423788 ops/sec (±6.25%) 467474 ops/sec (±11.38%) 1.10
Logging a nested 3-level object on root logger 322063 ops/sec (±5.38%) 324115 ops/sec (±5.76%) 1.01

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.