-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Send an approved public kudos to the #kudos slack channel. #2776
Merged
mkimberlin
merged 18 commits into
develop
from
feature-2751/post-approved-kudos-to-slack
Jan 7, 2025
Merged
Changes from 14 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
219c330
Send an approved public kudos to the #kudos slack channel.
ocielliottc 26fe554
Merge branch 'develop' into feature-2751/post-approved-kudos-to-slack
ocielliottc bce7706
Disable this test in native due to reflexive nature of Gson and records.
ocielliottc 59ec115
Handle situation where daysBetween is zero.
ocielliottc 917e22f
Inspect the posted slack block during kudos approval.
ocielliottc c980529
Non-functional cleanup.
ocielliottc 74c159d
Merge branch 'develop' into feature-2751/post-approved-kudos-to-slack
mkimberlin d4bcf53
Merge branch 'develop' into feature-2751/post-approved-kudos-to-slack
mkimberlin 9e3414d
Merge branch 'develop' into feature-2751/post-approved-kudos-to-slack
mkimberlin 94c6d41
Merge branch 'develop' into feature-2751/post-approved-kudos-to-slack
mkimberlin 52643a9
Merge branch 'develop' into feature-2751/post-approved-kudos-to-slack
mkimberlin 35143ef
Merge branch 'develop' into feature-2751/post-approved-kudos-to-slack
mkimberlin fa6db81
Merge branch 'develop' into feature-2751/post-approved-kudos-to-slack
mkimberlin 219d89c
Adjusted the configuration of the slack integration
mkimberlin b48fab5
Fixed missed configuration point
mkimberlin 0040daa
Injected the KudosConverter and the SlackSearch
mkimberlin 40f6318
Removed channel reference from the slack posting
mkimberlin 50688d0
Added slack parameters to deployment workflows
mkimberlin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
...er/src/main/java/com/objectcomputing/checkins/notifications/social_media/SlackPoster.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.objectcomputing.checkins.notifications.social_media; | ||
|
||
import com.objectcomputing.checkins.configuration.CheckInsConfiguration; | ||
import io.micronaut.http.HttpRequest; | ||
import io.micronaut.http.HttpResponse; | ||
import io.micronaut.http.HttpStatus; | ||
import io.micronaut.http.client.BlockingHttpClient; | ||
import io.micronaut.http.client.HttpClient; | ||
|
||
import jakarta.inject.Singleton; | ||
import jakarta.inject.Inject; | ||
|
||
import java.util.List; | ||
|
||
@Singleton | ||
public class SlackPoster { | ||
@Inject | ||
private HttpClient slackClient; | ||
|
||
@Inject | ||
private CheckInsConfiguration configuration; | ||
|
||
public HttpResponse post(String slackBlock) { | ||
// See if we can have a webhook URL. | ||
String slackWebHook = configuration.getApplication().getNotifications().getSlack().getWebhookUrl(); | ||
if (slackWebHook != null) { | ||
// POST it to Slack. | ||
BlockingHttpClient client = slackClient.toBlocking(); | ||
HttpRequest<String> request = HttpRequest.POST(slackWebHook, | ||
slackBlock); | ||
return client.exchange(request); | ||
} | ||
return HttpResponse.status(HttpStatus.GONE, | ||
"Slack Webhook URL is not configured"); | ||
} | ||
} | ||
|
78 changes: 78 additions & 0 deletions
78
...er/src/main/java/com/objectcomputing/checkins/notifications/social_media/SlackSearch.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package com.objectcomputing.checkins.notifications.social_media; | ||
|
||
import com.objectcomputing.checkins.configuration.CheckInsConfiguration; | ||
import com.slack.api.model.block.LayoutBlock; | ||
import com.slack.api.Slack; | ||
import com.slack.api.methods.MethodsClient; | ||
import com.slack.api.model.Conversation; | ||
import com.slack.api.methods.SlackApiException; | ||
import com.slack.api.methods.request.conversations.ConversationsListRequest; | ||
import com.slack.api.methods.response.conversations.ConversationsListResponse; | ||
import com.slack.api.methods.request.users.UsersLookupByEmailRequest; | ||
import com.slack.api.methods.response.users.UsersLookupByEmailResponse; | ||
|
||
import jakarta.inject.Singleton; | ||
import jakarta.inject.Inject; | ||
|
||
import java.util.List; | ||
import java.io.IOException; | ||
|
||
import jnr.ffi.annotations.In; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
@Singleton | ||
public class SlackSearch { | ||
private static final Logger LOG = LoggerFactory.getLogger(SlackSearch.class); | ||
private static final String env = "SLACK_BOT_TOKEN"; | ||
|
||
@Inject | ||
private CheckInsConfiguration configuration; | ||
|
||
public String findChannelId(String channelName) { | ||
String token = configuration.getApplication().getNotifications().getSlack().getBotToken(); | ||
if (token != null) { | ||
try { | ||
MethodsClient client = Slack.getInstance().methods(token); | ||
ConversationsListResponse response = client.conversationsList( | ||
ConversationsListRequest.builder().build() | ||
); | ||
|
||
if (response.isOk()) { | ||
for (Conversation conversation: response.getChannels()) { | ||
if (conversation.getName().equals(channelName)) { | ||
return conversation.getId(); | ||
} | ||
} | ||
} | ||
} catch(IOException e) { | ||
LOG.error("SlackSearch.findChannelId: " + e.toString()); | ||
} catch(SlackApiException e) { | ||
LOG.error("SlackSearch.findChannelId: " + e.toString()); | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
public String findUserId(String userEmail) { | ||
String token = System.getenv(env); | ||
if (token != null) { | ||
try { | ||
MethodsClient client = Slack.getInstance().methods(token); | ||
UsersLookupByEmailResponse response = client.usersLookupByEmail( | ||
UsersLookupByEmailRequest.builder().email(userEmail).build() | ||
); | ||
|
||
if (response.isOk()) { | ||
return response.getUser().getId(); | ||
} | ||
} catch(IOException e) { | ||
LOG.error("SlackSearch.findUserId: " + e.toString()); | ||
} catch(SlackApiException e) { | ||
LOG.error("SlackSearch.findUserId: " + e.toString()); | ||
} | ||
} | ||
return null; | ||
} | ||
} | ||
|
130 changes: 130 additions & 0 deletions
130
server/src/main/java/com/objectcomputing/checkins/services/kudos/KudosConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package com.objectcomputing.checkins.services.kudos; | ||
|
||
import com.objectcomputing.checkins.notifications.social_media.SlackPoster; | ||
import com.objectcomputing.checkins.notifications.social_media.SlackSearch; | ||
import com.objectcomputing.checkins.services.kudos.kudos_recipient.KudosRecipientServices; | ||
import com.objectcomputing.checkins.services.kudos.kudos_recipient.KudosRecipient; | ||
import com.objectcomputing.checkins.services.memberprofile.MemberProfileServices; | ||
import com.objectcomputing.checkins.services.memberprofile.MemberProfileUtils; | ||
import com.objectcomputing.checkins.services.memberprofile.MemberProfile; | ||
|
||
import com.slack.api.model.block.LayoutBlock; | ||
import com.slack.api.model.block.RichTextBlock; | ||
import com.slack.api.model.block.element.RichTextElement; | ||
import com.slack.api.model.block.element.RichTextSectionElement; | ||
import com.slack.api.util.json.GsonFactory; | ||
import com.google.gson.Gson; | ||
|
||
import java.util.UUID; | ||
import java.util.List; | ||
import java.util.ArrayList; | ||
|
||
public class KudosConverter { | ||
private record InternalBlock( | ||
List<LayoutBlock> blocks | ||
) {} | ||
|
||
private final MemberProfileServices memberProfileServices; | ||
private final KudosRecipientServices kudosRecipientServices; | ||
|
||
public KudosConverter(MemberProfileServices memberProfileServices, | ||
KudosRecipientServices kudosRecipientServices) { | ||
this.memberProfileServices = memberProfileServices; | ||
this.kudosRecipientServices = kudosRecipientServices; | ||
} | ||
|
||
public String toSlackBlock(Kudos kudos) { | ||
// Build the message text out of the Kudos data. | ||
List<RichTextElement> content = new ArrayList<>(); | ||
|
||
// Look up the channel id from Slack | ||
String channelName = "kudos"; | ||
SlackSearch search = new SlackSearch(); | ||
String channelId = search.findChannelId(channelName); | ||
if (channelId == null) { | ||
content.add( | ||
RichTextSectionElement.Text.builder() | ||
.text("#" + channelName) | ||
.style(boldItalic()) | ||
.build() | ||
); | ||
} else { | ||
content.add( | ||
RichTextSectionElement.Channel.builder() | ||
.channelId(channelId) | ||
.style(limitedBoldItalic()) | ||
.build() | ||
); | ||
} | ||
content.add( | ||
RichTextSectionElement.Text.builder() | ||
.text(" from ") | ||
.style(boldItalic()) | ||
.build() | ||
); | ||
content.add(memberAsRichText(kudos.getSenderId())); | ||
content.addAll(recipients(kudos)); | ||
|
||
content.add( | ||
RichTextSectionElement.Text.builder() | ||
.text("\n" + kudos.getMessage() + "\n") | ||
.style(boldItalic()) | ||
.build() | ||
); | ||
|
||
// Bring it all together. | ||
RichTextSectionElement element = RichTextSectionElement.builder() | ||
.elements(content).build(); | ||
RichTextBlock richTextBlock = RichTextBlock.builder() | ||
.elements(List.of(element)).build(); | ||
InternalBlock block = new InternalBlock(List.of(richTextBlock)); | ||
Gson mapper = GsonFactory.createSnakeCase(); | ||
return mapper.toJson(block); | ||
} | ||
|
||
private RichTextSectionElement.TextStyle boldItalic() { | ||
return RichTextSectionElement.TextStyle.builder() | ||
.bold(true).italic(true).build(); | ||
} | ||
|
||
private RichTextSectionElement.LimitedTextStyle limitedBoldItalic() { | ||
return RichTextSectionElement.LimitedTextStyle.builder() | ||
.bold(true).italic(true).build(); | ||
} | ||
|
||
private RichTextElement memberAsRichText(UUID memberId) { | ||
// Look up the user id by email address on Slack | ||
SlackSearch search = new SlackSearch(); | ||
MemberProfile profile = memberProfileServices.getById(memberId); | ||
String userId = search.findUserId(profile.getWorkEmail()); | ||
if (userId == null) { | ||
String name = MemberProfileUtils.getFullName(profile); | ||
return RichTextSectionElement.Text.builder() | ||
.text("@" + name) | ||
.style(boldItalic()) | ||
.build(); | ||
} else { | ||
return RichTextSectionElement.User.builder() | ||
.userId(userId) | ||
.style(limitedBoldItalic()) | ||
.build(); | ||
} | ||
} | ||
|
||
private List<RichTextElement> recipients(Kudos kudos) { | ||
List<RichTextElement> list = new ArrayList<>(); | ||
List<KudosRecipient> recipients = | ||
kudosRecipientServices.getAllByKudosId(kudos.getId()); | ||
String separator = " to "; | ||
for (KudosRecipient recipient : recipients) { | ||
list.add(RichTextSectionElement.Text.builder() | ||
.text(separator) | ||
.style(boldItalic()) | ||
.build()); | ||
list.add(memberAsRichText(recipient.getMemberId())); | ||
separator = ", "; | ||
} | ||
return list; | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I missed this...fixing.