From 4b24bd8f3ab6862dbafefc8e86924d2689efc1d7 Mon Sep 17 00:00:00 2001 From: Willie Ruemmele Date: Thu, 29 Aug 2024 16:15:43 -0600 Subject: [PATCH] chore: a lot of changes, writing xml in dirs, now to group by object --- .../filePerChildTypeMetadataTransformer.ts | 52 ++++++++++++------- .../metadataTransformerFactory.ts | 2 +- .../presets/decomposePermissionSetBeta2.json | 24 ++++----- src/registry/types.ts | 6 +-- src/resolve/adapters/sourceAdapterFactory.ts | 2 +- 5 files changed, 50 insertions(+), 36 deletions(-) diff --git a/src/convert/transformers/filePerChildTypeMetadataTransformer.ts b/src/convert/transformers/filePerChildTypeMetadataTransformer.ts index 882ffb6f1..07f009668 100644 --- a/src/convert/transformers/filePerChildTypeMetadataTransformer.ts +++ b/src/convert/transformers/filePerChildTypeMetadataTransformer.ts @@ -7,12 +7,11 @@ import { dirname, join } from 'node:path'; import fs from 'node:fs'; -import { AnyJson, JsonMap, ensureString, isJsonMap } from '@salesforce/ts-types'; -import { ensureArray } from '@salesforce/kit'; +import { AnyJson, JsonMap, ensureString } from '@salesforce/ts-types'; import { Messages } from '@salesforce/core'; import { calculateRelativePath } from '../../utils/path'; import { ForceIgnore } from '../../resolve/forceIgnore'; -import { extractUniqueElementValue, objectHasSomeRealValues } from '../../utils/decomposed'; +import { objectHasSomeRealValues } from '../../utils/decomposed'; import type { MetadataComponent } from '../../resolve/types'; import { type MetadataType } from '../../registry/types'; import { SourceComponent } from '../../resolve/sourceComponent'; @@ -59,7 +58,6 @@ export class FilePerChildTypeMetadataTransformer extends BaseMetadataTransformer // noop since the finalizer will push the writes to the component writer return []; } - public async toSourceFormat({ component, mergeWith }: ToSourceFormatInput): Promise { const forceIgnore = component.getForceIgnore(); @@ -77,15 +75,17 @@ export class FilePerChildTypeMetadataTransformer extends BaseMetadataTransformer const writeInfosForChildren = composedMetadata .filter(hasChildTypeId) .map(addChildType) - .flatMap(({ tagValue, childType }) => - // iterate each array member if it's Object-like (ex: customField of a CustomObject) - ensureArray(tagValue) - .filter(isJsonMap) - .map(toInfoContainer(mergeWith)(component)(childType)) - .filter(forceIgnoreAllowsComponent(forceIgnore)) // only process child types that aren't forceignored - .map(handleUnaddressableChildAlone(composedMetadata.length)(parentXmlObject)(stateSetter)) - .flatMap(getChildWriteInfos(stateSetter)(childrenOfMergeComponent)) - ); + .map((c) => { + if (c.childType.directoryName) { + // merge all child types that will end up in the same file + return toInfoContainer(mergeWith)(component)(c.childType)(c.tagValue as JsonMap); + } else { + return toInfoContainer(mergeWith)(component)(c.childType)(c.tagValue as JsonMap); + } + }) + .filter(forceIgnoreAllowsComponent(forceIgnore)) + .map(handleUnaddressableChildAlone(composedMetadata.length)(parentXmlObject)(stateSetter)) + .flatMap(getChildWriteInfos(stateSetter)(childrenOfMergeComponent)); const writeInfoForParent = mergeWith ? getWriteInfosFromMerge(mergeWith)(stateSetter)(parentXmlObject)(component) @@ -150,7 +150,9 @@ const getChildWriteInfos = (stateSetter: StateSetter) => (childrenOfMergeComponent: ComponentSet) => ({ mergeWith, childComponent, value, entryName }: InfoContainer): WriteInfo[] => { - const source = objectToSource(childComponent.type.name)(value); + const source = objectToSource(childComponent.parent!.type.name)(childComponent.type.name)( + value as unknown as JsonMap[] + ); // if there's nothing to merge with, push write operation now to default location if (!mergeWith) { return [{ source, output: getDefaultOutput(childComponent) }]; @@ -265,9 +267,10 @@ const getDefaultOutput = (component: MetadataComponent): SourcePath => { // there could be a '.' inside the child name (ex: PermissionSet.FieldPermissions.field uses Obj__c.Field__c) const childName = tail.length ? tail.join('.') : undefined; const output = join( - parent?.type.strategies?.decomposition === 'folderPerType' ? type.directoryName : '', + parent?.type.strategies?.decomposition === 'filePerType' ? type.directoryName : '', `${childName ?? baseName}.${ensureString(component.type.suffix)}${META_XML_SUFFIX}` ); + // const output = join(type.directoryName, `${baseName}.${ensureString(component.type.suffix)}${META_XML_SUFFIX}`); return join(calculateRelativePath('source')({ self: parent?.type ?? type })(fullName)(baseName), output); }; @@ -298,13 +301,23 @@ type InfoContainer = { mergeWith?: SourceComponent; }; -/** returns an data structure with lots of context information in it */ +// const toInfoContainer = +// (mergeWith: SourceComponent | undefined) => +// (parent: SourceComponent) => +// (c: MetadataType) => +// (tagValue: JsonMap[]): InfoContainer => { +// const entryName = childType.directoryName +// ? tagValue[childType.uniqueIdElement]?.split('.')?.at(0) ?? parent.name +// : parent.name; +/** returns a data structure with lots of context information in it */ const toInfoContainer = (mergeWith: SourceComponent | undefined) => (parent: SourceComponent) => (childType: MetadataType) => (tagValue: JsonMap): InfoContainer => { - const entryName = ensureString(extractUniqueElementValue(tagValue, childType.uniqueIdElement)); + const entryName = childType.directoryName + ? ((tagValue as unknown as JsonMap[]).at(0)?.[childType.uniqueIdElement!] as string).split('.')[0] + : parent.name; return { parentComponent: parent, entryName, @@ -325,9 +338,10 @@ const forceIgnoreAllowsComponent = /** wrap some xml in the Metadata type and add the NS stuff */ const objectToSource = + (parentType: string) => (childTypeName: string) => - (obj: JsonMap): JsToXml => - new JsToXml({ [childTypeName]: { [XML_NS_KEY]: XML_NS_URL, ...obj } }); + (obj: JsonMap[]): JsToXml => + new JsToXml({ [parentType]: { [childTypeName]: obj } }); /** filter out the children and create the remaining parentXml */ const buildParentXml = diff --git a/src/convert/transformers/metadataTransformerFactory.ts b/src/convert/transformers/metadataTransformerFactory.ts index acff4a9fe..d45ed5249 100644 --- a/src/convert/transformers/metadataTransformerFactory.ts +++ b/src/convert/transformers/metadataTransformerFactory.ts @@ -42,7 +42,7 @@ export class MetadataTransformerFactory { return new StaticResourceMetadataTransformer(this.registry, this.context); case 'nonDecomposed': return new NonDecomposedMetadataTransformer(this.registry, this.context); - case 'filePerChild': + case 'filePerType': return new FilePerChildTypeMetadataTransformer(this.registry, this.context); case 'decomposedLabels': return component.type.name === 'CustomLabels' diff --git a/src/registry/presets/decomposePermissionSetBeta2.json b/src/registry/presets/decomposePermissionSetBeta2.json index a6b9d8809..58662817b 100644 --- a/src/registry/presets/decomposePermissionSetBeta2.json +++ b/src/registry/presets/decomposePermissionSetBeta2.json @@ -58,7 +58,7 @@ }, "types": { "applicationvisibility": { - "directoryName": "applicationVisibilities", + "directoryName": "", "id": "applicationvisibility", "isAddressable": false, "name": "ApplicationVisibility", @@ -66,7 +66,7 @@ "uniqueIdElement": "application" }, "classaccess": { - "directoryName": "classAccesses", + "directoryName": "", "id": "classaccess", "isAddressable": false, "name": "ClassAccess", @@ -74,7 +74,7 @@ "uniqueIdElement": "apexClass" }, "custommetadatatypeaccess": { - "directoryName": "customMetadataTypeAccesses", + "directoryName": "", "id": "custommetadatatypeaccess", "isAddressable": false, "name": "CustomMetadataTypeAccess", @@ -82,7 +82,7 @@ "uniqueIdElement": "name" }, "custompermissions": { - "directoryName": "customPermissions", + "directoryName": "", "id": "custompermissions", "isAddressable": false, "name": "CustomPermission", @@ -90,7 +90,7 @@ "uniqueIdElement": "name" }, "customsettingaccess": { - "directoryName": "customSettingAccesses", + "directoryName": "", "id": "customsettingaccess", "isAddressable": false, "name": "CustomSettingAccess", @@ -98,7 +98,7 @@ "uniqueIdElement": "name" }, "externalcredentialprincipalaccess": { - "directoryName": "externalCredentialPrincipalAccesses", + "directoryName": "", "id": "externalcredentialprincipalaccess", "isAddressable": false, "name": "ExternalCredentialPrincipalAccess", @@ -106,7 +106,7 @@ "uniqueIdElement": "externalCredentialPrincipal" }, "externaldatasourceaccess": { - "directoryName": "externalDataSourceAccesses", + "directoryName": "", "id": "externaldatasourceaccess", "isAddressable": false, "name": "ExternalDataSourceAccess", @@ -123,7 +123,7 @@ "uniqueIdElement": "field" }, "flowaccess": { - "directoryName": "flowAccesses", + "directoryName": "", "id": "flowaccess", "isAddressable": false, "name": "FlowAccess", @@ -140,7 +140,7 @@ "uniqueIdElement": "object" }, "pageaccess": { - "directoryName": "pageAccesses", + "directoryName": "", "id": "pageaccess", "isAddressable": false, "name": "PageAccess", @@ -166,7 +166,7 @@ "uniqueIdElement": "tab" }, "userpermission": { - "directoryName": "userPermissions", + "directoryName": "", "id": "userpermission", "isAddressable": false, "name": "UserPermission", @@ -180,8 +180,8 @@ "name": "PermissionSet", "strategies": { "decomposition": "filePerType", - "adapter": "filePerChild", - "transformer": "filePerChild" + "adapter": "filePerType", + "transformer": "filePerType" }, "strictDirectoryName": true, "suffix": "permissionset", diff --git a/src/registry/types.ts b/src/registry/types.ts index 7074ca82a..cc502be89 100644 --- a/src/registry/types.ts +++ b/src/registry/types.ts @@ -145,10 +145,10 @@ export type MetadataType = { | 'decomposed' | 'digitalExperience' | 'bundle' - | 'filePerChild' + | 'filePerType' | 'default'; - transformer?: 'decomposed' | 'staticResource' | 'nonDecomposed' | 'standard' | 'decomposedLabels' | 'filePerChild'; - decomposition?: 'topLevel' | 'folderPerType' | 'filePerChild'; + transformer?: 'decomposed' | 'staticResource' | 'nonDecomposed' | 'standard' | 'decomposedLabels' | 'filePerType'; + decomposition?: 'topLevel' | 'folderPerType' | 'filePerType'; recomposition?: 'startEmpty'; }; }; diff --git a/src/resolve/adapters/sourceAdapterFactory.ts b/src/resolve/adapters/sourceAdapterFactory.ts index db839666c..9e7bcd1dd 100644 --- a/src/resolve/adapters/sourceAdapterFactory.ts +++ b/src/resolve/adapters/sourceAdapterFactory.ts @@ -43,7 +43,7 @@ export class SourceAdapterFactory { return new MixedContentSourceAdapter(type, this.registry, forceIgnore, this.tree); case 'digitalExperience': return new DigitalExperienceSourceAdapter(type, this.registry, forceIgnore, this.tree); - case 'filePerChild': + case 'filePerType': return new FilePerChildTypeSourceAdapter(type, this.registry, forceIgnore, this.tree); case 'default': case undefined: