Skip to content

Commit

Permalink
Reopening closed discussions (#74)
Browse files Browse the repository at this point in the history
* reopning closed discussions

* fixed errors

* console messages updated

* minor update

* log message added

* logged message addition

* updates in close discussions block
  • Loading branch information
khushail authored Dec 1, 2023
1 parent 662119d commit 711a981
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 71 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/handle-stale-discussions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ jobs:
steps:
- name: Stale discussions action
uses: aws-github-ops/handle-stale-discussions@v1
with:
# This will disable auto closing discussions
close-stale-as-answered: false
close-stale-as-outdated: false
close-answered-discussions: false
close-locked-discussions: false
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ inputs:
page-size:
description: 'Page size count for discussions to be loaded per page'
required: false
open-discussion-instructions-text:
description: 'Comment to post when a discussion is opened'
required: false
18 changes: 17 additions & 1 deletion dist/GithubDiscussionClient.js

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions dist/generated/graphql.js

Large diffs are not rendered by default.

123 changes: 86 additions & 37 deletions dist/index.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/types/GithubDiscussionClient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ export declare class GithubDiscussionClient {
markDiscussionCommentAsAnswer(commentId: string): Promise<void>;
addAttentionLabelToDiscussion(discussionId: string): Promise<void>;
updateDiscussionComment(commentId: string, body: string): Promise<void>;
reopenDiscussion(discussionId: string): Promise<void>;
}
14 changes: 14 additions & 0 deletions dist/types/generated/graphql.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39132,6 +39132,7 @@ export declare const AddLabelToDiscussion: import("graphql").DocumentNode;
export declare const CloseDiscussionAsOutdated: import("graphql").DocumentNode;
export declare const CloseDiscussionAsResolved: import("graphql").DocumentNode;
export declare const MarkDiscussionCommentAsAnswer: import("graphql").DocumentNode;
export declare const ReopenDiscussion: import("graphql").DocumentNode;
export declare const UpdateDiscussionComment: import("graphql").DocumentNode;
export declare const GetAnswerableDiscussionId: import("graphql").DocumentNode;
export declare const GetCommentMetaData: import("graphql").DocumentNode;
Expand Down Expand Up @@ -39217,6 +39218,19 @@ export type MarkDiscussionCommentAsAnswerMutation = {
clientMutationId?: string | null;
} | null;
};
export type ReopenDiscussionMutationVariables = Exact<{
discussionId: Scalars['ID']['input'];
}>;
export type ReopenDiscussionMutation = {
__typename?: 'Mutation';
reopenDiscussion?: {
__typename?: 'ReopenDiscussionPayload';
discussion?: {
__typename?: 'Discussion';
id: string;
} | null;
} | null;
};
export type UpdateDiscussionCommentMutationVariables = Exact<{
commentId: Scalars['ID']['input'];
body: Scalars['String']['input'];
Expand Down
21 changes: 20 additions & 1 deletion src/GithubDiscussionClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as core from '@actions/core';
import * as github from '@actions/github';
import fetch from 'cross-fetch';
import { DiscussionConnection } from "@octokit/graphql-schema";
import { GetDiscussionCountQuery, GetDiscussionCountQueryVariables, GetDiscussionCount, GetDiscussionDataQuery, GetDiscussionDataQueryVariables, GetDiscussionData, GetAnswerableDiscussionIdQuery, GetAnswerableDiscussionIdQueryVariables, GetAnswerableDiscussionId, GetLabelIdQuery, GetLabelId, CloseDiscussionAsResolvedMutation, CloseDiscussionAsResolved, CloseDiscussionAsOutdatedMutation, CloseDiscussionAsOutdated, AddDiscussionCommentMutation, AddDiscussionComment, MarkDiscussionCommentAsAnswerMutation, MarkDiscussionCommentAsAnswer, AddLabelToDiscussionMutation, AddLabelToDiscussion, UpdateDiscussionCommentMutation, UpdateDiscussionComment, GetDiscussionCommentCountQuery, GetDiscussionCommentCount, DiscussionCommentConnection, GetCommentMetaDataQuery, GetCommentMetaDataQueryVariables, GetCommentMetaData, CloseDiscussionAsResolvedMutationVariables, CloseDiscussionAsOutdatedMutationVariables, AddDiscussionCommentMutationVariables, MarkDiscussionCommentAsAnswerMutationVariables, AddLabelToDiscussionMutationVariables, UpdateDiscussionCommentMutationVariables, GetDiscussionCommentCountQueryVariables, AddInstructionTextReplyMutation, AddInstructionTextReplyMutationVariables, AddInstructionTextReply } from "./generated/graphql";
import { GetDiscussionCountQuery, GetDiscussionCountQueryVariables, GetDiscussionCount, GetDiscussionDataQuery, GetDiscussionDataQueryVariables, GetDiscussionData, GetAnswerableDiscussionIdQuery, GetAnswerableDiscussionIdQueryVariables, GetAnswerableDiscussionId, GetLabelIdQuery, GetLabelId, CloseDiscussionAsResolvedMutation, CloseDiscussionAsResolved, CloseDiscussionAsOutdatedMutation, CloseDiscussionAsOutdated, AddDiscussionCommentMutation, AddDiscussionComment, MarkDiscussionCommentAsAnswerMutation, MarkDiscussionCommentAsAnswer, AddLabelToDiscussionMutation, AddLabelToDiscussion, UpdateDiscussionCommentMutation, UpdateDiscussionComment, GetDiscussionCommentCountQuery, GetDiscussionCommentCount, DiscussionCommentConnection, GetCommentMetaDataQuery, GetCommentMetaDataQueryVariables, GetCommentMetaData, CloseDiscussionAsResolvedMutationVariables, CloseDiscussionAsOutdatedMutationVariables, AddDiscussionCommentMutationVariables, MarkDiscussionCommentAsAnswerMutationVariables, AddLabelToDiscussionMutationVariables, UpdateDiscussionCommentMutationVariables, GetDiscussionCommentCountQueryVariables, AddInstructionTextReplyMutation, AddInstructionTextReplyMutationVariables, AddInstructionTextReply, ReopenDiscussionMutation, ReopenDiscussionMutationVariables, ReopenDiscussion } from "./generated/graphql";

export class GithubDiscussionClient {
private _githubClient: ApolloClient<NormalizedCacheObject>;
Expand Down Expand Up @@ -267,4 +267,23 @@ export class GithubDiscussionClient {
throw new Error(`Error updating discussion comment ${commentId}: ${result.errors}`);
}
}

public async reopenDiscussion(discussionId: string) {
try
{
const result = await this.githubClient.mutate<ReopenDiscussionMutation,ReopenDiscussionMutationVariables>({
mutation: ReopenDiscussion,
variables: {
discussionId
}
});

if (result.errors) {
throw new Error(`Error in reopening discussion ${discussionId}: ${result.errors}`);
}
}
catch(error){
core.warning(`Error in reopening discussion ${discussionId}: ${error}`);
}
}
}
16 changes: 16 additions & 0 deletions src/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41413,6 +41413,15 @@ export const MarkDiscussionCommentAsAnswer = gql`
}
}
`;
export const ReopenDiscussion = gql`
mutation ReopenDiscussion($discussionId: ID!) {
reopenDiscussion(input: {discussionId: $discussionId}) {
discussion {
id
}
}
}
`;
export const UpdateDiscussionComment = gql`
mutation UpdateDiscussionComment($commentId: ID!, $body: String!) {
updateDiscussionComment(input: {commentId: $commentId, body: $body}) {
Expand Down Expand Up @@ -41602,6 +41611,13 @@ export type MarkDiscussionCommentAsAnswerMutationVariables = Exact<{

export type MarkDiscussionCommentAsAnswerMutation = { __typename?: 'Mutation', markDiscussionCommentAsAnswer?: { __typename?: 'MarkDiscussionCommentAsAnswerPayload', clientMutationId?: string | null } | null };

export type ReopenDiscussionMutationVariables = Exact<{
discussionId: Scalars['ID']['input'];
}>;


export type ReopenDiscussionMutation = { __typename?: 'Mutation', reopenDiscussion?: { __typename?: 'ReopenDiscussionPayload', discussion?: { __typename?: 'Discussion', id: string } | null } | null };

export type UpdateDiscussionCommentMutationVariables = Exact<{
commentId: Scalars['ID']['input'];
body: Scalars['String']['input'];
Expand Down
88 changes: 58 additions & 30 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as core from '@actions/core';
import * as github from '@actions/github';
import { GithubDiscussionClient } from "./GithubDiscussionClient";
import { containsKeyword, containsNegativeReaction, containsPositiveReaction, exceedsDaysUntilStale, hasReplies, triggeredByNewComment, hasNonBotReply } from './util';
import { DiscussionCommentEdge } from './generated/graphql';
import { DiscussionCommentEdge, MarkDiscussionCommentAsAnswer } from './generated/graphql';

const PAGE_SIZE = parseInt(core.getInput('page-size', { required: false })) || 50;
const GITHUB_BOT = core.getInput('github-bot', { required: false}) || 'github-actions';
Expand All @@ -18,20 +18,22 @@ const CLOSE_STALE_AS_ANSWERED = closeStaleAsAnsweredInput.toLowerCase() === 'fal
const CLOSE_FOR_STALENESS_RESPONSE_TEXT = core.getInput('stale-response-text', { required: false })
|| 'Closing the discussion for staleness. Please open a new discussion if you have further concerns.';
const INSTRUCTIONS_TEXT = core.getInput('instructions-response-text', { required: false })
|| 'Hello! A team member has marked the above comment as the likely answer to this discussion thread. '
+ '\n \n * If you agree, please upvote that comment, or click on Mark as answer. I will automatically mark the comment as the answer next time I check. '
+ '\n \n * If this answer does not help you, please downvote the answer instead and let us know why it was not helpful. '
|| 'Hello! A team member has suggested the above comment as the likely answer to this discussion thread. '
+ '\n \n * If you agree, please upvote that comment, or click on Mark as answer. I will automatically mark the discussion as answered with upvoted comment, next time I check. '
+ '\n \n * If this answer does not help you, please downvote the answer instead and let us know why it was not helpful. '
+ 'I will add a label to this discussion to gain attention from the team.';
const OPEN_DISCUSSION_INSTRUCTION_TEXT = core.getInput('open-discussion-instructions-text', { required: false })
|| 'Hello! Reopening this discussion to make it searchable. ';

async function main() {
const githubClient = new GithubDiscussionClient();
await githubClient.initializeAttentionLabelId();
if (triggeredByNewComment()) {
if (github.context.payload.comment?.body.indexOf(PROPOSED_ANSWER_KEYWORD) >= 0) {
core.info('Comment created with proposed answer keyword. Adding instuctions reply to comment');
core.info('Proposed keyword found. Adding Bot Instuctions reply!!');
githubClient.addInstructionTextReply(INSTRUCTIONS_TEXT, github.context.payload.discussion!.node_id, github.context.payload.comment!.node_id);
} else {
core.info('Comment created without proposed answer keyword. No action needed');
core.info('Comment created without proposed answer keyword. No action needed!!');
}
} else {
await processDiscussions(githubClient);
Expand All @@ -41,7 +43,7 @@ async function main() {
export async function processDiscussions(githubClient: GithubDiscussionClient) {
const discussionCategoryIDList: string[] = await githubClient.getAnswerableDiscussionCategoryIDs();
if (discussionCategoryIDList.length === 0) {
core.info('No answerable discussions found. Exiting.');
core.info('No answerable discussions found. Exiting!!');
return;
}

Expand All @@ -57,23 +59,23 @@ export async function processDiscussions(githubClient: GithubDiscussionClient) {
for (const discussion of discussions.edges!) {
var discussionId = discussion?.node?.id ? discussion?.node?.id : "";
var discussionNum = discussion?.node?.number ? discussion.node.number : 0;
core.debug(`Processing discussionId: ${discussionId} with number: ${discussionNum} and bodyText: ${discussion?.node?.bodyText}`);
core.info(`Processing discussionId: ${discussionId}, discussion number: ${discussionNum} and bodyText: ${discussion?.node?.bodyText}`);
if (discussionId === "" || discussionNum === 0) {
core.warning(`Can not proceed checking discussion, discussionId is null!`);
core.warning(`Current discussion ID is NULL. Cannot proceed!!`);
continue;
}
else if (discussion?.node?.closed) {
core.debug(`Discussion ${discussionId} is closed, so no action needed.`);
continue;
core.info(`Reopening closed discussion: ${discussionId}`);
reopenClosedDiscussion(discussionId, githubClient);
}
else if (discussion?.node?.locked && CLOSE_LOCKED_DISCUSSIONS) {
core.info(`Discussion ${discussionId} is locked, closing it as resolved`);
githubClient.closeDiscussionAsResolved(discussionId);
core.info(`Discussion ${discussionId} is locked, keeping it open to make it searchable`);
//githubClient.closeDiscussionAsResolved(discussionId);
continue;
}
else if (discussion?.node?.answer != null && CLOSE_ANSWERED_DISCUSSIONS) {
core.info(`Discussion ${discussionId} is already answered, so closing it as resolved.`);
githubClient.closeDiscussionAsResolved(discussionId);
core.info(`Discussion ${discussionId} is already answered, so no action needed!!`);
//githubClient.closeDiscussionAsResolved(discussionId);
continue;
}
else {
Expand All @@ -93,54 +95,66 @@ export async function processComments(discussion: octokit.DiscussionEdge, github
if (commentCount !== 0) {
for (const comment of comments.edges!) {
const commentId = comment?.node?.id;
core.debug(`Processing comment ${commentId} with bodytext: ${comment?.node?.bodyText}`);
core.info(`Processing comment ${commentId} with bodytext: ${comment?.node?.bodyText}`);
if (!comment?.node?.bodyText || !comment.node.id) {
core.warning(`Comment body or id is null in discussion ${discussionId}, skipping comment!`);
core.warning(`Comment body/Id is Null in discussion ${discussionId}, skipping comment!`);
continue;
}
if (!containsKeyword(comment!, PROPOSED_ANSWER_KEYWORD)) {
core.debug(`No answer proposed on comment ${commentId}, no action needed!`);
core.info(`No answer proposed on comment ${commentId}, No action needed!!`);
continue;
}
else {
//core.info("debugging the code for getting reactions");
if (containsNegativeReaction(comment)) {
core.info(`Negative reaction received. Adding attention label to discussion ${discussionId} to receive further attention from a repository maintainer`);
core.info(`Negative reaction received. Adding attention label to discussion ${discussionId} `);
githubClient.addAttentionLabelToDiscussion(discussionId);
}
else if (containsPositiveReaction(comment)) {
core.info(`Positive reaction received. Marking discussion ${discussionId} as answered, and editing answer to remove proposed answer keyword`);
closeAndMarkAsAnswered(comment, discussionId, githubClient);
core.info(`Positive reaction received. Marking discussion ${discussionId} as answered, removing keyword`);
markDiscussionCommentAsAnswer(comment, discussionId, githubClient);
}
else if (!hasReplies(comment)) {
core.info(`Since this has no reply, adding instructions reply to comment ${commentId} in discussion ${discussionId}`);
core.info(`Since this has no reply, adding Bot Instructions text to comment ${commentId} in discussion ${discussionId}`);
githubClient.addInstructionTextReply(INSTRUCTIONS_TEXT, discussionId, commentId!);
}
else if (hasNonBotReply(comment, GITHUB_BOT)) {
core.info(`Discussion ${discussionId} has a reply, but not an instructions reply. Adding attention label`);
core.info(`Discussion ${discussionId} has Non-Bot Reply. Adding attention label`);
githubClient.addAttentionLabelToDiscussion(discussionId);
}
else if (exceedsDaysUntilStale(comment, DAYS_UNTIL_STALE)) {
if (CLOSE_STALE_AS_ANSWERED) {
core.info(`No one has responded or provided a reaction, closing discussion ${discussionId} as answered`);
closeAndMarkAsAnswered(comment, discussionId, githubClient);
} else {
core.info(`No one has responded or provided a reaction, closing discussion ${discussionId} with a comment`);
closeDiscussionForStaleness(discussionId, githubClient);
if (!CLOSE_STALE_AS_ANSWERED) {
core.info(`No one has responded or provided a reaction, marking discussion ${discussionId} as answered`);
markDiscussionCommentAsAnswer(comment, discussionId, githubClient);
//closeAndMarkAsAnswered(comment, discussionId, githubClient);
}
else {
core.info(`No action needed for discussion ${discussionId} !!`);
//closeDiscussionForStaleness(discussionId, githubClient);
}
}
else
{
core.info(`No action needed for discussion ${discussionId} as nothing is found`);
}
}
};
}
else {
core.debug(`No comments found for discussion ${discussionId}, No action needed!`);
core.debug(`No comments found for discussion ${discussionId}, No action needed!!`);
}
}

/* This function is no longer used since we are marking the discussion as answered instead of closing it
function closeDiscussionForStaleness(discussionId: string, githubClient: GithubDiscussionClient) {
githubClient.addCommentToDiscussion(discussionId, CLOSE_FOR_STALENESS_RESPONSE_TEXT);
githubClient.closeDiscussionAsOutdated(discussionId);
}
*/

//This functioon is no longer used since we are marking the discussion as answered instead of closing it
/*
function closeAndMarkAsAnswered(comment: DiscussionCommentEdge, discussionId: string, githubClient: GithubDiscussionClient) {
const bodyText = comment?.node?.bodyText!;
const commentId = comment?.node?.id!;
Expand All @@ -149,5 +163,19 @@ function closeAndMarkAsAnswered(comment: DiscussionCommentEdge, discussionId: st
githubClient.markDiscussionCommentAsAnswer(commentId);
githubClient.closeDiscussionAsResolved(discussionId);
}
*/

function markDiscussionCommentAsAnswer(comment: DiscussionCommentEdge, discussionId: string, githubClient: GithubDiscussionClient) {
const bodyText = comment?.node?.bodyText!;
const commentId = comment?.node?.id!;
const updatedAnswerText = bodyText.replace(PROPOSED_ANSWER_KEYWORD, 'Answer: ');
githubClient.updateDiscussionComment(commentId, updatedAnswerText);
githubClient.markDiscussionCommentAsAnswer(commentId);
}

function reopenClosedDiscussion(discussionId: string, githubClient: GithubDiscussionClient) {
githubClient.addCommentToDiscussion(discussionId, OPEN_DISCUSSION_INSTRUCTION_TEXT);
githubClient.reopenDiscussion(discussionId);
}

main();
7 changes: 7 additions & 0 deletions src/mutations/reopen-discussion.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mutation ReopenDiscussion($discussionId: ID!){
reopenDiscussion(input: {discussionId: $discussionId}) {
discussion {
id
}
}
}

0 comments on commit 711a981

Please sign in to comment.