Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/utils/db/entities/entities.db.server.ts
T

801 lines
24 KiB
TypeScript

import { Entity, EntityRelationship, EntityTag, EntityTemplate, Prisma, Property, PropertyAttribute, PropertyOption } from "@toolctrl/db/prisma-browser";
import Constants from "~/application/Constants";
import { DefaultLogActions } from "~/application/dtos/shared/DefaultLogActions";
import { DefaultVisibility } from "~/application/dtos/shared/DefaultVisibility";
import { defaultProperties } from "~/utils/helpers/PropertyHelper";
import { db } from "../../db.server";
import { EntityRelationshipWithDetails } from "./entityRelationships.db.server";
import { EntityViewWithDetails } from "./entityViews.db.server";
import { createProperties, CreatePropertyDto } from "./properties.db.server";
import EntityModelHelper from "~/utils/helpers/models/EntityModelHelper";
import EntityViewModelHelper from "~/utils/helpers/models/EntityViewModelHelper";
import EntitiesSingleton from "~/modules/rows/repositories/EntitiesSingleton";
export type EntitySimple = {
id: string;
order: number;
name: string;
title: string;
titlePlural: string;
active: boolean;
slug: string;
type: string;
hasApi: boolean;
showInSidebar: boolean;
icon: string;
onEdit: string | null;
};
export type RowsUsage = { entityId: string; _count: number };
export type EntityWithProperties = Entity & {
properties: PropertyWithDetails[];
};
export type EntityWithDetails = Entity & {
properties: PropertyWithDetails[];
views: EntityViewWithDetails[];
tags: EntityTag[];
parentEntities: EntityRelationshipWithDetails[];
childEntities: EntityRelationshipWithDetails[];
templates: EntityTemplate[];
};
export type EntityWithDetailsAndRelationships = EntityWithDetails & {
parentEntities: (EntityRelationship & { parent: { id: string; name: string; title: string; titlePlural: string; slug: string; onEdit: string | null } })[];
childEntities: (EntityRelationship & { parent: { id: string; name: string; title: string; titlePlural: string; slug: string; onEdit: string | null } })[];
};
export type EntityWithCount = EntityWithDetails & {
_count: { rows: number };
};
export type PropertyWithDetails = Property & {
// entity: EntityWithDetails;
attributes: PropertyAttribute[];
options: PropertyOption[];
parent?: PropertyWithDetails;
formula: { name: string; resultAs: string; calculationTrigger: string } | null;
};
// const includeEntityWithDetails = {
// properties: {
// include: {
// options: true,
// },
// },
// };
export async function getEntitiesInIds(ids: string[]) {
return await db.entity.findMany({
where: { id: { in: ids } },
});
}
export async function getAllEntities({
tenantId,
active,
types,
}: {
tenantId: string | null | undefined;
active?: boolean;
types?: string[];
}): Promise<EntityWithDetails[]> {
let where: Prisma.EntityWhereInput = {};
if (active) {
where = { active };
}
if (types) {
where = { ...where, type: { in: types } };
}
const allEntities = await db.entity.findMany({
where,
orderBy: [
{ type: "asc" },
{
order: "asc",
},
],
include: {
views: { where: { ...filterTenantId(tenantId) }, include: { properties: true, filters: true, sort: true, groupByProperty: true } },
tags: true,
parentEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
childEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
properties: {
where: { ...filterTenantId(tenantId) },
orderBy: { order: "asc" },
include: {
attributes: true,
options: {
orderBy: {
order: "asc",
},
},
formula: { select: { name: true, resultAs: true, calculationTrigger: true } },
},
},
templates: {
where: { ...filterTenantId(tenantId) },
},
},
});
EntitiesSingleton.getInstance().setEntities(allEntities);
return allEntities;
}
export async function getAllEntitiesSimple({ active, types }: { active?: boolean; types?: string[] } = {}): Promise<EntitySimple[]> {
let where: Prisma.EntityWhereInput = {};
if (active) {
where = { active };
}
if (types) {
where = { ...where, type: { in: types } };
}
return await db.entity.findMany({
where,
orderBy: [
{ type: "asc" },
{
order: "asc",
},
],
select: EntityModelHelper.selectSimpleProperties,
});
}
export async function getEntitySimple({ id, name }: { id?: string; name?: string }): Promise<EntitySimple | null> {
if (!id && !name) {
return null;
} else if (id) {
return await db.entity.findUnique({
where: {
id,
},
select: EntityModelHelper.selectSimpleProperties,
});
} else {
return await db.entity.findUnique({
where: {
name,
},
select: EntityModelHelper.selectSimpleProperties,
});
}
}
export async function getEntitiesByName(name: string[]) {
return await db.entity.findMany({
where: {
name: {
in: name,
},
},
});
}
export async function getAllEntitiesWithRowCount({ tenantId }: { tenantId: string | null }): Promise<EntityWithCount[]> {
return await db.entity.findMany({
include: {
views: { where: { ...filterTenantId(tenantId) }, include: { properties: true, filters: true, sort: true, groupByProperty: true } },
tags: true,
parentEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
childEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
templates: {
where: { ...filterTenantId(tenantId) },
},
_count: {
select: {
rows: true,
},
},
properties: {
where: { ...filterTenantId(tenantId) },
orderBy: { order: "asc" },
include: {
attributes: true,
options: {
orderBy: {
order: "asc",
},
},
formula: { select: { name: true, resultAs: true, calculationTrigger: true } },
},
},
},
orderBy: [
{ type: "asc" },
{
order: "asc",
},
],
});
}
export async function getAllRowsUsage(tenantId: string): Promise<RowsUsage[]> {
const countEntities = await db.row.groupBy({
by: ["entityId"],
_count: true,
where: {
OR: [
{
tenantId,
},
],
},
});
return countEntities;
}
export async function getEntityById({ id, tenantId }: { id: string; tenantId: string | null | undefined }): Promise<EntityWithDetailsAndRelationships | null> {
return await db.entity.findUnique({
where: {
id,
},
include: {
views: { where: { ...filterTenantId(tenantId) }, include: { properties: true, filters: true, sort: true, groupByProperty: true } },
tags: true,
parentEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
childEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
properties: {
where: { ...filterTenantId(tenantId) },
orderBy: { order: "asc" },
include: {
attributes: true,
options: {
orderBy: {
order: "asc",
},
},
formula: { select: { name: true, resultAs: true, calculationTrigger: true } },
},
},
templates: {
where: { ...filterTenantId(tenantId) },
},
},
});
}
export async function getEntityBySlug({
tenantId,
slug,
activeOnly,
}: {
tenantId: string | null;
slug: string;
activeOnly?: boolean;
}): Promise<EntityWithDetails> {
const entity = await db.entity.findUnique({
where: {
slug,
},
include: {
views: { where: { ...filterTenantId(tenantId) }, include: { properties: true, filters: true, sort: true, groupByProperty: true } },
tags: true,
parentEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
childEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
properties: {
where: { ...filterTenantId(tenantId) },
orderBy: { order: "asc" },
include: {
attributes: true,
// entity: {
// include: {
// ...includePropertiesWithDetails,
// },
// },
options: {
orderBy: {
order: "asc",
},
},
formula: { select: { name: true, resultAs: true, calculationTrigger: true } },
},
},
templates: {
where: { ...filterTenantId(tenantId) },
},
},
});
if (!entity) {
throw new Error(`Entity '${slug}' not found`);
}
if (activeOnly && !entity.active) {
throw new Error(`Entity '${slug}' is not active`);
}
return entity;
}
export async function getSimpleEntityBySlug(slug: string): Promise<EntitySimple | null> {
return await db.entity.findUnique({
where: {
slug,
},
select: EntityModelHelper.selectSimpleProperties,
});
}
export async function findEntityBySlug(slug: string): Promise<Entity | null> {
return await db.entity.findUnique({
where: { slug },
});
}
export async function getEntityByName({ tenantId, name }: { tenantId: string | null | undefined; name: string }): Promise<EntityWithDetails> {
const entity = await db.entity.findUnique({
where: {
name,
},
include: {
views: { where: { ...filterTenantId(tenantId) }, include: { properties: true, filters: true, sort: true, groupByProperty: true } },
tags: true,
parentEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
childEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
properties: {
where: { ...filterTenantId(tenantId) },
orderBy: { order: "asc" },
include: {
// entity: {
// include: {
// ...includePropertiesWithDetails,
// },
// },
attributes: true,
options: {
orderBy: {
order: "asc",
},
},
formula: { select: { name: true, resultAs: true, calculationTrigger: true } },
},
},
templates: {
where: { ...filterTenantId(tenantId) },
},
},
});
if (!entity) {
throw new Error(`Entity with name ${name} not found`);
}
return entity;
}
export async function getEntityByIdOrName({
tenantId,
id,
name,
}: {
tenantId: string | null | undefined;
id?: string;
name?: string;
}): Promise<EntityWithDetails> {
let entity: EntityWithDetails | null = null;
if (id) {
entity = await getEntityById({ tenantId, id });
} else if (name) {
entity = await getEntityByName({ tenantId, name });
}
if (!entity) {
throw new Error(`Entity with id ${id} or name ${name} not found`);
}
return entity;
}
export async function getEntityByIdNameOrSlug({ tenantId, idNameOrSlug }: { tenantId: string | null; idNameOrSlug: string }): Promise<EntityWithDetails> {
return getEntityWithWhere({
tenantId,
where: {
OR: [{ id: idNameOrSlug }, { name: idNameOrSlug }, { slug: idNameOrSlug }],
},
});
}
async function getEntityWithWhere({ tenantId, where }: { tenantId: string | null; where: Prisma.EntityWhereInput }) {
const entity = await db.entity.findFirst({
where,
include: {
views: { where: { ...filterTenantId(tenantId) }, include: { properties: true, filters: true, sort: true, groupByProperty: true } },
tags: true,
parentEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
childEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
properties: {
where: { ...filterTenantId(tenantId) },
orderBy: { order: "asc" },
include: {
// entity: {
// include: {
// ...includePropertiesWithDetails,
// },
// },
attributes: true,
options: {
orderBy: {
order: "asc",
},
},
formula: { select: { name: true, resultAs: true, calculationTrigger: true } },
},
},
templates: {
where: { ...filterTenantId(tenantId) },
},
},
});
if (!entity) {
throw new Error(`Entity not found`);
}
return entity;
}
export async function findEntityByName({ tenantId, name }: { tenantId: string | null; name: string }): Promise<EntityWithDetails | null> {
return await db.entity.findUnique({
where: { name },
include: {
views: { where: { ...filterTenantId(tenantId) }, include: { properties: true, filters: true, sort: true, groupByProperty: true } },
tags: true,
parentEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
childEntities: {
include: {
parent: { select: EntityModelHelper.selectEntityWithoutIcon },
child: { select: EntityModelHelper.selectEntityWithoutIcon },
childEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
parentEntityView: { where: { ...filterTenantId(tenantId) }, include: EntityViewModelHelper.includeDetails },
},
orderBy: { order: "asc" },
},
properties: {
where: { ...filterTenantId(tenantId) },
orderBy: { order: "asc" },
include: {
// entity: {
// include: {
// ...includePropertiesWithDetails,
// },
// },
attributes: true,
options: {
orderBy: {
order: "asc",
},
},
formula: { select: { name: true, resultAs: true, calculationTrigger: true } },
},
},
templates: {
where: { ...filterTenantId(tenantId) },
},
},
});
}
export async function findSimpleEntityByName(name: string): Promise<{ id: string; title: string; titlePlural: string; slug: string } | null> {
if (EntitiesSingleton.getInstance().isSet()) {
return (
EntitiesSingleton.getInstance()
.getEntities()
.find((f) => f.name === name) ?? null
);
}
return await db.entity.findUnique({
where: { name },
select: { id: true, title: true, titlePlural: true, slug: true },
});
}
export async function getEntityByPrefix(prefix: string): Promise<Entity | null> {
return await db.entity.findUnique({
where: {
prefix,
},
});
}
export async function createEntity(
data: {
name: string;
slug: string;
prefix: string;
title: string;
titlePlural: string;
isAutogenerated: boolean;
type: string;
hasApi: boolean;
icon: string;
active: boolean;
showInSidebar: boolean;
hasTags: boolean;
hasComments: boolean;
hasTasks: boolean;
hasActivity: boolean;
hasBulkDelete: boolean;
hasViews: boolean;
defaultVisibility: string;
onCreated: string | null;
onEdit: string | null;
},
order?: number | null
) {
if (!order) {
order = (await getMaxEntityOrder()) + 1;
}
const entity = await db.entity.create({
data: {
...data,
order,
},
});
const webhooks = [
{
action: DefaultLogActions.Created,
method: "POST",
endpoint: "",
},
{
action: DefaultLogActions.Updated,
method: "POST",
endpoint: "",
},
{
action: DefaultLogActions.Created,
method: "POST",
endpoint: "",
},
];
await Promise.all(
webhooks.map(async (webhook) => {
return await db.entityWebhook.create({
data: {
entityId: entity.id,
action: webhook.action,
method: webhook.method,
endpoint: webhook.endpoint,
},
});
})
);
await Promise.all(
defaultProperties.map(async (property) => {
return await db.property.create({
data: {
entityId: entity.id,
...property,
},
});
})
);
return entity;
}
export async function createCoreEntity(
data: {
name: string;
slug: string;
title: string;
titlePlural: string;
prefix: string;
type: string;
isAutogenerated?: boolean;
hasApi?: boolean;
icon?: string;
active?: boolean;
showInSidebar?: boolean;
hasTags?: boolean;
hasComments?: boolean;
hasTasks?: boolean;
hasActivity?: boolean;
hasBulkDelete?: boolean;
hasViews?: boolean;
defaultVisibility?: DefaultVisibility;
onCreated?: string | null;
onEdit?: string | null;
},
properties?: CreatePropertyDto[]
) {
const entity = await createEntity({
name: data.name,
slug: data.slug,
prefix: data.prefix,
title: data.title,
titlePlural: data.titlePlural,
type: data.type,
isAutogenerated: data.isAutogenerated !== undefined ? data.isAutogenerated : true,
hasApi: data.hasApi !== undefined ? data.hasApi : true,
icon: data.icon !== undefined ? data.icon : "",
active: data.active !== undefined ? data.active : true,
showInSidebar: data.showInSidebar !== undefined ? data.showInSidebar : true,
hasTags: data.hasTags !== undefined ? data.hasTags : true,
hasComments: data.hasComments !== undefined ? data.hasComments : true,
hasTasks: data.hasTasks !== undefined ? data.hasTasks : false,
hasActivity: data.hasActivity !== undefined ? data.hasActivity : true,
hasBulkDelete: data.hasBulkDelete !== undefined ? data.hasBulkDelete : false,
hasViews: data.hasViews !== undefined ? data.hasViews : true,
defaultVisibility: data.defaultVisibility ?? Constants.DEFAULT_ROW_VISIBILITY,
onCreated: data.onCreated ?? "redirectToOverview",
onEdit: data.onEdit ?? "editRoute",
});
if (properties) {
await createProperties(entity.id, properties);
}
return entity;
}
export async function updateEntity(
id: string,
data: {
name?: string;
slug?: string;
order?: number;
prefix?: string;
title?: string;
titlePlural?: string;
isAutogenerated?: boolean;
type?: string;
hasApi?: boolean;
icon?: string;
active?: boolean;
showInSidebar?: boolean;
hasTags?: boolean;
hasComments?: boolean;
hasTasks?: boolean;
hasActivity?: boolean;
hasBulkDelete?: boolean;
hasViews?: boolean;
defaultVisibility?: string;
onCreated?: string | null;
onEdit?: string | null;
}
) {
return await db.entity.update({
where: {
id,
},
data,
});
}
export async function deleteEntity(id: string) {
return await db.entity.delete({
where: {
id,
},
});
}
export default async function getMaxEntityOrder(): Promise<number> {
return (
(
await db.entity.aggregate({
_max: {
order: true,
},
})
)._max?.order ?? 0
);
}
export async function getDefaultEntityVisibility(id: string): Promise<DefaultVisibility> {
return ((
await db.entity.findUnique({
where: { id },
select: {
defaultVisibility: true,
},
})
)?.defaultVisibility ?? Constants.DEFAULT_ROW_VISIBILITY) as DefaultVisibility;
}
function filterTenantId(tenantId: string | null | undefined) {
const filter: { tenantId?: string | null; OR?: { tenantId?: string | null }[] } = {};
if (tenantId === undefined) {
return filter;
} else if (tenantId === null) {
filter.tenantId = null;
} else {
filter.OR = [{ tenantId: null }, { tenantId }];
}
return filter;
}