Files
houston-be/common/util/common_util.go
Amitesh Vijaykumar Magar 47193fe64e NTP-40086 | Using GenericFilter (#480)
* NTP-24894 | Text change in slack modal.

* NTP-24894 | Adding more constant teamTypes.

* NTP-24894 | Added functions to allow for teamType update.

* NTP-24894 | Added validation in addTeamRequest.

* NTP-24894 | Comment resolution and new functions (#477)

* NTP-24894 | Added filter on Reporting teams.

* NTP-24894 | Added new function for all teams fetch in team service.

* NTP-24894 | sending all teams in response for slack modal.

* NTP-24894 | Added tests for UpdateTeam flow.

* NTP-24894 | Fixing logic and using util.

* NTP-24894 | Using enum-map for teamType validation.

* NTP-24894 | Variable name change.

* NTP-24894 | Adding teamType to TeamDTOs when fetching using productid.

* NTP-24894 | Revisions in function for team population.

* NTP-24894 | Removed unneeded function.

* NTP-24894 | Removed unneeded interface of function

* NTP-24894 | Updated test scripts.

* NTP-24894 | Logic changes, modal text change.

* NTP-24894 | Using ID for first occurence, updated tests.

* NTP-40086 | Selective filtering based on Product.

* NTP-40086 | Updated tests for incident_orch.

* NTP-40086 | replaced with constants.

* NTP-24894 | Moved functions to teamService.

* NTP-40086 | added new function in repo for filtering during fetch.

* NTP-40086 | Removed unused function.

* NTP-40086 | added tests and removed interface of previously removed fuc.

* NTP-40086 | Added direct filtering function during data fetch from repo.

* NTP-40086 | Refactoring and adding more tests.

* NTP-24894 | Text changed in incident summary.

* NTP-40086 | Refactoring function.

* NTP-40086 | Editting team service for unused functions.

* NTP-40086 | Added new util function for generic intersection.

* NTP-40086 | Text changed at multiple places.

* NTP-40086 | Text change.

* NTP-40086 | text change.

* NTP-40086 | Added flags for reporter teams fetch.

* NTP-240086 | changes in test.

* NTP-40086 | Using GenericFilter instead of query based filtering.
2025-02-21 19:26:20 +05:30

356 lines
9.0 KiB
Go

package util
import (
"fmt"
"github.com/lib/pq"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
"golang.org/x/exp/slices"
"gorm.io/gorm"
"houston/logger"
"math"
"reflect"
"runtime"
"strings"
"sync"
"time"
)
func SplitUntilWord(input, stopWord string) (string, string) {
// Convert both input and stopWord to lowercase for case-insensitive matching
lowercaseInput := strings.ToLower(input)
lowercaseStopWord := strings.ToLower(stopWord)
// Find the index of the stopWord in the lowercase input
stopIndex := strings.Index(lowercaseInput, lowercaseStopWord)
if stopIndex == -1 {
// If stopWord is not found, return the entire input as the first part
return input, ""
}
// Return the part of the input before the stopWord and the part after the stopWord
return strings.TrimSpace(input[:stopIndex]), strings.TrimSpace(input[stopIndex+len(stopWord):])
}
func RollbackTransaction(tx *gorm.DB) {
if r := recover(); r != nil {
tx.Rollback()
return
}
}
func RemoveDuplicate[T string | uint](sliceList []T) []T {
allKeys := make(map[T]bool)
list := []T{}
for _, item := range sliceList {
if _, value := allKeys[item]; !value {
allKeys[item] = true
list = append(list, item)
}
}
return list
}
// GetTimeElapsedInDaysAndHours - returns number of days and hours elapsed since the timestamp passed in argument
func GetTimeElapsedInDaysAndHours(timestamp time.Time) (int, int) {
// convert to IST time
locationName := "Asia/Kolkata"
location, err := time.LoadLocation(locationName)
if err != nil {
logger.Error(fmt.Sprintf("failed to load location for locationName %s", locationName))
}
timestampInIST := timestamp.In(location)
currentTimeInIST := time.Now().In(location)
// Calculate the time difference
timeDiff := currentTimeInIST.Sub(timestampInIST)
// Convert the duration into days and hours
days := int(timeDiff.Hours() / 24)
hours := int(timeDiff.Hours()) % 24
return days, hours
}
// Difference : finds difference of two slices and returns a new slice
func Difference(s1, s2 []string) []string {
combinedSlice := append(s1, s2...)
m := make(map[string]int)
for _, v := range combinedSlice {
if _, ok := m[v]; ok {
// remove element later as it exist in both slice.
m[v] += 1
continue
}
// new entry, add in map!
m[v] = 1
}
var result []string
for k, v := range m {
if v == 1 {
result = append(result, k)
}
}
return result
}
func GetDifference[T comparable](s1, s2 []T) []T {
combinedSlice := append(s1, s2...)
m := make(map[T]int)
for _, v := range combinedSlice {
if _, ok := m[v]; ok {
// remove element later as it exists in both slices.
m[v] += 1
continue
}
// new entry, add to the map!
m[v] = 1
}
var result []T
for k, v := range m {
if v == 1 {
result = append(result, k)
}
}
return result
}
func Intersection(s1, s2 []string) (inter []string) {
hash := make(map[string]bool)
for _, e := range s1 {
hash[e] = true
}
for _, e := range s2 {
// If elements present in the hashmap then append intersection list.
if hash[e] {
inter = append(inter, e)
}
}
//Remove dups from slice.
inter = RemoveDuplicate(inter)
return
}
func GenericIntersection[T any, K comparable](s1, s2 []T, keyExtractor func(T) K) []T {
set := make(map[K]struct{})
var intersect []T
for _, v := range s1 {
set[keyExtractor(v)] = struct{}{}
}
for _, v := range s2 {
if _, exists := set[keyExtractor(v)]; exists {
intersect = append(intersect, v)
}
}
return intersect
}
func GenericFilter[T any, K comparable](s1 []T, s2 []K, keyExtractor func(T) K) []T {
set := make(map[K]struct{})
var filteredList []T
for _, v := range s2 {
set[v] = struct{}{}
}
for _, v := range s1 {
if _, exists := set[keyExtractor(v)]; exists {
filteredList = append(filteredList, v)
}
}
return filteredList
}
// Contains checks if the given string exists on the slice of string and returns boolean
func Contains[S ~[]E, E comparable](s S, v E) bool {
return slices.Contains(s, v)
}
func GetColorBySeverity(severityId uint) string {
switch severityId {
case 1:
return "#fc3838"
case 2:
return "#fc9338"
case 3:
return "#ebfa1b"
case 4:
return "#7288db"
default:
return "#203da7"
}
}
func PostIncidentStatusUpdateMessage(userId, updatedStatus, channelId string, client *socketmode.Client) error {
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> > set status to %s", userId, updatedStatus), false)
_, _, errMessage := client.PostMessage(channelId, msgOption)
return errMessage
}
func PostIncidentTypeUpdateMessage(
userId, updatedTeam, severity, severityDesc, incidentName, incidentTitle, channelId string, client *socketmode.Client) error {
txt := fmt.Sprintf("<@%s> *>* set the channel topic: *%s · %s (%s) %s* | %s", userId, updatedTeam, severity, severityDesc, incidentName, incidentTitle)
att := slack.Attachment{
Text: txt,
Color: "#808080", // Grey color code
MarkdownIn: []string{"text"}, // Define which fields support markdown
}
_, _, errMessage := client.PostMessage(channelId, slack.MsgOptionAttachments(att))
return errMessage
}
func RemoveDuplicateStr(strSlice []string) []string {
allKeys := make(map[string]bool)
list := []string{}
for _, item := range strSlice {
if _, value := allKeys[item]; !value {
allKeys[item] = true
list = append(list, item)
}
}
return list
}
func RemoveString(slice []string, strToRemove string) []string {
result := make([]string, 0)
for _, str := range slice {
if str != strToRemove {
result = append(result, str)
}
}
return result
}
func ExecuteConcurrentAction(waitGroup *sync.WaitGroup, concurrentTask func()) {
defer waitGroup.Done()
concurrentTask()
}
func CalculateDifferenceInDays(fromTime, toTime time.Time) int {
fromDate := time.Date(fromTime.Year(), fromTime.Month(), fromTime.Day(), 0, 0, 0, 0, time.UTC)
toDate := time.Date(toTime.Year(), toTime.Month(), toTime.Day(), 0, 0, 0, 0, time.UTC)
return int(math.Abs(toDate.Sub(fromDate).Hours() / 24))
}
func PostMessageToIncidentChannel(message string, channelId string, client *socketmode.Client) error {
msgOption := slack.MsgOptionText(message, false)
_, _, errMessage := client.PostMessage(channelId, msgOption)
return errMessage
}
func ConvertSliceToMapOfString(input []string) map[string]string {
output := make(map[string]string)
for _, item := range input {
output[item] = item
}
return output
}
type CompareArrayResults struct {
UniqueElementsInArrayA []string
UniqueElementsInArrayB []string
CommonElements []string
}
// CompareAndGetStringArrayResults Takes two arrays as inputs and returns the common values of two arrays and unique values of both arrays
func CompareAndGetStringArrayResults(arrayA []string, arrayB []string) CompareArrayResults {
uniqueInA := make([]string, 0)
uniqueInB := make([]string, 0)
common := make([]string, 0)
map1 := make(map[string]bool)
map2 := make(map[string]bool)
for _, val := range arrayA {
map1[val] = true
}
for _, val := range arrayB {
map2[val] = true
}
for key := range map2 {
if _, ok := map1[key]; !ok {
uniqueInB = append(uniqueInB, key) //if element not present in map2 append elements in toBeAdded slice
} else {
common = append(common, key) // Add common elements
}
}
for key := range map1 {
if _, ok := map2[key]; !ok {
uniqueInA = append(uniqueInA, key) //if element not present in map2 append elements in toBeAdded slice
}
}
return CompareArrayResults{uniqueInA, uniqueInB, common}
}
func IsBlank(input string) bool {
trimmedInput := strings.TrimSpace(input)
return trimmedInput == ""
}
func GetFileExtensionFromMimeType(mimeType ContentType) string {
switch mimeType {
case ContentTypeTextHTML:
return ".html"
}
return ""
}
func AreTwoPqInt32ArraysEqual(arrayA pq.Int32Array, arrayB pq.Int32Array) bool {
if len(arrayA) != len(arrayB) {
return false
}
for index, value := range arrayA {
if value != arrayB[index] {
return false
}
}
return true
}
func RemoveDuplicates(slice interface{}, fieldName string) interface{} {
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
panic("RemoveDuplicates: not a slice")
}
// Create a map to store unique values
seen := make(map[interface{}]struct{})
elementType := v.Type().Elem()
// Get the field index by name
fieldIndex := -1
for i := 0; i < elementType.NumField(); i++ {
if elementType.Field(i).Name == fieldName {
fieldIndex = i
break
}
}
// If field doesn't exist, panic
if fieldIndex == -1 {
panic("RemoveDuplicates: field not found")
}
// Create a new slice without duplicates
resultSlice := reflect.MakeSlice(reflect.SliceOf(elementType), 0, v.Len())
for i := 0; i < v.Len(); i++ {
fieldValue := v.Index(i).Field(fieldIndex).Interface()
// If value is not seen before, add it to the result slice and mark it as seen
if _, ok := seen[fieldValue]; !ok {
resultSlice = reflect.Append(resultSlice, v.Index(i))
seen[fieldValue] = struct{}{}
}
}
return resultSlice.Interface()
}
func GetFunctionName(i interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}