Skip to content

Commit b897caa

Browse files
authored
Support downloading FDC secondary schema sources in firebase init (#9476)
* Support webhooks in Data Connect behind an experiment flag. * Update existing logic to specifically refer to the main schema. * Fix build errors. * lint * Support loading schema sources from either `schema` or `schemas` field. * Fix firebase init * Properly deploy secondary schemas as well. * Make `getSchema` take a schema ID, move secondary schema upsert after main schema upsert, and add some TODOs. * Fix listSchemas in `firebase init`. * Fix unit tests. * Actually fix unit tests. * Add secondary schema template. * Set default parameter for getSchema schemaId. * Specify just top-level fields in listSchema call. * Fix VSCode unit tests. * Download and write secondary schema files in `firebase init`. * Fix error in file path and actually write secondary schema files. * Fix template replacement formatting.
1 parent b1c0572 commit b897caa

File tree

3 files changed

+92
-22
lines changed

3 files changed

+92
-22
lines changed

src/init/features/dataconnect/index.ts

Lines changed: 86 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
createService,
1919
upsertSchema,
2020
} from "../../../dataconnect/client";
21-
import { Schema, Service, File, MAIN_SCHEMA_ID, mainSchema } from "../../../dataconnect/types";
21+
import { Schema, Service, File, MAIN_SCHEMA_ID, isMainSchema } from "../../../dataconnect/types";
2222
import { parseCloudSQLInstanceName, parseServiceName } from "../../../dataconnect/names";
2323
import { logger } from "../../../logger";
2424
import { readTemplateSync } from "../../../templates";
@@ -48,6 +48,7 @@ const DATACONNECT_YAML_TEMPLATE = readTemplateSync("init/dataconnect/dataconnect
4848
const DATACONNECT_WEBHOOKS_YAML_TEMPLATE = readTemplateSync(
4949
"init/dataconnect/dataconnect-fdcwebhooks.yaml",
5050
);
51+
const SECONDARY_SCHEMA_YAML_TEMPLATE = readTemplateSync("init/dataconnect/secondary_schema.yaml");
5152
const CONNECTOR_YAML_TEMPLATE = readTemplateSync("init/dataconnect/connector.yaml");
5253
const SCHEMA_TEMPLATE = readTemplateSync("init/dataconnect/schema.gql");
5354
const QUERIES_TEMPLATE = readTemplateSync("init/dataconnect/queries.gql");
@@ -83,6 +84,11 @@ export interface ServiceGQL {
8384
path: string;
8485
files: File[];
8586
}[];
87+
secondarySchemaGqls?: {
88+
id: string;
89+
files: File[];
90+
uri: string;
91+
}[];
8692
seedDataGql?: string;
8793
}
8894

