[go: nahoru, domu]

Skip to content

Commit

Permalink
init dataconnect:sdk and other SDK onboarding improvements (#7299)
Browse files Browse the repository at this point in the history
* Draft of init dataconnect:sdk

* Polishing

* Recommend command during dataconnect:sdk:generate

* Added changelog

* PR fixes

* Fixing link
  • Loading branch information
joehan committed Jun 26, 2024
1 parent 4947ea9 commit 22bb646
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
- Moved `dataconnect.location` key in `firebase.json` to `dataconnect.yaml`.
- Fixes issue where files were not properly being discovered and deployed to Firebase Hosting (#7363, #7378)
- Added new command `init dataconnect:sdk`, which interactively configures a generated SDK for a Data Connect connector.
40 changes: 36 additions & 4 deletions schema/connector-yaml.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,39 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"definitions": {
"generatable": {
"javascriptSdk": {
"additionalProperties": true,
"type": "object",
"properties": {
"outputDir": {
"type": "string",
"description": "Path to the directory where generated files should be written to."
},
"package": {
"type": "string",
"description": "The package name to use for the generated code."
},
"packageJSONDir": {
"type": "string",
"description": "The directory containining the package.json to install the generated package in."
}
}
},
"kotlinSdk": {
"additionalProperties": true,
"type": "object",
"properties": {
"outputDir": {
"type": "string",
"description": "Path to the directory where generated files should be written to."
},
"package": {
"type": "string",
"description": "The package name to use for the generated code."
}
}
},
"swiftSdk": {
"additionalProperties": true,
"type": "object",
"properties": {
Expand Down Expand Up @@ -30,21 +62,21 @@
"javascriptSdk": {
"type": "array",
"items": {
"$ref": "#/definitions/generatable"
"$ref": "#/definitions/javascriptSdk"
},
"description": "Configuration for a generated Javascript SDK"
},
"kotlinSdk": {
"type": "array",
"items": {
"$ref": "#/definitions/generatable"
"$ref": "#/definitions/kotlinSdk"
},
"description": "Configuration for a generated Kotlin SDK"
},
"swiftSdk": {
"type": "array",
"items": {
"$ref": "#/definitions/generatable"
"$ref": "#/definitions/swiftSdk"
},
"description": "Configuration for a generated Swift SDK"
}
Expand Down
6 changes: 5 additions & 1 deletion src/commands/dataconnect-sdk-generate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as path from "path";
import * as clc from "colorette";

import { Command } from "../command";
import { Options } from "../options";
Expand Down Expand Up @@ -31,7 +32,10 @@ export const command = new Command("dataconnect:sdk:generate")
if (!hasGeneratables) {
logger.warn("No generated SDKs have been declared in connector.yaml files.");
logger.warn(
"See https://firebase.google.com/docs/data-connect/quickstart#configure-sdk-outputs for examples of how to configure generated SDKs.",
`Run ${clc.bold("firebase init dataconnect:sdk")} to configure a generated SDK.`,
);
logger.warn(
`See https://firebase.google.com/docs/data-connect/gp/web-sdk for more details of how to configure generated SDKs.`,
);
return;
}
Expand Down
7 changes: 6 additions & 1 deletion src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ if (isEnabled("genkit")) {

choices.push({
value: "dataconnect",
name: "Data Connect: Set up a Firebase Data Connect service.",
name: "Data Connect: Set up a Firebase Data Connect service",
checked: false,
});
choices.push({
value: "dataconnect:sdk",
name: "Data Connect: Set up a generated SDK for your Firebase Data Connect service",
checked: false,
});

Expand Down
1 change: 1 addition & 0 deletions src/dataconnect/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export async function load(projectId: string, sourceDirectory: string): Promise<
const connectorYaml = await fileUtils.readConnectorYaml(connectorDir);
const connectorGqls = await fileUtils.readGQLFiles(connectorDir);
return {
directory: connectorDir,
connectorYaml,
connector: {
name: `${serviceName}/connectors/${connectorYaml.connectorId}`,
Expand Down
20 changes: 13 additions & 7 deletions src/dataconnect/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,35 +127,41 @@ export interface ConnectorYaml {
}

export interface Generate {
javascriptSdk?: JavascriptSDK[];
swiftSdk?: SwiftSDK[];
kotlinSdk?: KotlinSDK[];
javascriptSdk?: JavascriptSDK;
swiftSdk?: SwiftSDK;
kotlinSdk?: KotlinSDK;
}

export interface JavascriptSDK {
outputDir: string;
package?: string;
packageJSONDir?: string;
}
export interface SwiftSDK {
// Optional for Swift becasue XCode makes you import files.
outputDir?: string;
}
export interface KotlinSDK {
outputDir: string;
package?: string;
}

// Helper types && converters
export interface ServiceInfo {
serviceName: string;
sourceDirectory: string;
schema: Schema;
connectorInfo: {
connector: Connector;
connectorYaml: ConnectorYaml;
}[];
connectorInfo: ConnectorInfo[];
dataConnectYaml: DataConnectYaml;
deploymentMetadata?: DeploymentMetadata;
}

export interface ConnectorInfo {
directory: string;
connector: Connector;
connectorYaml: ConnectorYaml;
}

export function toDatasource(
projectId: string,
locationId: string,
Expand Down
7 changes: 7 additions & 0 deletions src/init/features/dataconnect/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { join } from "path";
import * as clc from "colorette";

import { confirm, promptOnce } from "../../../prompt";
import { Config } from "../../../config";
import { Setup } from "../..";
Expand All @@ -12,6 +14,7 @@ import { DEFAULT_POSTGRES_CONNECTION } from "../emulators";
import { parseCloudSQLInstanceName, parseServiceName } from "../../../dataconnect/names";
import { logger } from "../../../logger";
import { readTemplateSync } from "../../../templates";
import { logSuccess } from "../../../utils";

const DATACONNECT_YAML_TEMPLATE = readTemplateSync("init/dataconnect/dataconnect.yaml");
const CONNECTOR_YAML_TEMPLATE = readTemplateSync("init/dataconnect/connector.yaml");
Expand Down Expand Up @@ -96,6 +99,10 @@ export async function doSetup(setup: Setup, config: Config): Promise<void> {
waitForCreation: false,
});
}
logger.info("");
logSuccess(
`If you'd like to generate an SDK for your new connector, run ${clc.bold("firebase init dataconnect:sdk")}`,
);
}

function subValues(
Expand Down
137 changes: 137 additions & 0 deletions src/init/features/dataconnect/sdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import * as yaml from "yaml";
import * as fs from "fs-extra";
import { confirm, promptOnce } from "../../../prompt";
import * as clc from "colorette";
import * as path from "path";
import { readFirebaseJson } from "../../../dataconnect/fileUtils";
import { Config } from "../../../config";
import { Setup } from "../..";
import { load } from "../../../dataconnect/load";
import { logger } from "../../../logger";
import { ConnectorInfo, ConnectorYaml, JavascriptSDK, KotlinSDK } from "../../../dataconnect/types";
import { DataConnectEmulator } from "../../../emulator/dataconnectEmulator";

const IOS = "ios";
const WEB = "web";
const ANDROID = "android";
export async function doSetup(setup: Setup, config: Config): Promise<void> {
const serviceCfgs = readFirebaseJson(config);
const serviceInfos = await Promise.all(
serviceCfgs.map((c) => load(setup.projectId || "", path.join(process.cwd(), c.source))),
);
const connectorChoices: { name: string; value: ConnectorInfo }[] = serviceInfos
.map((si) => {
return si.connectorInfo.map((ci) => {
return {
name: `${si.dataConnectYaml.serviceId}/${ci.connectorYaml.connectorId}`,
value: ci,
};
});
})
.flat();
if (!connectorChoices.length) {
logger.info(
`Your config has no connectors to set up SDKs for. Run ${clc.bold(
"firebase init dataconnect",
)} to set up a service and conenctors.`,
);
return;
}
const connectorInfo: ConnectorInfo = await promptOnce({
message: "Which connector do you want set up a generated SDK for?",
type: "list",
choices: connectorChoices,
});

const platforms = await promptOnce({
message: "Which platforms do you want to set up a generated SDK for?",
type: "checkbox",
choices: [
{ name: "iOS (Swift)", value: IOS },
{ name: "Web (JavaScript)", value: WEB },
{ name: "Androd (Kotlin)", value: ANDROID },
],
});

const newConnectorYaml = JSON.parse(JSON.stringify(connectorInfo.connectorYaml)) as ConnectorYaml;
if (!newConnectorYaml.generate) {
newConnectorYaml.generate = {};
}

if (platforms.includes(IOS)) {
const defaultOutputDir = newConnectorYaml.generate.swiftSdk?.outputDir;
const outputDir = await promptOnce({
message: `What directory do you want to write your Swift SDK code to? (If not absolute, path will be relative to '${connectorInfo.directory}')`,
type: "input",
default: defaultOutputDir,
});
const swiftSdk = { outputDir };
newConnectorYaml.generate.swiftSdk = swiftSdk;
}
if (platforms.includes(WEB)) {
const outputDir = await promptOnce({
message: `What directory do you want to write your JavaScript SDK code to? (If not absolute, path will be relative to '${connectorInfo.directory}')`,
type: "input",
default: newConnectorYaml.generate.javascriptSdk?.outputDir,
});
const pkg = await promptOnce({
message: "What package name do you want to use for your JavaScript SDK?",
type: "input",
default:
newConnectorYaml.generate.javascriptSdk?.package ??
`@firebasegen/${connectorInfo.connectorYaml.connectorId}`,
});
const packageJSONDir = await promptOnce({
message:
"Which directory contains the package.json that you would like to add the JavaScript SDK dependency to? (Leave blank to skip)",
type: "input",
default: newConnectorYaml.generate.javascriptSdk?.packageJSONDir,
});
// ../.. since we ask relative to connector.yaml
const javascriptSdk: JavascriptSDK = {
outputDir,
package: pkg,
};
if (packageJSONDir) {
javascriptSdk.packageJSONDir = packageJSONDir;
}
newConnectorYaml.generate.javascriptSdk = javascriptSdk;
}
if (platforms.includes(ANDROID)) {
const outputDir = await promptOnce({
message: `What directory do you want to write your Kotlin SDK code to? (If not absolute, path will be relative to '${connectorInfo.directory}')`,
type: "input",
default: newConnectorYaml.generate.kotlinSdk?.outputDir,
});
const pkg = await promptOnce({
message: "What package name do you want to use for your Kotlin SDK?",
type: "input",
default:
newConnectorYaml.generate.kotlinSdk?.package ??
`com.google.firebase.dataconnect.connectors.${connectorInfo.connectorYaml.connectorId}`,
});
const kotlinSdk: KotlinSDK = {
outputDir,
package: pkg,
};
newConnectorYaml.generate.kotlinSdk = kotlinSdk;
}
// TODO: Prompt user about adding generated paths to .gitignore
const connectorYamlContents = yaml.stringify(newConnectorYaml);
const connectorYamlPath = `${connectorInfo.directory}/connector.yaml`;
fs.writeFileSync(connectorYamlPath, connectorYamlContents, "utf8");
logger.info(`Wrote new config to ${connectorYamlPath}`);
if (
setup.projectId &&
(await confirm({
message: "Would you like to generate SDK code now?",
default: true,
}))
) {
await DataConnectEmulator.generate({
configDir: connectorInfo.directory,
connectorId: connectorInfo.connectorYaml.connectorId,
});
logger.info(`Generated SDK code for ${connectorInfo.connectorYaml.connectorId}`);
}
}
1 change: 1 addition & 0 deletions src/init/features/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export { doSetup as project } from "./project";
export { doSetup as remoteconfig } from "./remoteconfig";
export { initGitHub as hostingGithub } from "./hosting/github";
export { doSetup as dataconnect } from "./dataconnect";
export { doSetup as dataconnectSdk } from "./dataconnect/sdk";
export { doSetup as apphosting } from "../../apphosting";
export { doSetup as genkit } from "./genkit";
1 change: 1 addition & 0 deletions src/init/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const featureFns = new Map<string, (setup: any, config: any, options?: any) => P
["database", features.database],
["firestore", features.firestore],
["dataconnect", features.dataconnect],
["dataconnect:sdk", features.dataconnectSdk],
["functions", features.functions],
["hosting", features.hosting],
["storage", features.storage],
Expand Down
3 changes: 3 additions & 0 deletions templates/_gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,6 @@ node_modules/

# dotenv environment variables file
.env

# dataconnect generated files
.dataconnect
11 changes: 11 additions & 0 deletions templates/init/dataconnect/connector.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
connectorId: "__connectorId__"
authMode: "PUBLIC"
## ## Here's an example of how to add generated SDKs.
## ## You'll need to replace the outputDirs with ones pointing to where you want the generated code in your app.
# generate:
# javascriptSdk:
# outputDir: <Path where you want the generated SDK to be written to, relative to this file>
# package: "@firebasegen/my-connector"
# packageJSONDir: < Optional. Path to your Javascript app's package.json>
# swiftSdk:
# outputDir: <Path where you want the generated SDK to be written to, relative to this file>
# kotlinSdk:
# outputDir: <Path where you want the generated SDK to be written to, relative to this file>

0 comments on commit 22bb646

Please sign in to comment.