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: 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 062a6eb
Showing
46 changed files
with
686 additions
and
18 deletions.
There are no files selected for viewing
44 changes: 44 additions & 0 deletions
44
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,44 @@ | ||
import { ApiBearerAuth, ApiOkResponse, ApiQuery, ApiTags } from '@nestjs/swagger'; | ||
import { Controller, Get, Param, Query, UseGuards } 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 { TimelineStepType } from '@pimp-my-pr/server/repository/core/domain'; | ||
|
||
@ApiTags('timeline') | ||
@UseGuards(AuthGuard) | ||
@ApiBearerAuth() | ||
@Controller('timeline') | ||
export class TimelineController { | ||
constructor(private timelineFacade: TimelineFacade) {} | ||
|
||
@ApiQuery({ name: 'step', enum: ['day', 'week', 'month', 'quarter', 'year'] }) | ||
@ApiQuery({ name: 'dateTimestamp', type: Number }) | ||
@ApiQuery({ name: 'queryEndTimestamp', type: Number }) | ||
@ApiQuery({ name: 'count', type: Number }) | ||
@ApiOkResponse({ type: [PrTimelineReadModel] }) | ||
@Get('pr/:repositoryId') | ||
listRepositories( | ||
@Credentials() credentials: RequestCredentials, | ||
@Param('repositoryId') repositoryId: string, | ||
@Query('step') step: TimelineStepType, | ||
@Query('dateTimestamp') dateTimestamp: number, | ||
@Query('queryEndTimestamp') queryEndTimeStamp: number, | ||
@Query('count') count: number | ||
): Promise<PrTimelineReadModel> { | ||
return this.timelineFacade.getPrTimeLine( | ||
new GetPrTimelineQuery( | ||
step, | ||
Number(dateTimestamp), | ||
Number(count), | ||
credentials.token, | ||
repositoryId, | ||
credentials.platform, | ||
Number(queryEndTimeStamp) | ||
) | ||
); | ||
} | ||
} |
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
18 changes: 18 additions & 0 deletions
18
...core/application-services/src/lib/queries/get-pr-timeline/get-pr-timeline.handler.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,18 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { GetPrTimelineHandler } from './get-pr-timeline.handler'; | ||
|
||
describe('GetPrTimelineHandler', () => { | ||
let handler: GetPrTimelineHandler; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [GetPrTimelineHandler] | ||
}).compile(); | ||
|
||
handler = module.get<GetPrTimelineHandler>(GetPrTimelineHandler); | ||
}); | ||
|
||
test('creates itself', () => { | ||
expect(handler).toBeDefined(); | ||
}); | ||
}); |
39 changes: 39 additions & 0 deletions
39
...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,39 @@ | ||
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 { | ||
getTimeLineHistory, | ||
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, dateTimestamp, count, token, platform, queryEndTimeStamp } = 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, | ||
new Date(queryEndTimeStamp) | ||
); | ||
const records = getTimeLineHistory(prs, step, new Date(dateTimestamp), count); | ||
return prTimelineModelFactory(records, query); | ||
} | ||
} |
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 dateTimestamp: number, | ||
public count: number, | ||
public token: string, | ||
public repositoryId: string, | ||
public platform: Platform, | ||
public queryEndTimeStamp: number | ||
) {} | ||
} |
17 changes: 17 additions & 0 deletions
17
...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,17 @@ | ||
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({ | ||
type: [TimelineRecord] | ||
}) | ||
data: TimelineRecord[]; | ||
} |
16 changes: 16 additions & 0 deletions
16
...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,16 @@ | ||
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 | ||
): PrTimelineReadModel => { | ||
return { | ||
data: prRecords, | ||
step: query.step, | ||
dateFrom: new Date(Number(query.dateTimestamp)) | ||
}; | ||
}; |
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() | ||
label: number; | ||
@ApiProperty() | ||
sumCount: number; | ||
@ApiProperty() | ||
avgCount: number; | ||
@ApiProperty() | ||
avgWaitingTime: number; | ||
} |
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'); |
33 changes: 33 additions & 0 deletions
33
...repository/core/domain/src/lib/utils/timeline/decrease-date-by-timeline-step.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,33 @@ | ||
import { decreaseDateByTimelineStep } from './decrease-date-by-timeline-step.util'; | ||
|
||
describe('#decreaseDateByTimelineStep', () => { | ||
it('should subtract n days', () => { | ||
expect(decreaseDateByTimelineStep(new Date('1995-12-17T00:00:00'), 'day', 7)).toEqual( | ||
new Date('1995-12-10T00:00:00') | ||
); | ||
}); | ||
it('should subtract n weeks', () => { | ||
expect(decreaseDateByTimelineStep(new Date('1995-12-17T00:00:00'), 'week', 2)).toEqual( | ||
new Date('1995-12-03T00:00:00') | ||
); | ||
}); | ||
it('should subtract n months', () => { | ||
expect(decreaseDateByTimelineStep(new Date('1995-12-17T00:00:00'), 'month', 7)).toEqual( | ||
new Date('1995-05-01T00:00:00') | ||
); | ||
}); | ||
it('should subtract n quarters', () => { | ||
expect(decreaseDateByTimelineStep(new Date('1995-12-17T00:00:00'), 'quarter', 2)).toEqual( | ||
new Date('1995-06-01T00:00:00') | ||
); | ||
}); | ||
it('should subtract n days', () => { | ||
expect(decreaseDateByTimelineStep(new Date('2000-12-17T00:00:00'), 'year', 5)).toEqual( | ||
new Date('1995-12-01T00:00:00') | ||
); | ||
}); | ||
it('should return new object', () => { | ||
const date = new Date('1995-12-17T00:00:00'); | ||
expect(decreaseDateByTimelineStep(date, 'day', 5)).not.toBe(date); | ||
}); | ||
}); |
22 changes: 22 additions & 0 deletions
22
...rver/repository/core/domain/src/lib/utils/timeline/decrease-date-by-timeline-step.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,22 @@ | ||
import { TimelineStepType } from '../../types/timeline-step.type'; | ||
import { subtractDaysFromDate } from './subtract-days-from-date.util'; | ||
import { subtractMonthsFromDate } from './subtract-months-from-date.util'; | ||
|
||
export const decreaseDateByTimelineStep = ( | ||
date: Date, | ||
step: TimelineStepType, | ||
numSteps: number | ||
): Date => { | ||
switch (step) { | ||
case 'day': | ||
return subtractDaysFromDate(date, numSteps); | ||
case 'week': | ||
return subtractDaysFromDate(date, 7 * numSteps); | ||
case 'month': | ||
return subtractMonthsFromDate(date, numSteps); | ||
case 'quarter': | ||
return subtractMonthsFromDate(date, 3 * numSteps); | ||
case 'year': | ||
return subtractMonthsFromDate(date, 12 * numSteps); | ||
} | ||
}; |
Oops, something went wrong.