Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/experiments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@ export const ALL_EXPERIMENTS = experiments({
default: true,
public: false,
},
fdcrealtime: {
shortDescription: "Enable Firebase Data Connect realtime feature.",
default: false,
public: false,
},
});

export type ExperimentName = keyof typeof ALL_EXPERIMENTS;
Expand Down
34 changes: 34 additions & 0 deletions src/init/features/dataconnect/sdk.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import * as auth from "../../../auth";
import * as utils from "../../../utils";
import * as prompt from "../../../prompt";
import * as experiments from "../../../experiments";

const expect = chai.expect;

Expand All @@ -37,7 +38,7 @@
connectorYaml: {
connectorId: "test-connector",
},
connector: {} as any,

Check warning on line 41 in src/init/features/dataconnect/sdk.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type

Check warning on line 41 in src/init/features/dataconnect/sdk.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe assignment of an `any` value
};
connectorYaml = {
connectorId: "test-connector",
Expand All @@ -49,6 +50,10 @@
};
});

afterEach(() => {
sinon.restore();
});

it("should add javascriptSdk for web platform", () => {
addSdkGenerateToConnectorYaml(connectorInfo, connectorYaml, app);
expect(connectorYaml.generate?.javascriptSdk).to.deep.equal([
Expand Down Expand Up @@ -120,6 +125,35 @@
},
]);
});

it("should conditionally inject clientCache if fdcrealtime is enabled", () => {
sinon.stub(experiments, "isEnabled").withArgs("fdcrealtime").returns(true);
addSdkGenerateToConnectorYaml(connectorInfo, connectorYaml, app);
expect((connectorYaml.generate?.javascriptSdk as any)[0].clientCache).to.deep.equal({});

Check warning on line 132 in src/init/features/dataconnect/sdk.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type

Check warning on line 132 in src/init/features/dataconnect/sdk.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access [0] on an `any` value
});

it("should NOT overwrite existing clientCache configuration", () => {
sinon.stub(experiments, "isEnabled").withArgs("fdcrealtime").returns(true);
connectorYaml.generate = {
javascriptSdk: [
{
outputDir: "../app/src/dataconnect-generated",
package: "@dataconnect/generated",
packageJsonDir: "../app",
react: false,
angular: false,
clientCache: {

Check warning on line 145 in src/init/features/dataconnect/sdk.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe assignment of an `any` value
type: "memory",
} as any,

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

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type
},
],
};
addSdkGenerateToConnectorYaml(connectorInfo, connectorYaml, app);
expect(connectorYaml.generate?.javascriptSdk).to.have.lengthOf(1);
expect((connectorYaml.generate?.javascriptSdk as any)[0].clientCache).to.deep.equal({

Check warning on line 153 in src/init/features/dataconnect/sdk.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type

Check warning on line 153 in src/init/features/dataconnect/sdk.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access [0] on an `any` value
type: "memory",
});
});
});

