Skip to content

Commit

Permalink
chore: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
peternhale committed Jan 6, 2025
1 parent 7ccca40 commit c5d5b3d
Show file tree
Hide file tree
Showing 16 changed files with 444 additions and 16 deletions.
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2024, Salesforce.com, Inc.
Copyright (c) 2025, Salesforce.com, Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"jszip": "^3.10.1",
"mime": "2.6.0",
"minimatch": "^9.0.5",
"proxy-agent": "^6.4.0"
"proxy-agent": "^6.4.0",
"yaml": "^2.6.1"
},
"devDependencies": {
"@jsforce/jsforce-node": "^3.6.3",
Expand Down
2 changes: 2 additions & 0 deletions src/convert/convertContext/convertContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { DecompositionFinalizer } from './decompositionFinalizer';
import { ConvertTransactionFinalizer } from './transactionFinalizer';
import { DecomposedLabelsFinalizer } from './decomposedLabelsFinalizer';
import { DecomposedPermissionSetFinalizer } from './decomposedPermissionSetFinalizer';
import { DecomposedExternalServiceRegistrationFinalizer } from './decomposedExternalServiceRegistrationFinalizer';
/**
* A state manager over the course of a single metadata conversion call.
*/
Expand All @@ -20,6 +21,7 @@ export class ConvertContext {
public readonly nonDecomposition = new NonDecompositionFinalizer();
public readonly decomposedLabels = new DecomposedLabelsFinalizer();
public readonly decomposedPermissionSet = new DecomposedPermissionSetFinalizer();
public readonly decomposedExternalServiceRegistration = new DecomposedExternalServiceRegistrationFinalizer();

// eslint-disable-next-line @typescript-eslint/require-await
public async *executeFinalizers(defaultDirectory?: string): AsyncIterable<WriterFormat[]> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2023, salesforce.com, inc.
* All rights reserved.
* 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 { WriterFormat } from '../types';
import { MetadataType } from '../../registry';
import { ConvertTransactionFinalizer } from './transactionFinalizer';

type ExternalServiceRegistration = unknown;

export class DecomposedExternalServiceRegistrationFinalizer extends ConvertTransactionFinalizer<ExternalServiceRegistration> {
/** to support custom presets (the only way this code should get hit at all pass in the type from a transformer that has registry access */
public externalServiceRegistration?: MetadataType;
protected transactionState: ExternalServiceRegistration;
// eslint-disable-next-line class-methods-use-this
protected defaultDir: string | undefined;

public finalize(defaultDirectory: string | undefined): Promise<WriterFormat[]> {
this.defaultDir = defaultDirectory;
return Promise.resolve([]);
}
}
4 changes: 2 additions & 2 deletions src/convert/transformers/baseMetadataTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
*/
import { MetadataTransformer, WriteInfo } from '../types';
import { ConvertContext } from '../convertContext/convertContext';
import { SourceComponent } from '../../resolve/sourceComponent';
import { RegistryAccess } from '../../registry/registryAccess';
import { SourceComponent } from '../../resolve';
import { RegistryAccess } from '../../registry';

export abstract class BaseMetadataTransformer implements MetadataTransformer {
public readonly context: ConvertContext;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright (c) 2024, salesforce.com, inc.
* All rights reserved.
* 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 * as fs from 'node:fs/promises';
// import * as path from 'node:path';
// import { Readable } from 'node:stream';
// import { XMLParser, XMLBuilder } from 'fast-xml-parser';
import * as yaml from 'yaml';
import { JsonMap } from '@salesforce/ts-types';
import { WriteInfo } from '../types';
import { SourceComponent } from '../../resolve';
import { BaseMetadataTransformer } from './baseMetadataTransformer';

export type ESR = JsonMap & {
ExternalServiceRegistration: {
schema?: {
_text: string;
};
};
};

export class DecomposeExternalServiceRegistrationTransformer extends BaseMetadataTransformer {
// private xmlParser = new XMLParser({ ignoreAttributes: false });
// private xmlBuilder = new XMLBuilder({ ignoreAttributes: false });

// eslint-disable-next-line @typescript-eslint/require-await,class-methods-use-this,@typescript-eslint/no-unused-vars
public async toSourceFormat(input: {
component: SourceComponent;
mergeWith?: SourceComponent | undefined;
}): Promise<WriteInfo[]> {
const writeInfos: WriteInfo[] = [];
// const { component, mergeWith } = input;
// const xmlContent = await component.parseXml<ESR>();
// const esrContent = xmlContent.ExternalServiceRegistration;
//
// // Extract schema content
// // eslint-disable-next-line no-underscore-dangle
// const schemaContent = esrContent.schema?._text ?? '';
// const schemaExtension = this.getSchemaExtension(schemaContent);
// const schemaFileName = `${component.fullName}.schema.${schemaExtension}`;
// const schemaFilePath = path.join(this.defaultDirectory ?? '', schemaFileName);
//
// // Write schema content to file
// writeInfos.push({
// source: Readable.from(schemaContent),
// output: schemaFilePath
// });
//
// // Remove schema content from ESR content
// delete esrContent.schema;
//
// // Write remaining ESR content to file
// const esrFileName = `${component.fullName}.externalServiceRegistration`;
// const esrFilePath = path.join(this.defaultDirectory ?? '', esrFileName);
// writeInfos.push({
// // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
// source: this.xmlBuilder.build({ ExternalServiceRegistration: esrContent }),
// output: esrFilePath
// });

return writeInfos;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/require-await
public async toMetadataFormat(component: SourceComponent): Promise<WriteInfo[]> {
// only need to do this once
this.context.decomposedExternalServiceRegistration.externalServiceRegistration ??=
this.registry.getTypeByName('ExternalServiceRegistration');
const writeInfos: WriteInfo[] = [];
// const esrFilePath = this.getOutputFile(component);
// const esrContent = await fs.readFile(esrFilePath, 'utf8');
// // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
// const esrXml = this.xmlParser.parse(esrContent);
//
// // Read schema content from file
// const schemaFileName = `${component.fullName}.schema.yaml`; // or .json based on your logic
// const schemaFilePath = path.join(this.defaultDirectory ?? '', schemaFileName);
// const schemaContent = await fs.readFile(schemaFilePath, 'utf8');
//
// // Add schema content back to ESR content
// // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
// esrXml.ExternalServiceRegistration.schema = { _text: schemaContent };
//
// // Write combined content back to source format
// writeInfos.push({
// // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
// source: this.xmlBuilder.build(esrXml),
// output: esrFilePath
// });

return writeInfos;
}

// eslint-disable-next-line class-methods-use-this
protected getOutputFile(component: SourceComponent, mergeWith?: SourceComponent): string {
if (mergeWith?.xml) {
return mergeWith.xml;
}
return component.xml ?? '';
}

// eslint-disable-next-line class-methods-use-this
protected getSchemaExtension(content: string): string {
try {
yaml.parse(content);
return 'yaml';
} catch {
return 'json';
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export class DecomposedPermissionSetTransformer extends BaseMetadataTransformer
}

/**
* will decomopse a .permissionset into a directory containing files, and an 'objectSettings' folder for object-specific settings
* will decompose a .permissionset into a directory containing files, and an 'objectSettings' folder for object-specific settings
*
* @param {SourceComponent} component A SourceComponent representing a metadata-formatted permission set
* @param {SourceComponent | undefined} mergeWith any existing source-formatted permission sets to be merged with, think existing source merging with new information from a retrieve
Expand Down
12 changes: 6 additions & 6 deletions src/convert/transformers/metadataTransformerFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,22 @@
*/
import { Messages } from '@salesforce/core';
import { MetadataTransformer } from '../types';
import { SourceComponent } from '../../resolve/sourceComponent';
import { SourceComponent } from '../../resolve';
import { ConvertContext } from '../convertContext/convertContext';
import { RegistryAccess } from '../../registry/registryAccess';
import { RegistryAccess } from '../../registry';
import { DefaultMetadataTransformer } from './defaultMetadataTransformer';
import { DecomposedMetadataTransformer } from './decomposedMetadataTransformer';
import { StaticResourceMetadataTransformer } from './staticResourceMetadataTransformer';
import { NonDecomposedMetadataTransformer } from './nonDecomposedMetadataTransformer';
import { LabelMetadataTransformer, LabelsMetadataTransformer } from './decomposeLabelsTransformer';
import { DecomposedPermissionSetTransformer } from './decomposedPermissionSetTransformer';
import { DecomposeExternalServiceRegistrationTransformer } from './decomposeExternalServiceRegistrationTransformer';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/source-deploy-retrieve', 'sdr');

export class MetadataTransformerFactory {
private registry: RegistryAccess;
private context: ConvertContext;

public constructor(registry: RegistryAccess, context = new ConvertContext()) {
public constructor(private readonly registry: RegistryAccess, private readonly context = new ConvertContext()) {
this.registry = registry;
this.context = context;
}
Expand All @@ -48,6 +46,8 @@ export class MetadataTransformerFactory {
return component.type.name === 'CustomLabels'
? new LabelsMetadataTransformer(this.registry, this.context)
: new LabelMetadataTransformer(this.registry, this.context);
case 'decomposeExternalServiceRegistration':
return new DecomposeExternalServiceRegistrationTransformer(this.registry, this.context);
default:
throw messages.createError('error_missing_transformer', [type.name, transformerId]);
}
Expand Down
43 changes: 43 additions & 0 deletions src/registry/presets/decomposeExternalServiceRegistration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"types": {
"externalserviceregistration": {
"children": {
"types": {
"schema": {
"directoryName": "externalServiceRegistrations",
"id": "schema",
"isAddressable": false,
"name": "Schema",
"suffix": "schema",
"xmlElementName": "schema"
}
},
"suffixes": {
"schema": "schema"
}
},
"directoryName": "externalServiceRegistrations",
"id": "externalserviceregistration",
"ignoreParsedFullName": false,
"name": "ExternalServiceRegistration",
"strategies": {
"adapter": "decomposed",
"decomposition": "topLevel",
"transformer": "decomposeExternalServiceRegistration"
},
"strictDirectoryName": true,
"suffix": "externalServiceRegistration",
"supportsPartialDelete": true
}
},
"suffixes": {
"schema": "schema",
"externalServiceRegistration": "externalserviceregistration"
},
"strictDirectoryNames": {
"externalServiceRegistrations": "externalserviceregistration"
},
"childTypes": {
"schema": "externalserviceregistration"
}
}
2 changes: 2 additions & 0 deletions src/registry/presets/presetMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import * as decomposePermissionSetBeta from './decomposePermissionSetBeta.json';
import * as decomposePermissionSetBeta2 from './decomposePermissionSetBeta2.json';
import * as decomposeSharingRulesBeta from './decomposeSharingRulesBeta.json';
import * as decomposeWorkflowBeta from './decomposeWorkflowBeta.json';
import * as decomposeExternalServiceRegistration from './decomposeExternalServiceRegistration.json';

export const presetMap = new Map<string, MetadataRegistry>([
['decomposeCustomLabelsBeta2', decomposeCustomLabelsBeta2 as MetadataRegistry],
Expand All @@ -22,4 +23,5 @@ export const presetMap = new Map<string, MetadataRegistry>([
['decomposePermissionSetBeta2', decomposePermissionSetBeta2 as MetadataRegistry],
['decomposeSharingRulesBeta', decomposeSharingRulesBeta as MetadataRegistry],
['decomposeWorkflowBeta', decomposeWorkflowBeta as MetadataRegistry],
['decomposeExternalServiceRegistration', decomposeExternalServiceRegistration as MetadataRegistry],
]);
3 changes: 2 additions & 1 deletion src/registry/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ export type MetadataType = {
| 'nonDecomposed'
| 'standard'
| 'decomposedLabels'
| 'decomposedPermissionSet';
| 'decomposedPermissionSet'
| 'decomposeExternalServiceRegistration';
decomposition?: 'topLevel' | 'folderPerType';
recomposition?: 'startEmpty';
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2023, salesforce.com, inc.
* All rights reserved.
* 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 { join } from 'node:path';
import { expect } from 'chai';
import { RegistryAccess } from '../../../src';
import { getEffectiveRegistry } from '../../../src/registry/variants';
import { presetMap } from '../../../src';
import { MD_FORMAT_ESR, SOURCE_FORMAT_ESR } from '../../mock/type-constants/decomposeExternalServiceRegistration';
import { DecomposeExternalServiceRegistrationTransformer } from '../../../src/convert/transformers/decomposeExternalServiceRegistrationTransformer';

describe('DecomposeExternalServiceRegistrationTransformer', () => {
const preset = presetMap.get('decomposeExternalServiceRegistration');
const regAcc = new RegistryAccess(getEffectiveRegistry({ presets: [preset!] }));

describe('toSourceFormat', () => {
describe('WriteInfo output (where the file will write to)', () => {
it('write yaml file and meta.xml', async () => {
const component = MD_FORMAT_ESR;
const xf = new DecomposeExternalServiceRegistrationTransformer(regAcc);
const result = await xf.toSourceFormat({ component });
expect(result).to.have.length(1);
result.map((l) => {
expect(l.output).to.include(join('main', 'default', 'externalServiceRegistrations'));
});
expect(result[0].output).to.match(/myESR.externalServiceRegistrations-meta.xml$/);
expect(result[1].output).to.match(/myESR.schema.yaml$/);
});

it('merge component in defaultDir', async () => {
const component = MD_FORMAT_ESR;
const xf = new DecomposeExternalServiceRegistrationTransformer(regAcc);
const result = await xf.toSourceFormat({
component,
mergeWith: SOURCE_FORMAT_ESR,
});
expect(result).to.have.length(5);
expect(result[4].output).to.equal(SOURCE_FORMAT_ESR.xml);
});
});
});

describe('toMetadataFormat', () => {
it('decomposed ESR combined to md-format', async () => {
const component = SOURCE_FORMAT_ESR;
const xf = new DecomposeExternalServiceRegistrationTransformer(regAcc);
const result = await xf.toMetadataFormat(component);
expect(result).to.deep.equal([]);
expect(xf.context.decomposedPermissionSet.permissionSetType).to.equal(regAcc.getTypeByName('PermissionSet'));
});
});
});
Loading

2 comments on commit c5d5b3d

@svc-cli-bot
Copy link
Collaborator

Choose a reason for hiding this comment

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

Benchmark

Benchmark suite Current: c5d5b3d Previous: 80a78c0 Ratio
eda-componentSetCreate-linux 209 ms 212 ms 0.99
eda-sourceToMdapi-linux 1923 ms 1954 ms 0.98
eda-sourceToZip-linux 1714 ms 1697 ms 1.01
eda-mdapiToSource-linux 2618 ms 3703 ms 0.71
lotsOfClasses-componentSetCreate-linux 419 ms 430 ms 0.97
lotsOfClasses-sourceToMdapi-linux 3550 ms 3531 ms 1.01
lotsOfClasses-sourceToZip-linux 2849 ms 2855 ms 1.00
lotsOfClasses-mdapiToSource-linux 3380 ms 3438 ms 0.98
lotsOfClassesOneDir-componentSetCreate-linux 716 ms 738 ms 0.97
lotsOfClassesOneDir-sourceToMdapi-linux 6163 ms 6241 ms 0.99
lotsOfClassesOneDir-sourceToZip-linux 4812 ms 4885 ms 0.99
lotsOfClassesOneDir-mdapiToSource-linux 6067 ms 6123 ms 0.99

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

@svc-cli-bot
Copy link
Collaborator

Choose a reason for hiding this comment

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

Benchmark

Benchmark suite Current: c5d5b3d Previous: 80a78c0 Ratio
eda-componentSetCreate-win32 622 ms 699 ms 0.89
eda-sourceToMdapi-win32 3690 ms 4049 ms 0.91
eda-sourceToZip-win32 2842 ms 3130 ms 0.91
eda-mdapiToSource-win32 5546 ms 6129 ms 0.90
lotsOfClasses-componentSetCreate-win32 1246 ms 1243 ms 1.00
lotsOfClasses-sourceToMdapi-win32 7802 ms 7562 ms 1.03
lotsOfClasses-sourceToZip-win32 5048 ms 4766 ms 1.06
lotsOfClasses-mdapiToSource-win32 7694 ms 7665 ms 1.00
lotsOfClassesOneDir-componentSetCreate-win32 2145 ms 2084 ms 1.03
lotsOfClassesOneDir-sourceToMdapi-win32 13721 ms 13442 ms 1.02
lotsOfClassesOneDir-sourceToZip-win32 9224 ms 8570 ms 1.08
lotsOfClassesOneDir-mdapiToSource-win32 13821 ms 13513 ms 1.02

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

Please sign in to comment.