Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/modules/featureFlags/db/featureFlags.db.server.ts
T

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;
});
}