From d30b7d2e42ec6cc603999baed3638557d17e09a2 Mon Sep 17 00:00:00 2001 From: dhruvjoshi Date: Wed, 31 Jul 2024 17:26:50 +0530 Subject: [PATCH] INFRA-3304 | Dhruv | Uploads aws policy file to a channel and uses the permaLink for jit --- deployment_manifest.json | 4 ++ gi_deployment_manifest.json | 4 ++ .../portal/v2/jit/service/JitServiceImpl.java | 38 ++++++---- .../infra/portal/v2/jit/utils/JitUtil.java | 1 + .../portal/v2/jit/utils/SlackBotUtil.java | 32 --------- .../v2/slackbotclient/SlackBotClient.java | 70 +++++++++++++++++-- src/main/resources/application-dev.properties | 1 + src/main/resources/application.properties | 1 + 8 files changed, 102 insertions(+), 49 deletions(-) diff --git a/deployment_manifest.json b/deployment_manifest.json index 88569a29..299cd88a 100644 --- a/deployment_manifest.json +++ b/deployment_manifest.json @@ -130,6 +130,10 @@ "name": "JIT_COMMON_CHANNEL", "value": "$JIT_COMMON_CHANNEL" }, + { + "name": "JIT_POLICY_UPLOAD_CHANNEL", + "value": "$JIT_POLICY_UPLOAD_CHANNEL" + }, { "name": "GITHUB_CLOUD_OAUTH_TOKEN", "value": "$GITHUB_CLOUD_OAUTH_TOKEN" diff --git a/gi_deployment_manifest.json b/gi_deployment_manifest.json index 4d488715..c5712f38 100644 --- a/gi_deployment_manifest.json +++ b/gi_deployment_manifest.json @@ -100,6 +100,10 @@ { "name": "JIT_COMMON_CHANNEL", "value": "$JIT_COMMON_CHANNEL" + }, + { + "name": "JIT_POLICY_UPLOAD_CHANNEL", + "value": "$JIT_POLICY_UPLOAD_CHANNEL" } ], "namespace": "$NAMESPACE", diff --git a/src/main/java/com/navi/infra/portal/v2/jit/service/JitServiceImpl.java b/src/main/java/com/navi/infra/portal/v2/jit/service/JitServiceImpl.java index 85426051..bb055a9b 100644 --- a/src/main/java/com/navi/infra/portal/v2/jit/service/JitServiceImpl.java +++ b/src/main/java/com/navi/infra/portal/v2/jit/service/JitServiceImpl.java @@ -142,12 +142,15 @@ class JitServiceImpl implements JitService { private void postReviewerDmOnSlack( User reviewer, + String policyPermaLink, JitApproval jitApproval, SlackBotAttachment reviewMessage ) throws IOException { - var result = slackBotClient.postMessage(userService.getUsersSlackId(reviewer), - reviewMessage); - + var channelId = userService.getUsersSlackId(reviewer); + var result = slackBotClient.postMessage(channelId, reviewMessage); + if (policyPermaLink != null) { + slackBotClient.postPermaLinkMessage(channelId, policyPermaLink); + } jitApproval.setReviewerSlackMessageTimestamp(result.getTs()); jitApproval.setBotChannelId(result.getChannel()); jitApprovalsRepository.save(jitApproval); @@ -163,11 +166,14 @@ class JitServiceImpl implements JitService { private void postRequestorDmOnSlack( JitRequest jitRequest, + String policyPermaLink, SlackBotAttachment personalMessage ) throws IOException { - var result = slackBotClient.postMessage( - userService.getUsersSlackId(jitRequest.getRequestedFor()), personalMessage); - + var channelId = userService.getUsersSlackId(jitRequest.getRequestedFor()); + var result = slackBotClient.postMessage(channelId, personalMessage); + if (policyPermaLink != null) { + slackBotClient.postPermaLinkMessage(channelId, policyPermaLink); + } jitRequest.setRequestorSlackMessageTimestamp(result.getTs()); jitRequest.setBotChannelId(result.getChannel()); } @@ -198,9 +204,13 @@ class JitServiceImpl implements JitService { private void postChannelOnSlack( JitRequest jitRequest, + String policyPermaLink, SlackBotAttachment commonChannelMessage ) throws IOException { var result = slackBotClient.postMessage(commonChannelId, commonChannelMessage); + if (policyPermaLink != null) { + slackBotClient.postPermaLinkMessage(commonChannelId, policyPermaLink); + } jitRequest.setChannelSlackMessageTimestamp(result.getTs()); } @@ -359,13 +369,13 @@ class JitServiceImpl implements JitService { actionEnabled = false; } if (reviewerEmail.equals(onCallApproverEmail)) { - postRequestorDmOnSlack(jitRequest, + postRequestorDmOnSlack(jitRequest, null, slackBotUtil.getRequestorDm(jitRequest, false, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), SlackColor.APPROVED)); - postChannelOnSlack(jitRequest, + postChannelOnSlack(jitRequest, null, slackBotUtil.getChannelMessage(jitRequest, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), @@ -616,15 +626,19 @@ class JitServiceImpl implements JitService { }); pendingTeams.sort(String::compareTo); + final String uploadedPolicyPermaLink = jitRequest.getAwsPolicy() != null + ? slackBotClient.sendAwsPolicyDocumentToCommonChannel( + objectMapper.writeValueAsString(jitRequest.getAwsPolicy())) + : null; // send personal message to user with details on pending and approved reviewers JitRequest jitRequestWithId = jitRequestRepository.save(jitRequest); - postRequestorDmOnSlack(jitRequestWithId, + postRequestorDmOnSlack(jitRequestWithId, uploadedPolicyPermaLink, slackBotUtil.getRequestorDm(jitRequestWithId, true, pendingTeams, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), SlackColor.INFO)); // send group message to common channel with details on pending and approved reviewers - postChannelOnSlack(jitRequest, + postChannelOnSlack(jitRequest, uploadedPolicyPermaLink, slackBotUtil.getChannelMessage(jitRequest, pendingTeams, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), SlackColor.INFO)); @@ -633,8 +647,8 @@ class JitServiceImpl implements JitService { JitRequest finalJitRequest = jitRequest; jitApprovalsWithId.stream().forEach(jitApproval -> { try { - postReviewerDmOnSlack(jitApproval.getReviewer(), jitApproval, - slackBotUtil.getReviewerDm(finalJitRequest.getRequestedFor().getEmail(), + postReviewerDmOnSlack(jitApproval.getReviewer(), uploadedPolicyPermaLink, + jitApproval, slackBotUtil.getReviewerDm(finalJitRequest.getRequestedFor().getEmail(), finalJitRequest, jitApproval, true, SlackColor.INFO)); } catch (IOException e) { throw new RuntimeException(e); diff --git a/src/main/java/com/navi/infra/portal/v2/jit/utils/JitUtil.java b/src/main/java/com/navi/infra/portal/v2/jit/utils/JitUtil.java index edfe2264..4b3d4dee 100644 --- a/src/main/java/com/navi/infra/portal/v2/jit/utils/JitUtil.java +++ b/src/main/java/com/navi/infra/portal/v2/jit/utils/JitUtil.java @@ -20,4 +20,5 @@ public class JitUtil { jitRequestDto.setTeam(jitTeamOverrideMap.get(team)); } } + } diff --git a/src/main/java/com/navi/infra/portal/v2/jit/utils/SlackBotUtil.java b/src/main/java/com/navi/infra/portal/v2/jit/utils/SlackBotUtil.java index d4a7cda9..ad6dfe57 100644 --- a/src/main/java/com/navi/infra/portal/v2/jit/utils/SlackBotUtil.java +++ b/src/main/java/com/navi/infra/portal/v2/jit/utils/SlackBotUtil.java @@ -22,23 +22,6 @@ import org.springframework.stereotype.Component; @Component public class SlackBotUtil { - private final ObjectMapper objectMapper = new ObjectMapper(); - - private String prettyPrintJson(Map json) { - try { - ObjectWriter writer = objectMapper.writerWithDefaultPrettyPrinter(); - return writer.writeValueAsString(json); - } catch (Exception e) { - return json.toString(); - } - } - - private SlackMessageText createJsonTextBoxField(String title, Map json) { - String formattedJson = prettyPrintJson(json); - String formattedText = String.format("*%s*\n```%s```", title, formattedJson); - return new SlackMessageText(SlackMessageTextType.MARKDOWN, formattedText); - } - private SlackMessageText createTextBoxField(String title, String text) { return new SlackMessageText(SlackMessageTextType.MARKDOWN, @@ -180,12 +163,6 @@ public class SlackBotUtil { style, value, actionId); } - SlackBotMessageBlock generateAwsPolicyBlocks(Map awsPolicy) { - SlackMessageText awsPolicyText = createJsonTextBoxField("POLICY", awsPolicy); - return new SlackBotMessageBlock( - SlackMessageBlockType.SECTION, awsPolicyText, null, null); - } - public SlackBotAttachment getReviewerDm( String userEmail, JitRequest jitRequest, JitApproval jitApproval, boolean actionEnabled, SlackColor color @@ -204,9 +181,6 @@ public class SlackBotUtil { ArrayList blocks = new ArrayList<>(); blocks.add(reviewRequestSection); - if (jitRequest.getAwsPolicy() != null) { - blocks.add(generateAwsPolicyBlocks(jitRequest.getAwsPolicy())); - } if (reviewRequestAction != null) { blocks.add(reviewRequestAction); } @@ -242,9 +216,6 @@ public class SlackBotUtil { blocks.add(reviewRequestSection); blocks.add(dividerSection); blocks.add(requestDetailSection); - if (jitRequest.getAwsPolicy() != null) { - blocks.add(generateAwsPolicyBlocks(jitRequest.getAwsPolicy())); - } if (actionEnabled) { ArrayList elements = new ArrayList<>(); @@ -325,9 +296,6 @@ public class SlackBotUtil { blocks.add(infoSection); blocks.add(dividerSection); blocks.add(reviewRequestSection); - if (jitRequest.getAwsPolicy() != null) { - blocks.add(generateAwsPolicyBlocks(jitRequest.getAwsPolicy())); - } return new SlackBotAttachment(color.color, blocks); } } diff --git a/src/main/java/com/navi/infra/portal/v2/slackbotclient/SlackBotClient.java b/src/main/java/com/navi/infra/portal/v2/slackbotclient/SlackBotClient.java index b8103358..cb122d23 100644 --- a/src/main/java/com/navi/infra/portal/v2/slackbotclient/SlackBotClient.java +++ b/src/main/java/com/navi/infra/portal/v2/slackbotclient/SlackBotClient.java @@ -5,14 +5,18 @@ import com.slack.api.methods.MethodsClient; import com.slack.api.methods.SlackApiException; import com.slack.api.methods.response.chat.ChatPostMessageResponse; import com.slack.api.methods.response.files.FilesUploadResponse; -import com.slack.api.model.File; +import com.slack.api.methods.response.files.FilesUploadV2Response; +import com.slack.api.model.Attachment; import com.slack.api.model.User; +import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -25,9 +29,10 @@ public class SlackBotClient { private static final String SLACK_API_URL = "https://api.slack.com/methods/"; private static final String postMessage = "chat.postMessage"; + @Value("${jit.slack.policy.upload.channel.id}") String policyUploadChannelId; + @Value("${slackbot.token}") private String slackBotToken; - @Autowired private MethodsClient client; @@ -53,6 +58,38 @@ public class SlackBotClient { return userSlackIdMap; } + public String sendAwsPolicyDocumentToCommonChannel( + String awsPolicyDocument + ) throws IOException { + FilesUploadResponse uploadResponse = null; + try { + uploadResponse = client.filesUpload(req -> req + .token(slackBotToken) + .content(awsPolicyDocument) + .channels(List.of(policyUploadChannelId)) + .filename("AwsPolicyDocument.json") + .filetype("json") + ); + var policyPermaLink = uploadResponse.getFile().getPermalink(); + var result = client.chatPostMessage(r -> r + .token(slackBotToken) + .channel(policyUploadChannelId) + .attachments(List.of( + Attachment.builder() + .title("AWS Policy Document") + .titleLink(policyPermaLink) + .build() + )) + ); + if (!result.isOk()) { + throw new IOException("Unable to send policy document : " + result.getError()); + } + return policyPermaLink; + } catch (Exception e) { + throw new IOException("Unable to upload policy document : " + e.getMessage()); + } + } + public ChatPostMessageResponse postMessage( String channelId, SlackBotAttachment slackBotAttachment @@ -70,14 +107,37 @@ public class SlackBotClient { .attachmentsAsString(textJson) ); if (!result.isOk()) { - log.error("Unable to process Slack API request: {}", result.getError()); + throw new IOException("Unable to process Slack API request: " + result.getError()); } - } catch (IOException | SlackApiException e) { - log.error("error: {}", e.getMessage(), e); + } catch (Exception e) { + throw new IOException("Unable to process Slack API request: " + e.getMessage()); } return result; } + public ChatPostMessageResponse postPermaLinkMessage( + String channelId, + String policyPermaLink + ) throws IOException { + try { + var result = client.chatPostMessage(r -> r + .token(slackBotToken) + .channel(channelId) + .attachments(List.of( + Attachment.builder().title("AWS Policy Document").titleLink(policyPermaLink) + .build() + )) + ); + if (!result.isOk()) { + throw new IOException("Unable to process Slack API request: " + result.getError()); + } + return result; + } catch (Exception e) { + throw new IOException("Unable to process Slack API request: " + e.getMessage()); + } + } + + public void updateMessage(String channelId, SlackBotAttachment slackBotAttachment, String ts) throws IOException { try { diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index 63a684d1..fd7c4bac 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -36,6 +36,7 @@ slackbot.token=${SLACK_BOT_TOKEN:xoxb-format-12345} jit.dag.id=${JIT_DAG_ID:jit_dag} jit.oncall-approver.email=jit-slackbot@jit.com jit.slack.common.channel.id=${JIT_COMMON_CHANNEL:C06NDTBFA1G} +jit.slack.policy.upload.channel.id=${JIT_POLICY_UPLOAD_CHANNEL:C0000000000} jit.request.config.path=classpath:jit jit.team-override.map={'Kubernetes Platform': 'Infra'} #pipeline creation diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 0f295ea5..0f6756d3 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -80,6 +80,7 @@ jit.dag.id=${JIT_DAG_ID:jit_dag} jit.gocd.dag.id=${JIT_GOCD_DAG_ID:jit_gocd_dag} jit.oncall-approver.email=jit-slackbot@jit.com jit.slack.common.channel.id=${JIT_COMMON_CHANNEL:C0000000000} +jit.slack.policy.upload.channel.id=${JIT_POLICY_UPLOAD_CHANNEL:C0000000000} github.token=${GITHUB_CLOUD_OAUTH_TOKEN} gocd.pipelines.config=${GOCD_PIPELINES_CONFIG} vertical.owner.map={'lending':{'navi-data-science':'navi-data-science','default':'navi-medici'},'insurance':{'default':'navi-gi'},'sa':{'default':'navi-sa'},'amc':{'default':'navi-amc'},'navi-pay':{'default':'navi-pay'},'navi-ppl':{'default':'navi-ppl'}}