Skip to content

Make this work in Firebase Studio #1077

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

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
6 changes: 3 additions & 3 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:

strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
node-version: [20.x, 22.x]

steps:
- uses: actions/checkout@v3
Expand All @@ -50,7 +50,7 @@ jobs:

strategy:
matrix:
node-version: [18.x, 20.x]
node-version: [20.x, 22.x]

steps:
- uses: actions/checkout@v3
Expand All @@ -72,7 +72,7 @@ jobs:

strategy:
matrix:
node-version: [16.x]
node-version: [20.x]

steps:
- uses: actions/checkout@v3
Expand Down
11,530 changes: 7,254 additions & 4,276 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "firebase-tools-ui",
"version": "1.14.0",
"version": "1.15.0",
"private": true,
"engines": {
"node": ">=16.4.0"
"node": ">=20.4.0"
},
"dependencies": {
"@rmwc/base": "^7.0.3",
Expand Down Expand Up @@ -42,7 +42,7 @@
"classnames": "^2.2.6",
"codemirror": "^5.61.1",
"express": "^4.17.1",
"firebase": "^10.7.1",
"firebase": "^11.7.0",
"font-awesome": "^4.7.0",
"immer": "^9.0.14",
"keycode": "^2.2.0",
Expand Down Expand Up @@ -76,7 +76,7 @@
},
"overrides": {
"reactfire": {
"firebase": "^10.7.1"
"firebase": "^11.7.0"
},
"vite-plugin-node": {
"vite": "$vite"
Expand Down
7 changes: 5 additions & 2 deletions server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import type { ListenOptions } from 'net';
import * as path from 'path';

import express from 'express';
import fetch from 'node-fetch';

import { fetchMaybeWithCredentials } from './src/components/common/rest_api';

/*
This file defines Node.js server-side logic for the Emulator UI.
Expand Down Expand Up @@ -53,7 +54,9 @@ app.get(
'/api/config',
jsonHandler(async () => {
const hubDiscoveryUrl = new URL(`http://${hubHost}/emulators`);
const emulatorsRes = await fetch(hubDiscoveryUrl.toString());
const emulatorsRes = await fetchMaybeWithCredentials(
hubDiscoveryUrl.toString()
);
const emulators = (await emulatorsRes.json()) as any;

const json = { projectId, experiments: [], ...emulators };
Expand Down
3 changes: 2 additions & 1 deletion src/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import { fetchMaybeWithCredentials } from './components/common/rest_api';
import { scrubPathData } from './routes';
import { AnalyticsSession, Config } from './store/config';

Expand Down Expand Up @@ -124,7 +125,7 @@ export function scrubAnalyticsPageData() {
export async function initGtag() {
let session: AnalyticsSession | undefined;
try {
const response = await fetch(USAGE_CONFIG_API);
const response = await fetchMaybeWithCredentials(USAGE_CONFIG_API);
const responseJson: Config = await response.json();
session = responseJson.analytics;
} catch (error) {
Expand Down
3 changes: 2 additions & 1 deletion src/components/Auth/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,8 @@ describe('API', () => {
const api = setup({ mockFetchResult });
const result = await api.getConfig();
expect(global.fetch).toHaveBeenCalledWith(
'//foo.example.com:9002/emulator/v1/projects/pelmen-the-project/config'
'//foo.example.com:9002/emulator/v1/projects/pelmen-the-project/config',
undefined
);

expect(result).toEqual(result);
Expand Down
6 changes: 4 additions & 2 deletions src/components/Auth/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { RestApi } from '../common/rest_api';
import { RestApi, fetchMaybeWithCredentials } from '../common/rest_api';
import {
AddAuthUserPayload,
AuthUser,
Expand Down Expand Up @@ -140,7 +140,9 @@ export default class AuthApi extends RestApi {
}

async getConfig(): Promise<EmulatorV1ProjectsConfig> {
const config = await (await fetch(`${this.baseEmulatorUrl}/config`)).json();
const config = await (
await fetchMaybeWithCredentials(`${this.baseEmulatorUrl}/config`)
).json();
return config;
}

Expand Down
9 changes: 7 additions & 2 deletions src/components/Database/DataViewer/common/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
switchMap,
} from 'rxjs/operators';

import { fetchMaybeWithCredentials } from '../../../common/rest_api';
import {
DEFAULT_PAGE_SIZE,
QueryParams,
Expand Down Expand Up @@ -91,7 +92,9 @@ export function canDoRealtime(
print: 'silent',
timeout: REST_TIMEOUT,
});
return defer(() => fetch(silent, { headers: ADMIN_AUTH_HEADERS })).pipe(
return defer(() =>
fetchMaybeWithCredentials(silent, { headers: ADMIN_AUTH_HEADERS })
).pipe(
mapTo(true),
catchError(() => of(false)),
shareReplay({ bufferSize: 1, refCount: true })
Expand Down Expand Up @@ -179,7 +182,9 @@ function fetchNonRealtime(
): Observable<string[]> {
const params = getRestQueryParams(query);
const shallow = restUrl(realtimeRef, { ...params, shallow: 'true' });
return defer(() => fetch(shallow, { headers: ADMIN_AUTH_HEADERS })).pipe(
return defer(() =>
fetchMaybeWithCredentials(shallow, { headers: ADMIN_AUTH_HEADERS })
).pipe(
map((r) => r.json()),
map((data) => Object.keys(data))
);
Expand Down
3 changes: 2 additions & 1 deletion src/components/Database/DatabaseContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import useSwr from 'swr';
import { DatabaseConfig } from '../../store/config';
import { Callout } from '../common/Callout';
import { useEmulatorConfig } from '../common/EmulatorConfigProvider';
import { fetchMaybeWithCredentials } from '../common/rest_api';
import DatabasePicker from './DatabasePicker';

export type Props = {
Expand Down Expand Up @@ -111,7 +112,7 @@ async function fetchDatabases(
config: DatabaseConfig,
primary: string
): Promise<string[]> {
const res = await fetch(
const res = await fetchMaybeWithCredentials(
`//${config.hostAndPort}/.inspect/databases.json?ns=${primary}`
);
if (!res.ok) {
Expand Down
3 changes: 2 additions & 1 deletion src/components/Database/useImport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import { DatabaseReference } from 'firebase/database';

import { useEmulatorConfig } from '../common/EmulatorConfigProvider';
import { fetchMaybeWithCredentials } from '../common/rest_api';
import { useNamespace } from './useNamespace';

/** Get absolute path for file upload (the path with `.upload` appended) */
Expand Down Expand Up @@ -60,7 +61,7 @@ export function useImport(
}

return () =>
fetch(`//${config.hostAndPort}/${path}`, {
fetchMaybeWithCredentials(`//${config.hostAndPort}/${path}`, {
method: 'POST',
body: formData,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import useSwr from 'swr';

import { useEmulatorConfig } from '../../../common/EmulatorConfigProvider';
import { fetchMaybeWithCredentials } from '../../../common/rest_api';
import { Backend } from '../../index';
import { ExtensionBackend } from '../useExtensions';

Expand All @@ -33,7 +34,9 @@ export function useExtensionBackends(): ExtensionBackend[] {
const config = useEmulatorConfig('extensions');

const fetcher = async () => {
const response = await fetch(`//${config.hostAndPort}/backends`);
const response = await fetchMaybeWithCredentials(
`//${config.hostAndPort}/backends`
);
const json = await response.json();
return json.backends;
};
Expand Down
3 changes: 2 additions & 1 deletion src/components/Firealerts/Form/FirealertsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { ChangeEvent, useEffect, useState } from 'react';

import { Callout } from '../../common/Callout';
import { useEmulatorConfig } from '../../common/EmulatorConfigProvider';
import { fetchMaybeWithCredentials } from '../../common/rest_api';
import { useFirealerts } from '../api/useFirealerts';
import { FirealertsTrigger } from '../models';
import AlertSentNotification from '../Notification/AlertSentNotification';
Expand Down Expand Up @@ -68,7 +69,7 @@ export const FirealertsForm = () => {
const event = generateCloudEventWithData(selectedAlert, alertData);
const payload = { events: [event] };
const url = `//${config.hostAndPort}/google/publishEvents`;
await fetch(url, {
await fetchMaybeWithCredentials(url, {
method: 'POST',
body: JSON.stringify(payload),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
import useSWR from 'swr';

import { useEmulatorConfig } from '../../../common/EmulatorConfigProvider';
import { fetchMaybeWithCredentials } from '../../../common/rest_api';
import { FirealertsTrigger } from '../../models';

export function useFirealertsTriggers(): FirealertsTrigger[] {
const config = useEmulatorConfig('eventarc');
const FIREALERTS_EVENT_TYPE =
'google.firebase.firebasealerts.alerts.v1.published-google';
const fetcher = async (url: URL) => {
const response = await fetch(url);
const response = await fetchMaybeWithCredentials(url.toString());
const json = await response.json();
return json;
};
Expand Down
52 changes: 26 additions & 26 deletions src/components/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ const Overview: React.FC<
testId="emulator-info-firestore"
/>
<EmulatorCard
name="Realtime Database emulator"
icon={<DatabaseIcon theme="secondary" />}
config={config.database}
linkTo="/database"
linkLabel="Go to database emulator"
testId="emulator-info-database"
name="Storage emulator"
icon={<StorageIcon />}
config={config.storage}
linkTo={storagePath}
linkLabel="Go to storage emulator"
testId="emulator-info-storage"
/>
<EmulatorCard
name="Functions emulator"
Expand All @@ -103,20 +103,12 @@ const Overview: React.FC<
testId="emulator-info-functions"
/>
<EmulatorCard
name="Storage emulator"
icon={<StorageIcon />}
config={config.storage}
linkTo={storagePath}
linkLabel="Go to storage emulator"
testId="emulator-info-storage"
/>
<EmulatorCard
name="Hosting emulator"
icon={<HostingIcon theme="secondary" />}
config={config.hosting}
testId="emulator-info-hosting"
linkToExternal={config.hosting && `//${config.hosting.hostAndPort}/`}
linkLabel="View website"
name="Extensions emulator"
icon={<ExtensionsIcon theme="secondary" />}
config={config.extensions}
linkTo="/extensions"
linkLabel="Go to extensions emulator"
testId="emulator-info-extensions"
/>
<EmulatorCard
name="PubSub emulator"
Expand All @@ -125,12 +117,20 @@ const Overview: React.FC<
testId="emulator-info-pubsub"
/>
<EmulatorCard
name="Extensions emulator"
icon={<ExtensionsIcon theme="secondary" />}
config={config.extensions}
linkTo="/extensions"
linkLabel="Go to extensions emulator"
testId="emulator-info-extensions"
name="Realtime Database emulator"
icon={<DatabaseIcon theme="secondary" />}
config={config.database}
linkTo="/database"
linkLabel="Go to database emulator"
testId="emulator-info-database"
/>
<EmulatorCard
name="Hosting emulator"
icon={<HostingIcon theme="secondary" />}
config={config.hosting}
testId="emulator-info-hosting"
linkToExternal={config.hosting && `//${config.hosting.hostAndPort}/`}
linkLabel="View website"
/>
</GridRow>
</GridCell>
Expand Down
5 changes: 4 additions & 1 deletion src/components/Storage/api/useBuckets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import useSWR from 'swr';

import { useEmulatorConfig } from '../../common/EmulatorConfigProvider';
import { fetchMaybeWithCredentials } from '../../common/rest_api';
import { useBucket } from './useBucket';

export interface Bucket {
Expand All @@ -28,7 +29,9 @@ export function useBuckets() {
const [bucket] = useBucket();

const fetcher = async () => {
const response = await fetch(`//${config.hostAndPort}/b`);
const response = await fetchMaybeWithCredentials(
`//${config.hostAndPort}/b`
);
const json = await response.json();
return json.items.map((b: Bucket) => b.name);
};
Expand Down
3 changes: 2 additions & 1 deletion src/components/Storage/api/useCreateFolder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import { useEmulatorConfig } from '../../common/EmulatorConfigProvider';
import { fetchMaybeWithCredentials } from '../../common/rest_api';
import { useBucket } from './useBucket';

const EMPTY_FOLDER_DATA = `--boundary\r
Expand All @@ -35,7 +36,7 @@ export function useCreateFolder() {
const normalizedPath = fullPath.replace(/\/+/g, '/').replace(/^\//, '');
const encodedPath = encodeURIComponent(normalizedPath);

await fetch(
await fetchMaybeWithCredentials(
`http://${
config!.hostAndPort
}/upload/storage/v1/b/${bucket}/o?name=${encodedPath}/`,
Expand Down
7 changes: 4 additions & 3 deletions src/components/Storage/api/useTokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import useSWR from 'swr';

import { useEmulatorConfig } from '../../common/EmulatorConfigProvider';
import { fetchMaybeWithCredentials } from '../../common/rest_api';
import { useBucket } from './useBucket';

export function useTokens(fullPath: string) {
Expand All @@ -29,7 +30,7 @@ export function useTokens(fullPath: string) {
fullPath
)}`;

const response = await fetch(url, {
const response = await fetchMaybeWithCredentials(url, {
method: 'GET',
headers: { Authorization: 'Bearer owner' },
});
Expand All @@ -44,7 +45,7 @@ export function useTokens(fullPath: string) {
});

async function createToken() {
await fetch(
await fetchMaybeWithCredentials(
`//${config!.hostAndPort}/v0/b/${bucket}/o/${encodeURIComponent(
fullPath
)}?create_token=true`,
Expand All @@ -59,7 +60,7 @@ export function useTokens(fullPath: string) {
}

async function deleteToken(token: string) {
await fetch(
await fetchMaybeWithCredentials(
`//${config!.hostAndPort}/v0/b/${bucket}/o/${encodeURIComponent(
fullPath
)}?delete_token=${token}`,
Expand Down
Loading
Loading