Skip to content

Commit 42aa664

Browse files
Merge pull request #287 from SheetMetalConnect/claude/review-pr-feature-flags-onp5z
Review merged PR and add feature flag migrations
2 parents 75b4598 + 86d9ea6 commit 42aa664

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

src/integrations/supabase/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3025,6 +3025,7 @@ export type Database = {
30253025
demo_data_seeded_by: string | null
30263026
demo_mode_acknowledged: boolean | null
30273027
demo_mode_enabled: boolean | null
3028+
external_feature_flags_config: Json | null
30283029
factory_closing_time: string | null
30293030
factory_opening_time: string | null
30303031
feature_flags: Json | null
@@ -3051,6 +3052,7 @@ export type Database = {
30513052
trial_end: string | null
30523053
trial_ends_at: string | null
30533054
updated_at: string | null
3055+
use_external_feature_flags: boolean | null
30543056
vat_number: string | null
30553057
whitelabel_app_name: string | null
30563058
whitelabel_enabled: boolean | null
@@ -3076,6 +3078,7 @@ export type Database = {
30763078
demo_data_seeded_by?: string | null
30773079
demo_mode_acknowledged?: boolean | null
30783080
demo_mode_enabled?: boolean | null
3081+
external_feature_flags_config?: Json | null
30793082
factory_closing_time?: string | null
30803083
factory_opening_time?: string | null
30813084
feature_flags?: Json | null
@@ -3102,6 +3105,7 @@ export type Database = {
31023105
trial_end?: string | null
31033106
trial_ends_at?: string | null
31043107
updated_at?: string | null
3108+
use_external_feature_flags?: boolean | null
31053109
vat_number?: string | null
31063110
whitelabel_app_name?: string | null
31073111
whitelabel_enabled?: boolean | null
@@ -3127,6 +3131,7 @@ export type Database = {
31273131
demo_data_seeded_by?: string | null
31283132
demo_mode_acknowledged?: boolean | null
31293133
demo_mode_enabled?: boolean | null
3134+
external_feature_flags_config?: Json | null
31303135
factory_closing_time?: string | null
31313136
factory_opening_time?: string | null
31323137
feature_flags?: Json | null
@@ -3153,6 +3158,7 @@ export type Database = {
31533158
trial_end?: string | null
31543159
trial_ends_at?: string | null
31553160
updated_at?: string | null
3161+
use_external_feature_flags?: boolean | null
31563162
vat_number?: string | null
31573163
whitelabel_app_name?: string | null
31583164
whitelabel_enabled?: boolean | null
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
-- Add feature flags to tenants table
2+
-- Allows tenants to enable/disable specific modules and features
3+
-- By default, uses internal database storage (not external service)
4+
5+
-- Add feature_flags column to store per-tenant feature configuration
6+
ALTER TABLE public.tenants
7+
ADD COLUMN IF NOT EXISTS feature_flags JSONB DEFAULT '{
8+
"analytics": true,
9+
"monitoring": true,
10+
"shipping": true,
11+
"operatorViews": true,
12+
"integrations": true,
13+
"issues": true,
14+
"capacity": true,
15+
"assignments": true
16+
}'::jsonb;
17+
18+
-- Add column to optionally use external feature flag service
19+
-- Default is FALSE = use internal database-based feature flags
20+
ALTER TABLE public.tenants
21+
ADD COLUMN IF NOT EXISTS use_external_feature_flags BOOLEAN DEFAULT FALSE;
22+
23+
-- Add external service configuration (only used when use_external_feature_flags = TRUE)
24+
ALTER TABLE public.tenants
25+
ADD COLUMN IF NOT EXISTS external_feature_flags_config JSONB DEFAULT NULL;
26+
27+
-- Add comments for documentation
28+
COMMENT ON COLUMN public.tenants.feature_flags IS 'Per-tenant feature flags stored as JSONB. Controls which modules are visible/enabled.';
29+
COMMENT ON COLUMN public.tenants.use_external_feature_flags IS 'Whether to use external feature flag service (e.g., LaunchDarkly, PostHog). Default FALSE = use internal flags.';
30+
COMMENT ON COLUMN public.tenants.external_feature_flags_config IS 'Configuration for external feature flag service (API key, project ID, etc.). Only used when use_external_feature_flags = TRUE.';
31+
32+
-- Create a function to get feature flags for a tenant
33+
-- This abstracts away whether internal or external flags are used
34+
CREATE OR REPLACE FUNCTION public.get_tenant_feature_flags(p_tenant_id UUID DEFAULT NULL)
35+
RETURNS JSONB
36+
LANGUAGE plpgsql
37+
SECURITY DEFINER
38+
SET search_path = public
39+
AS $$
40+
DECLARE
41+
v_tenant_id UUID;
42+
v_flags JSONB;
43+
v_use_external BOOLEAN;
44+
v_default_flags JSONB := '{
45+
"analytics": true,
46+
"monitoring": true,
47+
"shipping": true,
48+
"operatorViews": true,
49+
"integrations": true,
50+
"issues": true,
51+
"capacity": true,
52+
"assignments": true
53+
}'::jsonb;
54+
BEGIN
55+
-- Use provided tenant_id or get from current user
56+
IF p_tenant_id IS NOT NULL THEN
57+
v_tenant_id := p_tenant_id;
58+
ELSE
59+
SELECT tenant_id INTO v_tenant_id
60+
FROM profiles
61+
WHERE id = auth.uid();
62+
63+
-- Check for active tenant override (for root admins)
64+
IF EXISTS (
65+
SELECT 1 FROM profiles
66+
WHERE id = auth.uid()
67+
AND is_root_admin = true
68+
AND active_tenant_id IS NOT NULL
69+
) THEN
70+
SELECT active_tenant_id INTO v_tenant_id
71+
FROM profiles
72+
WHERE id = auth.uid();
73+
END IF;
74+
END IF;
75+
76+
-- Get tenant's feature flag settings
77+
SELECT
78+
COALESCE(feature_flags, v_default_flags),
79+
COALESCE(use_external_feature_flags, false)
80+
INTO v_flags, v_use_external
81+
FROM tenants
82+
WHERE id = v_tenant_id;
83+
84+
-- If using external service, return empty object (frontend will fetch from external)
85+
-- This is a placeholder - external service integration would go here
86+
IF v_use_external THEN
87+
-- For now, still return internal flags as fallback
88+
-- In production, this could call external service or return special marker
89+
RETURN COALESCE(v_flags, v_default_flags);
90+
END IF;
91+
92+
-- Return internal flags merged with defaults
93+
RETURN v_default_flags || COALESCE(v_flags, '{}'::jsonb);
94+
END;
95+
$$;
96+
97+
COMMENT ON FUNCTION public.get_tenant_feature_flags IS 'Returns feature flags for a tenant, with defaults applied. Handles internal vs external flag storage.';
98+
99+
-- Create a function to update feature flags
100+
CREATE OR REPLACE FUNCTION public.update_tenant_feature_flags(
101+
p_tenant_id UUID,
102+
p_flags JSONB
103+
)
104+
RETURNS JSONB
105+
LANGUAGE plpgsql
106+
SECURITY DEFINER
107+
SET search_path = public
108+
AS $$
109+
DECLARE
110+
v_existing_flags JSONB;
111+
v_merged_flags JSONB;
112+
BEGIN
113+
-- Verify user has access to this tenant (RLS will handle this, but double-check)
114+
IF NOT EXISTS (
115+
SELECT 1 FROM tenants WHERE id = p_tenant_id
116+
) THEN
117+
RAISE EXCEPTION 'Tenant not found';
118+
END IF;
119+
120+
-- Get existing flags
121+
SELECT COALESCE(feature_flags, '{}'::jsonb)
122+
INTO v_existing_flags
123+
FROM tenants
124+
WHERE id = p_tenant_id;
125+
126+
-- Merge new flags with existing
127+
v_merged_flags := v_existing_flags || p_flags;
128+
129+
-- Update the tenant
130+
UPDATE tenants
131+
SET
132+
feature_flags = v_merged_flags,
133+
updated_at = now()
134+
WHERE id = p_tenant_id;
135+
136+
RETURN v_merged_flags;
137+
END;
138+
$$;
139+
140+
COMMENT ON FUNCTION public.update_tenant_feature_flags IS 'Updates feature flags for a tenant, merging with existing flags.';
141+
142+
-- Ensure existing tenants have default feature flags
143+
UPDATE public.tenants
144+
SET feature_flags = '{
145+
"analytics": true,
146+
"monitoring": true,
147+
"shipping": true,
148+
"operatorViews": true,
149+
"integrations": true,
150+
"issues": true,
151+
"capacity": true,
152+
"assignments": true
153+
}'::jsonb
154+
WHERE feature_flags IS NULL;

0 commit comments

Comments
 (0)