Skip to content

Commit cf2f3b4

Browse files
committed
fix(dc): Use emulated credentials with Data Connect Emulator
1 parent 5fb1357 commit cf2f3b4

File tree

4 files changed

+58
-19
lines changed

4 files changed

+58
-19
lines changed

.github/workflows/ci.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@ jobs:
2828
run: npm run api-extractor
2929
- name: Run emulator-based integration tests
3030
run: |
31-
npm install -g firebase-tools@11.30.0
32-
firebase emulators:exec --project fake-project-id --only auth,database,firestore \
33-
'npx mocha \"test/integration/{auth,database,firestore}.spec.ts\" --slow 5000 --timeout 20000 --require ts-node/register'
31+
npm install -g firebase-tools@13.31.0
32+
firebase emulators:exec --project fake-project-id --only auth,database,firestore,dataconnect \
33+
'npx mocha \"test/integration/{auth,database,firestore,data-connect}.spec.ts\" --slow 5000 --timeout 20000 --require ts-node/register'

.github/workflows/nightly.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ jobs:
5252

5353
- name: Run emulator-based integration tests
5454
run: |
55-
npm install -g firebase-tools@11.30.0
56-
firebase emulators:exec --project fake-project-id --only auth,database,firestore \
57-
'npx mocha \"test/integration/{auth,database,firestore}.spec.ts\" --slow 5000 --timeout 20000 --require ts-node/register'
55+
npm install -g firebase-tools@13.31.0
56+
firebase emulators:exec --project fake-project-id --only auth,database,firestore,dataconnect \
57+
'npx mocha \"test/integration/{auth,database,firestore,data-connect}.spec.ts\" --slow 5000 --timeout 20000 --require ts-node/register'
5858
5959
- name: Run integration tests
6060
run: ./.github/scripts/run_integration_tests.sh

src/data-connect/data-connect-api-client-internal.ts

+50-11
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,15 @@ import * as utils from '../utils/index';
2525
import * as validator from '../utils/validator';
2626
import { ConnectorConfig, ExecuteGraphqlResponse, GraphqlOptions } from './data-connect-api';
2727

28-
// Data Connect backend constants
29-
const DATA_CONNECT_HOST = 'https://firebasedataconnect.googleapis.com';
30-
const DATA_CONNECT_API_URL_FORMAT =
31-
'{host}/v1alpha/projects/{projectId}/locations/{locationId}/services/{serviceId}:{endpointId}';
28+
const API_VERSION = 'v1alpha';
29+
30+
/** The Firebase Data Connect backend base URL format. */
31+
const FIREBASE_DATA_CONNECT_BASE_URL_FORMAT =
32+
'https://firebasedataconnect.googleapis.com/{version}/projects/{projectId}/locations/{locationId}/services/{serviceId}:{endpointId}';
33+
34+
/** Firebase Data Connect base URl format when using the Data Connect emultor. */
35+
const FIREBASE_DATA_CONNECT_EMULATOR_BASE_URL_FORMAT =
36+
'http://{host}/{version}/projects/{projectId}/locations/{locationId}/services/{serviceId}:{endpointId}';
3237

3338
const EXECUTE_GRAPH_QL_ENDPOINT = 'executeGraphql';
3439
const EXECUTE_GRAPH_QL_READ_ENDPOINT = 'executeGraphqlRead';
@@ -52,7 +57,7 @@ export class DataConnectApiClient {
5257
DATA_CONNECT_ERROR_CODE_MAPPING.INVALID_ARGUMENT,
5358
'First argument passed to getDataConnect() must be a valid Firebase app instance.');
5459
}
55-
this.httpClient = new AuthorizedHttpClient(app as FirebaseApp);
60+
this.httpClient = new DataConnectHttpClient(app as FirebaseApp);
5661
}
5762

