mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-24 11:39:31 -05:00
refactor: consolidate unifyFeedback into feedbackDirectories license flag (#8034)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,6 @@ export const CLOUD_STRIPE_FEATURE_LOOKUP_KEYS = {
|
||||
CONTACTS: "contacts",
|
||||
AI_SMART_TOOLS: "ai-smart-tools",
|
||||
AI_DATA_ANALYSIS: "ai-data-analysis",
|
||||
UNIFY_FEEDBACK: "unify-feedback",
|
||||
FEEDBACK_DIRECTORIES: "feedback-directories",
|
||||
DASHBOARDS: "dashboards",
|
||||
} as const;
|
||||
|
||||
@@ -152,7 +152,6 @@ describe("License Core Logic", () => {
|
||||
auditLogs: true,
|
||||
accessControl: true,
|
||||
quotas: true,
|
||||
unifyFeedback: false,
|
||||
feedbackDirectories: false,
|
||||
dashboards: false,
|
||||
};
|
||||
@@ -293,7 +292,6 @@ describe("License Core Logic", () => {
|
||||
auditLogs: false,
|
||||
accessControl: false,
|
||||
quotas: false,
|
||||
unifyFeedback: false,
|
||||
feedbackDirectories: false,
|
||||
dashboards: false,
|
||||
},
|
||||
@@ -318,7 +316,6 @@ describe("License Core Logic", () => {
|
||||
auditLogs: false,
|
||||
accessControl: false,
|
||||
quotas: false,
|
||||
unifyFeedback: false,
|
||||
feedbackDirectories: false,
|
||||
dashboards: false,
|
||||
},
|
||||
@@ -352,7 +349,6 @@ describe("License Core Logic", () => {
|
||||
auditLogs: false,
|
||||
accessControl: false,
|
||||
quotas: false,
|
||||
unifyFeedback: false,
|
||||
feedbackDirectories: false,
|
||||
dashboards: false,
|
||||
};
|
||||
@@ -545,7 +541,6 @@ describe("License Core Logic", () => {
|
||||
auditLogs: true,
|
||||
accessControl: true,
|
||||
quotas: true,
|
||||
unifyFeedback: false,
|
||||
feedbackDirectories: false,
|
||||
dashboards: false,
|
||||
},
|
||||
@@ -613,7 +608,6 @@ describe("License Core Logic", () => {
|
||||
auditLogs: true,
|
||||
accessControl: true,
|
||||
quotas: true,
|
||||
unifyFeedback: false,
|
||||
feedbackDirectories: false,
|
||||
dashboards: false,
|
||||
},
|
||||
@@ -672,7 +666,6 @@ describe("License Core Logic", () => {
|
||||
auditLogs: true,
|
||||
accessControl: true,
|
||||
quotas: true,
|
||||
unifyFeedback: false,
|
||||
feedbackDirectories: false,
|
||||
dashboards: false,
|
||||
},
|
||||
@@ -879,7 +872,6 @@ describe("License Core Logic", () => {
|
||||
auditLogs: false,
|
||||
accessControl: false,
|
||||
quotas: false,
|
||||
unifyFeedback: false,
|
||||
feedbackDirectories: false,
|
||||
dashboards: false,
|
||||
},
|
||||
@@ -1063,7 +1055,6 @@ describe("License Core Logic", () => {
|
||||
auditLogs: true,
|
||||
accessControl: true,
|
||||
quotas: true,
|
||||
unifyFeedback: false,
|
||||
feedbackDirectories: false,
|
||||
dashboards: false,
|
||||
},
|
||||
@@ -1193,7 +1184,6 @@ describe("License Core Logic", () => {
|
||||
auditLogs: true,
|
||||
accessControl: true,
|
||||
quotas: true,
|
||||
unifyFeedback: false,
|
||||
feedbackDirectories: false,
|
||||
dashboards: false,
|
||||
},
|
||||
|
||||
@@ -89,7 +89,6 @@ const LicenseFeaturesSchema = z.object({
|
||||
auditLogs: z.boolean(),
|
||||
accessControl: z.boolean(),
|
||||
quotas: z.boolean(),
|
||||
unifyFeedback: z.boolean().default(false),
|
||||
feedbackDirectories: z.boolean().default(false),
|
||||
dashboards: z.boolean().default(false),
|
||||
});
|
||||
@@ -160,7 +159,6 @@ const DEFAULT_FEATURES: TEnterpriseLicenseFeatures = {
|
||||
auditLogs: false,
|
||||
accessControl: false,
|
||||
quotas: false,
|
||||
unifyFeedback: false,
|
||||
feedbackDirectories: false,
|
||||
dashboards: false,
|
||||
};
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
getIsSpamProtectionEnabled,
|
||||
getIsSsoEnabled,
|
||||
getIsTwoFactorAuthEnabled,
|
||||
getIsUnifyFeedbackEnabled,
|
||||
getOrganizationWorkspacesLimit,
|
||||
getRemoveBrandingPermission,
|
||||
getWhiteLabelPermission,
|
||||
@@ -65,7 +64,6 @@ const defaultFeatures: TEnterpriseLicenseFeatures = {
|
||||
auditLogs: false,
|
||||
accessControl: false,
|
||||
quotas: false,
|
||||
unifyFeedback: false,
|
||||
feedbackDirectories: false,
|
||||
dashboards: false,
|
||||
};
|
||||
@@ -271,20 +269,6 @@ describe("License Utils", () => {
|
||||
expect(dataAnalysis).toBe(false);
|
||||
});
|
||||
|
||||
test("uses cloud unify feedback entitlement", async () => {
|
||||
vi.mocked(constants).IS_FORMBRICKS_CLOUD = true;
|
||||
vi.mocked(hasOrganizationEntitlementWithLicenseGuard).mockResolvedValueOnce(true);
|
||||
|
||||
const result = await getIsUnifyFeedbackEnabled("org_1");
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(hasOrganizationEntitlementWithLicenseGuard).toHaveBeenCalledWith(
|
||||
"org_1",
|
||||
CLOUD_STRIPE_FEATURE_LOOKUP_KEYS.UNIFY_FEEDBACK
|
||||
);
|
||||
expect(getEnterpriseLicense).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("uses cloud feedback record directories entitlement", async () => {
|
||||
vi.mocked(constants).IS_FORMBRICKS_CLOUD = true;
|
||||
vi.mocked(hasOrganizationEntitlementWithLicenseGuard).mockResolvedValueOnce(true);
|
||||
@@ -313,44 +297,39 @@ describe("License Utils", () => {
|
||||
expect(getEnterpriseLicense).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("returns self-hosted unify feedback / FRD / dashboards from license", async () => {
|
||||
test("returns self-hosted FRD / dashboards from license", async () => {
|
||||
vi.mocked(constants).IS_FORMBRICKS_CLOUD = false;
|
||||
vi.mocked(getEnterpriseLicense).mockResolvedValue({
|
||||
...defaultLicense,
|
||||
features: {
|
||||
...defaultFeatures,
|
||||
unifyFeedback: true,
|
||||
feedbackDirectories: true,
|
||||
dashboards: true,
|
||||
},
|
||||
});
|
||||
|
||||
const [unify, frd, dashboards] = await Promise.all([
|
||||
getIsUnifyFeedbackEnabled("org_1"),
|
||||
const [frd, dashboards] = await Promise.all([
|
||||
getIsFeedbackDirectoriesEnabled("org_1"),
|
||||
getIsDashboardsEnabled("org_1"),
|
||||
]);
|
||||
|
||||
expect(unify).toBe(true);
|
||||
expect(frd).toBe(true);
|
||||
expect(dashboards).toBe(true);
|
||||
expect(hasOrganizationEntitlementWithLicenseGuard).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("returns false for self-hosted unify feedback / FRD / dashboards when not enabled", async () => {
|
||||
test("returns false for self-hosted FRD / dashboards when not enabled", async () => {
|
||||
vi.mocked(constants).IS_FORMBRICKS_CLOUD = false;
|
||||
vi.mocked(getEnterpriseLicense).mockResolvedValue({
|
||||
...defaultLicense,
|
||||
features: defaultFeatures,
|
||||
});
|
||||
|
||||
const [unify, frd, dashboards] = await Promise.all([
|
||||
getIsUnifyFeedbackEnabled("org_1"),
|
||||
const [frd, dashboards] = await Promise.all([
|
||||
getIsFeedbackDirectoriesEnabled("org_1"),
|
||||
getIsDashboardsEnabled("org_1"),
|
||||
]);
|
||||
|
||||
expect(unify).toBe(false);
|
||||
expect(frd).toBe(false);
|
||||
expect(dashboards).toBe(false);
|
||||
expect(hasOrganizationEntitlementWithLicenseGuard).not.toHaveBeenCalled();
|
||||
|
||||
@@ -36,7 +36,6 @@ const getCustomPlanFeaturePermission = async (
|
||||
| "contacts"
|
||||
| "aiSmartTools"
|
||||
| "aiDataAnalysis"
|
||||
| "unifyFeedback"
|
||||
| "feedbackDirectories"
|
||||
| "dashboards"
|
||||
>
|
||||
@@ -48,7 +47,6 @@ const getCustomPlanFeaturePermission = async (
|
||||
contacts: CLOUD_STRIPE_FEATURE_LOOKUP_KEYS.CONTACTS,
|
||||
aiSmartTools: CLOUD_STRIPE_FEATURE_LOOKUP_KEYS.AI_SMART_TOOLS,
|
||||
aiDataAnalysis: CLOUD_STRIPE_FEATURE_LOOKUP_KEYS.AI_DATA_ANALYSIS,
|
||||
unifyFeedback: CLOUD_STRIPE_FEATURE_LOOKUP_KEYS.UNIFY_FEEDBACK,
|
||||
feedbackDirectories: CLOUD_STRIPE_FEATURE_LOOKUP_KEYS.FEEDBACK_DIRECTORIES,
|
||||
dashboards: CLOUD_STRIPE_FEATURE_LOOKUP_KEYS.DASHBOARDS,
|
||||
};
|
||||
@@ -164,10 +162,6 @@ export const getAccessControlPermission = async (organizationId: string): Promis
|
||||
return getCustomPlanFeaturePermission(organizationId, "accessControl");
|
||||
};
|
||||
|
||||
export const getIsUnifyFeedbackEnabled = async (organizationId: string): Promise<boolean> => {
|
||||
return getCustomPlanFeaturePermission(organizationId, "unifyFeedback");
|
||||
};
|
||||
|
||||
export const getIsFeedbackDirectoriesEnabled = async (organizationId: string): Promise<boolean> => {
|
||||
return getCustomPlanFeaturePermission(organizationId, "feedbackDirectories");
|
||||
};
|
||||
|
||||
@@ -19,7 +19,6 @@ const ZEnterpriseLicenseFeatures = z.object({
|
||||
auditLogs: z.boolean(),
|
||||
accessControl: z.boolean(),
|
||||
quotas: z.boolean(),
|
||||
unifyFeedback: z.boolean().default(false),
|
||||
feedbackDirectories: z.boolean().default(false),
|
||||
dashboards: z.boolean().default(false),
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-clie
|
||||
import { AuthenticatedActionClientCtx } from "@/lib/utils/action-client/types/context";
|
||||
import { getOrganizationIdFromWorkspaceId } from "@/lib/utils/helper";
|
||||
import { getFeedbackDirectoriesByWorkspaceId } from "@/modules/ee/feedback-directory/lib/feedback-directory";
|
||||
import { getIsUnifyFeedbackEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getIsFeedbackDirectoriesEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import {
|
||||
createFeedbackRecord,
|
||||
deleteFeedbackRecord,
|
||||
@@ -30,8 +30,8 @@ const ensureAccess = async (
|
||||
minPermission: "read" | "readWrite"
|
||||
): Promise<void> => {
|
||||
const organizationId = await getOrganizationIdFromWorkspaceId(workspaceId);
|
||||
const isUnifyFeedbackAllowed = await getIsUnifyFeedbackEnabled(organizationId);
|
||||
if (!isUnifyFeedbackAllowed) {
|
||||
const isFeedbackDirectoriesAllowed = await getIsFeedbackDirectoriesEnabled(organizationId);
|
||||
if (!isFeedbackDirectoriesAllowed) {
|
||||
throw new OperationNotAllowedError("Unify Feedback is not enabled for this organization");
|
||||
}
|
||||
await checkAuthorizationUpdated({
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ENTERPRISE_LICENSE_REQUEST_FORM_URL, IS_FORMBRICKS_CLOUD } from "@/lib/
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { NoFeedbackDirectoryEmptyState } from "@/modules/ee/feedback-directory/components/no-feedback-directory-empty-state";
|
||||
import { getFeedbackDirectoriesByWorkspaceId } from "@/modules/ee/feedback-directory/lib/feedback-directory";
|
||||
import { getIsUnifyFeedbackEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getIsFeedbackDirectoriesEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { UnifyConfigNavigation } from "@/modules/ee/unify-feedback/components/unify-config-navigation";
|
||||
import { listFeedbackRecords } from "@/modules/hub/service";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
@@ -34,8 +34,8 @@ export default async function UnifyFeedbackRecordsPage(
|
||||
return notFound();
|
||||
}
|
||||
|
||||
const isUnifyFeedbackAllowed = await getIsUnifyFeedbackEnabled(organization.id);
|
||||
if (!isUnifyFeedbackAllowed) {
|
||||
const isFeedbackDirectoriesAllowed = await getIsFeedbackDirectoriesEnabled(organization.id);
|
||||
if (!isFeedbackDirectoriesAllowed) {
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("workspace.unify.feedback_records")}>
|
||||
@@ -45,7 +45,7 @@ export default async function UnifyFeedbackRecordsPage(
|
||||
<UpgradePrompt
|
||||
title={t("workspace.unify.upgrade_prompt_title")}
|
||||
description={t("workspace.unify.upgrade_prompt_description")}
|
||||
feature="unify-feedback"
|
||||
feature="feedback-directories"
|
||||
buttons={[
|
||||
{
|
||||
text: IS_FORMBRICKS_CLOUD ? t("common.upgrade_plan") : t("common.request_trial_license"),
|
||||
|
||||
@@ -7,7 +7,7 @@ import { getSurveys } from "@/lib/survey/service";
|
||||
import { authenticatedActionClient } from "@/lib/utils/action-client";
|
||||
import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-client-middleware";
|
||||
import { getOrganizationIdFromWorkspaceId } from "@/lib/utils/helper";
|
||||
import { getIsUnifyFeedbackEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getIsFeedbackDirectoriesEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { transformToUnifySurvey } from "./lib";
|
||||
import { TUnifySurvey } from "./types";
|
||||
|
||||
@@ -19,8 +19,8 @@ export const getSurveysForUnifyAction = authenticatedActionClient
|
||||
.schema(ZGetSurveysForUnifyAction)
|
||||
.action(async ({ ctx, parsedInput }): Promise<TUnifySurvey[]> => {
|
||||
const organizationId = await getOrganizationIdFromWorkspaceId(parsedInput.workspaceId);
|
||||
const isUnifyFeedbackAllowed = await getIsUnifyFeedbackEnabled(organizationId);
|
||||
if (!isUnifyFeedbackAllowed) {
|
||||
const isFeedbackDirectoriesAllowed = await getIsFeedbackDirectoriesEnabled(organizationId);
|
||||
if (!isFeedbackDirectoriesAllowed) {
|
||||
throw new OperationNotAllowedError("Unify Feedback is not enabled for this organization");
|
||||
}
|
||||
await checkAuthorizationUpdated({
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ENTERPRISE_LICENSE_REQUEST_FORM_URL, IS_FORMBRICKS_CLOUD } from "@/lib/
|
||||
import { getSurveys } from "@/lib/survey/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getFeedbackDirectoriesByWorkspaceId } from "@/modules/ee/feedback-directory/lib/feedback-directory";
|
||||
import { getIsUnifyFeedbackEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getIsFeedbackDirectoriesEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
|
||||
import { getWorkspaceAuth } from "@/modules/workspaces/lib/utils";
|
||||
@@ -37,15 +37,15 @@ export const WorkspaceFeedbackSourcesPage = async (
|
||||
return notFound();
|
||||
}
|
||||
|
||||
const isUnifyFeedbackAllowed = await getIsUnifyFeedbackEnabled(organization.id);
|
||||
if (!isUnifyFeedbackAllowed) {
|
||||
const isFeedbackDirectoriesAllowed = await getIsFeedbackDirectoriesEnabled(organization.id);
|
||||
if (!isFeedbackDirectoriesAllowed) {
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<div className="flex items-center justify-center">
|
||||
<UpgradePrompt
|
||||
title={t("workspace.unify.upgrade_prompt_title")}
|
||||
description={t("workspace.unify.upgrade_prompt_description")}
|
||||
feature="unify-feedback"
|
||||
feature="feedback-directories"
|
||||
buttons={[
|
||||
{
|
||||
text: IS_FORMBRICKS_CLOUD ? t("common.upgrade_plan") : t("common.request_trial_license"),
|
||||
|
||||
@@ -8,7 +8,7 @@ import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-clie
|
||||
import { AuthenticatedActionClientCtx } from "@/lib/utils/action-client/types/context";
|
||||
import { getOrganizationIdFromWorkspaceId } from "@/lib/utils/helper";
|
||||
import { getFeedbackDirectoriesByWorkspaceId } from "@/modules/ee/feedback-directory/lib/feedback-directory";
|
||||
import { getIsUnifyFeedbackEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getIsFeedbackDirectoriesEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { semanticSearchFeedbackRecords } from "@/modules/hub/service";
|
||||
import type { SemanticSearchResultItem } from "@/modules/hub/types";
|
||||
|
||||
@@ -35,8 +35,8 @@ export type TTopicsPreviewSearchActionResult = {
|
||||
|
||||
const ensureReadAccess = async (userId: string, workspaceId: string): Promise<void> => {
|
||||
const organizationId = await getOrganizationIdFromWorkspaceId(workspaceId);
|
||||
const isUnifyFeedbackAllowed = await getIsUnifyFeedbackEnabled(organizationId);
|
||||
if (!isUnifyFeedbackAllowed) {
|
||||
const isFeedbackDirectoriesAllowed = await getIsFeedbackDirectoriesEnabled(organizationId);
|
||||
if (!isFeedbackDirectoriesAllowed) {
|
||||
throw new OperationNotAllowedError("Unify Feedback is not enabled for this organization");
|
||||
}
|
||||
await checkAuthorizationUpdated({
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ENTERPRISE_LICENSE_REQUEST_FORM_URL, IS_FORMBRICKS_CLOUD } from "@/lib/
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { NoFeedbackDirectoryEmptyState } from "@/modules/ee/feedback-directory/components/no-feedback-directory-empty-state";
|
||||
import { getFeedbackDirectoriesByWorkspaceId } from "@/modules/ee/feedback-directory/lib/feedback-directory";
|
||||
import { getIsUnifyFeedbackEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getIsFeedbackDirectoriesEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { UnifyConfigNavigation } from "@/modules/ee/unify-feedback/components/unify-config-navigation";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
@@ -29,8 +29,8 @@ export const UnifyTopicsSubtopicsPage = async (
|
||||
return notFound();
|
||||
}
|
||||
|
||||
const isUnifyFeedbackAllowed = await getIsUnifyFeedbackEnabled(organization.id);
|
||||
if (!isUnifyFeedbackAllowed) {
|
||||
const isFeedbackDirectoriesAllowed = await getIsFeedbackDirectoriesEnabled(organization.id);
|
||||
if (!isFeedbackDirectoriesAllowed) {
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("workspace.unify.feedback_records")}>
|
||||
@@ -40,7 +40,7 @@ export const UnifyTopicsSubtopicsPage = async (
|
||||
<UpgradePrompt
|
||||
title={t("workspace.unify.upgrade_prompt_title")}
|
||||
description={t("workspace.unify.upgrade_prompt_description")}
|
||||
feature="unify-feedback"
|
||||
feature="feedback-directories"
|
||||
buttons={[
|
||||
{
|
||||
text: IS_FORMBRICKS_CLOUD ? t("common.upgrade_plan") : t("common.request_trial_license"),
|
||||
|
||||
@@ -12,7 +12,6 @@ const LICENSE_GUARDED_ENTITLEMENTS: Partial<Record<string, keyof TEnterpriseLice
|
||||
contacts: "contacts",
|
||||
"ai-smart-tools": "aiSmartTools",
|
||||
"ai-data-analysis": "aiDataAnalysis",
|
||||
"unify-feedback": "unifyFeedback",
|
||||
"feedback-directories": "feedbackDirectories",
|
||||
dashboards: "dashboards",
|
||||
};
|
||||
|
||||
@@ -142,21 +142,6 @@ describe("getSelfHostedOrganizationEntitlementsContext", () => {
|
||||
expect(result.features).toContain("ai-data-analysis");
|
||||
});
|
||||
|
||||
test("maps unifyFeedback feature to unify-feedback entitlement", async () => {
|
||||
mockGetOrg.mockResolvedValue({ id: "org1" } as any);
|
||||
mockGetLicense.mockResolvedValue({
|
||||
status: "active",
|
||||
active: true,
|
||||
features: { unifyFeedback: true },
|
||||
} as any);
|
||||
|
||||
const result = await getSelfHostedOrganizationEntitlementsContext("org1");
|
||||
|
||||
expect(result.features).toContain("unify-feedback");
|
||||
expect(result.features).not.toContain("feedback-directories");
|
||||
expect(result.features).not.toContain("dashboards");
|
||||
});
|
||||
|
||||
test("maps feedbackDirectories feature to feedback-directories entitlement", async () => {
|
||||
mockGetOrg.mockResolvedValue({ id: "org1" } as any);
|
||||
mockGetLicense.mockResolvedValue({
|
||||
@@ -168,7 +153,6 @@ describe("getSelfHostedOrganizationEntitlementsContext", () => {
|
||||
const result = await getSelfHostedOrganizationEntitlementsContext("org1");
|
||||
|
||||
expect(result.features).toContain("feedback-directories");
|
||||
expect(result.features).not.toContain("unify-feedback");
|
||||
expect(result.features).not.toContain("dashboards");
|
||||
});
|
||||
|
||||
@@ -183,21 +167,19 @@ describe("getSelfHostedOrganizationEntitlementsContext", () => {
|
||||
const result = await getSelfHostedOrganizationEntitlementsContext("org1");
|
||||
|
||||
expect(result.features).toContain("dashboards");
|
||||
expect(result.features).not.toContain("unify-feedback");
|
||||
expect(result.features).not.toContain("feedback-directories");
|
||||
});
|
||||
|
||||
test("maps all three Hub features when all enabled", async () => {
|
||||
test("maps both Hub features when all enabled", async () => {
|
||||
mockGetOrg.mockResolvedValue({ id: "org1" } as any);
|
||||
mockGetLicense.mockResolvedValue({
|
||||
status: "active",
|
||||
active: true,
|
||||
features: { unifyFeedback: true, feedbackDirectories: true, dashboards: true },
|
||||
features: { feedbackDirectories: true, dashboards: true },
|
||||
} as any);
|
||||
|
||||
const result = await getSelfHostedOrganizationEntitlementsContext("org1");
|
||||
|
||||
expect(result.features).toContain("unify-feedback");
|
||||
expect(result.features).toContain("feedback-directories");
|
||||
expect(result.features).toContain("dashboards");
|
||||
});
|
||||
@@ -207,7 +189,7 @@ describe("getSelfHostedOrganizationEntitlementsContext", () => {
|
||||
mockGetLicense.mockResolvedValue({
|
||||
status: "expired",
|
||||
active: false,
|
||||
features: { unifyFeedback: true, feedbackDirectories: true, dashboards: true },
|
||||
features: { feedbackDirectories: true, dashboards: true },
|
||||
} as any);
|
||||
|
||||
const result = await getSelfHostedOrganizationEntitlementsContext("org1");
|
||||
|
||||
@@ -33,9 +33,6 @@ const mapLicenseFeaturesToEntitlements = (
|
||||
if (features.aiDataAnalysis) {
|
||||
entitlementKeys.push(CLOUD_STRIPE_FEATURE_LOOKUP_KEYS.AI_DATA_ANALYSIS);
|
||||
}
|
||||
if (features.unifyFeedback) {
|
||||
entitlementKeys.push(CLOUD_STRIPE_FEATURE_LOOKUP_KEYS.UNIFY_FEEDBACK);
|
||||
}
|
||||
if (features.feedbackDirectories) {
|
||||
entitlementKeys.push(CLOUD_STRIPE_FEATURE_LOOKUP_KEYS.FEEDBACK_DIRECTORIES);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const {
|
||||
mockGetFeedbackRecordTenant,
|
||||
mockCheckAuthorizationUpdated,
|
||||
mockUserFindUnique,
|
||||
mockGetIsUnifyFeedbackEnabled,
|
||||
mockGetIsFeedbackDirectoriesEnabled,
|
||||
} = vi.hoisted(() => ({
|
||||
mockAuthenticateApiKeyFromHeaders: vi.fn(),
|
||||
mockGetApiKeyFromHeaders: vi.fn(),
|
||||
@@ -23,7 +23,7 @@ const {
|
||||
mockGetFeedbackRecordTenant: vi.fn(),
|
||||
mockCheckAuthorizationUpdated: vi.fn(),
|
||||
mockUserFindUnique: vi.fn(),
|
||||
mockGetIsUnifyFeedbackEnabled: vi.fn(),
|
||||
mockGetIsFeedbackDirectoriesEnabled: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/api/lib/api-key-auth", () => ({
|
||||
@@ -57,7 +57,7 @@ vi.mock("@/modules/ee/feedback-directory/lib/feedback-directory", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/ee/license-check/lib/utils", () => ({
|
||||
getIsUnifyFeedbackEnabled: mockGetIsUnifyFeedbackEnabled,
|
||||
getIsFeedbackDirectoriesEnabled: mockGetIsFeedbackDirectoriesEnabled,
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/hub/service", () => ({
|
||||
@@ -118,7 +118,7 @@ describe("authorizeEnvoyRequest", () => {
|
||||
});
|
||||
mockCheckAuthorizationUpdated.mockResolvedValue(true);
|
||||
mockUserFindUnique.mockResolvedValue({ id: "user_1", isActive: true });
|
||||
mockGetIsUnifyFeedbackEnabled.mockResolvedValue(true);
|
||||
mockGetIsFeedbackDirectoriesEnabled.mockResolvedValue(true);
|
||||
});
|
||||
|
||||
test("allows create requests with an API key and body tenant_id", async () => {
|
||||
@@ -457,7 +457,7 @@ describe("authorizeEnvoyRequest", () => {
|
||||
});
|
||||
|
||||
test("returns 403 when unify feedback entitlement is disabled", async () => {
|
||||
mockGetIsUnifyFeedbackEnabled.mockResolvedValue(false);
|
||||
mockGetIsFeedbackDirectoriesEnabled.mockResolvedValue(false);
|
||||
mockGetApiKeyFromHeaders.mockReturnValue("fbk_test");
|
||||
mockAuthenticateApiKeyFromHeaders.mockResolvedValue({
|
||||
type: "apiKey",
|
||||
@@ -476,7 +476,7 @@ describe("authorizeEnvoyRequest", () => {
|
||||
);
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
expect(mockGetIsUnifyFeedbackEnabled).toHaveBeenCalledWith("org_1");
|
||||
expect(mockGetIsFeedbackDirectoriesEnabled).toHaveBeenCalledWith("org_1");
|
||||
});
|
||||
|
||||
test("returns 403 for archived directories", async () => {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { verifyFeedbackRecordsGatewayToken } from "@/lib/jwt";
|
||||
import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-client-middleware";
|
||||
import { getBearerTokenFromHeaders } from "@/modules/api/lib/api-key-auth";
|
||||
import { getFeedbackDirectoryAuthContext } from "@/modules/ee/feedback-directory/lib/feedback-directory";
|
||||
import { getIsUnifyFeedbackEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getIsFeedbackDirectoriesEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import {
|
||||
TGatewayAuthenticatedPrincipal,
|
||||
TGatewayRequestAuthorizer,
|
||||
@@ -264,8 +264,10 @@ const authorizeFeedbackRecordsGatewayRequest = async (
|
||||
return { allowed: false };
|
||||
}
|
||||
|
||||
const isUnifyFeedbackAllowed = await getIsUnifyFeedbackEnabled(feedbackDirectory.organizationId);
|
||||
if (!isUnifyFeedbackAllowed) {
|
||||
const isFeedbackDirectoriesAllowed = await getIsFeedbackDirectoriesEnabled(
|
||||
feedbackDirectory.organizationId
|
||||
);
|
||||
if (!isFeedbackDirectoriesAllowed) {
|
||||
return { allowed: false };
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ const {
|
||||
mockGetFeedbackRecordTenant,
|
||||
mockCheckAuthorizationUpdated,
|
||||
mockUserFindUnique,
|
||||
mockGetIsUnifyFeedbackEnabled,
|
||||
mockGetIsFeedbackDirectoriesEnabled,
|
||||
} = vi.hoisted(() => ({
|
||||
mockAuthenticateApiKeyFromHeaders: vi.fn(),
|
||||
mockGetApiKeyFromHeaders: vi.fn(),
|
||||
@@ -23,7 +23,7 @@ const {
|
||||
mockGetFeedbackRecordTenant: vi.fn(),
|
||||
mockCheckAuthorizationUpdated: vi.fn(),
|
||||
mockUserFindUnique: vi.fn(),
|
||||
mockGetIsUnifyFeedbackEnabled: vi.fn(),
|
||||
mockGetIsFeedbackDirectoriesEnabled: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/api/lib/api-key-auth", () => ({
|
||||
@@ -57,7 +57,7 @@ vi.mock("@/modules/ee/feedback-directory/lib/feedback-directory", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/ee/license-check/lib/utils", () => ({
|
||||
getIsUnifyFeedbackEnabled: mockGetIsUnifyFeedbackEnabled,
|
||||
getIsFeedbackDirectoriesEnabled: mockGetIsFeedbackDirectoriesEnabled,
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/hub/service", () => ({
|
||||
@@ -127,7 +127,7 @@ describe("authorizeTraefikRequest", () => {
|
||||
});
|
||||
mockCheckAuthorizationUpdated.mockResolvedValue(true);
|
||||
mockUserFindUnique.mockResolvedValue({ id: "user_1", isActive: true });
|
||||
mockGetIsUnifyFeedbackEnabled.mockResolvedValue(true);
|
||||
mockGetIsFeedbackDirectoriesEnabled.mockResolvedValue(true);
|
||||
});
|
||||
|
||||
test("allows requests using Traefik forwarded method and URI metadata", async () => {
|
||||
|
||||
Reference in New Issue
Block a user