Skip to content
Closed
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 .changeset/tailordb-noop-type-updates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tailor-platform/sdk": patch
---

Fix deploy re-updating every TailorDB type on each run even when nothing changed
64 changes: 64 additions & 0 deletions packages/sdk/src/cli/commands/deploy/tailordb/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,70 @@ describe("planTailorDB (service level)", () => {
expect(result.changeSet.type.updates).toHaveLength(0);
});

test("treats remote optionalOnCreate=false as unchanged against a local manifest omitting it", async () => {
const tailordbType: TailorDBType = {
name: "Invoice",
pluralForm: "Invoices",
description: "Invoice type",
fields: {
code: {
name: "code",
config: {
type: "string",
required: true,
},
},
},
forwardRelationships: {},
backwardRelationships: {},
settings: {},
permissions: {},
files: {},
};

const tailorDBService = createMockTailorDBService("test-tailordb");
Object.defineProperty(tailorDBService, "types", {
value: { [tailordbType.name]: tailordbType },
});

const client = createRemoteTypeClient("test-tailordb", {
name: "Invoice",
description: "Invoice type",
pluralForm: "invoices",
fields: {
code: {
type: "string",
required: true,
allowedValues: [],
description: "",
validate: [],
array: false,
index: false,
unique: false,
foreignKey: false,
vector: false,
optionalOnCreate: false,
fields: {},
},
},
});

const application = createMockApplication([tailorDBService]);
const ctx: PlanContext = {
client,
workspaceId,
application,
forRemoval: false,
config: mockConfig,
noSchemaCheck: true,
};

const result = await planTailorDB(ctx);

expect(result.changeSet.type.unchanged).toEqual([{ name: "Invoice" }]);
expect(result.changeSet.type.updates).toHaveLength(0);
});

test("updates matching type when forceApplyAll is enabled", async () => {
const tailordbType: TailorDBType = {
name: "Invoice",
Expand Down
9 changes: 9 additions & 0 deletions packages/sdk/src/cli/commands/deploy/tailordb/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1763,6 +1763,15 @@ function normalizeTailorDBCompareValue(
return value;
}

// Platform returns `optionalOnCreate: false` explicitly, while local
// manifests omit the flag entirely. Treat false as unset so they match.
if (typeof value === "boolean") {
if (path.at(-1) === "optionalOnCreate" && value === false) {

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.

This same issue was independently fixed on fix/deploy-drift, now opened as → #1797

Hardcoding individual field names like optionalOnCreate isn't great here, since the trigger is a Platform-side change rather than an SDK-side one, so the same class of bug will keep recurring.

#1797 is still being polished, but it absorbs this generically via proto3's zero-value omission rules, so it might be worth reviewing that one instead.

return undefined;
}
return value;
}

if (typeof value === "number" || typeof value === "bigint" || typeof value === "string") {
if (matchesNumericStringPath(path) && isNumericLikeValue(value)) {
return String(value);
Expand Down
Loading