Skip to content

Commit

Permalink
fix: move DecodeableMap to a new file
Browse files Browse the repository at this point in the history
  • Loading branch information
shetzel committed Aug 15, 2023
1 parent ed756b9 commit 9d24d82
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 74 deletions.
77 changes: 3 additions & 74 deletions src/collections/componentSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
PackageTypeMembers,
} from './types';
import { LazyCollection } from './lazyCollection';
import { DecodeableMap } from './decodeableMap';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/source-deploy-retrieve', 'sdr');
Expand All @@ -43,78 +44,6 @@ export type RetrieveSetOptions = Omit<MetadataApiRetrieveOptions, 'components'>;

const KEY_DELIMITER = '#';

/**
* This is an extension of the Map class that treats keys as the same by matching first normally,
* then decoded. Decoding the key before comparing can solve some edge cases in component fullNames
* such as Layouts. See: https://github.com/forcedotcom/cli/issues/1683
*
* Examples:
*
* Given a map with entries:
* ```javascript
* 'layout#Layout__Broker__c-v1%2E1 Broker Layout' : {...}
* 'layout#Layout__Broker__c-v9.2 Broker Layout' : {...}
* ```
*
* `decodeableMap.has('layout#Layout__Broker__c-v1.1 Broker Layout')` --> returns `true`
* `decodeableMap.has('layout#Layout__Broker__c-v9%2E2 Broker Layout')` --> returns `true`
*/
class DecodeableMap<K extends string, V> extends Map<string, V> {
/**
* boolean indicating whether an element with the specified key (matching decoded) exists or not.
*/
public has(key: K): boolean {
return super.has(key) || this.hasDecoded(key);
}

/**
* Returns a specified element from the Map object. If the value that is associated to
* the provided key (matching decoded) is an object, then you will get a reference to
* that object and any change made to that object will effectively modify it inside the Map.
*/
public get(key: K): V | undefined {
return super.get(key) ?? this.getDecoded(key);
}

/**
* Adds a new element with a specified key and value to the Map. If an element with the
* same key (matching decoded) already exists, the element will be updated.
*/
public set(key: K, value: V): this {
const sKey = this.getExistingKey(key) ?? key;
return super.set(sKey, value);
}

/**
* true if an element in the Map existed (matching decoded) and has been removed, or false
* if the element does not exist.
*/
public delete(key: K): boolean {
const sKey = this.getExistingKey(key) ?? key;
return super.delete(sKey);
}

// Returns true if the passed `key` matches an existing key entry when both keys are decoded.
private hasDecoded(key: string): boolean {
return !!this.getExistingKey(key);
}

// Returns the value of an entry matching on decoded keys.
private getDecoded(key: string): V | undefined {
const existingKey = this.getExistingKey(key);
return existingKey ? super.get(existingKey) : undefined;
}

// Returns the key as it is in the map, matching on decoded keys.
private getExistingKey(key: string): string | undefined {
for (const compKey of this.keys()) {
if (decodeURIComponent(compKey) === decodeURIComponent(key)) {
return compKey;
}
}
}
}

/**
* A collection containing no duplicate metadata members (`fullName` and `type` pairs). `ComponentSets`
* are a convenient way of constructing a unique collection of components to perform operations such as
Expand Down Expand Up @@ -184,11 +113,11 @@ export class ComponentSet extends LazyCollection<MetadataComponent> {
return size;
}

public get destructiveChangesPre(): Map<string, Map<string, SourceComponent>> {
public get destructiveChangesPre(): DecodeableMap<string, DecodeableMap<string, SourceComponent>> {
return this.destructiveComponents[DestructiveChangesType.PRE];
}

public get destructiveChangesPost(): Map<string, Map<string, SourceComponent>> {
public get destructiveChangesPost(): DecodeableMap<string, DecodeableMap<string, SourceComponent>> {
return this.destructiveComponents[DestructiveChangesType.POST];
}

Expand Down
78 changes: 78 additions & 0 deletions src/collections/decodeableMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* 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
*/