5863
/**
@@ -101,13 +106,12 @@ export class DataConnectApiClient {
101106
'GraphqlOptions must be a non-null object');
102107
}
103108
}
104-
const host = (process.env.DATA_CONNECT_EMULATOR_HOST || DATA_CONNECT_HOST);
105109
const data = {
106110
query,
107111
...(options?.variables && { variables: options?.variables }),
108112
...(options?.operationName && { operationName: options?.operationName }),
109113
};
110-
return this.getUrl(host, this.connectorConfig.location, this.connectorConfig.serviceId, endpoint)
114+
return this.getUrl(API_VERSION, this.connectorConfig.location, this.connectorConfig.serviceId, endpoint)
111115
.then(async (url) => {
112116
const request: HttpRequestConfig = {
113117
method: 'POST',
@@ -133,18 +137,25 @@ export class DataConnectApiClient {
133137
});
134138
}
135139

136-
private async getUrl(host: string, locationId: string, serviceId: string, endpointId: string): Promise<string> {
140+
private async getUrl(version: string, locationId: string, serviceId: string, endpointId: string): Promise<string> {
137141
return this.getProjectId()
138142
.then((projectId) => {
139143
const urlParams = {
140-
host,
144+
version,
141145
projectId,
142146
locationId,
143147
serviceId,
144148
endpointId
145149
};
146-
const baseUrl = utils.formatString(DATA_CONNECT_API_URL_FORMAT, urlParams);
147-
return utils.formatString(baseUrl);
150+
let urlFormat: string;
151+
if (useEmulator()) {
152+
urlFormat = utils.formatString(FIREBASE_DATA_CONNECT_EMULATOR_BASE_URL_FORMAT, {
153+
host: emulatorHost()
154+
});
155+
} else {
156+
urlFormat = FIREBASE_DATA_CONNECT_BASE_URL_FORMAT;
157+
}
158+
return utils.formatString(urlFormat, urlParams);
148159
});
149160
}
150161

@@ -188,6 +199,34 @@ export class DataConnectApiClient {
188199
}
189200
}
190201

202+
/**
203+
* Data Connect-specific HTTP client which uses the special "owner" token
204+
* when communicating with the Data Connect Emulator.
205+
*/
206+
class DataConnectHttpClient extends AuthorizedHttpClient {
207+
208+
protected getToken(): Promise<string> {
209+
if (useEmulator()) {
210+
return Promise.resolve('owner');
211+
}
212+
213+
return super.getToken();
214+
}
215+
216+
}
217+
218+
function emulatorHost(): string | undefined {
219+
return process.env.DATA_CONNECT_EMULATOR_HOST
220+
}
221+
222+
/**
223+
* When true the SDK should communicate with the Data Connect Emulator for all API
224+
* calls and also produce unsigned tokens.
225+
*/
226+
export function useEmulator(): boolean {
227+
return !!emulatorHost();
228+
}
229+
191230
interface ErrorResponse {
192231
error?: ServerError;
193232
}

test/unit/data-connect/data-connect-api-client-internal.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,15 @@ describe('DataConnectApiClient', () => {
209209
});
210210

211211
it('should use DATA_CONNECT_EMULATOR_HOST if set', () => {
212-
process.env.DATA_CONNECT_EMULATOR_HOST = 'http://localhost:9000';
212+
process.env.DATA_CONNECT_EMULATOR_HOST = 'localhost:9399';
213213
const stub = sandbox
214214
.stub(HttpClient.prototype, 'send')
215215
.resolves(utils.responseFrom(TEST_RESPONSE, 200));
216216
return apiClient.executeGraphql('query', {})
217217
.then(() => {
218218
expect(stub).to.have.been.calledOnce.and.calledWith({
219219
method: 'POST',
220-
url: `http://localhost:9000/v1alpha/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}:executeGraphql`,
220+
url: `http://localhost:9399/v1alpha/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}:executeGraphql`,
221221
headers: EXPECTED_HEADERS,
222222
data: { query: 'query' }
223223
});

0 commit comments

Comments
 (0)