Kaizen
Browse modulesInsightsinsights/serverClasses

Class: SavedReportsRepository

Defined in: server/insights/saved-reports-repository.ts:112

Base repository providing transaction routing, audit-field helpers, and opt-in activity logging. Extend one of the specialized classes instead of using this directly:

  • ReadRepository — read-only
  • WriteRepository — create + update (append-only)
  • CrudRepository — full CRUD (most common)
  • VersionRepository — parent-child versioning

TClient is the consumer's generated Prisma client (for model accessors on this.tx). TTx is the transaction handle type (the inner argument of prisma.$transaction(fn)); it defaults to TClient because modern Prisma transaction handles are structurally compatible with the top-level client apart from the $-prefixed control methods.

Extends

Constructors

Constructor

new SavedReportsRepository(prismaClient: PrismaClientLike<Tx<PrismaClient<PrismaClientOptions, never, DefaultArgs>>> & PrismaClient<PrismaClientOptions, never, DefaultArgs>, options?: RepositoryOptions): SavedReportsRepository;

Defined in: server/persistence/base-repository.ts:68

Parameters

ParameterType
prismaClientPrismaClientLike<Tx<PrismaClient<PrismaClientOptions, never, DefaultArgs>>> & PrismaClient<PrismaClientOptions, never, DefaultArgs>
optionsRepositoryOptions

Returns

SavedReportsRepository

Inherited from

BaseRepository.constructor

Properties

ENTITY_TYPE?

protected readonly optional ENTITY_TYPE?: string;

Defined in: server/persistence/base-repository.ts:61

Set ENTITY_TYPE in child repositories to enable automatic activity logging. When set, create/update/delete operations auto-log unless silenced with .silent. Requires an activity model on the consumer's Prisma schema.

Inherited from

BaseRepository.ENTITY_TYPE


logger?

protected readonly optional logger?: Logger;

Defined in: server/persistence/base-repository.ts:64

Inherited from

BaseRepository.logger


prismaClient

protected prismaClient: PrismaClientLike<Tx<PrismaClient<PrismaClientOptions, never, DefaultArgs>>> & PrismaClient<PrismaClientOptions, never, DefaultArgs>;

Defined in: server/persistence/base-repository.ts:69

Inherited from

BaseRepository.prismaClient

Accessors

actorId

Get Signature

get protected actorId(): string | null | undefined;

Defined in: server/persistence/base-repository.ts:76

Returns

string | null | undefined

Inherited from

BaseRepository.actorId


auditCreate

Get Signature

get protected auditCreate(): {
  createdBy: string | null | undefined;
  updatedBy: string | null | undefined;
};

Defined in: server/persistence/base-repository.ts:90

Returns
{
  createdBy: string | null | undefined;
  updatedBy: string | null | undefined;
}
createdBy
createdBy: string | null | undefined = actorId;
updatedBy
updatedBy: string | null | undefined = actorId;

Inherited from

BaseRepository.auditCreate


auditDelete

Get Signature

get protected auditDelete(): {
  deletedAt: Date;
  deletedBy: string | null | undefined;
};

Defined in: server/persistence/base-repository.ts:99

Returns
{
  deletedAt: Date;
  deletedBy: string | null | undefined;
}
deletedAt
deletedAt: Date;
deletedBy
deletedBy: string | null | undefined;

Inherited from

BaseRepository.auditDelete


auditUpdate

Get Signature

get protected auditUpdate(): {
  updatedBy: string | null | undefined;
};

Defined in: server/persistence/base-repository.ts:95

Returns
{
  updatedBy: string | null | undefined;
}
updatedBy
updatedBy: string | null | undefined;

Inherited from

BaseRepository.auditUpdate


silent

Get Signature

get silent(): this;

Defined in: server/persistence/base-repository.ts:115

Suppress activity logging for the next mutation call. One-shot: the flag is consumed by the next create/update/delete.

Example
await repo.silent.update(id, data);   // no activity logged
await repo.update(id, data);          // activity logged normally
Returns

this

Inherited from

BaseRepository.silent


tx

Get Signature

get protected tx(): TTx;

Defined in: server/persistence/base-repository.ts:86

The current Prisma handle: the active transaction if one is running on this async context, otherwise the raw client.

Returns

TTx

Inherited from

BaseRepository.tx

Methods

consumeSilent()

protected consumeSilent(): boolean;

Defined in: server/persistence/base-repository.ts:128

Consume and reset the silent flag. Called by template methods.

Returns

boolean

Inherited from

BaseRepository.consumeSilent


create()

create(input: {
  definition: {
     aggregations?: {
        alias: string;
        field?: string;
        fn: "min" | "max" | "count" | "avg" | "sum";
     }[];
     columns: string[];
     filter?: FilterNode;
     groupBy?: {
        bucket?: "year" | "month" | "day" | "week" | "quarter";
        field: string;
     }[];
     schemaVersion: 1;
     sort: {
        by: string;
        direction: "asc" | "desc";
     }[];
     subject: string;
     timezone: string;
     totals?: boolean;
  };
  description?: string | null;
  name: string;
  organizationId: string;
  subjectKey: string;
  userId: string;
  visibility?: "public" | "private";
}): Promise<SavedReport>;

