Skip to content

Commit ba0198d

Browse files
committed
Fixed fromUrl
1 parent df180ca commit ba0198d

File tree

2 files changed

+89
-9
lines changed

2 files changed

+89
-9
lines changed

packages/functions/src/callable.test.ts

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ import {
3737
AppCheckInternalComponentName
3838
} from '@firebase/app-check-interop-types';
3939
import { makeFakeApp, createTestService } from '../test/utils';
40-
import { FunctionsService, httpsCallable } from './service';
40+
import {
41+
FunctionsService,
42+
httpsCallable,
43+
httpsCallableFromURL
44+
} from './service';
4145
import { FUNCTIONS_TYPE } from './constants';
4246
import { FunctionsError } from './error';
4347

@@ -526,6 +530,7 @@ describe('Firebase Functions > Stream', () => {
526530
expect(options.credentials).to.equal(undefined);
527531
expect(options.headers['Accept']).to.equal('text/event-stream');
528532
});
533+
529534
it('calls cloud workstations with credentials', async () => {
530535
const authMock: FirebaseAuthInternal = {
531536
getToken: async () => ({ accessToken: 'auth-token' })
@@ -589,6 +594,69 @@ describe('Firebase Functions > Stream', () => {
589594
expect(options.credentials).to.equal('include');
590595
});
591596

597+
it.only('calls streamFromURL cloud workstations with credentials', async () => {
598+
const authMock: FirebaseAuthInternal = {
599+
getToken: async () => ({ accessToken: 'auth-token' })
600+
} as unknown as FirebaseAuthInternal;
601+
const authProvider = new Provider<FirebaseAuthInternalName>(
602+
'auth-internal',
603+
new ComponentContainer('test')
604+
);
605+
authProvider.setComponent(
606+
new Component('auth-internal', () => authMock, ComponentType.PRIVATE)
607+
);
608+
const appCheckMock: FirebaseAppCheckInternal = {
609+
getToken: async () => ({ token: 'app-check-token' })
610+
} as unknown as FirebaseAppCheckInternal;
611+
const appCheckProvider = new Provider<AppCheckInternalComponentName>(
612+
'app-check-internal',
613+
new ComponentContainer('test')
614+
);
615+
appCheckProvider.setComponent(
616+
new Component(
617+
'app-check-internal',
618+
() => appCheckMock,
619+
ComponentType.PRIVATE
620+
)
621+
);
622+
623+
const functions = createTestService(
624+
app,
625+
region,
626+
authProvider,
627+
undefined,
628+
appCheckProvider
629+
);
630+
functions.emulatorOrigin = 'test.cloudworkstations.dev';
631+
const mockFetch = sinon.stub(functions, 'fetchImpl' as any);
632+
633+
const mockResponse = new ReadableStream({
634+
start(controller) {
635+
controller.enqueue(
636+
new TextEncoder().encode('data: {"result":"Success"}\n')
637+
);
638+
controller.close();
639+
}
640+
});
641+
642+
mockFetch.resolves({
643+
body: mockResponse,
644+
headers: new Headers({ 'Content-Type': 'text/event-stream' }),
645+
status: 200,
646+
statusText: 'OK'
647+
} as Response);
648+
649+
const func = httpsCallableFromURL<Record<string, any>, string, string>(
650+
functions,
651+
'stream'
652+
);
653+
await func.stream({});
654+
655+
expect(mockFetch.calledOnce).to.be.true;
656+
const [_, options] = mockFetch.firstCall.args;
657+
expect(options.credentials).to.equal('include');
658+
});
659+
592660
it('aborts during initial fetch', async () => {
593661
const controller = new AbortController();
594662

packages/functions/src/service.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -245,18 +245,27 @@ export function httpsCallableFromURL<
245245
return callable as HttpsCallable<RequestData, ResponseData, StreamData>;
246246
}
247247

248+
function getCredentials(functionsInstance: FunctionsService) {
249+
return functionsInstance.emulatorOrigin &&
250+
isCloudWorkstation(functionsInstance.emulatorOrigin)
251+
? 'include'
252+
: undefined;
253+
}
254+
248255
/**
249256
* Does an HTTP POST and returns the completed response.
250257
* @param url The url to post to.
251258
* @param body The JSON body of the post.
252259
* @param headers The HTTP headers to include in the request.
260+
* @param functionsInstance functions instance that is calling postJSON
253261
* @return A Promise that will succeed when the request finishes.
254262
*/
255263
async function postJSON(
256264
url: string,
257265
body: unknown,
258266
headers: { [key: string]: string },
259-
fetchImpl: typeof fetch
267+
fetchImpl: typeof fetch,
268+
functionsInstance: FunctionsService
260269
): Promise<HttpResponse> {
261270
headers['Content-Type'] = 'application/json';
262271

@@ -265,7 +274,8 @@ async function postJSON(
265274
response = await fetchImpl(url, {
266275
method: 'POST',
267276
body: JSON.stringify(body),
268-
headers
277+
headers,
278+
credentials: getCredentials(functionsInstance)
269279
});
270280
} catch (e) {
271281
// This could be an unhandled error on the backend, or it could be a
@@ -353,7 +363,13 @@ async function callAtURL(
353363

354364
const failAfterHandle = failAfter(timeout);
355365
const response = await Promise.race([
356-
postJSON(url, body, headers, functionsInstance.fetchImpl),
366+
postJSON(
367+
url,
368+
body,
369+
headers,
370+
functionsInstance.fetchImpl,
371+
functionsInstance
372+
),
357373
failAfterHandle.promise,
358374
functionsInstance.cancelAllRequests
359375
]);
@@ -440,11 +456,7 @@ async function streamAtURL(
440456
body: JSON.stringify(body),
441457
headers,
442458
signal: options?.signal,
443-
credentials:
444-
functionsInstance.emulatorOrigin &&
445-
isCloudWorkstation(functionsInstance.emulatorOrigin)
446-
? 'include'
447-
: undefined
459+
credentials: getCredentials(functionsInstance)
448460
});
449461
} catch (e) {
450462
if (e instanceof Error && e.name === 'AbortError') {

0 commit comments

Comments
 (0)