Skip to content
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

Include SDK generation as part of firebase init dataconnect #7693

Merged
merged 8 commits into from
Sep 23, 2024
39 changes: 26 additions & 13 deletions src/init/features/dataconnect/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
getSchema,
listConnectors,
} from "../../../dataconnect/client";
import { Schema, Service, File } from "../../../dataconnect/types";
import { Schema, Service, File, Platform } from "../../../dataconnect/types";
import { parseCloudSQLInstanceName, parseServiceName } from "../../../dataconnect/names";
import { logger } from "../../../logger";
import { readTemplateSync } from "../../../templates";
import { logSuccess } from "../../../utils";
import { logBullet, logSuccess } from "../../../utils";
import { checkBillingEnabled } from "../../../gcp/cloudbilling";
import * as sdk from "./sdk";
import { getPlatformFromFolder } from "../../../dataconnect/fileUtils";

const DATACONNECT_YAML_TEMPLATE = readTemplateSync("init/dataconnect/dataconnect.yaml");
const DATACONNECT_YAML_COMPAT_EXPERIMENT_TEMPLATE = readTemplateSync(
Expand Down Expand Up @@ -63,18 +65,33 @@
};

// doSetup is split into 2 phases - ask questions and then actuate files and API calls based on those answers.
export async function doSetup(setup: Setup, config: Config): Promise<void> {

Check warning on line 68 in src/init/features/dataconnect/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing JSDoc comment
const info = await askQuestions(setup, config);
const info = await askQuestions(setup);
await actuate(setup, config, info);

const cwdPlatformGuess = await getPlatformFromFolder(process.cwd());
if (cwdPlatformGuess !== Platform.UNDETERMINED) {
await sdk.doSetup(setup, config);
} else {
const promptForSDKGeneration = await confirm({
message: `Would you like to configure generated SDKs now?`,
default: false,
});
if (promptForSDKGeneration) {
await sdk.doSetup(setup, config);
} else {
logBullet(
`If you'd like to generate an SDK for your new connector later, run ${clc.bold("firebase init dataconnect:sdk")}`,
);
}
}

logger.info("");
logSuccess(
`If you'd like to generate an SDK for your new connector, run ${clc.bold("firebase init dataconnect:sdk")}`,
);
}