@@ -406,10 +412,13 @@ async function writeFiles(
406412
options: any,
407413
): Promise<void> {
408414
const dir: string = config.get("dataconnect.source") || "dataconnect";
409-
const subbedDataconnectYaml = subDataconnectYamlValues({
410-
...info,
411-
connectorDirs: serviceGql.connectors.map((c) => c.path),
412-
});
415+
const subbedDataconnectYaml = subDataconnectYamlValues(
416+
{
417+
...info,
418+
connectorDirs: serviceGql.connectors.map((c) => c.path),
419+
},
420+
serviceGql.secondarySchemaGqls?.map((sch) => ({ id: sch.id, uri: sch.uri })),
421+
);
413422
config.set("dataconnect", { source: dir });
414423
await config.askWriteProjectFile(
415424
join(dir, "dataconnect.yaml"),
@@ -435,6 +444,17 @@ async function writeFiles(
435444
// Even if the schema is empty, lets give them an empty .gql file to get started.
436445
fs.ensureFileSync(join(dir, "schema", "schema.gql"));
437446
}
447+
if (serviceGql.secondarySchemaGqls?.length) {
448+
for (const sch of serviceGql.secondarySchemaGqls) {
449+
for (const f of sch.files) {
450+
await config.askWriteProjectFile(
451+
join(dir, `schema_${sch.id}`, f.path),
452+
f.content,
453+
!!options.force,
454+
);
455+
}
456+
}
457+
}
438458

439459
for (const c of serviceGql.connectors) {
440460
await writeConnectorFiles(config, c, options);
@@ -468,23 +488,54 @@ async function writeConnectorFiles(
468488
}
469489
}
470490

471-
function subDataconnectYamlValues(replacementValues: {
472-
serviceId: string;
473-
cloudSqlInstanceId: string;
474-
cloudSqlDatabase: string;
475-
connectorDirs: string[];
476-
locationId: string;
477-
}): string {
491+
function subDataconnectYamlValues(
492+
replacementValues: {
493+
serviceId: string;
494+
cloudSqlInstanceId: string;
495+
cloudSqlDatabase: string;
496+
connectorDirs: string[];
497+
locationId: string;
498+
},
499+
secondarySchemas?: {
500+
id: string;
501+
uri: string;
502+
}[],
503+
): string {
478504
const replacements: Record<string, string> = {
479505
serviceId: "__serviceId__",
480506
locationId: "__location__",
481507
cloudSqlDatabase: "__cloudSqlDatabase__",
482508
cloudSqlInstanceId: "__cloudSqlInstanceId__",
483509
connectorDirs: "__connectorDirs__",
510+
secondarySchemaId: "__secondarySchemaId__",
511+
secondarySchemaSource: "__secondarySchemaSource__",
512+
secondarySchemaUri: "__secondarySchemaUri__",
484513
};
485514
let replaced = experiments.isEnabled("fdcwebhooks")
486515
? DATACONNECT_WEBHOOKS_YAML_TEMPLATE
487516
: DATACONNECT_YAML_TEMPLATE;
517+
if (secondarySchemas && secondarySchemas.length > 0) {
518+
let secondaryReplaced = "";
519+
for (const schema of secondarySchemas) {
520+
secondaryReplaced += SECONDARY_SCHEMA_YAML_TEMPLATE;
521+
secondaryReplaced = secondaryReplaced.replace(
522+
replacements.secondarySchemaId,
523+
JSON.stringify(schema.id),
524+
);
525+
secondaryReplaced = secondaryReplaced.replace(
526+
replacements.secondarySchemaSource,
527+
`"./schema_${schema.id}"`,
528+
);
529+
secondaryReplaced = secondaryReplaced.replace(
530+
replacements.secondarySchemaUri,
531+
JSON.stringify(schema.uri),
532+
);
533+
}
534+
replaced = replaced.replace("#__secondarySchemaPlaceholder__\n", secondaryReplaced);
535+
} else {
536+
// If no secondary schemas, remove the secondary schema placeholder.
537+
replaced = replaced.replace("#__secondarySchemaPlaceholder__\n", "");
538+
}
488539
for (const [k, v] of Object.entries(replacementValues)) {
489540
replaced = replaced.replace(replacements[k], JSON.stringify(v));
490541
}
@@ -552,17 +603,30 @@ async function downloadService(info: RequiredInfo, serviceName: string): Promise
552603
},
553604
],
554605
};
555-
const mainSch = mainSchema(schemas);
556-
const primaryDatasource = mainSch.datasources.find((d) => d.postgresql);
557-
if (primaryDatasource?.postgresql?.cloudSql?.instance) {
558-
const instanceName = parseCloudSQLInstanceName(primaryDatasource.postgresql.cloudSql.instance);
559-
info.cloudSqlInstanceId = instanceName.instanceId;
560-
}
561-
// TODO: Update dataconnect.yaml with downloaded secondary schemas as well.
562-
if (mainSch.source.files?.length) {
563-
info.serviceGql.schemaGql = mainSch.source.files;
606+
for (const sch of schemas) {
607+
if (isMainSchema(sch)) {
608+
const primaryDatasource = sch.datasources.find((d) => d.postgresql);
609+
if (primaryDatasource?.postgresql?.cloudSql?.instance) {
610+
const instanceName = parseCloudSQLInstanceName(
611+
primaryDatasource.postgresql.cloudSql.instance,
612+
);
613+
info.cloudSqlInstanceId = instanceName.instanceId;
614+
}
615+
info.cloudSqlDatabase = primaryDatasource?.postgresql?.database ?? "";
616+
if (sch.source.files?.length) {
617+
info.serviceGql.schemaGql = sch.source.files;
618+
}
619+
} else {
620+
if (!info.serviceGql.secondarySchemaGqls) {
621+
info.serviceGql.secondarySchemaGqls = [];
622+
}
623+
info.serviceGql.secondarySchemaGqls.push({
624+
id: sch.name.split("/").pop()!,
625+
files: sch.source.files || [],
626+
uri: sch.datasources[0].httpGraphql?.uri ?? "",
627+
});
628+
}
564629
}
565-
info.cloudSqlDatabase = primaryDatasource?.postgresql?.database ?? "";
566630
const connectors = await listConnectors(serviceName, [
567631
"connectors.name",
568632
"connectors.source.files",

templates/init/dataconnect/dataconnect-fdcwebhooks.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ schemas:
1010
instanceId: __cloudSqlInstanceId__
1111
# schemaValidation: "STRICT" # STRICT mode makes Postgres schema match Data Connect exactly.
1212
# schemaValidation: "COMPATIBLE" # COMPATIBLE mode makes Postgres schema compatible with Data Connect.
13+
#__secondarySchemaPlaceholder__
1314
connectorDirs: __connectorDirs__
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
- id: __secondarySchemaId__
2+
source: __secondarySchemaSource__
3+
datasource:
4+
httpGraphql:
5+
uri: __secondarySchemaUri__

0 commit comments

Comments
 (0)