first commit

This commit is contained in:
2026-10-03 13:40:26 +02:00
parent 732a5b5a16
commit 566f415fa7
555 changed files with 62704 additions and 0 deletions

View File

@@ -0,0 +1,169 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import {
UpsertMaterializedActivityRepository,
UpsertMaterializedCommentRepository,
UpsertMaterializedIssueRepository,
UpsertMaterializedProjectRepository,
UpsertMaterializedReleaseRepository,
} from '@task-stream/api/tracker/application';
import {
ActivityReadModel,
CommentReadModel,
IssueLinkReadModel,
IssueReadModel,
ProjectReadModel,
ReleaseReadModel,
} from '@task-stream/api/tracker/domain';
import { Model } from 'mongoose';
import {
ActivityDoc,
CommentDoc,
IssueDoc,
ProjectDoc,
ReadModels,
ReleaseDoc,
} from './read-model.schemas';
import { upsertIfNewer } from './upsert-if-newer';
/* Materialized views written by the event handlers (MongoDB). */
@Injectable()
export class MongoUpsertMaterializedProjectRepository extends UpsertMaterializedProjectRepository {
constructor(
@InjectModel(ReadModels.Project)
private readonly projects: Model<ProjectDoc>,
) {
super();
}
async upsert({
id,
version,
...fields
}: Pick<ProjectReadModel, 'id' | 'version'> & Partial<ProjectReadModel>) {
const memberIds = fields.members
? { memberIds: fields.members.map((m) => m.user.id) }
: {};
await upsertIfNewer(this.projects, id, version, {
...fields,
...memberIds,
});
}
}
@Injectable()
export class MongoUpsertMaterializedIssueRepository extends UpsertMaterializedIssueRepository {
constructor(
@InjectModel(ReadModels.Issue) private readonly issues: Model<IssueDoc>,
) {
super();
}
async upsert({
id,
version,
...fields
}: Pick<IssueReadModel, 'id' | 'version'> &
Partial<Omit<IssueReadModel, 'links' | 'commentCount'>>) {
await upsertIfNewer(this.issues, id, version, fields, {
outwardLinks: [],
inwardLinks: [],
commentCount: 0,
});
}
async addLink(issueId: string, link: IssueLinkReadModel) {
const field = link.direction === 'OUTWARD' ? 'outwardLinks' : 'inwardLinks';
await this.issues.updateOne(
{
_id: issueId,
[field]: {
$not: { $elemMatch: { type: link.type, issueId: link.issueId } },
},
},
{ $push: { [field]: link } },
);
}
async removeLink(
issueId: string,
link: Omit<IssueLinkReadModel, 'issueKey'>,
) {
const field = link.direction === 'OUTWARD' ? 'outwardLinks' : 'inwardLinks';
await this.issues.updateOne(
{ _id: issueId },
{ $pull: { [field]: { type: link.type, issueId: link.issueId } } },
);
}
async setCommentCount(issueId: string, commentCount: number) {
await this.issues.updateOne({ _id: issueId }, { $set: { commentCount } });
}
async renameRelease(releaseId: string, name: string) {
await this.issues.updateMany(
{ 'release.id': releaseId },
{ $set: { 'release.name': name } },
);
}
}
@Injectable()
export class MongoUpsertMaterializedCommentRepository extends UpsertMaterializedCommentRepository {
constructor(
@InjectModel(ReadModels.Comment)
private readonly comments: Model<CommentDoc>,
) {
super();
}
async upsert({
id,
version,
...fields
}: Pick<CommentReadModel, 'id' | 'version'> & Partial<CommentReadModel>) {
await upsertIfNewer(this.comments, id, version, fields);
}
async remove(id: string) {
await this.comments.deleteOne({ _id: id });
}
}
@Injectable()
export class MongoUpsertMaterializedReleaseRepository extends UpsertMaterializedReleaseRepository {
constructor(
@InjectModel(ReadModels.Release)
private readonly releases: Model<ReleaseDoc>,
) {
super();
}
async upsert({
id,
version,
...fields
}: Pick<ReleaseReadModel, 'id' | 'version'> & Partial<ReleaseReadModel>) {
await upsertIfNewer(this.releases, id, version, fields);
}
}
@Injectable()
export class MongoUpsertMaterializedActivityRepository extends UpsertMaterializedActivityRepository {
constructor(
@InjectModel(ReadModels.Activity)
private readonly activity: Model<ActivityDoc>,
) {
super();
}
async insert({ id, ...fields }: ActivityReadModel): Promise<boolean> {
const result = await this.activity.updateOne(
{ _id: id },
{ $setOnInsert: fields },
{ upsert: true },
);
return result.upsertedCount > 0;
}
}