Skip to content

Commit b119293

Browse files
feat(dataconnect): dataconnect:sdk:generate automatically run firebase init dataconnect if no gen SDK is setup (#9325)
* feat(dataconnect): improve `dataconnect:sdk:generate`'s behavior when the project folder hasn't been setup - If the Firebase project hasn't been setup or dataconnect feature hasn't been setup, run the same flow as `firebase init dataconnect` (in src/init/features/dataconnect/index.ts) - If the dataconnect feature is setup, but generated SDK hasn't been setup, run the same flow as `firebase init dataconnect:sdk` (in src/init/features/dataconnect/sdk.ts) Then continue with the remaining steps~ * polish * chaneglog * non-interactive * non-interactive * update message * m --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent 611930a commit b119293

File tree

4 files changed

+109
-44
lines changed

4 files changed

+109
-44
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- `firebase dataconnect:sdk:generate` will run `init dataconnect:sdk` automatically if no SDKs are configured (#9325).

src/commands/dataconnect-sdk-generate.ts

Lines changed: 95 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ import { Options } from "../options";
55
import { DataConnectEmulator } from "../emulator/dataconnectEmulator";
66
import { needProjectId } from "../projectUtils";
77
import { loadAll } from "../dataconnect/load";
8-
import { logger } from "../logger";
98
import { getProjectDefaultAccount } from "../auth";
10-
import { logLabeledSuccess } from "../utils";
9+
import { logBullet, logLabeledSuccess, logWarning } from "../utils";
1110
import { ServiceInfo } from "../dataconnect/types";
11+
import { Config } from "../config";
12+
import { Setup } from "../init";
13+
import * as dataconnectInit from "../init/features/dataconnect";
14+
import * as dataconnectSdkInit from "../init/features/dataconnect/sdk";
15+
import { FirebaseError } from "../error";
16+
import { postInitSaves } from "./init";
1217

1318
type GenerateOptions = Options & { watch?: boolean };
1419

@@ -21,42 +26,98 @@ export const command = new Command("dataconnect:sdk:generate")
2126
.action(async (options: GenerateOptions) => {
2227
const projectId = needProjectId(options);
2328

24-
const serviceInfos = await loadAll(projectId, options.config);
25-
const serviceInfosWithSDKs = serviceInfos.filter((serviceInfo) =>
26-
serviceInfo.connectorInfo.some((c) => {
27-
return (
28-
c.connectorYaml.generate?.javascriptSdk ||
29-
c.connectorYaml.generate?.kotlinSdk ||
30-
c.connectorYaml.generate?.swiftSdk ||
31-
c.connectorYaml.generate?.dartSdk
29+
let justRanInit = false;
30+
let config = options.config;
31+
if (!config || !config.has("dataconnect")) {
32+
if (options.nonInteractive) {
33+
throw new FirebaseError(
34+
`No dataconnect project directory found. Please run ${clc.bold("firebase init dataconnect")} to set it up first.`,
3235
);
33-
}),
34-
);
35-
if (!serviceInfosWithSDKs.length) {
36-
logger.warn("No generated SDKs have been declared in connector.yaml files.");
37-
logger.warn(`Run ${clc.bold("firebase init dataconnect:sdk")} to configure a generated SDK.`);
38-
logger.warn(
39-
`See https://firebase.google.com/docs/data-connect/web-sdk for more details of how to configure generated SDKs.`,
36+
}
37+
logWarning("No dataconnect project directory found.");
38+
logBullet(
39+
`Running ${clc.bold("firebase init dataconnect")} to setup a dataconnect project directory.`,
4040
);
41-
return;
42-
}
43-
async function generateSDK(serviceInfo: ServiceInfo): Promise<void> {
44-
return DataConnectEmulator.generate({
45-
configDir: serviceInfo.sourceDirectory,
46-
watch: options.watch,
47-
account: getProjectDefaultAccount(options.projectRoot),
48-
});
41+
if (!config) {
42+
const cwd = options.cwd || process.cwd();
43+
config = new Config({}, { projectDir: cwd, cwd: cwd });
44+
}
45+
const setup: Setup = {
46+
config: config.src,
47+
rcfile: options.rc.data,
48+
instructions: [],
49+
};
50+
await dataconnectInit.askQuestions(setup);
51+
await dataconnectInit.actuate(setup, config, options);
52+
await postInitSaves(setup, config);
53+
justRanInit = true;
54+
options.config = config;
4955
}
50-
if (options.watch) {
51-
await Promise.race(serviceInfosWithSDKs.map(generateSDK));
52-
} else {
53-
for (const s of serviceInfosWithSDKs) {
54-
await generateSDK(s);
56+
57+
let serviceInfosWithSDKs = await loadAllWithSDKs(projectId, config);
58+
if (!serviceInfosWithSDKs.length) {
59+
if (justRanInit || options.nonInteractive) {
60+
throw new FirebaseError(
61+
`No generated SDKs are configured during init. Please run ${clc.bold("firebase init dataconnect:sdk")} to configure a generated SDK.`,
62+
);
5563
}
56-
const services = serviceInfosWithSDKs.map((s) => s.dataConnectYaml.serviceId).join(", ");
57-
logLabeledSuccess(
58-
"dataconnect",
59-
`Successfully Generated SDKs for services: ${clc.bold(services)}`,
64+
logWarning("No generated SDKs have been configured.");
65+
logBullet(
66+
`Running ${clc.bold("firebase init dataconnect:sdk")} to configure a generated SDK.`,
6067
);
68+
const setup: Setup = {
69+
config: config.src,
70+
rcfile: options.rc.data,
71+
instructions: [],
72+
};
73+
await dataconnectSdkInit.askQuestions(setup);
74+
await dataconnectSdkInit.actuate(setup, config);
75+
justRanInit = true;
76+
serviceInfosWithSDKs = await loadAllWithSDKs(projectId, config);
6177
}
78+
79+
await generateSDKsInAll(options, serviceInfosWithSDKs, justRanInit);
6280
});
81+
82+
async function loadAllWithSDKs(projectId: string, config: Config): Promise<ServiceInfo[]> {
83+
const serviceInfos = await loadAll(projectId, config);
84+
return serviceInfos.filter((serviceInfo) =>
85+
serviceInfo.connectorInfo.some((c) => {
86+
return (
87+
c.connectorYaml.generate?.javascriptSdk ||
88+
c.connectorYaml.generate?.kotlinSdk ||
89+
c.connectorYaml.generate?.swiftSdk ||
90+
c.connectorYaml.generate?.dartSdk
91+
);
92+
}),
93+
);
94+
}
95+
96+
async function generateSDKsInAll(
97+
options: GenerateOptions,
98+
serviceInfosWithSDKs: ServiceInfo[],
99+
justRanInit: boolean,
100+
): Promise<void> {
101+
async function generateSDK(serviceInfo: ServiceInfo): Promise<void> {
102+
return DataConnectEmulator.generate({
103+
configDir: serviceInfo.sourceDirectory,
104+
watch: options.watch,
105+
account: getProjectDefaultAccount(options.projectRoot),
106+
});
107+
}
108+
if (options.watch) {
109+
await Promise.race(serviceInfosWithSDKs.map(generateSDK));
110+
} else {
111+
if (justRanInit) {
112+
return; // SDKs are already generated during init
113+
}
114+
for (const s of serviceInfosWithSDKs) {
115+
await generateSDK(s);
116+
}
117+
const services = serviceInfosWithSDKs.map((s) => s.dataConnectYaml.serviceId).join(", ");
118+
logLabeledSuccess(
119+
"dataconnect",
120+
`Successfully Generated SDKs for services: ${clc.bold(services)}`,
121+
);
122+
}
123+
}

src/commands/init.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,17 @@ export async function initAction(feature: string, options: Options): Promise<voi
266266
}
267267

268268
await init(setup, config, options);
269+
await postInitSaves(setup, config);
269270

271+
if (setup.instructions.length) {
272+
logger.info(`\n${clc.bold("To get started:")}\n`);
273+
for (const i of setup.instructions) {
274+
logBullet(i + "\n");
275+
}
276+
}
277+
}
278+
279+
export async function postInitSaves(setup: Setup, config: Config): Promise<void> {
270280
logger.info();
271281
config.writeProjectFile("firebase.json", setup.config);
272282
config.writeProjectFile(".firebaserc", setup.rcfile);
@@ -275,11 +285,4 @@ export async function initAction(feature: string, options: Options): Promise<voi
275285
}
276286
logger.info();
277287
utils.logSuccess("Firebase initialization complete!");
278-
279-
if (setup.instructions.length) {
280-
logger.info(`\n${clc.bold("To get started:")}\n`);
281-
for (const i of setup.instructions) {
282-
logBullet(i + "\n");
283-
}
284-
}
285288
}

src/init/features/dataconnect/sdk.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export async function askQuestions(setup: Setup): Promise<void> {
7070
{ name: `React${npxMissingWarning}`, value: "react" },
7171
{ name: `Next.JS${npxMissingWarning}`, value: "next" },
7272
{ name: `Flutter${flutterMissingWarning}`, value: "flutter" },
73-
{ name: "no", value: "no" },
73+
{ name: "skip", value: "skip" },
7474
],
7575
});
7676
try {
@@ -84,7 +84,7 @@ export async function askQuestions(setup: Setup): Promise<void> {
8484
case "flutter":
8585
await createFlutterApp(newUniqueId("flutter_app", listFiles(cwd)));
8686
break;
87-
case "no":
87+
case "skip":
8888
break;
8989
}
9090
} catch (err: unknown) {
@@ -105,7 +105,7 @@ export async function chooseApp(): Promise<App[]> {
105105
`Detected existing apps ${apps.map((a) => appDescription(a)).join(", ")}`,
106106
);
107107
} else {
108-
logLabeledWarning("dataconnect", "No app exists in the current directory.");
108+
logLabeledWarning("dataconnect", "Cannot detect an existing app in the current directory.");
109109
}
110110
// Check for environment variables override.
111111
const envAppFolder = envOverride(FDC_APP_FOLDER, "");

0 commit comments

Comments
 (0)