forked from valueadd-poland/pimp-my-pr
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(api-repository): add backend for timeline statistics
-add endpoint for acquiring statistics -upgrade github/gitlab/bitbucket remote repositories to handle pagination and state filters -add util for processing prs into timeline staticstics, currently the following fields are available: { sumCount: all PR that were open in subperiod avgCount: avg count of PR that were open in subperiod avgWaitingTime: avg time taht prs were waiting since creation } resolve valueadd-poland#160 - backend
- Loading branch information
1 parent
be06135
commit 2fa6acc
Showing
49 changed files
with
771 additions
and
18 deletions.
There are no files selected for viewing
42 changes: 42 additions & 0 deletions
42
libs/server/repository/api-rest/src/lib/controllers/timeline.controller.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { ApiBearerAuth, ApiOkResponse, ApiTags } from '@nestjs/swagger'; | ||
import { Controller, Get, Param, Query, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common'; | ||
import { AuthGuard, Credentials, RequestCredentials } from '@pimp-my-pr/server/auth/public'; | ||
import { | ||
GetPrTimelineQuery, | ||
TimelineFacade | ||
} from '@pimp-my-pr/server/repository/core/application-services'; | ||
import { PrTimelineReadModel } from '@pimp-my-pr/server/repository/core/application-services'; | ||
import { GenerateTimelineDto } from '../dtos/generate-timeline.dto'; | ||
import { decreaseDateByTimelineStep } from '@pimp-my-pr/server/repository/core/domain'; | ||
|
||
@ApiTags('timeline') | ||
@UseGuards(AuthGuard) | ||
@ApiBearerAuth() | ||
@Controller('timeline') | ||
export class TimelineController { | ||
constructor(private timelineFacade: TimelineFacade) {} | ||
|
||
@ApiOkResponse({ type: [PrTimelineReadModel] }) | ||
@UsePipes( | ||
new ValidationPipe({ transform: true, transformOptions: { enableImplicitConversion: true } }) | ||
) | ||
@Get('pr/:repositoryId') | ||
generateTimeline( | ||
@Credentials() credentials: RequestCredentials, | ||
@Param('repositoryId') repositoryId: string, | ||
@Query() query: GenerateTimelineDto | ||
): Promise<PrTimelineReadModel> { | ||
return this.timelineFacade.getPrTimeLine( | ||
new GetPrTimelineQuery( | ||
query.step, | ||
query.timelineFrom, | ||
query.timelineTo, | ||
credentials.token, | ||
repositoryId, | ||
credentials.platform, | ||
// TODO: Find a better solution to handle this problem | ||
decreaseDateByTimelineStep(query.timelineFrom, 'month', 3) | ||
) | ||
); | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
libs/server/repository/api-rest/src/lib/dtos/generate-timeline.dto.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { TimelineStepType } from '@pimp-my-pr/server/repository/core/domain'; | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { IsDate, IsIn } from 'class-validator'; | ||
import { Transform } from 'class-transformer'; | ||
|
||
const STEPS = ['day', 'week', 'month', 'quarter', 'year']; | ||
|
||
export class GenerateTimelineDto { | ||
@ApiProperty({ enum: STEPS }) | ||
@IsIn(STEPS) | ||
step: TimelineStepType; | ||
|
||
@ApiProperty({ | ||
description: 'Beginning of the tracked period - every subsequent record is older' | ||
}) | ||
@IsDate() | ||
@Transform(value => new Date(value)) | ||
timelineFrom: Date; | ||
|
||
@ApiProperty({ | ||
description: 'Beginning of the tracked period - every subsequent record is older' | ||
}) | ||
@IsDate() | ||
@Transform(value => new Date(value)) | ||
timelineTo: Date; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
...tory/core/application-services/src/lib/queries/get-pr-timeline/get-pr-timeline.handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { QueryHandler, IQueryHandler } from '@nestjs/cqrs'; | ||
import { GetPrTimelineQuery } from './get-pr-timeline.query'; | ||
import { | ||
PrRepository, | ||
prRepositoryFactoryToken, | ||
RepositoryRepository | ||
} from '@pimp-my-pr/server/repository/core/domain-services'; | ||
import { PrTimelineReadModel } from '@pimp-my-pr/server/repository/core/application-services'; | ||
import { | ||
getStepsCount, | ||
getTimeLineHistory, | ||
InvalidTimelineParametersException, | ||
PrEntity, | ||
traversePagesUntil | ||
} 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'; | ||
|
||
@QueryHandler(GetPrTimelineQuery) | ||
export class GetPrTimelineHandler implements IQueryHandler<GetPrTimelineQuery> { | ||
constructor( | ||
@Inject(prRepositoryFactoryToken) | ||
private prRepositoryFactory: (platform: Platform) => PrRepository, | ||
private repoRepository: RepositoryRepository | ||
) {} | ||
|
||
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, 'all', page, 100), | ||
100, | ||
createdAfter | ||
); | ||
const prsSum = prs.reduce( | ||
(total, current) => | ||
(!current.closedAt || current.closedAt.getTime() > timelineFrom.getTime()) && | ||
current.createdAt.getTime() < timelineTo.getTime() | ||
? total + 1 | ||
: total, | ||
0 | ||
); | ||
const stepsCount = getStepsCount(timelineFrom, timelineTo, step); | ||
if (stepsCount === 0) throw new InvalidTimelineParametersException('timelineFrom'); | ||
|
||
const records = getTimeLineHistory(prs, step, timelineTo, stepsCount); | ||
const avgCount = records.reduce((total, current) => total + current.avgCount, 0) / prsSum; | ||
const avgWaitingTime = | ||
records.reduce((total, current) => total + current.avgWaitingTime, 0) / prsSum; | ||
return prTimelineModelFactory(records, query, prsSum, avgCount, avgWaitingTime); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...sitory/core/application-services/src/lib/queries/get-pr-timeline/get-pr-timeline.query.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { IQuery } from '@nestjs/cqrs'; | ||
import { TimelineStepType } from '@pimp-my-pr/server/repository/core/domain'; | ||
import { Platform } from '@pimp-my-pr/shared/domain'; | ||
|
||
export class GetPrTimelineQuery implements IQuery { | ||
constructor( | ||
public step: TimelineStepType, | ||
public timelineFrom: Date, | ||
public timelineTo: Date, | ||
public token: string, | ||
public repositoryId: string, | ||
public platform: Platform, | ||
public createdAfter: Date | ||
) {} | ||
} |
34 changes: 34 additions & 0 deletions
34
...itory/core/application-services/src/lib/queries/get-pr-timeline/pr-timeline.read-model.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { TimelineRecord, TimelineStepType } from '@pimp-my-pr/server/repository/core/domain'; | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
|
||
export class PrTimelineReadModel { | ||
@ApiProperty({ | ||
enum: ['day', 'week', 'month', 'quarter', 'year'] | ||
}) | ||
step: TimelineStepType; | ||
|
||
@ApiProperty() | ||
dateFrom: Date; | ||
|
||
@ApiProperty() | ||
dateTo: Date; | ||
|
||
@ApiProperty({ | ||
description: 'Pull requests were taken into account that were created after this date' | ||
}) | ||
createdAfter: Date; | ||
|
||
@ApiProperty({ | ||
type: [TimelineRecord] | ||
}) | ||
data: TimelineRecord[]; | ||
|
||
@ApiProperty() | ||
totalPrs: number; | ||
|
||
@ApiProperty() | ||
avgCount: number; | ||
|
||
@ApiProperty() | ||
avgWaitingTime: number; | ||
} |
24 changes: 24 additions & 0 deletions
24
...tory/core/application-services/src/lib/read-models/factories/pr-timeline-model.factory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { | ||
GetPrTimelineQuery, | ||
PrTimelineReadModel | ||
} from '@pimp-my-pr/server/repository/core/application-services'; | ||
import { TimelineRecord } from '@pimp-my-pr/server/repository/core/domain'; | ||
|
||
export const prTimelineModelFactory = ( | ||
prRecords: TimelineRecord[], | ||
query: GetPrTimelineQuery, | ||
sum: number, | ||
avgCount: number, | ||
avgWaitingTime: number | ||
): PrTimelineReadModel => { | ||
return { | ||
data: prRecords, | ||
step: query.step, | ||
dateFrom: query.timelineFrom, | ||
dateTo: query.timelineTo, | ||
createdAfter: query.createdAfter, | ||
totalPrs: sum, | ||
avgCount: avgCount, | ||
avgWaitingTime: avgWaitingTime | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
libs/server/repository/core/application-services/src/lib/timeline.facade.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { QueryBus } from '@nestjs/cqrs'; | ||
import { GetPrTimelineQuery } from './queries/get-pr-timeline/get-pr-timeline.query'; | ||
import { PrTimelineReadModel } from './queries/get-pr-timeline/pr-timeline.read-model'; | ||
import { Injectable } from '@nestjs/common'; | ||
|
||
@Injectable() | ||
export class TimelineFacade { | ||
constructor(private queryBus: QueryBus) {} | ||
|
||
getPrTimeLine(query: GetPrTimelineQuery): Promise<PrTimelineReadModel> { | ||
return this.queryBus.execute(query); | ||
} | ||
} |
9 changes: 8 additions & 1 deletion
9
libs/server/repository/core/domain-services/src/lib/repositories/pr.repository.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,14 @@ | ||
import { PrEntity } from '@pimp-my-pr/server/repository/core/domain'; | ||
import { PrStateType } from '@pimp-my-pr/server/repository/core/domain'; | ||
|
||
export const prRepositoryFactoryToken = Symbol('prRepositoryFactory'); | ||
|
||
export abstract class PrRepository { | ||
abstract findByRepositoryId(repositoryId: string, token: string): Promise<PrEntity[]>; | ||
abstract findByRepositoryId( | ||
repositoryId: string, | ||
token: string, | ||
prState?: PrStateType, | ||
page?: number, | ||
onPage?: number | ||
): Promise<PrEntity[]>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 2 additions & 1 deletion
3
libs/server/repository/core/domain/src/lib/entities/pr.entity.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
libs/server/repository/core/domain/src/lib/entities/timeline-record.entity.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
|
||
export class TimelineRecord { | ||
@ApiProperty() | ||
dataFrom: Date; | ||
@ApiProperty() | ||
sumCount: number; | ||
@ApiProperty() | ||
avgCount: number; | ||
@ApiProperty() | ||
avgWaitingTime: number; | ||
} |
7 changes: 7 additions & 0 deletions
7
...server/repository/core/domain/src/lib/exceptions/invalid-timeline-parameters.exception.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { CoreUnprocessableEntityException } from '@pimp-my-pr/server/shared/domain'; | ||
|
||
export class InvalidTimelineParametersException extends CoreUnprocessableEntityException { | ||
constructor(paramName: string) { | ||
super(`Invalid ${paramName}`); | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
libs/server/repository/core/domain/src/lib/interfaces/i-time-trackable.interface.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface ITimeTrackable { | ||
createdAt: Date; | ||
closedAt?: Date; | ||
} |
4 changes: 4 additions & 0 deletions
4
libs/server/repository/core/domain/src/lib/interfaces/timeline-bucket-item.interface.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface TimelineBucketItem<T> { | ||
entity: T; | ||
timeIn: number; | ||
} |
5 changes: 5 additions & 0 deletions
5
libs/server/repository/core/domain/src/lib/interfaces/timeline-bucket.interface.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { TimelineBucketItem } from './timeline-bucket-item.interface'; | ||
|
||
export interface TimelineBuckets<T> { | ||
[label: number]: TimelineBucketItem<T>[]; | ||
} |
4 changes: 4 additions & 0 deletions
4
libs/server/repository/core/domain/src/lib/interfaces/timeline-date-range.interface.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface TimelineDateRange { | ||
dateFrom: Date; | ||
dateTo: Date; | ||
} |
5 changes: 5 additions & 0 deletions
5
libs/server/repository/core/domain/src/lib/interfaces/timeline-division-base.interface.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { TimelineDateRange } from './timeline-date-range.interface'; | ||
|
||
export interface TimelineDivisionBase { | ||
[timestamp: number]: TimelineDateRange; | ||
} |
1 change: 1 addition & 0 deletions
1
libs/server/repository/core/domain/src/lib/types/pr-state.type.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type PrStateType = 'open' | 'closed' | 'all'; |
1 change: 1 addition & 0 deletions
1
libs/server/repository/core/domain/src/lib/types/timeline-step.type.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type TimelineStepType = 'day' | 'month' | 'week' | 'quarter' | 'year'; |
19 changes: 19 additions & 0 deletions
19
libs/server/repository/core/domain/src/lib/utils/iterator-to-array.util.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
export function iteratorToArray<T>(a: Iterator<T>): T[] { | ||
const result: T[] = []; | ||
let current = a.next(); | ||
while (!current.done) { | ||
result.push(current.value); | ||
current = a.next(); | ||
} | ||
return result; | ||
} | ||
|
||
export async function asyncIteratorToArray<T>(a: AsyncIterator<T>): Promise<T[]> { | ||
const result: T[] = []; | ||
let current = await a.next(); | ||
while (!current.done) { | ||
result.push(current.value); | ||
current = await a.next(); | ||
} | ||
return result; | ||
} |
11 changes: 11 additions & 0 deletions
11
libs/server/repository/core/domain/src/lib/utils/pad-number-to-places.util.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { padNumberToPlaces } from './pad-number-to-places.util'; | ||
|
||
describe('#padNumberToPlaces', () => { | ||
it('should does not change input if it is longer than specified', () => { | ||
expect(padNumberToPlaces(1000, 3)).toBe('1000'); | ||
}); | ||
|
||
it('should add zeros at he beginning if the number is shorter', () => { | ||
expect(padNumberToPlaces(56, 4)).toBe('0056'); | ||
}); | ||
}); |
2 changes: 2 additions & 0 deletions
2
libs/server/repository/core/domain/src/lib/utils/pad-number-to-places.util.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export const padNumberToPlaces = (num: number, places = 2): string => | ||
String(num).padStart(places, '0'); |
Oops, something went wrong.