Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/pr cache #288

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Register application:
without `.sample` key word and fill that files with proper configuration
- serve frontend application with `ng serve`
- (optional) start a database with docker `docker run --name pmp-postgres -e POSTGRES_PASSWORD=mysecretpassword -e POSTGRES_USER=pmp -p 5432:5432 -d postgres`
- (optional) start redis with docker `docker run --name pmp-redis -p 6379:6379 -d redis`
- serve api with `ng serve pmp-api`

### Build production
Expand Down
5 changes: 5 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ services:
- POSTGRES_DB=${DB_NAME}
networks:
- backend
redis:
image: redis
restart: always
networks:
- backend

networks:
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<ng-container matColumnDef="title">
<mat-header-cell *matHeaderCellDef class="title-column" mat-sort-header>Title</mat-header-cell>
<mat-cell *matCellDef="let element" class="title-column">
{{ element.title }}
<span class="title-column__label" [matTooltip]="element.title">{{ element.title }}</span>
</mat-cell>
</ng-container>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
}
}

.title-column__label {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

.comments-column {
color: rgba(0, 0, 0, 0.6);
flex: unset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<ng-container matColumnDef="title">
<mat-header-cell *matHeaderCellDef class="title-column" mat-sort-header>Title</mat-header-cell>
<mat-cell *matCellDef="let element" class="title-column">
{{ element.title }}
<span class="title-column__label" [matTooltip]="element.title">{{ element.title }}</span>
</mat-cell>
<mat-footer-cell *matFooterCellDef="let element" class="title-column"></mat-footer-cell>
</ng-container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
}
}

