Skip to content

feat: Wcp.FeatureFlags extension and extension infrastructure consolidation#4950

Merged
adrians5j merged 37 commits intonextfrom
copilot/add-multi-tenancy-condition
Mar 3, 2026
Merged

feat: Wcp.FeatureFlags extension and extension infrastructure consolidation#4950
adrians5j merged 37 commits intonextfrom
copilot/add-multi-tenancy-condition

Conversation

Copy link
Contributor

Copilot AI commented Feb 20, 2026

Adds a Wcp.FeatureFlags extension that lets users override WCP feature flags at the project-config level (webiny.config.tsx), independently of the fetched WCP license. Useful for disabling licensed features in dev/staging environments while keeping them active in production.

Usage

import { Wcp } from "webiny/extensions";

export const Extensions = () => (
  <>
    <Wcp.FeatureFlags
      features={{
        advancedAccessControlLayer: { enabled: false, options: { teams: false } },
        advancedPublishingWorkflow: { enabled: false }
      }}
    />
  </>
);

The features prop mirrors the WCP license structure. Supported keys:

Key Sub-options
multiTenancy enabled
advancedPublishingWorkflow enabled
advancedAccessControlLayer enabled, options.teams, options.privateFiles, options.folderLevelPermissions
auditLogs enabled
recordLocking enabled
fileManager options.threatDetection

Users can only disable features — attempting to enable a feature the WCP license does not permit has no effect (overrides are ANDed with the license value).

Changes

New: Wcp.FeatureFlags extension (packages/project/src/extensions/FeatureFlags.tsx)

  • Defined in packages/project using the render method pattern (same as Cognito.tsx)
  • File named FeatureFlags.tsx (generic — future non-WCP feature flags can be added here)
  • render outputs two build params — <ApiBuildParam paramName="FeatureFlags" value={{ wcp: features }} /> and <AdminBuildParam paramName="FeatureFlags" value={{ wcp: features }} /> — delegating all file-generation to the existing build infrastructure
  • The entire feature flags object is stored under the single "FeatureFlags" build param key as { wcp: { ... } }
  • Zod schema validation mirrors the IFeatureFlags shape from @webiny/feature-flags
  • Exported as export const Wcp = { FeatureFlags: ... } (consistent with EnvVar.ts pattern)
  • Extension registered via Wcp.FeatureFlags.def in packages/project/src/extensions/index.ts

