diff --git a/src/ui/src/core/index.ts b/src/ui/src/core/index.ts index 33e406aef..18d20211e 100644 --- a/src/ui/src/core/index.ts +++ b/src/ui/src/core/index.ts @@ -148,8 +148,19 @@ export function generateCore() { const isWriterCloudApp = computed(() => Boolean(writerAppId.value || writerOrgId.value), ); + // ABv1 edit gating is only enforced while an ABv2 deprecation flag is + // active. Without a deprecation flag (e.g. the producer that sends the + // permission headers hasn't shipped, or the org is not in the deprecation + // rollout), editing is always permitted so the block cannot engage + // unintentionally. + const isAbv1EditGatingEnabled = computed( + () => + featureFlags.value.includes("beforeDeprecationCutoffAbv2") || + featureFlags.value.includes("afterDeprecationCutoffAbv2"), + ); const canEditAgent = computed( () => + !isAbv1EditGatingEnabled.value || !isWriterCloudApp.value || isOrganizationAdmin.value || Boolean(writerApplication.value?.canEdit), diff --git a/src/ui/src/tests/canEditAgent.spec.ts b/src/ui/src/tests/canEditAgent.spec.ts new file mode 100644 index 000000000..e44b24bad --- /dev/null +++ b/src/ui/src/tests/canEditAgent.spec.ts @@ -0,0 +1,66 @@ +import { describe, it, expect } from "vitest"; +import { buildMockCore } from "./mocks"; + +describe("canEditAgent gating", () => { + function setup() { + const { core, featureFlags, writerApplication } = buildMockCore(); + return { core, featureFlags, writerApplication }; + } + + it("allows editing outside of Writer cloud", () => { + const { core, featureFlags } = setup(); + featureFlags.value = ["beforeDeprecationCutoffAbv2"]; + expect(core.canEditAgent.value).toBe(true); + }); + + it("does not block editing when no deprecation flag is active", () => { + const { core, featureFlags, writerApplication } = setup(); + writerApplication.value = { + id: "app-1", + organizationId: "1", + isOrganizationAdmin: false, + canEdit: false, + }; + featureFlags.value = []; + expect(core.canEditAgent.value).toBe(true); + }); + + it.each(["beforeDeprecationCutoffAbv2", "afterDeprecationCutoffAbv2"])( + "blocks editing for a non-admin without edit access when %s is active", + (flag) => { + const { core, featureFlags, writerApplication } = setup(); + writerApplication.value = { + id: "app-1", + organizationId: "1", + isOrganizationAdmin: false, + canEdit: false, + }; + featureFlags.value = [flag]; + expect(core.canEditAgent.value).toBe(false); + }, + ); + + it("allows editing for an org admin when gating is active", () => { + const { core, featureFlags, writerApplication } = setup(); + writerApplication.value = { + id: "app-1", + organizationId: "1", + isOrganizationAdmin: true, + canEdit: false, + }; + featureFlags.value = ["beforeDeprecationCutoffAbv2"]; + expect(core.canEditAgent.value).toBe(true); + }); + + it("allows editing when canEdit is granted and gating is active", () => { + const { core, featureFlags, writerApplication } = setup(); + writerApplication.value = { + id: "app-1", + organizationId: "1", + isOrganizationAdmin: false, + canEdit: true, + }; + featureFlags.value = ["afterDeprecationCutoffAbv2"]; + expect(core.canEditAgent.value).toBe(true); + }); +}); diff --git a/src/ui/src/tests/mocks.ts b/src/ui/src/tests/mocks.ts index 1c6ca7bb8..095623b65 100644 --- a/src/ui/src/tests/mocks.ts +++ b/src/ui/src/tests/mocks.ts @@ -78,6 +78,10 @@ export function buildMockCore() { ); core.canEditAgent = computed( () => + !( + featureFlags.value.includes("beforeDeprecationCutoffAbv2") || + featureFlags.value.includes("afterDeprecationCutoffAbv2") + ) || !core.isWriterCloudApp.value || core.isOrganizationAdmin.value || Boolean(writerApplication.value?.canEdit),