-
Notifications
You must be signed in to change notification settings - Fork 98
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/main' into sh/remove-unzipper
- Loading branch information
Showing
7 changed files
with
609 additions
and
1,148 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
/* | ||
* Copyright (c) 2020, 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 { expect } from 'chai'; | ||
import * as sinon from 'sinon'; | ||
import { DecodeableMap } from '../../src/collections/decodeableMap'; | ||
|
||
describe('DecodeableMap', () => { | ||
let dMap: DecodeableMap<string, string>; | ||
const ENCODED_KEY = 'encodedKey'; | ||
const DECODED_KEY = 'decodedKey'; | ||
|
||
const sandbox = sinon.createSandbox(); | ||
let hasDecodedSpy: sinon.SinonSpy; | ||
let getDecodedSpy: sinon.SinonSpy; | ||
let hasMapSpy: sinon.SinonSpy; | ||
let getMapSpy: sinon.SinonSpy; | ||
let setMapSpy: sinon.SinonSpy; | ||
let deleteMapSpy: sinon.SinonSpy; | ||
|
||
beforeEach(() => { | ||
dMap = new DecodeableMap([ | ||
['Layout-v1%2E1 Layout', ENCODED_KEY], | ||
['Layout-v9.2 Layout', DECODED_KEY], | ||
]); | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
hasDecodedSpy = sandbox.spy(dMap, 'hasDecoded' as any); | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
getDecodedSpy = sandbox.spy(dMap, 'getDecoded' as any); | ||
hasMapSpy = sandbox.spy(Map.prototype, 'has'); | ||
getMapSpy = sandbox.spy(Map.prototype, 'get'); | ||
setMapSpy = sandbox.spy(Map.prototype, 'set'); | ||
deleteMapSpy = sandbox.spy(Map.prototype, 'delete'); | ||
}); | ||
|
||
afterEach(() => { | ||
sandbox.restore(); | ||
}); | ||
|
||
describe('has()', () => { | ||
it('should match on exact key without decoding', () => { | ||
expect(dMap.has('Layout-v1%2E1 Layout')).to.be.true; | ||
expect(hasMapSpy.called).to.be.true; | ||
expect(hasDecodedSpy.called).to.be.false; | ||
}); | ||
|
||
it('should match encoded key with decoded value', () => { | ||
expect(dMap.has('Layout-v1.1 Layout')).to.be.true; | ||
expect(hasMapSpy.called).to.be.true; | ||
expect(hasDecodedSpy.called).to.be.true; | ||
}); | ||
|
||
it('should match decoded key with encoded value', () => { | ||
expect(dMap.has('Layout-v9%2E2 Layout')).to.be.true; | ||
expect(hasMapSpy.called).to.be.true; | ||
expect(hasDecodedSpy.called).to.be.true; | ||
}); | ||
|
||
it('should not match on no existing key', () => { | ||
expect(dMap.has('Layout-MISSING Layout')).to.be.false; | ||
expect(hasMapSpy.called).to.be.true; | ||
expect(hasDecodedSpy.called).to.be.true; | ||
}); | ||
}); | ||
|
||
describe('get()', () => { | ||
it('should get value with exact key without decoding', () => { | ||
expect(dMap.get('Layout-v1%2E1 Layout')).to.equal(ENCODED_KEY); | ||
expect(getMapSpy.calledOnce).to.be.true; | ||
expect(getDecodedSpy.called).to.be.false; | ||
}); | ||
|
||
it('should get value of encoded key using decoded key', () => { | ||
expect(dMap.get('Layout-v1.1 Layout')).to.equal(ENCODED_KEY); | ||
expect(getMapSpy.calledTwice).to.be.true; | ||
expect(getDecodedSpy.calledOnce).to.be.true; | ||
}); | ||
|
||
it('should get value of decoded key using encoded key', () => { | ||
expect(dMap.get('Layout-v9%2E2 Layout')).to.equal(DECODED_KEY); | ||
expect(getMapSpy.calledTwice).to.be.true; | ||
expect(getDecodedSpy.calledOnce).to.be.true; | ||
}); | ||
|
||
it('should return undefined on no existing key', () => { | ||
expect(dMap.get('Layout-MISSING Layout')).to.be.undefined; | ||
expect(getMapSpy.calledOnce).to.be.true; | ||
expect(getDecodedSpy.called).to.be.true; | ||
}); | ||
}); | ||
|
||
describe('set()', () => { | ||
const NEW_VALUE = 'new value from set'; | ||
|
||
it('should set value with exact key', () => { | ||
expect(dMap.set('Layout-v1%2E1 Layout', NEW_VALUE)).to.equal(dMap); | ||
expect(setMapSpy.called).to.be.true; | ||
expect(setMapSpy.lastCall.args[0]).to.equal('Layout-v1%2E1 Layout'); | ||
expect(setMapSpy.lastCall.args[1]).to.equal(NEW_VALUE); | ||
expect(dMap.size).to.equal(2); | ||
expect(dMap.get('Layout-v1%2E1 Layout')).to.equal(NEW_VALUE); | ||
}); | ||
|
||
it('should set value of encoded key using decoded key', () => { | ||
expect(dMap.set('Layout-v1.1 Layout', NEW_VALUE)).to.equal(dMap); | ||
expect(setMapSpy.called).to.be.true; | ||
expect(setMapSpy.lastCall.args[0]).to.equal('Layout-v1%2E1 Layout'); | ||
expect(setMapSpy.lastCall.args[1]).to.equal(NEW_VALUE); | ||
expect(dMap.size).to.equal(2); | ||
expect(dMap.get('Layout-v1%2E1 Layout')).to.equal(NEW_VALUE); | ||
}); | ||
|
||
it('should set value of decoded key using encoded key', () => { | ||
expect(dMap.set('Layout-v9%2E2 Layout', NEW_VALUE)).to.equal(dMap); | ||
expect(setMapSpy.called).to.be.true; | ||
expect(setMapSpy.lastCall.args[0]).to.equal('Layout-v9.2 Layout'); | ||
expect(setMapSpy.lastCall.args[1]).to.equal(NEW_VALUE); | ||
expect(dMap.size).to.equal(2); | ||
expect(dMap.get('Layout-v9.2 Layout')).to.equal(NEW_VALUE); | ||
}); | ||
|
||
it('should set new entry on no existing key', () => { | ||
expect(dMap.set('Layout-MISSING Layout', NEW_VALUE)).to.equal(dMap); | ||
expect(setMapSpy.called).to.be.true; | ||
expect(setMapSpy.lastCall.args[0]).to.equal('Layout-MISSING Layout'); | ||
expect(setMapSpy.lastCall.args[1]).to.equal(NEW_VALUE); | ||
expect(dMap.size).to.equal(3); | ||
expect(dMap.get('Layout-MISSING Layout')).to.equal(NEW_VALUE); | ||
}); | ||
}); | ||
|
||
describe('delete()', () => { | ||
it('should delete using exact key', () => { | ||
expect(dMap.delete('Layout-v1%2E1 Layout')).to.be.true; | ||
expect(deleteMapSpy.calledOnce).to.be.true; | ||
expect(deleteMapSpy.firstCall.args[0]).to.equal('Layout-v1%2E1 Layout'); | ||
expect(dMap.size).to.equal(1); | ||
expect(dMap.has('Layout-v1%2E1 Layout')).to.be.false; | ||
}); | ||
|
||
it('should delete the encoded key using decoded value', () => { | ||
expect(dMap.delete('Layout-v1.1 Layout')).to.be.true; | ||
expect(deleteMapSpy.calledOnce).to.be.true; | ||
expect(deleteMapSpy.firstCall.args[0]).to.equal('Layout-v1%2E1 Layout'); | ||
expect(dMap.size).to.equal(1); | ||
expect(dMap.has('Layout-v1.1 Layout')).to.be.false; | ||
}); | ||
|
||
it('should delete the decoded key using encoded value', () => { | ||
expect(dMap.delete('Layout-v9%2E2 Layout')).to.be.true; | ||
expect(deleteMapSpy.calledOnce).to.be.true; | ||
expect(deleteMapSpy.firstCall.args[0]).to.equal('Layout-v9.2 Layout'); | ||
expect(dMap.size).to.equal(1); | ||
expect(dMap.has('Layout-v9%2E2 Layout')).to.be.false; | ||
}); | ||
|
||
it('should not delete on no existing key', () => { | ||
expect(dMap.delete('Layout-MISSING Layout')).to.be.false; | ||
expect(deleteMapSpy.calledOnce).to.be.true; | ||
expect(deleteMapSpy.firstCall.args[0]).to.equal('Layout-MISSING Layout'); | ||
expect(dMap.size).to.equal(2); | ||
expect(dMap.has('Layout-MISSING Layout')).to.be.false; | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.
a987037
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.
Benchmark
eda-componentSetCreate-linux
3097
ms341
ms9.08
eda-sourceToMdapi-linux
10755
ms8527
ms1.26
eda-sourceToZip-linux
9599
ms5973
ms1.61
eda-mdapiToSource-linux
8081
ms5257
ms1.54
lotsOfClasses-componentSetCreate-linux
23970
ms681
ms35.20
lotsOfClasses-sourceToMdapi-linux
34661
ms11682
ms2.97
lotsOfClasses-sourceToZip-linux
31935
ms9140
ms3.49
lotsOfClasses-mdapiToSource-linux
29111
ms6011
ms4.84
lotsOfClassesOneDir-componentSetCreate-linux
86237
ms900
ms95.82
lotsOfClassesOneDir-sourceToMdapi-linux
101128
ms15846
ms6.38
lotsOfClassesOneDir-sourceToZip-linux
95651
ms15459
ms6.19
lotsOfClassesOneDir-mdapiToSource-linux
94881
ms11322
ms8.38
This comment was automatically generated by workflow using github-action-benchmark.
a987037
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.
Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold
2
.eda-componentSetCreate-linux
3097
ms341
ms9.08
lotsOfClasses-componentSetCreate-linux
23970
ms681
ms35.20
lotsOfClasses-sourceToMdapi-linux
34661
ms11682
ms2.97
lotsOfClasses-sourceToZip-linux
31935
ms9140
ms3.49
lotsOfClasses-mdapiToSource-linux
29111
ms6011
ms4.84
lotsOfClassesOneDir-componentSetCreate-linux
86237
ms900
ms95.82
lotsOfClassesOneDir-sourceToMdapi-linux
101128
ms15846
ms6.38
lotsOfClassesOneDir-sourceToZip-linux
95651
ms15459
ms6.19
lotsOfClassesOneDir-mdapiToSource-linux
94881
ms11322
ms8.38
This comment was automatically generated by workflow using github-action-benchmark.