/**
* This is an extension of the Map class that treats keys as the same by matching first normally,
* then decoded. Decoding the key before comparing can solve some edge cases in component fullNames
* such as Layouts. See: https://github.com/forcedotcom/cli/issues/1683
*
* Examples:
*
* Given a map with entries:
* ```javascript
* 'layout#Layout__Broker__c-v1%2E1 Broker Layout' : {...}
* 'layout#Layout__Broker__c-v9.2 Broker Layout' : {...}
* ```
*
* `decodeableMap.has('layout#Layout__Broker__c-v1.1 Broker Layout')` --> returns `true`
* `decodeableMap.has('layout#Layout__Broker__c-v9%2E2 Broker Layout')` --> returns `true`
*/
export class DecodeableMap<K extends string, V> extends Map<string, V> {
/**
* boolean indicating whether an element with the specified key (matching decoded) exists or not.
*/
public has(key: K): boolean {
return super.has(key) || this.hasDecoded(key);
}

/**
* Returns a specified element from the Map object. If the value that is associated to
* the provided key (matching decoded) is an object, then you will get a reference to
* that object and any change made to that object will effectively modify it inside the Map.
*/
public get(key: K): V | undefined {
return super.get(key) ?? this.getDecoded(key);
}

/**
* Adds a new element with a specified key and value to the Map. If an element with the
* same key (matching decoded) already exists, the element will be updated.
*/
public set(key: K, value: V): this {
const sKey = this.getExistingKey(key) ?? key;
return super.set(sKey, value);
}

/**
* true if an element in the Map existed (matching decoded) and has been removed, or false
* if the element does not exist.
*/
public delete(key: K): boolean {
const sKey = this.getExistingKey(key) ?? key;
return super.delete(sKey);
}

// Returns true if the passed `key` matches an existing key entry when both keys are decoded.
private hasDecoded(key: string): boolean {
return !!this.getExistingKey(key);
}

// Returns the value of an entry matching on decoded keys.
private getDecoded(key: string): V | undefined {
const existingKey = this.getExistingKey(key);
return existingKey ? super.get(existingKey) : undefined;
}

// Returns the key as it is in the map, matching on decoded keys.
private getExistingKey(key: string): string | undefined {
for (const compKey of this.keys()) {
if (decodeURIComponent(compKey) === decodeURIComponent(key)) {
return compKey;
}
}
}
}

2 comments on commit 9d24d82

@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: 9d24d82 Previous: ed756b9 Ratio
eda-componentSetCreate-linux 2307 ms 2961 ms 0.78
eda-sourceToMdapi-linux 8275 ms 8470 ms 0.98
eda-sourceToZip-linux 7539 ms 8326 ms 0.91
eda-mdapiToSource-linux 5123 ms 6712 ms 0.76
lotsOfClasses-componentSetCreate-linux 17776 ms 22910 ms 0.78
lotsOfClasses-sourceToMdapi-linux 25207 ms 31958 ms 0.79
lotsOfClasses-sourceToZip-linux 23512 ms 30345 ms 0.77
lotsOfClasses-mdapiToSource-linux 20931 ms 26598 ms 0.79
lotsOfClassesOneDir-componentSetCreate-linux 65913 ms 83757 ms 0.79
lotsOfClassesOneDir-sourceToMdapi-linux 74781 ms 94341 ms 0.79
lotsOfClassesOneDir-sourceToZip-linux 74046 ms 92174 ms 0.80
lotsOfClassesOneDir-mdapiToSource-linux 70466 ms 94160 ms 0.75

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: 9d24d82 Previous: ed756b9 Ratio
eda-componentSetCreate-win32 8920 ms 7723 ms 1.15
eda-sourceToMdapi-win32 17956 ms 16210 ms 1.11
eda-sourceToZip-win32 14906 ms 14599 ms 1.02
eda-mdapiToSource-win32 14398 ms 12390 ms 1.16
lotsOfClasses-componentSetCreate-win32 83853 ms 72369 ms 1.16
lotsOfClasses-sourceToMdapi-win32 96948 ms 92158 ms 1.05
lotsOfClasses-sourceToZip-win32 92351 ms 78463 ms 1.18
lotsOfClasses-mdapiToSource-win32 91697 ms 81838 ms 1.12
lotsOfClassesOneDir-componentSetCreate-win32 278916 ms 232567 ms 1.20
lotsOfClassesOneDir-sourceToMdapi-win32 293641 ms 252707 ms 1.16
lotsOfClassesOneDir-sourceToZip-win32 285803 ms 245403 ms 1.16
lotsOfClassesOneDir-mdapiToSource-win32 287577 ms 247082 ms 1.16

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

Please sign in to comment.