New: IFeatureFlags interface (packages/feature-flags/src/index.ts)

  • Bare-bones package with no @webiny/* dependencies — easy to include in packages/project, packages/api-core, and packages/app-admin
  • IFeatureFlags { wcp?: { ... } } — extensible top-level interface for all feature flag namespaces

New: FeatureFlags feature (packages/api-core)

  • packages/api-core/src/features/wcp/WcpFeatureFlags/FeatureFlagsImpl class (abstraction: FeatureFlagsAbstraction) reads buildParams.get<IFeatureFlags>("FeatureFlags") and maps WCP feature names to their nested path under .wcp; registered as WcpFeatureFlagsFeature
  • WcpContextWithFeatureFlagsDecorator (WcpContextWithFeatureFlagsDecorator.ts) — decorator (open/closed principle) that wraps WcpContext and applies FeatureFlags checks to all canUse* methods via WcpContextWithFeatureFlagsDecoratorImpl; WcpContextImpl is unchanged

Updated: Admin WCP feature checks respect feature flags

  • Wcp.tsx (packages/app-admin) — all CanUse* components read the "FeatureFlags" build param typed as IFeatureFlags via useContainer() and AND the result with the license check

Consolidated: Extension primitives moved to packages/project

  • packages/api-core/src/extensions/BuildParam.tspackages/project/src/extensions/ApiBuildParam.ts
  • packages/api-core/src/extensions/GenericExtension.tspackages/project/src/extensions/ApiExtension.ts
  • packages/app-admin/src/extensions/AdminBuildParam.tspackages/project/src/extensions/AdminBuildParam.ts
  • packages/app-admin/src/extensions/AdminExtension.tspackages/project/src/extensions/AdminExtension.ts
  • No backwards-compatibility shims — originals removed; packages/app-admin/src/extensions/index.ts, packages/api-core/src/extensions/index.ts, and the entire packages/api-core/src/extensions/eventHandlers/ folder deleted — event handler registration is now handled entirely via Api.Extension
  • packages/project-aws/src/security.ts deleted — Security.* event handler namespace removed; use Api.Extension instead
  • packages/project-aws updated to import BuildParam, AdminBuildParam, AdminExtension, and ApiExtension from @webiny/project

Exports

Wcp exported via packages/project/src/exports/extensions.tspackages/project-aws/src/exports/extensions.tspackages/webiny/src/extensions.ts


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits February 20, 2026 10:40
Co-authored-by: adrians5j <5121148+adrians5j@users.noreply.github.com>
…gn export naming

Co-authored-by: adrians5j <5121148+adrians5j@users.noreply.github.com>
Copilot AI changed the title [WIP] Add conditional rendering for tenant management extensions feat: Wcp.Feature component for user-controlled WCP feature overrides Feb 20, 2026
Copilot AI requested a review from adrians5j February 20, 2026 10:49
tags: { runtimeContext: "app-build", appName: "api" },
multiple: true,
paramsSchema,
async build(params, ctx) {
Copy link
Member

Choose a reason for hiding this comment

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

This sux. rather use render method like

packages/cognito/src/Cognito.tsx:14

this easily allows you to reuse existing Api.BuildParam and Admin.BuildParams. @copilot

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Refactored in e76b322. Moved Wcp.Feature to packages/project-aws/src/wcp.tsx and rewrote it using the render method pattern — it now simply renders <Api.BuildParam> and <Admin.BuildParam> with the wcp.feature.{name} param key, delegating all file-generation to the existing build logic. Removed all custom ts-morph/fs code.

….Feature

Co-authored-by: adrians5j <5121148+adrians5j@users.noreply.github.com>
Copy link
Member

@adrians5j adrians5j left a comment

Choose a reason for hiding this comment

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

constructor(private license: ILicense) {}
constructor(
private license: ILicense,
private buildParams?: IBuildParams
Copy link
Member

Choose a reason for hiding this comment

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

? not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in c9cb6faWcpContextImpl is back to its original form with no ? and no BuildParams.


canUsePrivateFiles(): boolean {
return this.license.canUsePrivateFiles();
return this.license.canUsePrivateFiles() && this.isFeatureEnabled("privateFiles");
Copy link
Member

Choose a reason for hiding this comment

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

Instead of directly involving BuildParams here, let's rather do an intermediary abstraction, called maybe something like WcpFeatureSwitcher. Or maybe you make a better name for the abstraction. IDK any other name.

Then, let's use open/closed principle. Instead of modifying, let's rather create a WcpContext decorator, which adds this extra WcpFeatureSwitcher layer of functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in c9cb6fa. Introduced WcpFeatureOverrides abstraction (packages/api-core/src/features/wcp/WcpFeatureOverride/) that reads wcp.feature.{name} build params, and a WcpContextWithFeatureOverrides decorator (WcpContext/decorators/) that wraps WcpContext and applies the overrides to all canUse* methods — without touching WcpContextImpl at all. Registered via WcpFeature.ts.

…hFeatureOverrides decorator

Co-authored-by: adrians5j <5121148+adrians5j@users.noreply.github.com>
@adrians5j
Copy link
Member

adrians5j commented Feb 20, 2026

You know what... I just made up my mind..

Instead of writing multiple

<Wcp.Feature name="teams" enabled={false} />
    <Wcp.Feature name="workflows" enabled={false} />`.. let's rather have smt like
 <Wcp.FeatureFlags features={{ advancedAccessControLayer: { enabled: false, options: { teams: false }} }} />

Basically the same structure of license I mentioned, which then gets merged with this config here.

The most important thing when doing the merge ofc is preventing users from allowng a feature via this comp, and being able to use it, despite real fetched license saying the feat is not allowed. @copilot

Copy link
Contributor Author

Copilot AI commented Feb 20, 2026

You know what... I just made up my mind..

Instead of writing multiple ` <Wcp.Feature name="teams" enabled={false} />
...

The comment appears to have been truncated — the code block didn't come through. Could you share the full suggestion?

@adrians5j
Copy link
Member

pasting comment again

You know what... I just made up my mind..

Instead of writing multiple

<Wcp.Feature name="teams" enabled={false} />
    <Wcp.Feature name="workflows" enabled={false} />`.. let's rather have smt like
 <Wcp.FeatureFlags features={{ advancedAccessControLayer: { enabled: false, options: { teams: false }} }} />

Basically the same structure of license I mentioned, which then gets merged with this config here.

The most important thing when doing the merge ofc is preventing users from allowng a feature via this comp, and being able to use it, despite real fetched license saying the feat is not allowed. @copilot

@copilot

Copy link
Contributor Author

Copilot AI commented Feb 20, 2026

pasting comment again

You know what... I just made up my mind..
...

Still getting a truncated code block — the content after ```tsx isn't coming through. Could you try posting the code example as plain text or in a new comment?

@adrians5j
Copy link
Member

pasting comment again

You know what... I just made up my mind..

Instead of writing multiple

<Wcp.Feature name="teams" enabled={false} />
<Wcp.Feature name="workflows" enabled={false} />`.. let's rather have smt like

<Wcp.FeatureFlags features={{ advancedAccessControLayer: { enabled: false, options: { teams: false }} }} />

Basically the same structure of license I mentioned, which then gets merged with this config here.

The most important thing when doing the merge ofc is preventing users from allowng a feature via this comp, and being able to use it, despite real fetched license saying the feat is not allowed. @copilot

@copilot

@adrians5j
Copy link
Member

update PR body.. I made soem changes @copilot

Copilot AI changed the title feat: Wcp.FeatureFlags component and extension infrastructure consolidation feat: FeatureFlags extension and extension infrastructure consolidation Feb 25, 2026
@adrians5j
Copy link
Member

let's write PR body from scratch @copilot

Copy link
Contributor Author

Copilot AI commented Feb 25, 2026

let's write PR body from scratch @copilot

Done — PR body rewritten from scratch.

Copilot AI changed the title feat: FeatureFlags extension and extension infrastructure consolidation feat: Wcp.FeatureFlags extension and extension infrastructure consolidation Feb 25, 2026
@adrians5j adrians5j marked this pull request as ready for review March 2, 2026 10:58
@adrians5j
Copy link
Member

/e2e

@github-actions
Copy link

github-actions bot commented Mar 2, 2026

Cypress E2E tests have been initiated (for more information, click here). ✨

@adrians5j
Copy link
Member

/e2e

@github-actions
Copy link

github-actions bot commented Mar 2, 2026

Cypress E2E tests have been initiated (for more information, click here). ✨

@adrians5j
Copy link
Member

/e2e

@github-actions
Copy link

github-actions bot commented Mar 2, 2026

Cypress E2E tests have been initiated (for more information, click here). ✨

@adrians5j adrians5j merged commit 63729ba into next Mar 3, 2026
18 checks passed
@adrians5j adrians5j deleted the copilot/add-multi-tenancy-condition branch March 5, 2026 08:21
Copilot AI added a commit to loomery/aibenchmarking-webiny-js-backend-ai-arena that referenced this pull request Mar 6, 2026
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: adrians5j <5121148+adrians5j@users.noreply.github.com>
Co-authored-by: Adrian Smijulj <adrian1358@gmail.com>
Co-authored-by: adrians5j <adrian@webiny.com>
Copilot AI added a commit to loomery/aibenchmarking-webiny-js-backend-ai-arena that referenced this pull request Mar 6, 2026
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: adrians5j <5121148+adrians5j@users.noreply.github.com>
Co-authored-by: Adrian Smijulj <adrian1358@gmail.com>
Co-authored-by: adrians5j <adrian@webiny.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants