Add the PR-side state mutations mirroring the issue-side slice: - `pr edit` applies title/body/base/milestone and the recomputed assignee list in one PATCH, with additive label endpoints and (per the ADR 0007 amendment) the dedicated requested-reviewers POST/DELETE endpoints for `--add-reviewer`/`--remove-reviewer`. - `pr close --comment` posts the comment after the PATCH and surfaces a comment-post failure; an already-closed or merged PR is an `already: true` no-op reporting the actual state. - `pr reopen` is an `already: true` no-op when already open. Extract the fetch-then-patch assignee merge into a shared `src/assignees.ts` (`mergeAssignees` + `assigneeLogins`), now used by both `issue edit` and `pr edit`.
45 lines
1.6 KiB
TypeScript
45 lines
1.6 KiB
TypeScript
/** The subset of a Gitea `User` this module reads: just the login, if present. */
|
|
interface AssigneeLike {
|
|
login?: string;
|
|
}
|
|
|
|
/**
|
|
* The logins of an entity's current assignees, dropping any without one. The
|
|
* shared read side of fetch-then-patch: `issue edit` and `pr edit` both take the
|
|
* `assignees` off a freshly fetched entity and feed the result to
|
|
* {@link mergeAssignees}.
|
|
*/
|
|
export function assigneeLogins(assignees: AssigneeLike[] | undefined): string[] {
|
|
return (assignees ?? []).flatMap((assignee) => (assignee.login ? [assignee.login] : []));
|
|
}
|
|
|
|
/**
|
|
* The full assignee login list to PATCH under fetch-then-patch semantics
|
|
* (ADR 0007): the entity's current assignees with the requested additions
|
|
* appended and removals dropped. Matching is case-insensitive and the result is
|
|
* de-duplicated, order-preserving, so a login already assigned never lands in the
|
|
* list twice. Shared by `issue edit` and `pr edit`, whose PATCH bodies both
|
|
* replace the whole assignee list rather than adding or removing individual
|
|
* entries.
|
|
*/
|
|
export function mergeAssignees(current: string[], add: string[], remove: string[]): string[] {
|
|
const removeSet = new Set(remove.map((login) => login.toLowerCase()));
|
|
const seen = new Set<string>();
|
|
const result: string[] = [];
|
|
const push = (login: string): void => {
|
|
const key = login.toLowerCase();
|
|
if (removeSet.has(key) || seen.has(key)) {
|
|
return;
|
|
}
|
|
seen.add(key);
|
|
result.push(login);
|
|
};
|
|
for (const login of current) {
|
|
push(login);
|
|
}
|
|
for (const login of add) {
|
|
push(login);
|
|
}
|
|
return result;
|
|
}
|