describe("chooseApp", () => {
Expand Down Expand Up @@ -223,7 +257,7 @@

expect(promptStub.callCount).to.equal(1);
const promptCall = promptStub.getCall(0);
const appsPassedToCheckbox = promptCall.args[0].choices.map(

Check warning on line 260 in src/init/features/dataconnect/sdk.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .choices on an `any` value

Check warning on line 260 in src/init/features/dataconnect/sdk.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe assignment of an `any` value
(choice: prompt.Choice<App>) => choice.value,
);
expect(appsPassedToCheckbox).to.have.deep.members(uniqueApps);
Expand Down
44 changes: 31 additions & 13 deletions src/init/features/dataconnect/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import {
DartSDK,
JavascriptSDK,
KotlinSDK,
SwiftSDK,
} from "../../../dataconnect/types";
import * as experiments from "../../../experiments";
import { FirebaseError } from "../../../error";
import { isArray } from "lodash";
import {
Expand Down Expand Up @@ -381,19 +383,23 @@ export function addSdkGenerateToConnectorYaml(

case Platform.WEB: {
const javascriptSdk: JavascriptSDK = {
outputDir: path.relative(connectorDir, path.join(appDir, `src/dataconnect-generated`)),
package: `@dataconnect/generated`,
packageJsonDir: path.relative(connectorDir, appDir),
react: false,
angular: false,
outputDir: path.relative(
connectorDir,
path.join(app.directory, "src/dataconnect-generated"),
),
package: "@dataconnect/generated",
packageJsonDir: path.relative(connectorDir, app.directory),
react: app.frameworks?.includes(Framework.REACT) ?? false,
angular: app.frameworks?.includes(Framework.ANGULAR) ?? false,
};
for (const f of app.frameworks || []) {
javascriptSdk[f] = true;
if (experiments.isEnabled("fdcrealtime")) {
javascriptSdk.clientCache = {};
}
if (!isArray(generate?.javascriptSdk)) {
generate.javascriptSdk = generate.javascriptSdk ? [generate.javascriptSdk] : [];
}
if (!generate.javascriptSdk.some((s) => s.outputDir === javascriptSdk.outputDir)) {
const existing = generate.javascriptSdk.find((s) => s.outputDir === javascriptSdk.outputDir);
if (!existing) {
generate.javascriptSdk.push(javascriptSdk);
}
Comment on lines +395 to 404
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic only adds clientCache to newly created SDK configurations. If a configuration for this outputDir already exists, it is not modified. This means clientCache won't be added if it's missing. Similarly, the refactoring to detect React/Angular frameworks is only applied to new configurations.

Consider updating the logic to modify existing configurations. This would make firebase init dataconnect:sdk more robust and idempotent, ensuring that running it again updates configurations with new defaults or detected frameworks.

      if (!isArray(generate?.javascriptSdk)) {
        generate.javascriptSdk = generate.javascriptSdk ? [generate.javascriptSdk] : [];
      }
      const existing = generate.javascriptSdk.find((s) => s.outputDir === javascriptSdk.outputDir);
      if (existing) {
        if (experiments.isEnabled("fdcrealtime") && !existing.clientCache) {
          existing.clientCache = {};
        }
        existing.react = javascriptSdk.react;
        existing.angular = javascriptSdk.angular;
      } else {
        if (experiments.isEnabled("fdcrealtime")) {
          javascriptSdk.clientCache = {};
        }
        generate.javascriptSdk.push(javascriptSdk);
      }

break;
Expand All @@ -403,39 +409,51 @@ export function addSdkGenerateToConnectorYaml(
outputDir: path.relative(connectorDir, path.join(appDir, `lib/dataconnect_generated`)),
package: "dataconnect_generated/generated.dart",
};
if (experiments.isEnabled("fdcrealtime")) {
dartSdk.clientCache = {};
}
if (!isArray(generate?.dartSdk)) {
generate.dartSdk = generate.dartSdk ? [generate.dartSdk] : [];
}
if (!generate.dartSdk.some((s) => s.outputDir === dartSdk.outputDir)) {
const existing = generate.dartSdk.find((s) => s.outputDir === dartSdk.outputDir);
if (!existing) {
generate.dartSdk.push(dartSdk);
}
Comment on lines +412 to 421
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the web platform case, this logic only adds clientCache to newly created Dart SDK configurations. If a configuration for this outputDir already exists, it is not modified.

To make init more robust, consider updating existing configurations if they are missing the clientCache.

      if (!isArray(generate?.dartSdk)) {
        generate.dartSdk = generate.dartSdk ? [generate.dartSdk] : [];
      }
      const existing = generate.dartSdk.find((s) => s.outputDir === dartSdk.outputDir);
      if (existing) {
        if (experiments.isEnabled("fdcrealtime") && !existing.clientCache) {
          existing.clientCache = {};
        }
      } else {
        if (experiments.isEnabled("fdcrealtime")) {
          dartSdk.clientCache = {};
        }
        generate.dartSdk.push(dartSdk);
      }

break;
}
case Platform.ANDROID: {
const kotlinSdk: KotlinSDK = {
outputDir: path.relative(connectorDir, path.join(appDir, `src/main/kotlin`)),
outputDir: path.relative(connectorDir, path.join(app.directory, "src/main/kotlin")),
package: `com.google.firebase.dataconnect.generated`,
};
if (experiments.isEnabled("fdcrealtime")) {
kotlinSdk.clientCache = {};
}
if (!isArray(generate?.kotlinSdk)) {
generate.kotlinSdk = generate.kotlinSdk ? [generate.kotlinSdk] : [];
}
if (!generate.kotlinSdk.some((s) => s.outputDir === kotlinSdk.outputDir)) {
const existing = generate.kotlinSdk.find((s) => s.outputDir === kotlinSdk.outputDir);
if (!existing) {
generate.kotlinSdk.push(kotlinSdk);
}
Comment on lines +429 to 438
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic only adds clientCache to newly created Kotlin SDK configurations, but doesn't update existing ones.

To improve idempotency, consider updating existing configurations if they are missing the clientCache when the fdcrealtime experiment is enabled.

      if (!isArray(generate?.kotlinSdk)) {
        generate.kotlinSdk = generate.kotlinSdk ? [generate.kotlinSdk] : [];
      }
      const existing = generate.kotlinSdk.find((s) => s.outputDir === kotlinSdk.outputDir);
      if (existing) {
        if (experiments.isEnabled("fdcrealtime") && !existing.clientCache) {
          existing.clientCache = {};
        }
      } else {
        if (experiments.isEnabled("fdcrealtime")) {
          kotlinSdk.clientCache = {};
        }
        generate.kotlinSdk.push(kotlinSdk);
      }

break;
}
case Platform.IOS: {
const swiftSdk = {
const swiftSdk: SwiftSDK = {
outputDir: path.relative(
connectorDir,
path.join(app.directory, `../FirebaseDataConnectGenerated`),
),
package: "DataConnectGenerated",
};
if (experiments.isEnabled("fdcrealtime")) {
swiftSdk.clientCache = {};
}
if (!isArray(generate?.swiftSdk)) {
generate.swiftSdk = generate.swiftSdk ? [generate.swiftSdk] : [];
}
if (!generate.swiftSdk.some((s) => s.outputDir === swiftSdk.outputDir)) {
const existing = generate.swiftSdk.find((s) => s.outputDir === swiftSdk.outputDir);
if (!existing) {
generate.swiftSdk.push(swiftSdk);
}
Comment on lines +449 to 458
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic only adds clientCache to newly created Swift SDK configurations. Existing configurations are not updated.

To make the init command more robust, consider updating existing configurations to include clientCache if it's missing and the experiment is enabled.

      if (!isArray(generate?.swiftSdk)) {
        generate.swiftSdk = generate.swiftSdk ? [generate.swiftSdk] : [];
      }
      const existing = generate.swiftSdk.find((s) => s.outputDir === swiftSdk.outputDir);
      if (existing) {
        if (experiments.isEnabled("fdcrealtime") && !existing.clientCache) {
          existing.clientCache = {};
        }
      } else {
        if (experiments.isEnabled("fdcrealtime")) {
          swiftSdk.clientCache = {};
        }
        generate.swiftSdk.push(swiftSdk);
      }

break;
Expand Down
Loading