Skip to content

Commit bab2afc

Browse files
authored
Enable assigning to Copilot (#6804)
* Enable assigning to Copilot Part of #6793 Part of #6742 * Fix wrong copilot string in PR body
1 parent 5738963 commit bab2afc

12 files changed

Lines changed: 190 additions & 53 deletions

src/github/createPRViewProvider.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ export abstract class BaseCreatePullRequestViewProvider<T extends BasePullReques
278278
return;
279279
}
280280
try {
281+
// TODO: We need to resolve the user and then use the replace function.
281282
await pr.addAssignees([resolved]);
282283
} catch (e) {
283284
Logger.error(`Unable to assign pull request to user ${resolved}.`, BaseCreatePullRequestViewProvider.ID);
@@ -298,7 +299,7 @@ export abstract class BaseCreatePullRequestViewProvider<T extends BasePullReques
298299

299300
private async setAssignees(pr: PullRequestModel, assignees: IAccount[]): Promise<void> {
300301
if (assignees.length) {
301-
await pr.addAssignees(assignees.map(assignee => assignee.login));
302+
await pr.replaceAssignees(assignees);
302303
} else {
303304
await this.autoAssign(pr);
304305
}

src/github/githubRepository.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
PullRequestTemplatesResponse,
3939
RepoProjectsResponse,
4040
RevertPullRequestResponse,
41+
SuggestedActorsResponse,
4142
ViewerPermissionResponse,
4243
} from './graphql';
4344
import {
@@ -1207,29 +1208,46 @@ export class GitHubRepository extends Disposable {
12071208

12081209
do {
12091210
try {
1210-
const result: { data: AssignableUsersResponse } = await query<AssignableUsersResponse>({
1211-
query: schema.GetAssignableUsers,
1212-
variables: {
1213-
owner: remote.owner,
1214-
name: remote.repositoryName,
1215-
first: 100,
1216-
after: after,
1217-
},
1218-
}, true); // we ignore SAML errors here because this query can happen at startup
1211+
let result: { data: AssignableUsersResponse | SuggestedActorsResponse } | undefined;
1212+
if (schema.GetSuggestedActors) {
1213+
result = await query<SuggestedActorsResponse>({
1214+
query: schema.GetSuggestedActors,
1215+
variables: {
1216+
owner: remote.owner,
1217+
name: remote.repositoryName,
1218+
capabilities: ['CAN_BE_ASSIGNED'],
1219+
first: 100,
1220+
after: after,
1221+
},
1222+
});
1223+
1224+
} else {
1225+
result = await query<AssignableUsersResponse>({
1226+
query: schema.GetAssignableUsers,
1227+
variables: {
1228+
owner: remote.owner,
1229+
name: remote.repositoryName,
1230+
first: 100,
1231+
after: after,
1232+
},
1233+
}, true); // we ignore SAML errors here because this query can happen at startup
1234+
}
12191235

12201236
if (result.data.repository === null) {
12211237
Logger.error('Unexpected null repository when getting assignable users', this.id);
12221238
return [];
12231239
}
12241240

1241+
const users = (result.data as AssignableUsersResponse).repository?.assignableUsers ?? (result.data as SuggestedActorsResponse).repository?.suggestedActors;
1242+
12251243
ret.push(
1226-
...result.data.repository.assignableUsers.nodes.map(node => {
1244+
...users?.nodes.map(node => {
12271245
return parseAccount(node, this);
12281246
}),
12291247
);
12301248

1231-
hasNextPage = result.data.repository.assignableUsers.pageInfo.hasNextPage;
1232-
after = result.data.repository.assignableUsers.pageInfo.endCursor;
1249+
hasNextPage = users?.pageInfo.hasNextPage;
1250+
after = users?.pageInfo.endCursor;
12331251
} catch (e) {
12341252
Logger.debug(`Unable to fetch assignable users: ${e}`, this.id);
12351253
if (

src/github/graphql.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,16 @@ export interface AssignableUsersResponse {
300300
rateLimit: RateLimit;
301301
}
302302

303+
export interface SuggestedActorsResponse {
304+
repository: {
305+
suggestedActors: {
306+
nodes: Actor[];
307+
pageInfo: PageInfo;
308+
};
309+
} | null;
310+
rateLimit: RateLimit;
311+
}
312+
303313
export interface OrganizationTeamsCountResponse {
304314
organization: {
305315
teams: {

src/github/interface.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ export interface MergeQueueEntry {
103103
}
104104

105105
export function reviewerId(reviewer: ITeam | IAccount): string {
106-
return isTeam(reviewer) ? reviewer.id : reviewer.login;
106+
// We can literally get different login values for copilot depending on where it's coming from (already assignee vs suggested assingee)
107+
return isTeam(reviewer) ? reviewer.id : (reviewer.specialDisplayName ?? reviewer.login);
107108
}
108109

109110
export function reviewerLabel(reviewer: ITeam | IAccount | IActor | any): string {

src/github/issueModel.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -370,17 +370,46 @@ export class IssueModel<TItem extends Issue = Issue> {
370370
}
371371
}
372372

373-
async addAssignees(assignees: string[]): Promise<void> {
373+
async replaceAssignees(allAssignees: IAccount[]): Promise<void> {
374+
Logger.debug(`Replace assignees of issue #${this.number} - enter`, IssueModel.ID);
375+
const { mutate, schema } = await this.githubRepository.ensure();
376+
377+
try {
378+
if (schema.ReplaceActorsForAssignable) {
379+
const assigneeIds = allAssignees.map(assignee => assignee.id);
380+
await mutate({
381+
mutation: schema.ReplaceActorsForAssignable,
382+
variables: {
383+
input: {
384+
actorIds: assigneeIds,
385+
assignableId: this.graphNodeId
386+
}
387+
}
388+
});
389+
} else {
390+
const addAssignees = allAssignees.map(assignee => assignee.login);
391+
const removeAssignees = (this.assignees?.filter(currentAssignee => !allAssignees.find(newAssignee => newAssignee.login === currentAssignee.login)) ?? []).map(assignee => assignee.login);
392+
await this.addAssignees(addAssignees);
393+
await this.deleteAssignees(removeAssignees);
394+
}
395+
this.assignees = allAssignees;
396+
} catch (e) {
397+
Logger.error(e, IssueModel.ID);
398+
}
399+
Logger.debug(`Replace assignees of issue #${this.number} - done`, IssueModel.ID);
400+
}
401+
402+
async addAssignees(assigneesToAdd: string[]): Promise<void> {
374403
const { octokit, remote } = await this.githubRepository.ensure();
375404
await octokit.call(octokit.api.issues.addAssignees, {
376405
owner: remote.owner,
377406
repo: remote.repositoryName,
378407
issue_number: this.number,
379-
assignees,
408+
assignees: assigneesToAdd,
380409
});
381410
}
382411

383-
async deleteAssignees(assignees: string[]): Promise<void> {
412+
private async deleteAssignees(assignees: string[]): Promise<void> {
384413
const { octokit, remote } = await this.githubRepository.ensure();
385414
await octokit.call(octokit.api.issues.removeAssignees, {
386415
owner: remote.owner,

src/github/issueOverview.ts

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -153,14 +153,7 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
153153
body: issue.body,
154154
bodyHTML: issue.bodyHTML,
155155
labels: issue.item.labels,
156-
author: {
157-
login: issue.author.login,
158-
name: issue.author.name,
159-
avatarUrl: issue.userAvatar,
160-
url: issue.author.url,
161-
id: issue.author.id,
162-
accountType: issue.author.accountType,
163-
},
156+
author: issue.author,
164157
state: issue.state,
165158
events: timelineEvents,
166159
continueOnGitHub: this.continueOnGitHub(),
@@ -383,11 +376,7 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
383376

384377
if (allAssignees) {
385378
const newAssignees: IAccount[] = allAssignees.map(item => item.user);
386-
const removeAssignees: IAccount[] = this._item.assignees?.filter(currentAssignee => !newAssignees.find(newAssignee => newAssignee.login === currentAssignee.login)) ?? [];
387-
this._item.assignees = newAssignees;
388-
389-
await this._item.addAssignees(newAssignees.map(assignee => assignee.login));
390-
await this._item.deleteAssignees(removeAssignees.map(assignee => assignee.login));
379+
await this._item.replaceAssignees(newAssignees);
391380
await this._replyMessage(message, {
392381
assignees: newAssignees,
393382
});
@@ -449,8 +438,8 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
449438
const currentUser = await this._folderRepositoryManager.getCurrentUser();
450439
const alreadyAssigned = this._item.assignees?.find(user => user.login === currentUser.login);
451440
if (!alreadyAssigned) {
452-
this._item.assignees = this._item.assignees?.concat(currentUser);
453-
await this._item.addAssignees([currentUser.login]);
441+
const newAssigness = (this._item.assignees ?? []).concat(currentUser);
442+
await this._item.replaceAssignees(newAssigness);
454443
}
455444
this._replyMessage(message, {
456445
assignees: this._item.assignees,

src/github/queries.gql

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
# Queries that are also in the limit file, but are not limited (by scope or API availability) here
99

10-
1110
fragment Node on Node {
1211
id
1312
}
@@ -52,6 +51,7 @@ fragment IssueBase on Issue {
5251
title
5352
titleHTML
5453
author {
54+
...Node
5555
...Actor
5656
...User
5757
...Organization
@@ -67,6 +67,7 @@ fragment IssueBase on Issue {
6767
}
6868
assignees(first: 10) {
6969
nodes {
70+
...Node
7071
...Actor
7172
...User
7273
}
@@ -127,6 +128,7 @@ fragment PullRequestFragment on PullRequest {
127128
title
128129
titleHTML
129130
author {
131+
...Node
130132
...Actor
131133
...User
132134
...Organization
@@ -142,6 +144,7 @@ fragment PullRequestFragment on PullRequest {
142144
}
143145
assignees(first: 10) {
144146
nodes {
147+
...Node
145148
...Actor
146149
...User
147150
}
@@ -334,6 +337,25 @@ mutation RevertPullRequest($input: RevertPullRequestInput!) {
334337

335338
# Queries that only exist in this file and in extra
336339

340+
query GetSuggestedActors($owner: String!, $name: String!, $capabilities: [RepositorySuggestedActorFilter!]!, $first: Int!, $after: String) {
341+
repository(owner: $owner, name: $name) {
342+
suggestedActors(first: $first, after: $after, capabilities: $capabilities) {
343+
nodes {
344+
...Node
345+
...Actor
346+
...User
347+
}
348+
pageInfo {
349+
hasNextPage
350+
endCursor
351+
}
352+
}
353+
}
354+
rateLimit {
355+
...RateLimit
356+
}
357+
}
358+
337359
mutation DequeuePullRequest($input: DequeuePullRequestInput!) {
338360
dequeuePullRequest(input: $input) {
339361
mergeQueueEntry {
@@ -348,4 +370,22 @@ mutation EnqueuePullRequest($input: EnqueuePullRequestInput!) {
348370
...MergeQueueEntryFragment
349371
}
350372
}
373+
}
374+
375+
mutation ReplaceActorsForAssignable($input: ReplaceActorsForAssignableInput!) {
376+
replaceActorsForAssignable(input: $input) {
377+
assignable {
378+
assignees(first: 100) {
379+
nodes {
380+
...Node
381+
...Actor
382+
...User
383+
}
384+
pageInfo {
385+
hasNextPage
386+
endCursor
387+
}
388+
}
389+
}
390+
}
351391
}

src/github/queriesExtra.gql

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ fragment IssueBase on Issue {
5252
title
5353
titleHTML
5454
author {
55+
...Node
5556
...Actor
5657
...User
5758
...Organization
@@ -67,6 +68,7 @@ fragment IssueBase on Issue {
6768
}
6869
assignees(first: 10) {
6970
nodes {
71+
...Node
7072
...Actor
7173
...User
7274
}
@@ -136,6 +138,7 @@ fragment PullRequestFragment on PullRequest {
136138
title
137139
titleHTML
138140
author {
141+
...Node
139142
...Actor
140143
...User
141144
...Organization
@@ -151,6 +154,7 @@ fragment PullRequestFragment on PullRequest {
151154
}
152155
assignees(first: 10) {
153156
nodes {
157+
...Node
154158
...Actor
155159
...User
156160
}
@@ -334,6 +338,25 @@ query PullRequestMergeabilityMergeRequirements($owner: String!, $name: String!,
334338
}
335339
}
336340

341+
query GetSuggestedActors($owner: String!, $name: String!, $capabilities: [RepositorySuggestedActorFilter!]!, $first: Int!, $after: String) {
342+
repository(owner: $owner, name: $name) {
343+
suggestedActors(first: $first, after: $after, capabilities: $capabilities) {
344+
nodes {
345+
...Node
346+
...Actor
347+
...User
348+
}
349+
pageInfo {
350+
hasNextPage
351+
endCursor
352+
}
353+
}
354+
}
355+
rateLimit {
356+
...RateLimit
357+
}
358+
}
359+
337360
mutation CreatePullRequest($input: CreatePullRequestInput!) {
338361
createPullRequest(input: $input) {
339362
pullRequest {
@@ -342,6 +365,24 @@ mutation CreatePullRequest($input: CreatePullRequestInput!) {
342365
}
343366
}
344367

368+
mutation ReplaceActorsForAssignable($input: ReplaceActorsForAssignableInput!) {
369+
replaceActorsForAssignable(input: $input) {
370+
assignable {
371+
assignees(first: 100) {
372+
nodes {
373+
...Node
374+
...Actor
375+
...User
376+
}
377+
pageInfo {
378+
hasNextPage
379+
endCursor
380+
}
381+
}
382+
}
383+
}
384+
}
385+
345386
# Queries that only exist in this file
346387

347388
query GetRepoProjects($owner: String!, $name: String!) {

0 commit comments

Comments
 (0)