-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(scratch): add org-capitalize-record-types
#1020
Changes from all commits
725bf67
4a3a511
fc32218
5e00c9a
94fe825
b0ae322
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# noCapitalizeRecordTypeConfigVar | ||
|
||
Record types defined in the scratch org definition file will stop being capitalized by default in a future release. | ||
Set the `org-capitalize-record-types` config var to `true` to enforce capitalization. | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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'; | ||
|
@@ -150,7 +150,12 @@ export const scratchOrgResume = async (jobId: string): Promise<ScratchOrgCreateR | |
const configAggregator = await ConfigAggregator.create(); | ||
|
||
await emit({ stage: 'deploy settings', scratchOrgInfo: soi }); | ||
const settingsGenerator = new SettingsGenerator(); | ||
|
||
const capitalizeRecordTypes = await getCapitalizeRecordTypesConfig(); | ||
|
||
const settingsGenerator = new SettingsGenerator({ | ||
capitalizeRecordTypes, | ||
}); | ||
await settingsGenerator.extract({ ...soi, ...definitionjson }); | ||
const [authInfo] = await Promise.all([ | ||
resolveUrl(scratchOrgAuthInfo), | ||
|
@@ -228,8 +233,13 @@ export const scratchOrgCreate = async (options: ScratchOrgCreateOptions): Promis | |
ignoreAncestorIds, | ||
}); | ||
|
||
const capitalizeRecordTypes = await getCapitalizeRecordTypesConfig(); | ||
|
||
// 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, | ||
}); | ||
|
||
const settings = await settingsGenerator.extract(scratchOrgInfo); | ||
logger.debug(`the scratch org def file has settings: ${settingsGenerator.hasSettings()}`); | ||
|
||
|
@@ -328,3 +338,13 @@ const getSignupTargetLoginUrl = async (): Promise<string | undefined> => { | |
// a project isn't required for org:create | ||
} | ||
}; | ||
|
||
async function getCapitalizeRecordTypesConfig(): Promise<boolean | undefined> { | ||
const configAgg = await ConfigAggregator.create(); | ||
const value = configAgg.getInfo('org-capitalize-record-types').value as string | undefined; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the type of
if it's if not undefined we cast it to |
||
|
||
if (value !== undefined) return toBoolean(value); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion | ||
return value as undefined; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,9 +16,13 @@ import { StatusResult } from '../status/types'; | |
import { PollingClient } from '../status/pollingClient'; | ||
import { ZipWriter } from '../util/zipWriter'; | ||
import { DirectoryWriter } from '../util/directoryWriter'; | ||
import { Lifecycle } from '../lifecycleEvents'; | ||
import { Messages } from '../messages'; | ||
import { ScratchOrgInfo, ObjectSetting } from './scratchOrgTypes'; | ||
import { Org } from './org'; | ||
|
||
Messages.importMessagesDirectory(__dirname); | ||
|
||
export enum RequestStatus { | ||
Pending = 'Pending', | ||
InProgress = 'InProgress', | ||
|
@@ -82,13 +86,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`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this wasn't in the scope of the inv. we got but it's the same behavior (for the listed 4 objects above) so I made this follow the config var too. |
||
switch (objectName) { | ||
case 'Case': | ||
businessProcessPicklistVal = 'New'; | ||
|
@@ -110,7 +120,8 @@ export const createRecordTypeAndBusinessProcessFileContent = ( | |
objectName: string, | ||
json: Record<string, unknown>, | ||
allRecordTypes: string[], | ||
allBusinessProcesses: string[] | ||
allBusinessProcesses: string[], | ||
capitalizeRecordTypes: boolean | ||
): JsonMap => { | ||
let output = { | ||
'@': { | ||
|
@@ -126,15 +137,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, | ||
}; | ||
|
||
|
@@ -186,9 +205,22 @@ 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'); | ||
if (options?.capitalizeRecordTypes === undefined) { | ||
const messages = Messages.loadMessages('@salesforce/core', 'scratchOrgSettingsGenerator'); | ||
void Lifecycle.getInstance().emitWarning(messages.getMessage('noCapitalizeRecordTypeConfigVar')); | ||
shetzel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.capitalizeRecordTypes = true; | ||
} else { | ||
this.capitalizeRecordTypes = options.capitalizeRecordTypes; | ||
} | ||
// 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()}`; | ||
|
@@ -344,7 +376,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')); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this warning will be visible to both projects using sfdx-core and the CLI.
For lib users, I wanted to make this an object option rather than the config (that way the config var would live in plugin-org) but decided against it (more about that below).