Defined in: server/insights/saved-reports-repository.ts:232

Parameters

ParameterTypeDescription
input{ definition: { aggregations?: { alias: string; field?: string; fn: "min" | "max" | "count" | "avg" | "sum"; }[]; columns: string[]; filter?: FilterNode; groupBy?: { bucket?: "year" | "month" | "day" | "week" | "quarter"; field: string; }[]; schemaVersion: 1; sort: { by: string; direction: "asc" | "desc"; }[]; subject: string; timezone: string; totals?: boolean; }; description?: string | null; name: string; organizationId: string; subjectKey: string; userId: string; visibility?: "public" | "private"; }-
input.definition{ aggregations?: { alias: string; field?: string; fn: "min" | "max" | "count" | "avg" | "sum"; }[]; columns: string[]; filter?: FilterNode; groupBy?: { bucket?: "year" | "month" | "day" | "week" | "quarter"; field: string; }[]; schemaVersion: 1; sort: { by: string; direction: "asc" | "desc"; }[]; subject: string; timezone: string; totals?: boolean; }-
input.definition.aggregations?{ alias: string; field?: string; fn: "min" | "max" | "count" | "avg" | "sum"; }[]-
input.definition.columnsstring[]The SELECT list — and the ONLY source of output columns, for grouped runs as much as detail runs: the compiler derives its projection from columns alone and never from groupBy/aggregations. So an empty columns has no compilable projection at all (not merely a semantically odd one), which makes it a STRUCTURAL failure and puts the check here rather than in validateDefinition. Keeping it in the schema also means every surface that parses a definition — run input, saved-report create/update, normalizeSavedReportDefinition on read, canned reports in the registry — fails closed without having to remember a second validation pass.
input.definition.filter?FilterNode-
input.definition.groupBy?{ bucket?: "year" | "month" | "day" | "week" | "quarter"; field: string; }[]-
input.definition.schemaVersion1-
input.definition.sort{ by: string; direction: "asc" | "desc"; }[]-
input.definition.subjectstring-
input.definition.timezonestring-
input.definition.totals?boolean-
input.description?string | null-
input.namestring-
input.organizationIdstring-
input.subjectKeystring-
input.userIdstring-
input.visibility?"public" | "private"-

Returns

Promise<SavedReport>


delete()

delete(input: {
  id: string;
  organizationId: string;
  userId: string;
}): Promise<void>;

Defined in: server/insights/saved-reports-repository.ts:335

Parameters

ParameterType
input{ id: string; organizationId: string; userId: string; }
input.idstring
input.organizationIdstring
input.userIdstring

Returns

Promise<void>


existsActive()

existsActive(input: {
  id: string;
  organizationId: string;
}): Promise<boolean>;

Defined in: server/insights/saved-reports-repository.ts:170

Whether an active row exists in the organization, deliberately ignoring viewer visibility. The scheduled-delivery worker uses this SYSTEM read to distinguish a deleted report from a still-existing report its actor can no longer read; it must never expose the result to a request caller.

Parameters

ParameterType
input{ id: string; organizationId: string; }
input.idstring
input.organizationIdstring

Returns

Promise<boolean>


get()

get(input: {
  id: string;
  organizationId: string;
  userId: string;
}): Promise<SavedReport>;

Defined in: server/insights/saved-reports-repository.ts:147

Parameters

ParameterType
input{ id: string; organizationId: string; userId: string; }
input.idstring
input.organizationIdstring
input.userIdstring

Returns

Promise<SavedReport>


getAuthorizationFields()

getAuthorizationFields(input: {
  id: string;
  organizationId: string;
  userId: string;
}): Promise<SavedReportAuthorizationFields>;

Defined in: server/insights/saved-reports-repository.ts:208

The authorization columns for one report — with the stored definition neither fetched nor parsed.

get ends in toDomain, which runs normalizeSavedReportDefinition and throws SavedReportValidationError on a definition that no longer parses. Failing closed is right for a READ; it is fatal as a WRITE precondition, because delete and updateDefinition are precisely the operations that exist to remove or repair such a row. Authorizing a write through get therefore makes a corrupt row unfixable: the recovery paths fail before they reach the repository at all.

So this method MUST NOT normalize, and must keep definition out of its select so it cannot start to. That is its contract, not an optimization — every input an authorization decision needs (existence, org scope, visibility, ownership) plus subjectKey is a raw column.

Same visibility gate as SavedReportsRepository.get: a non-owner's PRIVATE row misses the filter and surfaces as SavedReportNotFoundError (no existence leak). Ownership is left to the caller to judge from createdBy — the mutating methods still re-check it authoritatively inside their own transaction via getOwnedOrThrow.

