Skip to content

Commit

Permalink
Merge branch 'main' into sm/reduce-type-errors
Browse files Browse the repository at this point in the history
  • Loading branch information
mshanemc authored Jul 23, 2024
2 parents a436b16 + f981904 commit 065b815
Show file tree
Hide file tree
Showing 9 changed files with 1,217 additions and 1,527 deletions.
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
## [3.3.21](https://github.com/salesforcecli/plugin-custom-metadata/compare/3.3.20...3.3.21) (2024-07-23)

### Bug Fixes

- handle null scale ([2aaab06](https://github.com/salesforcecli/plugin-custom-metadata/commit/2aaab069f874551e17503009969bc7dbb8212a0a))

## [3.3.20](https://github.com/salesforcecli/plugin-custom-metadata/compare/3.3.19...3.3.20) (2024-07-20)

### Bug Fixes

- **deps:** bump @salesforce/sf-plugins-core from 11.1.7 to 11.2.0 ([2007261](https://github.com/salesforcecli/plugin-custom-metadata/commit/200726103958ff341576d7edf0f34c456379f4b0))

## [3.3.19](https://github.com/salesforcecli/plugin-custom-metadata/compare/3.3.18...3.3.19) (2024-07-19)

### Bug Fixes

- no nullish in template literals ([#868](https://github.com/salesforcecli/plugin-custom-metadata/issues/868)) ([ea0d3b9](https://github.com/salesforcecli/plugin-custom-metadata/commit/ea0d3b9cef66571260f3d28224e9c3ad614dcae0))

## [3.3.18](https://github.com/salesforcecli/plugin-custom-metadata/compare/3.3.17...3.3.18) (2024-07-14)

### Bug Fixes

- **deps:** bump @salesforce/sf-plugins-core from 11.1.6 to 11.1.7 ([6fa8d7f](https://github.com/salesforcecli/plugin-custom-metadata/commit/6fa8d7fd70c3515911742b69f74ada59bda01a39))

## [3.3.17](https://github.com/salesforcecli/plugin-custom-metadata/compare/3.3.16...3.3.17) (2024-07-07)

### Bug Fixes

- **deps:** bump @oclif/core from 4.0.6 to 4.0.8 ([8e9ca4a](https://github.com/salesforcecli/plugin-custom-metadata/commit/8e9ca4acafc03dd94610e670a081105755b7d482))

## [3.3.16](https://github.com/salesforcecli/plugin-custom-metadata/compare/3.3.15...3.3.16) (2024-06-29)

### Bug Fixes

- **deps:** bump @salesforce/core from 8.0.3 to 8.1.0 ([09a76dc](https://github.com/salesforcecli/plugin-custom-metadata/commit/09a76dc831705a7264563955f46b9ff54ad8f32c))

## [3.3.15](https://github.com/salesforcecli/plugin-custom-metadata/compare/3.3.14...3.3.15) (2024-06-23)

### Bug Fixes
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ FLAG DESCRIPTIONS
The value must be greater than or equal to zero. Default value is 0.
```

_See code: [src/commands/cmdt/generate/field.ts](https://github.com/salesforcecli/plugin-custom-metadata/blob/3.3.15/src/commands/cmdt/generate/field.ts)_
_See code: [src/commands/cmdt/generate/field.ts](https://github.com/salesforcecli/plugin-custom-metadata/blob/3.3.21/src/commands/cmdt/generate/field.ts)_

## `sf cmdt generate fromorg`

Expand Down Expand Up @@ -238,7 +238,7 @@ FLAG DESCRIPTIONS
https://help.salesforce.com/s/articleView?id=sf.custommetadatatypes_ui_create.htm&type=5.
```

_See code: [src/commands/cmdt/generate/fromorg.ts](https://github.com/salesforcecli/plugin-custom-metadata/blob/3.3.15/src/commands/cmdt/generate/fromorg.ts)_
_See code: [src/commands/cmdt/generate/fromorg.ts](https://github.com/salesforcecli/plugin-custom-metadata/blob/3.3.21/src/commands/cmdt/generate/fromorg.ts)_

## `sf cmdt generate object`

Expand Down Expand Up @@ -300,7 +300,7 @@ FLAG DESCRIPTIONS
https://help.salesforce.com/s/articleView?id=sf.custommetadatatypes_ui_create.htm&type=5.
```

_See code: [src/commands/cmdt/generate/object.ts](https://github.com/salesforcecli/plugin-custom-metadata/blob/3.3.15/src/commands/cmdt/generate/object.ts)_
_See code: [src/commands/cmdt/generate/object.ts](https://github.com/salesforcecli/plugin-custom-metadata/blob/3.3.21/src/commands/cmdt/generate/object.ts)_

## `sf cmdt generate record`

Expand Down Expand Up @@ -355,7 +355,7 @@ FLAG DESCRIPTIONS
Protected records can only be accessed by code in the same managed package namespace.
```

_See code: [src/commands/cmdt/generate/record.ts](https://github.com/salesforcecli/plugin-custom-metadata/blob/3.3.15/src/commands/cmdt/generate/record.ts)_
_See code: [src/commands/cmdt/generate/record.ts](https://github.com/salesforcecli/plugin-custom-metadata/blob/3.3.21/src/commands/cmdt/generate/record.ts)_

## `sf cmdt generate records`

Expand Down Expand Up @@ -406,7 +406,7 @@ FLAG DESCRIPTIONS
The '__mdt' suffix is appended to the end of the name if it's omitted.
```

_See code: [src/commands/cmdt/generate/records.ts](https://github.com/salesforcecli/plugin-custom-metadata/blob/3.3.15/src/commands/cmdt/generate/records.ts)_
_See code: [src/commands/cmdt/generate/records.ts](https://github.com/salesforcecli/plugin-custom-metadata/blob/3.3.21/src/commands/cmdt/generate/records.ts)_

<!-- commandsstop -->

Expand Down
25 changes: 13 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@salesforce/plugin-custom-metadata",
"description": "Tools for working with custom metadata types and their records.",
"version": "3.3.15",
"version": "3.3.21",
"contributors": [
{
"name": "Carolyn Grabill",
Expand Down Expand Up @@ -42,22 +42,22 @@
],
"bugs": "https://github.com/salesforcecli/plugin-custom-metadata/issues",
"dependencies": {
"@oclif/core": "^4",
"@salesforce/core": "^8.0.1",
"@salesforce/sf-plugins-core": "^11.1.2",
"@salesforce/core": "^8.1.0",
"@salesforce/sf-plugins-core": "^11.2.0",
"csv-parse": "^5.5.6",
"fast-xml-parser": "^4.4.0"
},
"devDependencies": {
"@jsforce/jsforce-node": "^3.2.0",
"@oclif/plugin-command-snapshot": "^5.2.3",
"@salesforce/cli-plugins-testkit": "^5.3.16",
"@salesforce/dev-scripts": "^10.2.2",
"@salesforce/plugin-command-reference": "^3.1.5",
"eslint-plugin-sf-plugin": "^1.18.8",
"oclif": "^4.13.9",
"@jsforce/jsforce-node": "^3.2.4",
"@oclif/core": "^4.0.7",
"@oclif/plugin-command-snapshot": "^5.2.5",
"@salesforce/cli-plugins-testkit": "^5.3.19",
"@salesforce/dev-scripts": "^10.2.4",
"@salesforce/plugin-command-reference": "^3.1.10",
"eslint-plugin-sf-plugin": "^1.18.11",
"oclif": "^4.14.7",
"ts-node": "^10.9.2",
"typescript": "^5.5.2"
"typescript": "^5.5.3"
},
"engines": {
"node": ">=18.0.0"
Expand Down Expand Up @@ -117,6 +117,7 @@
"test": "wireit",
"test:nuts": "nyc mocha \"**/*.nut.ts\" --slow 4500 --timeout 600000 --parallel --jobs 20",
"test:nuts:local": "nyc mocha \"**/local/*.nut.ts\" --slow 4500 --timeout 600000 --parallel --jobs 20",
"test:nuts:remote": "nyc mocha \"**/nuts/*.nut.ts\" --slow 4500 --timeout 600000 --parallel --jobs 20",
"test:only": "wireit",
"version": "oclif readme"
},
Expand Down
62 changes: 44 additions & 18 deletions src/commands/cmdt/generate/fromorg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ import {
isValidMetadataRecordName,
} from '../../../shared/helpers/validationUtil.js';
import { canConvert, createObjectXML, createFieldXML } from '../../../shared/templates/templates.js';
import {
ensureFullName,
CustomFieldWithFullNameTypeAndLabel,
CustomObjectWithFullName,
} from '../../../shared/types.js';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-custom-metadata', 'fromorg');
Expand All @@ -33,6 +38,7 @@ export type CmdtGenerateResponse = {
outputDir: string;
recordsOutputDir: string;
};

export default class Generate extends SfCommand<CmdtGenerateResponse> {
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
Expand Down Expand Up @@ -101,21 +107,10 @@ export default class Generate extends SfCommand<CmdtGenerateResponse> {
const conn = flags['target-org'].getConnection(flags['api-version']);

// use default target org connection to get object describe if no source is provided.
const describeObj = await conn.metadata.read('CustomObject', flags.sobject);

// throw error if the object doesnot exist(empty json as response from the describe call.)
if (describeObj.fields.length === 0) {
const errMsg = messages.getMessage('sobjectnameNoResultError', [flags.sobject]);
throw new SfError(errMsg, 'sobjectnameNoResultError');
}
// check for custom setting
if (describeObj.customSettingsType) {
// if custom setting check for type and visibility
if (!validCustomSettingType(describeObj)) {
const errMsg = messages.getMessage('customSettingTypeError', [flags.sobject]);
throw new SfError(errMsg, 'customSettingTypeError');
}
}
const describeObj = validateCustomObjectDescribe(
await conn.metadata.read('CustomObject', flags.sobject),
flags.sobject
);

const label = flags.label ?? flags['dev-name'];
const pluralLabel = flags['plural-label'] ?? label;
Expand All @@ -132,11 +127,16 @@ export default class Generate extends SfCommand<CmdtGenerateResponse> {
// get all the field details before creating field metadata
const fields = describeObjFields(describeObj)
// added type check here to skip the creation of un supported fields
.filter(fieldHasFullnameTypeLabel)
.filter((f) => !flags['ignore-unsupported'] || canConvert(f['type']))
.flatMap((f) =>
// check for Geo Location fields before hand and create two different fields for longitude and latitude.
f.type !== 'Location' ? [f] : convertLocationFieldToText(f)
);
/* if there's no fullName, we won't be able to write the file.
* in the wsdl, metadata types inherit fullName (optional) from the Metadata base type,
* but CustomObject does always have one */

// create custom metdata fields
await Promise.all(
fields.map((f) =>
Expand Down Expand Up @@ -202,13 +202,21 @@ export default class Generate extends SfCommand<CmdtGenerateResponse> {
}
}

const getSoqlQuery = (describeResult: CustomObject): string => {
const fieldNames = describeResult.fields.map((field) => field.fullName).join(',');
const fieldHasFullnameTypeLabel = (field: CustomField): field is CustomFieldWithFullNameTypeAndLabel =>
typeof field.fullName === 'string' && typeof field.label === 'string' && typeof field.type === 'string';

const getSoqlQuery = (describeResult: CustomObjectWithFullName): string => {
const fieldNames = describeResult.fields
.filter(fieldHasFullnameTypeLabel)
.map((field) => field.fullName)
.join(',');
// Added Name hardcoded as Name field is not retrieved as part of object describe.
return `SELECT Name, ${fieldNames} FROM ${describeResult.fullName}`;
};

const convertLocationFieldToText = (field: CustomField): CustomField[] => {
const convertLocationFieldToText = (
field: CustomFieldWithFullNameTypeAndLabel
): CustomFieldWithFullNameTypeAndLabel[] => {
const baseTextField = {
required: field['required'],
trackHistory: field['trackHistory'],
Expand All @@ -223,3 +231,21 @@ const convertLocationFieldToText = (field: CustomField): CustomField[] => {
label: `${prefix}${field.label}`,
}));
};

/** throw errors if describe is empty (doesn't exist) or is CustomSetting or missing fullname */
const validateCustomObjectDescribe = (describeObj: CustomObject, objectName: string): CustomObjectWithFullName => {
// throw error if the object doesnot exist(empty json as response from the describe call.)
if (describeObj.fields.length === 0) {
const errMsg = messages.getMessage('sobjectnameNoResultError', [objectName]);
throw new SfError(errMsg, 'sobjectnameNoResultError');
}
// check for custom setting
if (describeObj.customSettingsType) {
// if custom setting check for type and visibility
if (!validCustomSettingType(describeObj)) {
const errMsg = messages.getMessage('customSettingTypeError', [objectName]);
throw new SfError(errMsg, 'customSettingTypeError');
}
}
return ensureFullName(describeObj);
};
1 change: 0 additions & 1 deletion src/shared/helpers/createUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ export const createRecord = async (createConfig: CreateConfig): Promise<void> =>
*/
export const getFieldPrimitiveType = (fileData: CustomField[] = [], fieldName?: string): string => {
const matchingFile = fileData.find((file) => file.fullName === fieldName);

if (matchingFile && typeof matchingFile.type === 'string' && ['Number', 'Percent'].includes(matchingFile.type)) {
return getNumberType(matchingFile.type, matchingFile.scale);
}
Expand Down
11 changes: 4 additions & 7 deletions src/shared/helpers/fileWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type FileWriterResult = {
dir: string;
fileName: string;
updated: boolean;
}
};

/**
* Using the given file system, creates a file representing a new custom metadata type.
Expand Down Expand Up @@ -60,15 +60,11 @@ export const writeTypeFile = async (
export const writeFieldFile = async (
corefs = fs,
dir: string,
fieldName: string | undefined | null,
fieldName: string,
fieldXML: string
): Promise<FileWriterResult> => {
// appending __c if its not already there
if (fieldName?.endsWith('__c') === false) {
fieldName += '__c';
}
const outputFilePath = path.join(removeTrailingSlash(dir), 'fields');
const fileName = `${fieldName}.field-meta.xml`;
const fileName = `${ensureDoubleUnderscoreC(fieldName)}.field-meta.xml`;
const updated = fs.existsSync(path.join(outputFilePath, fileName));
await corefs.promises.mkdir(outputFilePath, { recursive: true });
await corefs.promises.writeFile(path.join(outputFilePath, fileName), fieldXML);
Expand All @@ -77,3 +73,4 @@ export const writeFieldFile = async (
};

const removeTrailingSlash = (dir: string): string => dir.replace(/\/+$/, '');
const ensureDoubleUnderscoreC = (name: string): string => (name.endsWith('__c') ? name : `${name}__c`);
25 changes: 15 additions & 10 deletions src/shared/templates/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import { SfError, Messages } from '@salesforce/core';
import type { CustomValue, CustomField } from '@jsforce/jsforce-node/lib/api/metadata.js';
import { CustomFieldWithFullNameTypeAndLabel, customValueHasFullNameAndLabel } from '../types.js';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-custom-metadata', 'template');
Expand Down Expand Up @@ -38,7 +39,7 @@ export const createObjectXML = (
* @param data Record details
* @param defaultToString If the defaultToString set type to Text for unsupported field types
*/
export const createFieldXML = (data: CustomField, defaultToString: boolean): string => {
export const createFieldXML = (data: CustomFieldWithFullNameTypeAndLabel, defaultToString: boolean): string => {
let returnValue = '<?xml version="1.0" encoding="UTF-8"?>\n';
returnValue += '<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">\n';
returnValue += getFullName(data);
Expand All @@ -59,7 +60,9 @@ export const createFieldXML = (data: CustomField, defaultToString: boolean): str
if (canConvert(data.type)) {
returnValue += getValueSet(data);
returnValue += getPrecisionTag(data);
returnValue += getScaleTag(data);
if (data.scale != null) {
returnValue += getScaleTag(data.scale);
}
}

returnValue += '</CustomField>\n';
Expand All @@ -72,7 +75,7 @@ export const createDefaultTypeStructure = (
label: string,
picklistValues: string[] = [],
decimalplaces = 0
): CustomField => {
): CustomFieldWithFullNameTypeAndLabel => {
const precision = 18 - decimalplaces;
const scale = decimalplaces;
const baseObject = { fullName, type, label, summaryFilterItems: [] };
Expand Down Expand Up @@ -133,7 +136,7 @@ export const canConvert = (type: string | undefined | null): boolean => {
const createPicklistValues = (values: string[]): CustomValue[] =>
values.map((value) => ({ fullName: value, label: value, default: false }));

const getType = (data: CustomField, defaultToMetadataType: boolean): string => {
const getType = (data: CustomFieldWithFullNameTypeAndLabel, defaultToMetadataType: boolean): string => {
if (canConvert(data.type)) {
// To handle the text formula field scenario where field type will be Text with no length attribute
if (data.type === 'Text' && data.length === undefined) {
Expand Down Expand Up @@ -179,14 +182,14 @@ const getDefaultValue = (data: CustomField): string => {
return data.defaultValue ? `\t<defaultValue>${data.defaultValue}</defaultValue>\n` : '';
};

const getValueSet = (data: CustomField): string => {
const getValueSet = (data: CustomFieldWithFullNameTypeAndLabel): string => {
let fieldValue = '';
if (data.valueSet) {
fieldValue += '\t<valueSet>\n';
fieldValue += `\t\t<restricted>${data.valueSet.restricted ?? false}</restricted>\n`;
fieldValue += '\t\t<valueSetDefinition>\n';
fieldValue += `\t\t\t<sorted>${data.valueSet.valueSetDefinition?.sorted ?? false}</sorted>\n`;
data.valueSet.valueSetDefinition?.value.forEach((value) => {
(data.valueSet.valueSetDefinition?.value ?? []).filter(customValueHasFullNameAndLabel).map((value) => {
fieldValue += '\t\t\t<value>\n';
fieldValue += `\t\t\t\t<fullName>${value.fullName}</fullName>\n`;
fieldValue += `\t\t\t\t<default>${value.default || false}</default>\n`;
Expand All @@ -211,7 +214,7 @@ const getConvertType = (data: CustomField): string => {
}
};

const getFullName = (data: CustomField): string => {
const getFullName = (data: CustomFieldWithFullNameTypeAndLabel): string => {
const name = data.fullName?.endsWith('__c') ? data.fullName : `${data.fullName}__c`;
return `\t<fullName>${name}</fullName>\n`;
};
Expand All @@ -228,13 +231,15 @@ const getFieldManageability = (data: CustomField): string =>
const getInlineHelpText = (data: CustomField): string =>
data.inlineHelpText ? `\t<inlineHelpText>${data.inlineHelpText}</inlineHelpText>\n` : '';

const getLabel = (data: CustomField): string => `\t<label>${data.label}</label>\n`;
const getLabel = (data: CustomFieldWithFullNameTypeAndLabel): string => `\t<label>${data.label}</label>\n`;

const getRequiredTag = (data: CustomField): string =>
typeof data.unique === 'boolean' ? `\t<unique>${data.unique}</unique>\n` : '';

const getPrecisionTag = (data: CustomField): string =>
data.precision ? `\t<precision>${data.precision}</precision>\n` : '';

const getScaleTag = (data: CustomField): string =>
typeof data.scale !== 'undefined' ? `\t<scale>${data.scale}</scale>\n` : '';
const getScaleTag = (scale: NonNullable<CustomField['scale']>): string =>
// CustomField thinks this is a number. The UT had it as a string.
// This will work for either(filtering out null / undefined because only ==)
typeof scale !== null ? `\t<scale>${scale}</scale>\n` : '';
Loading

0 comments on commit 065b815

Please sign in to comment.