Skip to content

Commit

Permalink
feat(pmp-api): add endpoint for getting reviewers statistics
Browse files Browse the repository at this point in the history
Refactor endpoint for getting users to return repository contributors.

Needed for #17
  • Loading branch information
wjanaszek authored and MaciejSikorski committed Jan 10, 2020
1 parent b092d32 commit 3e80a3f
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ export class RepositoryController {
return this.repositoryFacade.list();
}

@Get('users')
listUsers(): Promise<ListRepositoryUsersResponse> {
return this.repositoryFacade.listUsers();
@Get('contributors')
listContributors(): Promise<ListRepositoryUsersResponse> {
return this.repositoryFacade.listContributors();
}

@Get('reviewers')
listReviewers(): Promise<ListRepositoryUsersResponse> {
return this.repositoryFacade.listReviewers();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import { PmpApiApiServiceRepositoryDataAccessModule } from '@pimp-my-pr/pmp-api/
import { GetRepositoryPrsHandler } from './queries/handlers/get-repository-prs.handler';
import { GetPrChangesHandler } from './queries/handlers/get-pr-changes.handler';
import { GetUserPrsHandler } from './queries/handlers/get-user-prs.handler';
import { ListRepositoryUsersHandler } from './queries/handlers/list-repository-users.handler';
import { ListRepositoryContributorsHandler } from './queries/handlers/list-repository-contributors.handler';
import { PrsService } from './services/prs.service';
import { ListRepositoryReviewersHandler } from './queries/handlers/list-repository-reviewers.handler';

const QueryHandlers = [
ListRepositoriesHandler,
ListRepositoryUsersHandler,
ListRepositoryContributorsHandler,
ListRepositoryReviewersHandler,
GetRepositoryPrsHandler,
GetPrChangesHandler,
GetUserPrsHandler
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { PrsService } from '../../services/prs.service';
import { QueryBus } from '@nestjs/cqrs';
import { RepositoryDataService } from '@pimp-my-pr/pmp-api/api-service/repository/data-access';
import { GetUserPrsQuery } from '../get-user-prs.query';
import {
PrModel,
RepositoryModel,
RepositoryUserStatisticsReadModel,
UserModel
} from '@pimp-my-pr/pmp-api/api-service/repository/domain';

export abstract class BaseListRepositoryUsersHandler {
constructor(
protected prsService: PrsService,
protected queryBus: QueryBus,
protected repositoryRepository: RepositoryDataService
) {}

protected getRepositoryUsersStatistics(
users: UserModel[],
repositories: RepositoryModel[]
): Promise<RepositoryUserStatisticsReadModel[][]> {
return Promise.all(
users.map(user =>
this.queryBus
.execute<GetUserPrsQuery, PrModel[]>(new GetUserPrsQuery(user, repositories))
.then(prs =>
Promise.all(
prs.length
? repositories.map(repository =>
this.prsService
.getPrsWithChanges(repository, prs)
.then(
prsWithChanges =>
new RepositoryUserStatisticsReadModel(user, prsWithChanges)
)
)
: []
)
)
)
);
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
import { IQueryHandler, QueryBus, QueryHandler } from '@nestjs/cqrs';
import { ListRepositoryUsersQuery } from '../list-repository-users.query';
import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import { ListRepositoryContributorsQuery } from '../list-repository-contributors.query';
import {
PrModel,
RepositoryUserStatisticsReadModel
} from '@pimp-my-pr/pmp-api/api-service/repository/domain';
import { RepositoryDataService } from '@pimp-my-pr/pmp-api/api-service/repository/data-access';
import { BaseListRepositoryUsersHandler } from './base-list-repository-users.handler';
import { GetUserPrsQuery } from '../get-user-prs.query';
import { PrsService } from '../../services/prs.service';

@QueryHandler(ListRepositoryUsersQuery)
export class ListRepositoryUsersHandler
implements IQueryHandler<ListRepositoryUsersQuery, RepositoryUserStatisticsReadModel[]> {
constructor(
private prsService: PrsService,
private queryBus: QueryBus,
private repositoryRepository: RepositoryDataService
) {}

async execute(query: ListRepositoryUsersQuery): Promise<RepositoryUserStatisticsReadModel[]> {
@QueryHandler(ListRepositoryContributorsQuery)
export class ListRepositoryContributorsHandler extends BaseListRepositoryUsersHandler
implements IQueryHandler<ListRepositoryContributorsQuery, RepositoryUserStatisticsReadModel[]> {
async execute(
query: ListRepositoryContributorsQuery
): Promise<RepositoryUserStatisticsReadModel[]> {
const repositoryContributors = await this.repositoryRepository.getRepositoryContributors();
const repositories = await this.repositoryRepository.find();
const repositoryUsers = await this.repositoryRepository.getRepositoryUsers();

const result = await Promise.all(
repositoryUsers.map(user =>
repositoryContributors.map(user =>
this.queryBus
.execute<GetUserPrsQuery, PrModel[]>(new GetUserPrsQuery(user, repositories))
.then(prs =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { IQueryHandler, QueryBus, QueryHandler } from '@nestjs/cqrs';
import { ListRepositoryReviewersQuery } from '../list-repository-reviewers.query';
import {
PrModel,
RepositoryUserStatisticsReadModel
} from '@pimp-my-pr/pmp-api/api-service/repository/domain';
import { PrsService } from '../../services/prs.service';
import { RepositoryDataService } from '@pimp-my-pr/pmp-api/api-service/repository/data-access';
import { GetUserPrsQuery } from '../get-user-prs.query';

@QueryHandler(ListRepositoryReviewersQuery)
export class ListRepositoryReviewersHandler
implements IQueryHandler<ListRepositoryReviewersQuery, RepositoryUserStatisticsReadModel[]> {
constructor(
private prsService: PrsService,
private queryBus: QueryBus,
private repositoryRepository: RepositoryDataService
) {}

async execute(query: ListRepositoryReviewersQuery): Promise<RepositoryUserStatisticsReadModel[]> {
const repositoryReviewers = await this.repositoryRepository.getRepositoryReviewers();
const repositories = await this.repositoryRepository.find();
const result = await Promise.all(
repositoryReviewers.map(user =>
this.queryBus
.execute<GetUserPrsQuery, PrModel[]>(new GetUserPrsQuery(user, repositories))
.then(prs =>
Promise.all(
repositories.map(repository =>
this.prsService
.getPrsWithChanges(repository, prs)
.then(
prsWithChanges => new RepositoryUserStatisticsReadModel(user, prsWithChanges)
)
)
)
)
)
);

return result.flatMap<RepositoryUserStatisticsReadModel>(res => res);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export class ListRepositoryContributorsQuery {}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export class ListRepositoryReviewersQuery {}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
RepositoryStatisticsReadModel,
RepositoryUserStatisticsReadModel
} from '@pimp-my-pr/pmp-api/api-service/repository/domain';
import { ListRepositoryUsersQuery } from './queries/list-repository-users.query';
import { ListRepositoryContributorsQuery } from './queries/list-repository-contributors.query';
import { ListRepositoryReviewersQuery } from './queries/list-repository-reviewers.query';

@Injectable()
export class RepositoryFacade {
Expand All @@ -15,7 +16,11 @@ export class RepositoryFacade {
return this.queryBus.execute(new ListRepositoriesQuery());
}

listUsers(): Promise<RepositoryUserStatisticsReadModel[]> {
return this.queryBus.execute(new ListRepositoryUsersQuery());
listContributors(): Promise<RepositoryUserStatisticsReadModel[]> {
return this.queryBus.execute(new ListRepositoryContributorsQuery());
}

listReviewers(): Promise<RepositoryUserStatisticsReadModel[]> {
return this.queryBus.execute(new ListRepositoryReviewersQuery());
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { HttpService, Injectable } from '@nestjs/common';
import {
githubConfig,
PmpApiServiceConfigService
} from '@pimp-my-pr/pmp-api/shared/config';
import { githubConfig, PmpApiServiceConfigService } from '@pimp-my-pr/pmp-api/shared/config';
import { urlFactory } from '@valueadd/typed-urls';
import { map } from 'rxjs/operators';
import { AxiosResponse } from 'axios';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { GithubRepositoryMapper } from '../mappers/github-repository.mapper';
import { AxiosError, AxiosResponse } from 'axios';
import { GithubPrEntity } from '../domain/entities/github-pr.entity';
import { GithubPrMapper } from '../mappers/github-pr.mapper';
import { throwError } from 'rxjs';
import { Observable, throwError } from 'rxjs';
import { catchRequestExceptions } from '@pimp-my-pr/pmp-api/shared/util';
import { CoreException, CoreNotFoundException } from '@pimp-my-pr/pmp-api/shared/domain';
import { GithubUserEntity } from '../domain/entities/github-user.entity';
Expand All @@ -27,7 +27,7 @@ export class RepositoryDataService {
true
),
getRepositoryPrs: urlFactory<'fullName'>(githubConfig.apiUrl + '/repos/:fullName/pulls', true),
getRepositoryUsers: urlFactory<'owner' | 'title'>(
getRepositoryContributors: urlFactory<'owner' | 'title'>(
githubConfig.apiUrl + '/repos/:owner/:title/contributors',
true
)
Expand Down Expand Up @@ -67,26 +67,46 @@ export class RepositoryDataService {
}

getRepositoryPrs(repositoryFullName: string): Promise<PrModel[]> {
return this.getRepositoryPrsAsObservable(repositoryFullName).toPromise();
}

getRepositoryContributors(): Promise<UserModel[]> {
const owner = this.pmpApiServiceConfigService.getRepositoryOwner();
const title = this.pmpApiServiceConfigService.getRepositoryTitle();
return this.httpService
.get<GithubPrEntity>(this.endpoints.getRepositoryPrs.url({ fullName: repositoryFullName }))
.get<GithubUserEntity>(this.endpoints.getRepositoryContributors.url({ owner, title }))
.pipe(
map((res: AxiosResponse) => res.data),
map(prs => prs.map(pr => this.prMapper.mapFrom(pr))),
map(users => users.map(user => this.userMapper.mapFrom(user))),
catchRequestExceptions()
)
.toPromise();
}

getRepositoryUsers(): Promise<UserModel[]> {
getRepositoryReviewers(): Promise<UserModel[]> {
const owner = this.pmpApiServiceConfigService.getRepositoryOwner();
const title = this.pmpApiServiceConfigService.getRepositoryTitle();
return this.httpService
.get<GithubUserEntity>(this.endpoints.getRepositoryUsers.url({ owner, title }))
const repositoryTitle = this.pmpApiServiceConfigService.getRepositoryTitle();

return this.getRepositoryPrsAsObservable(`${owner}/${repositoryTitle}`)
.pipe(
map((res: AxiosResponse) => res.data),
map(users => users.map(user => this.userMapper.mapFrom(user))),
catchRequestExceptions()
map(prs =>
this.removeDuplicateUsers(prs.map(pr => pr.reviewers).flatMap<UserModel>(users => users))
)
)
.toPromise();
}

private getRepositoryPrsAsObservable(fullName: string): Observable<PrModel[]> {
return this.httpService
.get<GithubPrEntity[]>(this.endpoints.getRepositoryPrs.url({ fullName }))
.pipe(
map((res: AxiosResponse<GithubPrEntity[]>) => res.data),
map(prs => prs.map(pr => this.prMapper.mapFrom(pr))),
catchRequestExceptions()
);
}

private removeDuplicateUsers(users: UserModel[]): UserModel[] {
return [...new Map(users.map(obj => [JSON.stringify(obj), obj])).values()];
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"extends": "../../../../../tsconfig.json",
"compilerOptions": {
"types": ["node", "jest"]
"types": ["node", "jest"],
"lib": ["es2019"]
},
"include": ["**/*.ts"]
}

0 comments on commit 3e80a3f

Please sign in to comment.