Skip to content

Commit

Permalink
Remove assignee check API (#933)
Browse files Browse the repository at this point in the history
In a previous PR, there was a buggy implementation of
checking whether assignees are authorized to be assigned
due to lack of pagination. It makes the code unnecessarily
complex.

Let's remove the check altogether and just display the
"Validation Failed" message from GitHub to the user as it is.
  • Loading branch information
chunweii authored Apr 23, 2022
1 parent 5441407 commit b8a5a34
Show file tree
Hide file tree
Showing 11 changed files with 27 additions and 54 deletions.
2 changes: 1 addition & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ import { SharedModule } from './shared/shared.module';
{
provide: IssueService,
useFactory: IssueServiceFactory,
deps: [GithubService, UserService, PhaseService, ElectronService, DataService]
deps: [GithubService, UserService, PhaseService, ElectronService, DataService, LoggingService]
},
{
provide: ErrorHandler,
Expand Down
2 changes: 2 additions & 0 deletions src/app/core/services/error-handling.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export class ErrorHandlingService implements ErrorHandler {
this.handleHttpError(error, actionCallback);
} else if (error.constructor.name === 'RequestError') {
this.handleHttpError(error as RequestError, actionCallback);
} else if (typeof error === 'string') {
this.handleGeneralError(error);
} else {
this.handleGeneralError(error.message || JSON.stringify(error));
}
Expand Down
6 changes: 4 additions & 2 deletions src/app/core/services/factories/factory.issue.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DataService } from '../data.service';
import { ElectronService } from '../electron.service';
import { GithubService } from '../github.service';
import { IssueService } from '../issue.service';
import { LoggingService } from '../logging.service';
import { MockIssueService } from '../mocks/mock.issue.service';
import { PhaseService } from '../phase.service';
import { UserService } from '../user.service';
Expand All @@ -12,10 +13,11 @@ export function IssueServiceFactory(
userService: UserService,
phaseService: PhaseService,
electronService: ElectronService,
dataService: DataService
dataService: DataService,
logger: LoggingService
) {
if (AppConfig.test) {
return new MockIssueService(githubService, phaseService, dataService);
}
return new IssueService(githubService, userService, phaseService, electronService, dataService);
return new IssueService(githubService, userService, phaseService, electronService, dataService, logger);
}
22 changes: 0 additions & 22 deletions src/app/core/services/github.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,28 +259,6 @@ export class GithubService {
octokit.issues.updateLabel({ owner: ORG_NAME, repo: REPO, name: labelName, current_name: labelName, color: labelColor });
}

/**
* Checks if the given list of users are allowed to be assigned to an issue.
* @param assignees - GitHub usernames to be checked
*/
areUsersAssignable(assignees: string[]): Observable<void> {
return from(
octokit.issues.listAssignees({
owner: ORG_NAME,
repo: REPO
})
).pipe(
map(({ data }: { data: { login: string }[] }) => data.map(({ login }) => login)),
map((assignables: string[]) =>
assignees.forEach((assignee) => {
if (!assignables.includes(assignee)) {
throw new Error(`Cannot assign ${assignee} to the issue. Please check if ${assignee} is authorized.`);
}
})
)
);
}

closeIssue(id: number): Observable<GithubIssue> {
return from(octokit.issues.update({ owner: ORG_NAME, repo: REPO, issue_number: id, state: 'closed' })).pipe(
map((response: GithubResponse<GithubIssue>) => {
Expand Down
28 changes: 12 additions & 16 deletions src/app/core/services/issue.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { appVersion } from './application.service';
import { DataService } from './data.service';
import { ElectronService } from './electron.service';
import { GithubService } from './github.service';
import { LoggingService } from './logging.service';
import { PhaseService } from './phase.service';
import { UserService } from './user.service';

Expand Down Expand Up @@ -42,7 +43,8 @@ export class IssueService {
private userService: UserService,
private phaseService: PhaseService,
private electronService: ElectronService,
private dataService: DataService
private dataService: DataService,
private logger: LoggingService
) {
this.issues$ = new BehaviorSubject(new Array<Issue>());
}
Expand Down Expand Up @@ -134,11 +136,6 @@ export class IssueService {
.pipe(map((response: GithubIssue) => this.createIssueModel(response)));
}

updateIssueWithAssigneeCheck(issue: Issue): Observable<Issue> {
const assignees = this.phaseService.currentPhase === Phase.phaseModeration ? [] : issue.assignees;
return this.githubService.areUsersAssignable(assignees).pipe(flatMap(() => this.updateIssue(issue)));
}

updateIssue(issue: Issue): Observable<Issue> {
const assignees = this.phaseService.currentPhase === Phase.phaseModeration ? [] : issue.assignees;
return this.githubService
Expand All @@ -147,6 +144,10 @@ export class IssueService {
map((response: GithubIssue) => {
response.comments = issue.githubComments;
return this.createIssueModel(response);
}),
catchError((err) => {
this.logger.error(err); // Log full details of error first
return throwError(err.response.data.message); // More readable error message
})
);
}
Expand Down Expand Up @@ -190,16 +191,11 @@ export class IssueService {

createTeamResponse(issue: Issue): Observable<Issue> {
const teamResponse = issue.createGithubTeamResponse();
return this.githubService.areUsersAssignable(issue.assignees || []).pipe(
flatMap(() =>
this.githubService.createIssueComment(issue.id, teamResponse).pipe(
flatMap((githubComment: GithubComment) => {
issue.githubComments = [githubComment, ...issue.githubComments.filter((c) => c.id !== githubComment.id)];
return this.updateIssue(issue);
})
)
),
catchError((err) => throwError(err))
return this.githubService.createIssueComment(issue.id, teamResponse).pipe(
flatMap((githubComment: GithubComment) => {
issue.githubComments = [githubComment, ...issue.githubComments.filter((c) => c.id !== githubComment.id)];
return this.updateIssue(issue);
})
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/app/shared/issue/assignee/assignee.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class AssigneeComponent implements OnInit {
const newIssue = this.issue.clone(this.phaseService.currentPhase);
const oldAssignees = newIssue.assignees;
newIssue.assignees = this.assignees;
this.issueService.updateIssueWithAssigneeCheck(newIssue).subscribe(
this.issueService.updateIssue(newIssue).subscribe(
(updatedIssue: Issue) => {
this.issueUpdated.emit(updatedIssue);
// Update assignees of duplicate issues
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('IssuesFaultyComponent', () => {
const DUMMY_RESPONSE = 'dummy response';

beforeEach(() => {
issueService = new IssueService(null, null, null, null, null);
issueService = new IssueService(null, null, null, null, null, null);
issueService.updateLocalStore(dummyIssue);
issuesFaultyComponent = new IssuesFaultyComponent(issueService, userService, null);
issuesFaultyComponent.ngOnInit();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('IssuesPendingComponent', () => {
const dummyTeam: Team = TEAM_4;
let dummyIssue: Issue;
let issuesPendingComponent: IssuesPendingComponent;
const issueService: IssueService = new IssueService(null, null, null, null, null);
const issueService: IssueService = new IssueService(null, null, null, null, null, null);
const userService: UserService = new UserService(null, null);
userService.currentUser = USER_Q;
const DUMMY_DUPLICATE_ISSUE_ID = 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('IssuesRespondedComponent', () => {
const DUMMY_RESPONSE = 'dummy response';
let dummyIssue: Issue;

const issueService = new IssueService(null, null, null, null, null);
const issueService = new IssueService(null, null, null, null, null, null);
const userService = new UserService(null, null);
userService.currentUser = USER_Q;
const issuesRespondedComponent = new IssuesRespondedComponent(issueService, userService);
Expand Down
2 changes: 1 addition & 1 deletion tests/app/shared/issue-tables/search-filter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('search-filter', () => {
TABLE_COLUMNS.ASSIGNEE,
TABLE_COLUMNS.DUPLICATED_ISSUES
];
const issueService: IssueService = new IssueService(null, null, null, null, null);
const issueService: IssueService = new IssueService(null, null, null, null, null, null);

beforeEach(() => {
issueService.updateLocalStore(mediumSeverityIssueWithResponse);
Expand Down
11 changes: 3 additions & 8 deletions tests/app/shared/issue/assignee/assignee.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,7 @@ describe('AssigneeComponent', () => {
});

const phaseService: any = jasmine.createSpyObj('PhaseService', [], { currentPhase: Phase.phaseTeamResponse });
const issueService: any = jasmine.createSpyObj('IssueService', [
'getDuplicateIssuesFor',
'getLatestIssue',
'updateIssue',
'updateIssueWithAssigneeCheck'
]);
const issueService: any = jasmine.createSpyObj('IssueService', ['getDuplicateIssuesFor', 'getLatestIssue', 'updateIssue']);
const permissionsService: any = jasmine.createSpyObj('PermissionService', ['isIssueLabelsEditable']);

beforeEach(async(() => {
Expand Down Expand Up @@ -131,7 +126,7 @@ describe('AssigneeComponent', () => {
const updatedDuplicateIssue = duplicateIssue.clone(phaseService.currentPhase);
updatedDuplicateIssue.assignees = [testStudent.loginId];

expect(issueService.updateIssueWithAssigneeCheck).toHaveBeenCalledWith(updatedIssue);
expect(issueService.updateIssue).toHaveBeenCalledWith(updatedIssue);
expect(issueService.updateIssue).toHaveBeenCalledWith(updatedDuplicateIssue);
});

Expand All @@ -149,7 +144,7 @@ describe('AssigneeComponent', () => {

function dispatchClosedEvent() {
const matSelectElement: HTMLElement = debugElement.query(By.css('.mat-select')).nativeElement;
issueService.updateIssueWithAssigneeCheck.and.callFake((updatedIssue: Issue) => of(updatedIssue));
issueService.updateIssue.and.callFake((updatedIssue: Issue) => of(updatedIssue));
matSelectElement.dispatchEvent(new Event('closed'));
fixture.detectChanges();
}
Expand Down

0 comments on commit b8a5a34

Please sign in to comment.