diff --git a/src/dataconnect/build.spec.ts b/src/dataconnect/build.spec.ts index 94144c4bddf..533f80723a6 100644 --- a/src/dataconnect/build.spec.ts +++ b/src/dataconnect/build.spec.ts @@ -126,6 +126,26 @@ describe("handleBuildErrors", () => { dryRun: false, expectErr: false, }, + { + desc: "Required force evolution error, force=false", + graphqlErr: [ + { message: "inaccessible error", extensions: { warningLevel: "REQUIRE_FORCE" } }, + ], + nonInteractive: false, + force: false, + dryRun: false, + expectErr: true, + }, + { + desc: "Required force evolution error, force=true", + graphqlErr: [ + { message: "inaccessible error", extensions: { warningLevel: "REQUIRE_FORCE" } }, + ], + nonInteractive: false, + force: true, + dryRun: false, + expectErr: false, + }, ]; for (const c of cases) { it(c.desc, async () => { diff --git a/src/dataconnect/build.ts b/src/dataconnect/build.ts index fb7b3055f9c..bd67e0760a0 100644 --- a/src/dataconnect/build.ts +++ b/src/dataconnect/build.ts @@ -41,6 +41,17 @@ export async function handleBuildErrors( `There are errors in your schema and connector files:\n${errors.map(prettify).join("\n")}`, ); } + + const requiredForces = errors.filter((w) => w.extensions?.warningLevel === "REQUIRE_FORCE"); + if (requiredForces.length && !force) { + utils.logLabeledError( + "dataconnect", + `There are changes in your schema or connectors that will result in broken behavior:\n` + + prettifyWithWorkaround(requiredForces), + ); + throw new FirebaseError("Rerun this command with --force to deploy these changes."); + } + const interactiveAcks = errors.filter((w) => w.extensions?.warningLevel === "INTERACTIVE_ACK"); const requiredAcks = errors.filter((w) => w.extensions?.warningLevel === "REQUIRE_ACK"); const choices = [ diff --git a/src/dataconnect/graphqlError.ts b/src/dataconnect/graphqlError.ts index 198029fafc4..5f48a892102 100644 --- a/src/dataconnect/graphqlError.ts +++ b/src/dataconnect/graphqlError.ts @@ -17,6 +17,8 @@ export function prettifyWithWorkaround(errs: GraphqlError[]): string { const table = new Table({ head: ["Issue", "Workaround", "Reason"], style: { head: ["yellow"] }, + colWidths: [50, 50, 50], + wordWrap: true, }); for (const e of errs) { if (!e.extensions?.workarounds?.length) { @@ -25,9 +27,9 @@ export function prettifyWithWorkaround(errs: GraphqlError[]): string { const workarounds = e.extensions.workarounds; for (let i = 0; i < workarounds.length; i++) { if (i === 0) { - table.push([prettify(e), workarounds[i].Description, workarounds[i].Reason]); + table.push([prettify(e), workarounds[i].description, workarounds[i].reason]); } else { - table.push(["", workarounds[i].Description, workarounds[i].Reason]); + table.push(["", workarounds[i].description, workarounds[i].reason]); } } } diff --git a/src/dataconnect/types.ts b/src/dataconnect/types.ts index 798dd497dbf..f4d86171ecf 100644 --- a/src/dataconnect/types.ts +++ b/src/dataconnect/types.ts @@ -71,13 +71,12 @@ export interface Diff { destructive: boolean; } -export type WarningLevel = "INTERACTIVE_ACK" | "REQUIRE_ACK"; +export type WarningLevel = "INTERACTIVE_ACK" | "REQUIRE_ACK" | "REQUIRE_FORCE"; export interface Workaround { - // TODO: Make these lower-case after fixing the emulator, to match the style convention. - Description: string; - Reason: string; - ReplaceWith: string; + description: string; + reason: string; + replaceWith: string; } export interface GraphqlError {