.title-column__label {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

.comments-column {
color: rgba(0, 0, 0, 0.6);
flex: unset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import {
} from '@pimp-my-pr/server/repository/core/domain';
import { RepositoryRepository } from '@pimp-my-pr/server/repository/core/domain-services';
import { AddRepositoryCommand } from './add-repository.command';
import { PrSyncService } from '../../queue/pr-sync.service';

@CommandHandler(AddRepositoryCommand)
export class AddRepositoryHandler implements ICommandHandler<AddRepositoryCommand> {
constructor(private repositoryRepository: RepositoryRepository) {}
constructor(private repositoryRepository: RepositoryRepository, private prSync: PrSyncService) {}

async execute(command: AddRepositoryCommand): Promise<void> {
const { repositoryName, maxLines, maxWaitingTime, userId, maxPrs } = command;
Expand Down Expand Up @@ -42,7 +43,13 @@ export class AddRepositoryHandler implements ICommandHandler<AddRepositoryComman
maxWaitingTime,
maxPrs
);
await this.repositoryRepository.save(repository);

return this.repositoryRepository.save(repository);
return this.prSync.syncPrsInBackground(
repository.repositoryId,
repositoryName,
command.token,
command.platform
);
}
}
Original file line number Diff line number Diff line change
@@ -1,45 +1,22 @@
import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import { GetPrTimelineQuery } from './get-pr-timeline.query';
import {
PrRepository,
prRepositoryFactoryToken,
RepositoryRepository
} from '@pimp-my-pr/server/repository/core/domain-services';
import { PrRepository } from '@pimp-my-pr/server/repository/core/domain-services';
import { PrTimelineReadModel } from './pr-timeline.read-model';
import {
getTimeLineHistory,
InvalidTimelineParametersException,
PrEntity,
PrState
InvalidTimelineParametersException
} from '@pimp-my-pr/server/repository/core/domain';
import { prTimelineModelFactory } from '../../read-models/factories/pr-timeline-model.factory';
import { Inject } from '@nestjs/common';
import { Platform } from '@pimp-my-pr/shared/domain';
import { getStepsCount, traversePagesUntil } from '@pimp-my-pr/server/repository/util';
import { getStepsCount } from '@pimp-my-pr/server/repository/util';

@QueryHandler(GetPrTimelineQuery)
export class GetPrTimelineHandler implements IQueryHandler<GetPrTimelineQuery> {
constructor(
@Inject(prRepositoryFactoryToken)
private prRepositoryFactory: (platform: Platform) => PrRepository,
private repoRepository: RepositoryRepository
) {}
constructor(private prRepository: PrRepository) {}

async execute(query: GetPrTimelineQuery): Promise<PrTimelineReadModel> {
const { repositoryId, step, timelineFrom, timelineTo, token, platform, createdAfter } = query;
const { fullName } = await this.repoRepository.getById(repositoryId);
const prRepository = this.prRepositoryFactory(platform);

const prs = await traversePagesUntil<PrEntity>(
async page =>
await prRepository.findByRepositoryId(fullName, token, {
prState: PrState.ALL,
page,
onPage: 100
}),
100,
createdAfter
);
const prs = await this.prRepository.findByRepositoryId(repositoryId);
const stepsCount = getStepsCount(timelineFrom, timelineTo, step);
if (stepsCount === 0) throw new InvalidTimelineParametersException('timelineFrom');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import { PrEntity } from '@pimp-my-pr/server/repository/core/domain';
import {
PrRepository,
prRepositoryFactoryToken,
RepositoryRepository
} from '@pimp-my-pr/server/repository/core/domain-services';
import { Platform } from '@pimp-my-pr/shared/domain';
import { repositoryPrsStatisticsReadModelFactory } from '../../read-models/factories/repository-prs-statistics-read-model.factory';
import { GetRepositoryStatisticsQuery } from './get-repository-statistics.query';
import { RepositoryStatisticsReadModel } from './repository-statistics.read-model';
Expand All @@ -16,18 +14,15 @@ import { RepositoryStatisticsReadModel } from './repository-statistics.read-mode
export class GetRepositoryStatisticsHandler
implements IQueryHandler<GetRepositoryStatisticsQuery, RepositoryStatisticsReadModel> {
constructor(
@Inject(prRepositoryFactoryToken)
private prRepositoryFactory: (platform: Platform) => PrRepository,
private prRepository: PrRepository,
private repositoryRepository: RepositoryRepository
) {}

async execute(query: GetRepositoryStatisticsQuery): Promise<RepositoryStatisticsReadModel> {
const repository = await this.repositoryRepository.getById(query.repositoryId);

const prRepository = this.prRepositoryFactory(query.platform);

return await prRepository
.findByRepositoryId(repository.fullName, query.token)
return await this.prRepository
.findByRepositoryId(repository.fullName)
.then((prs: PrEntity[]) => repositoryPrsStatisticsReadModelFactory(repository, prs));
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { Inject } from '@nestjs/common';
import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import {
ContributorRepository,
PrRepository,
prRepositoryFactoryToken,
RepositoryRepository,
ReviewerRepository,
reviewerRepositoryFactoryToken
RepositoryRepository
} from '@pimp-my-pr/server/repository/core/domain-services';
import { Platform } from '@pimp-my-pr/shared/domain';
import { repositoryPrsStatisticsReadModelFactory } from '../../read-models/factories/repository-prs-statistics-read-model.factory';
import { GetReviewerStatisticsQuery } from './get-reviewer-statistics.query';
import { ReviewerStatisticsReadModel } from './reviewer-statistics.read-model';
Expand All @@ -16,24 +12,19 @@ import { ReviewerStatisticsReadModel } from './reviewer-statistics.read-model';
export class GetReviewerStatisticsHandler
implements IQueryHandler<GetReviewerStatisticsQuery, ReviewerStatisticsReadModel> {
constructor(
@Inject(prRepositoryFactoryToken)
private prRepositoryFactory: (platform: Platform) => PrRepository,
@Inject(reviewerRepositoryFactoryToken)
private reviewerRepositoryFactory: (platform: Platform) => ReviewerRepository,
private prRepository: PrRepository,
private contributorRepository: ContributorRepository,
private repositoryRepository: RepositoryRepository
) {}

async execute(query: GetReviewerStatisticsQuery): Promise<ReviewerStatisticsReadModel> {
const prRepository = this.prRepositoryFactory(query.platform);
const reviewerRepository = this.reviewerRepositoryFactory(query.platform);

const repositories = await this.repositoryRepository.findByUserId(query.userId);
const reviewer = await reviewerRepository.get(query.reviewerId, query.token);
const reviewer = await this.contributorRepository.get(query.reviewerId);

const repositoryStatistics = await Promise.all(
repositories.map(repository =>
prRepository
.findByRepositoryId(repository.fullName, query.token)
this.prRepository
.findByRepositoryId(repository.id)
.then(prs => prs.filter(pr => pr.reviewers.some(rev => rev.id === query.reviewerId)))
.then(pr => {
if (pr.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,24 @@ import {
prRepositoryFactoryToken,
RepositoryRepository
} from '@pimp-my-pr/server/repository/core/domain-services';
import { Platform } from '@pimp-my-pr/shared/domain';
import { ListRepositoriesStatisticsQuery } from './list-repositories-statistics.query';
import { RepositoriesStatisticsItemReadModel } from './repositories-statistics-item.read-model';

@QueryHandler(ListRepositoriesStatisticsQuery)
export class ListRepositoriesStatisticsHandler
implements IQueryHandler<ListRepositoriesStatisticsQuery, RepositoriesStatisticsItemReadModel[]> {
constructor(
@Inject(prRepositoryFactoryToken)
private prRepositoryFactory: (platform: Platform) => PrRepository,
private prRepository: PrRepository,
private repositoryRepository: RepositoryRepository
) {}

async execute(
query: ListRepositoriesStatisticsQuery
): Promise<RepositoriesStatisticsItemReadModel[]> {
const prRepository = this.prRepositoryFactory(query.platform);

const repositories = await this.repositoryRepository.findByUserId(query.userId);

const result = repositories.map(async repository => {
const prs = await prRepository.findByRepositoryId(repository.fullName, query.token);
const prs = await this.prRepository.findByRepositoryId(repository.fullName);

return new RepositoriesStatisticsItemReadModel(repository, prs);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import { Inject } from '@nestjs/common';
import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';

import {
PrEntity,
RepositoryEntity,
ReviewerEntity
} from '@pimp-my-pr/server/repository/core/domain';
import { PrEntity, RepositoryEntity } from '@pimp-my-pr/server/repository/core/domain';
import {
PrRepository,
prRepositoryFactoryToken,
RepositoryRepository
} from '@pimp-my-pr/server/repository/core/domain-services';
import { Platform } from '@pimp-my-pr/shared/domain';
import { ReviewerModelWithPr } from '../../read-models/reviewer-model-with-pr.interface';
import { ListReviewersStatisticsQuery } from './list-reviewers-statistics.query';
import { ReviewersStatisticsItemReadModel } from './reviewers-statistics-item-read.model';
Expand All @@ -21,22 +16,17 @@ import { getTimeDiffInHours } from '@pimp-my-pr/shared/util-time-diff-in-hours';
export class ListReviewersStatisticsHandler
implements IQueryHandler<ListReviewersStatisticsQuery, ReviewersStatisticsItemReadModel[]> {
constructor(
@Inject(prRepositoryFactoryToken)
private prRepositoryFactory: (platform: Platform) => PrRepository,
private prRepository: PrRepository,
private repositoryRepository: RepositoryRepository
) {}

async execute(query: ListReviewersStatisticsQuery): Promise<ReviewersStatisticsItemReadModel[]> {
const prRepository = this.prRepositoryFactory(query.platform);

const repositories = await this.repositoryRepository.findByUserId(query.userId);

const repositoriesPrs = new Map<RepositoryEntity, PrEntity[]>();

const nestedPrs = await Promise.all(
repositories.map(repository =>
prRepository.findByRepositoryId(repository.fullName, query.token)
)
repositories.map(repository => this.prRepository.findByRepositoryId(repository.fullName))
);

nestedPrs.forEach((prs, index) => repositoriesPrs.set(repositories[index], prs));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Module } from '@nestjs/common';
import { BullModule } from '@nestjs/bull';
import { PR_QUEUE_NAME } from '@pimp-my-pr/server/repository/core/domain';
import { PrSyncService } from './pr-sync.service';
import { PrSyncConsumer } from './pr-sync.consumer';

@Module({
imports: [
BullModule.registerQueue({
name: PR_QUEUE_NAME
})
],
providers: [PrSyncService, PrSyncConsumer],
exports: [PrSyncService]
})
export class PrQueueModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Process, Processor } from '@nestjs/bull';
import { Job } from 'bull';
import {
PR_QUEUE_NAME,
PrEntity,
PrState,
PrSyncJob
} from '@pimp-my-pr/server/repository/core/domain';
import {
PrRepository,
RemotePrRepository,
remotePrRepositoryFactoryToken
} from '@pimp-my-pr/server/repository/core/domain-services';
import { traversePagesUntilGen } from '@pimp-my-pr/server/repository/util';
import { Inject } from '@nestjs/common';
import { Platform } from '@pimp-my-pr/shared/domain';

@Processor(PR_QUEUE_NAME)
export class PrSyncConsumer {
constructor(
private prRepository: PrRepository,
@Inject(remotePrRepositoryFactoryToken)
private prRepositoryFactory: (platform: Platform) => RemotePrRepository
) {}

@Process()
async syncPrs(job: Job<PrSyncJob>): Promise<void> {
const prRepository = this.prRepositoryFactory(job.data.platform);
const prFetcher = traversePagesUntilGen<PrEntity>(
async page =>
await prRepository.findByRepositoryId(job.data.repositoryName, job.data.token, {
prState: PrState.OPEN,
page,
onPage: 100
}),
100
);
for await (const prsBatch of prFetcher) {
for (const pr of prsBatch) {
try {
delete pr.id;
delete pr.author.id;
await this.prRepository.save(pr);
} catch (ex) {
console.log(ex);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Injectable } from '@nestjs/common';
import { InjectQueue } from '@nestjs/bull';
import { PR_QUEUE_NAME, PrSyncJob } from '@pimp-my-pr/server/repository/core/domain';
import { Queue } from 'bull';
import { Platform } from '@pimp-my-pr/shared/domain';

@Injectable()
export class PrSyncService {
constructor(@InjectQueue(PR_QUEUE_NAME) private prQueue: Queue<PrSyncJob>) {}
async syncPrsInBackground(
repositoryId: string,
repositoryName: string,
token: string,
platform: Platform
): Promise<void> {
await this.prQueue.add({
repositoryId,
token,
platform,
repositoryName
});
}
}
Loading