Parameters

ParameterType
input{ id: string; organizationId: string; userId: string; }
input.idstring
input.organizationIdstring
input.userIdstring

Returns

Promise<SavedReportAuthorizationFields>


list()

list(input: {
  organizationId: string;
  subjectKey?: string;
  userId: string;
}): Promise<SavedReport[]>;

Defined in: server/insights/saved-reports-repository.ts:130

Parameters

ParameterType
input{ organizationId: string; subjectKey?: string; userId: string; }
input.organizationIdstring
input.subjectKey?string
input.userIdstring

Returns

Promise<SavedReport[]>


logActivity()

protected logActivity(
   _entityId: string, 
   _action: string, 
   _metadata?: Record<string, unknown>
): Promise<void>;

Defined in: server/persistence/base-repository.ts:140

Log an activity entry.

NOTE: This is not implemented because there's no activity model implemented in taproot yet! https://linear.app/kaizenlabs/issue/ENG-7846/add-activity-log-tables

Parameters

ParameterType
_entityIdstring
_actionstring
_metadata?Record<string, unknown>

Returns

Promise<void>

Inherited from

BaseRepository.logActivity


rename()

rename(input: {
  id: string;
  name: string;
  organizationId: string;
  userId: string;
}): Promise<SavedReport>;

Defined in: server/insights/saved-reports-repository.ts:287

Parameters

ParameterType
input{ id: string; name: string; organizationId: string; userId: string; }
input.idstring
input.namestring
input.organizationIdstring
input.userIdstring

Returns

Promise<SavedReport>


setVisibility()

setVisibility(input: {
  id: string;
  organizationId: string;
  userId: string;
  visibility: "public" | "private";
}): Promise<SavedReport>;

Defined in: server/insights/saved-reports-repository.ts:321

Parameters

ParameterType
input{ id: string; organizationId: string; userId: string; visibility: "public" | "private"; }
input.idstring
input.organizationIdstring
input.userIdstring
input.visibility"public" | "private"

Returns

Promise<SavedReport>


updateDefinition()

updateDefinition(input: {
  definition: {
     aggregations?: {
        alias: string;
        field?: string;
        fn: "min" | "max" | "count" | "avg" | "sum";
     }[];
     columns: string[];
     filter?: FilterNode;
     groupBy?: {
        bucket?: "year" | "month" | "day" | "week" | "quarter";
        field: string;
     }[];
     schemaVersion: 1;
     sort: {
        by: string;
        direction: "asc" | "desc";
     }[];
     subject: string;
     timezone: string;
     totals?: boolean;
  };
  description?: string | null;
  id: string;
  name?: string;
  organizationId: string;
  userId: string;
}): Promise<SavedReport>;

Defined in: server/insights/saved-reports-repository.ts:301

Parameters

ParameterTypeDescription
input{ definition: { aggregations?: { alias: string; field?: string; fn: "min" | "max" | "count" | "avg" | "sum"; }[]; columns: string[]; filter?: FilterNode; groupBy?: { bucket?: "year" | "month" | "day" | "week" | "quarter"; field: string; }[]; schemaVersion: 1; sort: { by: string; direction: "asc" | "desc"; }[]; subject: string; timezone: string; totals?: boolean; }; description?: string | null; id: string; name?: string; organizationId: string; userId: string; }-
input.definition{ aggregations?: { alias: string; field?: string; fn: "min" | "max" | "count" | "avg" | "sum"; }[]; columns: string[]; filter?: FilterNode; groupBy?: { bucket?: "year" | "month" | "day" | "week" | "quarter"; field: string; }[]; schemaVersion: 1; sort: { by: string; direction: "asc" | "desc"; }[]; subject: string; timezone: string; totals?: boolean; }-
input.definition.aggregations?{ alias: string; field?: string; fn: "min" | "max" | "count" | "avg" | "sum"; }[]-
input.definition.columnsstring[]The SELECT list — and the ONLY source of output columns, for grouped runs as much as detail runs: the compiler derives its projection from columns alone and never from groupBy/aggregations. So an empty columns has no compilable projection at all (not merely a semantically odd one), which makes it a STRUCTURAL failure and puts the check here rather than in validateDefinition. Keeping it in the schema also means every surface that parses a definition — run input, saved-report create/update, normalizeSavedReportDefinition on read, canned reports in the registry — fails closed without having to remember a second validation pass.
input.definition.filter?FilterNode-
input.definition.groupBy?{ bucket?: "year" | "month" | "day" | "week" | "quarter"; field: string; }[]-
input.definition.schemaVersion1-
input.definition.sort{ by: string; direction: "asc" | "desc"; }[]-
input.definition.subjectstring-
input.definition.timezonestring-
input.definition.totals?boolean-
input.description?string | null-
input.idstring-
input.name?string-
input.organizationIdstring-
input.userIdstring-

Returns

Promise<SavedReport>

On this page