// askQuestions prompts the user about the Data Connect service they want to init. Any prompting
// logic should live here, and _no_ actuation logic should live here.
async function askQuestions(setup: Setup, config: Config): Promise<RequiredInfo> {
async function askQuestions(setup: Setup): Promise<RequiredInfo> {
let info: RequiredInfo = {
serviceId: "",
locationId: "",
Expand All @@ -94,7 +111,7 @@
}

if (info.cloudSqlDatabase === "") {
info = await promptForDatabase(setup, config, info);
info = await promptForDatabase(setup, info);
}

info.shouldProvisionCSQL = !!(
Expand All @@ -111,7 +128,7 @@

// actuate writes product specific files and makes product specifc API calls.
// It does not handle writing firebase.json and .firebaserc
export async function actuate(setup: Setup, config: Config, info: RequiredInfo) {

Check warning on line 131 in src/init/features/dataconnect/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function

Check warning on line 131 in src/init/features/dataconnect/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing JSDoc comment
await writeFiles(config, info);

if (setup.projectId && info.shouldProvisionCSQL) {
Expand All @@ -126,8 +143,8 @@
}
}

async function writeFiles(config: Config, info: RequiredInfo) {

Check warning on line 146 in src/init/features/dataconnect/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
const dir: string = config.get("dataconnect.source") || "dataconnect";

Check warning on line 147 in src/init/features/dataconnect/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe assignment of an `any` value
const subbedDataconnectYaml = subDataconnectYamlValues({
...info,
connectorDirs: info.connectors.map((c) => c.path),
Expand All @@ -151,7 +168,7 @@
}
}

async function writeConnectorFiles(

Check warning on line 171 in src/init/features/dataconnect/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
config: Config,
connectorInfo: {
id: string;
Expand All @@ -160,7 +177,7 @@
},
) {
const subbedConnectorYaml = subConnectorYamlValues({ connectorId: connectorInfo.id });
const dir: string = config.get("dataconnect.source") || "dataconnect";

Check warning on line 180 in src/init/features/dataconnect/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe assignment of an `any` value
await config.askWriteProjectFile(
join(dir, connectorInfo.path, "connector.yaml"),
subbedConnectorYaml,
Expand Down Expand Up @@ -224,7 +241,7 @@
}),
);
if (existingServicesAndSchemas.length) {
const choices: { name: string; value: any }[] = existingServicesAndSchemas.map((s) => {

Check warning on line 244 in src/init/features/dataconnect/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type
const serviceName = parseServiceName(s.service.name);
return {
name: `${serviceName.location}/${serviceName.serviceId}`,
Expand All @@ -232,7 +249,7 @@
};
});
choices.push({ name: "Create a new service", value: undefined });
const choice: { service: Service; schema: Schema } = await promptOnce({

Check warning on line 252 in src/init/features/dataconnect/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe assignment of an `any` value
message:
"Your project already has existing services. Which would you like to set up local files for?",
type: "list",
Expand Down Expand Up @@ -260,7 +277,7 @@
]);
if (connectors.length) {
info.connectors = connectors.map((c) => {
const id = c.name.split("/").pop()!;

Check warning on line 280 in src/init/features/dataconnect/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Forbidden non-null assertion
return {
id,
path: connectors.length === 1 ? "./connector" : `./${id}`,
Expand Down Expand Up @@ -350,11 +367,7 @@
}
}

async function promptForDatabase(
setup: Setup,
config: Config,
info: RequiredInfo,
): Promise<RequiredInfo> {
async function promptForDatabase(setup: Setup, info: RequiredInfo): Promise<RequiredInfo> {
if (!info.isNewInstance && setup.projectId) {
try {
const dbs = await cloudsql.listDatabases(setup.projectId, info.cloudSqlInstanceId);
Expand Down
19 changes: 12 additions & 7 deletions src/init/features/dataconnect/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export type SDKInfo = {
export async function doSetup(setup: Setup, config: Config): Promise<void> {
const sdkInfo = await askQuestions(setup, config);
await actuate(sdkInfo, setup.projectId);
logSuccess(
`If you'd like to generate additional SDKs later, run ${clc.bold("firebase init dataconnect:sdk")}`,
);
}

async function askQuestions(setup: Setup, config: Config): Promise<SDKInfo> {
Expand All @@ -56,14 +59,9 @@ async function askQuestions(setup: Setup, config: Config): Promise<SDKInfo> {
throw new FirebaseError(
`Your config has no connectors to set up SDKs for. Run ${clc.bold(
"firebase init dataconnect",
)} to set up a service and conenctors.`,
)} to set up a service and connectors.`,
);
}
const connectorInfo: ConnectorInfo = await promptOnce({
message: "Which connector do you want set up a generated SDK for?",
type: "list",
choices: connectorChoices,
});

// First, lets check if we are in a app directory
let targetPlatform: Platform = Platform.UNDETERMINED;
Expand All @@ -79,7 +77,8 @@ async function askQuestions(setup: Setup, config: Config): Promise<SDKInfo> {
logBullet(`Couldn't automatically detect your app directory.`);
appDir = await promptForDirectory({
config,
message: "Where is your app directory?",
message:
"Where is your app directory? Leave blank to set up a generated SDK in your current directory.",
});
const platformGuess = await getPlatformFromFolder(appDir);
if (platformGuess !== Platform.UNDETERMINED) {
Expand All @@ -102,6 +101,12 @@ async function askQuestions(setup: Setup, config: Config): Promise<SDKInfo> {
}
}

const connectorInfo: ConnectorInfo = await promptOnce({
message: "Which connector do you want set up a generated SDK for?",
type: "list",
choices: connectorChoices,
});

const newConnectorYaml = JSON.parse(JSON.stringify(connectorInfo.connectorYaml)) as ConnectorYaml;
if (!newConnectorYaml.generate) {
newConnectorYaml.generate = {};
Expand Down
2 changes: 1 addition & 1 deletion src/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export async function promptForDirectory(args: {
while (!dir) {
const target = args.config.path(
await promptOnce({
message: "Where is your app directory?",
message: args.message,
}),
);
if (fileExistsSync(target)) {
Expand Down
Loading