-
Notifications
You must be signed in to change notification settings - Fork 412
fix(data-connect): Include connector in DataConnect cache key #3055
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
208b680
69b1d21
eb3d944
358d2db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -224,3 +224,128 @@ describe('DataConnect', () => { | |
| }); | ||
| }); | ||
| }); | ||
|
|
||
| describe('getDataConnect() caching', () => { | ||
|
||
| const mockOptions = { | ||
| credential: new mocks.MockCredential(), | ||
| projectId: 'test-project', | ||
| }; | ||
| let mockApp: FirebaseApp; | ||
| let getAppStub: sinon.SinonStub; | ||
|
|
||
| beforeEach(() => { | ||
| mockApp = mocks.appWithOptions(mockOptions); | ||
| getAppStub = sinon.stub(appIndex, 'getApp').returns(mockApp); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| getAppStub.restore(); | ||
| return mockApp.delete(); | ||
| }); | ||
|
|
||
| describe('should cache DataConnect instances correctly', () => { | ||
| it('should return the same instance for identical connector configs', () => { | ||
| const config: ConnectorConfig = { | ||
| location: 'us-west2', | ||
| serviceId: 'my-service', | ||
| connector: 'my-connector', | ||
| }; | ||
|
|
||
| const dc1 = getDataConnect(config); | ||
| const dc2 = getDataConnect(config); | ||
|
|
||
| expect(dc1).to.equal(dc2); | ||
|
||
| }); | ||
|
|
||
| it('should return different instances for different connectors with same location and serviceId', () => { | ||
| const config1: ConnectorConfig = { | ||
| location: 'us-west2', | ||
| serviceId: 'my-service', | ||
| connector: 'connector-a', | ||
| }; | ||
| const config2: ConnectorConfig = { | ||
| location: 'us-west2', | ||
| serviceId: 'my-service', | ||
| connector: 'connector-b', | ||
| }; | ||
|
|
||
| const dc1 = getDataConnect(config1); | ||
| const dc2 = getDataConnect(config2); | ||
|
|
||
| expect(dc1).to.not.equal(dc2); | ||
|
||
| expect(dc1.connectorConfig.connector).to.equal('connector-a'); | ||
| expect(dc2.connectorConfig.connector).to.equal('connector-b'); | ||
| }); | ||
|
|
||
| it('should return different instances for different locations', () => { | ||
| const config1: ConnectorConfig = { | ||
| location: 'us-west2', | ||
| serviceId: 'my-service', | ||
| connector: 'my-connector', | ||
| }; | ||
| const config2: ConnectorConfig = { | ||
| location: 'us-east1', | ||
| serviceId: 'my-service', | ||
| connector: 'my-connector', | ||
| }; | ||
|
|
||
| const dc1 = getDataConnect(config1); | ||
| const dc2 = getDataConnect(config2); | ||
|
|
||
| expect(dc1).to.not.equal(dc2); | ||
| }); | ||
|
|
||
| it('should return different instances for different serviceIds', () => { | ||
| const config1: ConnectorConfig = { | ||
| location: 'us-west2', | ||
| serviceId: 'service-a', | ||
| connector: 'my-connector', | ||
| }; | ||
| const config2: ConnectorConfig = { | ||
| location: 'us-west2', | ||
| serviceId: 'service-b', | ||
| connector: 'my-connector', | ||
| }; | ||
|
|
||
| const dc1 = getDataConnect(config1); | ||
| const dc2 = getDataConnect(config2); | ||
|
|
||
| expect(dc1).to.not.equal(dc2); | ||
| }); | ||
|
|
||
| it('should handle connector being undefined vs defined', () => { | ||
|
||
| const configWithConnector: ConnectorConfig = { | ||
| location: 'us-west2', | ||
| serviceId: 'my-service', | ||
| connector: 'my-connector', | ||
| }; | ||
| const configWithoutConnector: ConnectorConfig = { | ||
| location: 'us-west2', | ||
| serviceId: 'my-service', | ||
| }; | ||
|
|
||
| const dc1 = getDataConnect(configWithConnector); | ||
| const dc2 = getDataConnect(configWithoutConnector); | ||
|
|
||
| expect(dc1).to.not.equal(dc2); | ||
| expect(dc1.connectorConfig.connector).to.equal('my-connector'); | ||
| expect(dc2.connectorConfig.connector).to.be.undefined; | ||
| }); | ||
|
|
||
| it('should not have cache collisions with ambiguous keys', () => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a good test. could you also add one for different ordering of |
||
| const config1: ConnectorConfig = { | ||
| location: 'us-west2-a', | ||
| serviceId: 'b', | ||
| }; | ||
| const config2: ConnectorConfig = { | ||
| location: 'us-west2', | ||
| serviceId: 'a-b', | ||
| }; | ||
|
|
||
| const dc1 = getDataConnect(config1); | ||
| const dc2 = getDataConnect(config2); | ||
|
|
||
| expect(dc1).to.not.equal(dc2); | ||
| }); | ||
| }); | ||
itok01 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }); | ||
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.
This successfully makes the cache keys unique per connector.
JSON.stringify()is also what we use in Data Connect's Client JS SDK (link), so that's definitely the approach we want.I feel that instead of using an empty string when the
connectorfield isundefined, we should keep itundefinedand just callJSON.stringify(connectorConfig), so that we're aligned with the Client JS SDK. This also makes it so that if a user explicitly sets theirconnectorfield to be an empty string, there's no collision with a config that has the field unset.I also think that in both the Client JS SDK and here, we should be ordering our keys before we stringify. That way, two
ConnectorConfigobjects defined as{ serviceId: "s", location: "l" }and{ location: "l", serviceId: "s" }return the sameDataConnectinstance. Something like: