163 lines
4.0 KiB
TypeScript
163 lines
4.0 KiB
TypeScript
import { db } from "~/utils/db.server";
|
|
import { FeatureFlagsFilterType } from "../dtos/FeatureFlagsFilterTypes";
|
|
import { AnalyticsEvent, FeatureFlag, FeatureFlagFilter, Prisma } from "@toolctrl/db/prisma-browser";
|
|
import { clearCacheKeyPrefix } from "~/utils/cache.server";
|
|
|
|
export type FeatureFlagWithDetails = FeatureFlag & {
|
|
filters: FeatureFlagFilter[];
|
|
};
|
|
|
|
export type FeatureFlagWithEvents = FeatureFlagWithDetails & {
|
|
events: AnalyticsEvent[];
|
|
};
|
|
|
|
export type FeatureFlagScopeLookup = {
|
|
enabled?: boolean | undefined;
|
|
forcedFlags?: string[];
|
|
tenantId?: string | null;
|
|
userId?: string | null;
|
|
scope?: string | null;
|
|
};
|
|
|
|
export async function getFeatureFlag(where: { name?: string; id?: string; description?: string; enabled?: boolean | undefined }) {
|
|
return db.featureFlag.findFirst({
|
|
where,
|
|
include: {
|
|
filters: true,
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function getAllFeatureFlags(): Promise<FeatureFlagWithDetails[]> {
|
|
return db.featureFlag.findMany({
|
|
include: {
|
|
filters: true,
|
|
},
|
|
orderBy: {
|
|
createdAt: "desc",
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function getFeatureFlags(where: FeatureFlagScopeLookup): Promise<FeatureFlagWithDetails[]> {
|
|
const forcedFlags = where.forcedFlags ?? [];
|
|
const scopedOverrides: Prisma.FeatureFlagWhereInput[] = [];
|
|
scopedOverrides.push({ scope: "global", tenantId: null, userId: null });
|
|
if (where.tenantId) scopedOverrides.push({ scope: "tenant", tenantId: where.tenantId, userId: null });
|
|
if (where.userId) scopedOverrides.push({ scope: "user", tenantId: null, userId: where.userId });
|
|
if (where.tenantId && where.userId) scopedOverrides.push({ scope: "user", tenantId: where.tenantId, userId: where.userId });
|
|
|
|
return db.featureFlag.findMany({
|
|
where: {
|
|
AND: [
|
|
{
|
|
OR: [{ enabled: where.enabled }, { name: { in: forcedFlags } }, ...scopedOverrides],
|
|
},
|
|
{ OR: scopedOverrides },
|
|
],
|
|
},
|
|
include: {
|
|
filters: true,
|
|
},
|
|
orderBy: {
|
|
createdAt: "desc",
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function getFeatureFlagsWithEvents(where: { enabled: boolean | undefined }): Promise<FeatureFlagWithEvents[]> {
|
|
return db.featureFlag.findMany({
|
|
where: {
|
|
enabled: where.enabled,
|
|
},
|
|
include: {
|
|
filters: true,
|
|
events: true,
|
|
},
|
|
orderBy: {
|
|
createdAt: "desc",
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function createFeatureFlag(data: {
|
|
name: string;
|
|
description: string;
|
|
enabled: boolean;
|
|
filters: {
|
|
type: FeatureFlagsFilterType;
|
|
value: string | null;
|
|
action?: string | null;
|
|
}[];
|
|
}) {
|
|
return await db.featureFlag
|
|
.create({
|
|
data: {
|
|
name: data.name,
|
|
description: data.description,
|
|
enabled: data.enabled,
|
|
filters: {
|
|
create: data.filters.map((f) => ({
|
|
type: f.type.toString(),
|
|
value: f.value ?? null,
|
|
action: f.action ?? null,
|
|
})),
|
|
},
|
|
},
|
|
})
|
|
.then((item) => {
|
|
clearCacheKeyPrefix(`featureFlags:enabled`);
|
|
return item;
|
|
});
|
|
}
|
|
|
|
export async function updateFeatureFlag(
|
|
id: string,
|
|
{
|
|
name,
|
|
description,
|
|
filters,
|
|
enabled,
|
|
}: {
|
|
name?: string;
|
|
description?: string;
|
|
filters?: { type: FeatureFlagsFilterType; value: string | null; action?: string | null }[];
|
|
enabled?: boolean;
|
|
}
|
|
) {
|
|
return await db.featureFlag
|
|
.update({
|
|
where: { id },
|
|
data: {
|
|
name,
|
|
description,
|
|
enabled,
|
|
filters: !filters
|
|
? undefined
|
|
: {
|
|
deleteMany: {},
|
|
create: filters.map((f) => ({
|
|
type: f.type.toString(),
|
|
value: f.value ?? null,
|
|
action: f.action ?? null,
|
|
})),
|
|
},
|
|
},
|
|
})
|
|
.then((flag) => {
|
|
clearCacheKeyPrefix(`featureFlags:enabled`);
|
|
return flag;
|
|
});
|
|
}
|
|
|
|
export async function deleteFeatureFlag(id: string) {
|
|
return await db.featureFlag
|
|
.delete({
|
|
where: { id },
|
|
})
|
|
.then((item) => {
|
|
clearCacheKeyPrefix(`featureFlags:enabled`);
|
|
return item;
|
|
});
|
|
}
|