@@ -46,21 +46,12 @@ const (
4646
4747 pipelineContainsCycle = "The pipeline has a cycle with dependencies, make sure there are no cyclic dependencies"
4848
49- pipelineSlackFieldEmptyChannel = "Slack notifications must have a `channel` attribute"
50- pipelineSlackChannelFieldNotUnique = "The `channel` attribute under the Slack notifications must be unique"
51-
52- pipelineMSTeamsConnectionFieldNotUnique = "The `connection` attribute under the MS Teams notifications must be unique"
53- pipelineMSTeamsConnectionFieldEmpty = "MS Teams notifications `connection` attribute must not be empty"
54-
55- pipelineDiscordConnectionFieldEmpty = "Discord notifications `connection` attribute must not be empty"
56- pipelineDiscordConnectionFieldNotUnique = "The `connection` attribute under the Discord notifications must be unique"
57-
58- assetSlackFieldEmptyChannel = "Asset-level Slack notifications must have a `channel` attribute"
59- assetSlackChannelFieldNotUnique = "The `channel` attribute under the asset-level Slack notifications must be unique"
60- assetMSTeamsConnectionFieldEmpty = "Asset-level MS Teams notifications `connection` attribute must not be empty"
61- assetMSTeamsConnectionFieldNotUnique = "The `connection` attribute under the asset-level MS Teams notifications must be unique"
62- assetDiscordConnectionFieldEmpty = "Asset-level Discord notifications `connection` attribute must not be empty"
63- assetDiscordConnectionFieldNotUnique = "The `connection` attribute under the asset-level Discord notifications must be unique"
49+ slackChannelEmpty = "Slack notifications must have a `channel` attribute"
50+ slackChannelNotUnique = "The `channel` attribute under the Slack notifications must be unique"
51+ msTeamsConnectionEmpty = "MS Teams notifications `connection` attribute must not be empty"
52+ msTeamsConnectionNotUnique = "The `connection` attribute under the MS Teams notifications must be unique"
53+ discordConnectionEmpty = "Discord notifications `connection` attribute must not be empty"
54+ discordConnectionNotUnique = "The `connection` attribute under the Discord notifications must be unique"
6455
6556 pipelineConcurrencyMustBePositive = "Pipeline concurrency must be 1 or greater"
6657 pipelineMaxActiveStepsMustBePositive = "Pipeline max_active_steps must be a positive number"
@@ -931,170 +922,95 @@ func EnsurePipelineHasNoCycles(ctx context.Context, p *pipeline.Pipeline) ([]*Is
931922 return issues , nil
932923}
933924
934- func isStringInArray ( arr [] string , str string ) bool {
935- for _ , a := range arr {
936- if str == a {
937- return true
938- }
925+ // validateNotifications checks a single Notifications value for empty/duplicate targets.
926+ // issueContext is an optional label (e.g. "custom_check:my_check") included in issues for traceability.
927+ func validateNotifications ( n * pipeline. Notifications , issueContext [] string ) [] * Issue {
928+ if n == nil {
929+ return nil
939930 }
940- return false
941- }
942931
943- func EnsureSlackFieldInPipelineIsValid (ctx context.Context , p * pipeline.Pipeline ) ([]* Issue , error ) {
944- issues := make ([]* Issue , 0 )
932+ var issues []* Issue
945933
946- slackChannels := make ([]string , 0 , len (p .Notifications .Slack ))
947- for _ , slack := range p .Notifications .Slack {
948- channelWithoutHash := strings .TrimPrefix (slack .Channel , "#" )
949- if channelWithoutHash == "" {
950- issues = append (issues , & Issue {
951- Description : pipelineSlackFieldEmptyChannel ,
952- })
934+ slackChannels := make ([]string , 0 , len (n .Slack ))
935+ for _ , s := range n .Slack {
936+ ch := strings .TrimPrefix (s .Channel , "#" )
937+ if ch == "" {
938+ issues = append (issues , & Issue {Description : slackChannelEmpty , Context : issueContext })
953939 continue
954940 }
955-
956- if isStringInArray (slackChannels , channelWithoutHash ) {
957- issues = append (issues , & Issue {
958- Description : pipelineSlackChannelFieldNotUnique ,
959- })
941+ if slices .Contains (slackChannels , ch ) {
942+ issues = append (issues , & Issue {Description : slackChannelNotUnique , Context : issueContext })
960943 }
961-
962- slackChannels = append (slackChannels , channelWithoutHash )
944+ slackChannels = append (slackChannels , ch )
963945 }
964946
965- return issues , nil
966- }
967-
968- func EnsureMSTeamsFieldInPipelineIsValid (ctx context.Context , p * pipeline.Pipeline ) ([]* Issue , error ) {
969- issues := make ([]* Issue , 0 )
970-
971- MSTeamsConnections := make ([]string , 0 , len (p .Notifications .MSTeams ))
972- for _ , notification := range p .Notifications .MSTeams {
973- if notification .Connection == "" {
974- issues = append (issues , & Issue {
975- Description : pipelineMSTeamsConnectionFieldEmpty ,
976- })
977- continue
978- }
947+ issues = append (issues , validateConnectionKeys (
948+ mapSlice (n .MSTeams , func (t pipeline.MSTeamsNotification ) string { return t .Connection }),
949+ msTeamsConnectionEmpty , msTeamsConnectionNotUnique , issueContext ,
950+ )... )
951+ issues = append (issues , validateConnectionKeys (
952+ mapSlice (n .Discord , func (d pipeline.DiscordNotification ) string { return d .Connection }),
953+ discordConnectionEmpty , discordConnectionNotUnique , issueContext ,
954+ )... )
979955
980- if isStringInArray (MSTeamsConnections , notification .Connection ) {
981- issues = append (issues , & Issue {
982- Description : pipelineMSTeamsConnectionFieldNotUnique ,
983- })
984- }
985-
986- MSTeamsConnections = append (MSTeamsConnections , notification .Connection )
987- }
988-
989- return issues , nil
956+ return issues
990957}
991958
992- func EnsureDiscordFieldInPipelineIsValid (ctx context.Context , p * pipeline.Pipeline ) ([]* Issue , error ) {
993- issues := make ([]* Issue , 0 )
994-
995- discordConnections := make ([]string , 0 , len (p .Notifications .Discord ))
996- for _ , notification := range p .Notifications .Discord {
997- if notification .Connection == "" {
998- issues = append (issues , & Issue {
999- Description : pipelineDiscordConnectionFieldEmpty ,
1000- })
1001- continue
1002- }
1003-
1004- if isStringInArray (discordConnections , notification .Connection ) {
1005- issues = append (issues , & Issue {
1006- Description : pipelineDiscordConnectionFieldNotUnique ,
1007- })
1008- }
1009-
1010- discordConnections = append (discordConnections , notification .Connection )
959+ func mapSlice [T any ](items []T , key func (T ) string ) []string {
960+ out := make ([]string , len (items ))
961+ for i , item := range items {
962+ out [i ] = key (item )
1011963 }
1012-
1013- return issues , nil
964+ return out
1014965}
1015966
1016- func EnsureSlackFieldInAssetIsValid (ctx context.Context , p * pipeline.Pipeline , asset * pipeline.Asset ) ([]* Issue , error ) {
1017- issues := make ([]* Issue , 0 )
1018-
1019- if asset .Notifications == nil {
1020- return issues , nil
1021- }
1022-
1023- slackChannels := make ([]string , 0 , len (asset .Notifications .Slack ))
1024- for _ , slack := range asset .Notifications .Slack {
1025- channelWithoutHash := strings .TrimPrefix (slack .Channel , "#" )
1026- if channelWithoutHash == "" {
1027- issues = append (issues , & Issue {
1028- Description : assetSlackFieldEmptyChannel ,
1029- })
967+ // validateConnectionKeys checks a list of connection keys for empty/duplicate values.
968+ func validateConnectionKeys (keys []string , emptyMsg , dupeMsg string , issueContext []string ) []* Issue {
969+ var issues []* Issue
970+ var seen []string
971+ for _ , k := range keys {
972+ if k == "" {
973+ issues = append (issues , & Issue {Description : emptyMsg , Context : issueContext })
1030974 continue
1031975 }
1032-
1033- if isStringInArray (slackChannels , channelWithoutHash ) {
1034- issues = append (issues , & Issue {
1035- Description : assetSlackChannelFieldNotUnique ,
1036- })
976+ if slices .Contains (seen , k ) {
977+ issues = append (issues , & Issue {Description : dupeMsg , Context : issueContext })
978+ continue
1037979 }
1038-
1039- slackChannels = append (slackChannels , channelWithoutHash )
980+ seen = append (seen , k )
1040981 }
1041-
1042- return issues , nil
982+ return issues
1043983}
1044984
1045- func EnsureMSTeamsFieldInAssetIsValid (ctx context.Context , p * pipeline.Pipeline , asset * pipeline.Asset ) ([]* Issue , error ) {
1046- issues := make ([]* Issue , 0 )
1047-
1048- if asset .Notifications == nil {
1049- return issues , nil
1050- }
1051-
1052- MSTeamsConnections := make ([]string , 0 , len (asset .Notifications .MSTeams ))
1053- for _ , notification := range asset .Notifications .MSTeams {
1054- if notification .Connection == "" {
1055- issues = append (issues , & Issue {
1056- Description : assetMSTeamsConnectionFieldEmpty ,
1057- })
1058- continue
1059- }
1060-
1061- if isStringInArray (MSTeamsConnections , notification .Connection ) {
1062- issues = append (issues , & Issue {
1063- Description : assetMSTeamsConnectionFieldNotUnique ,
1064- })
1065- }
1066-
1067- MSTeamsConnections = append (MSTeamsConnections , notification .Connection )
985+ // EnsurePipelineNotificationsAreValid validates all notification targets on the pipeline.
986+ func EnsurePipelineNotificationsAreValid (ctx context.Context , p * pipeline.Pipeline ) ([]* Issue , error ) {
987+ issues := validateNotifications (& p .Notifications , nil )
988+ if issues == nil {
989+ return []* Issue {}, nil
1068990 }
1069-
1070991 return issues , nil
1071992}
1072993
1073- func EnsureDiscordFieldInAssetIsValid (ctx context.Context , p * pipeline.Pipeline , asset * pipeline.Asset ) ([]* Issue , error ) {
1074- issues := make ([]* Issue , 0 )
994+ // EnsureAssetNotificationsAreValid validates notification targets on the asset,
995+ // its custom checks, and its column checks.
996+ func EnsureAssetNotificationsAreValid (ctx context.Context , p * pipeline.Pipeline , asset * pipeline.Asset ) ([]* Issue , error ) {
997+ var issues []* Issue
1075998
1076- if asset .Notifications == nil {
1077- return issues , nil
1078- }
999+ issues = append (issues , validateNotifications (asset .Notifications , nil )... )
10791000
1080- discordConnections := make ([]string , 0 , len (asset .Notifications .Discord ))
1081- for _ , notification := range asset .Notifications .Discord {
1082- if notification .Connection == "" {
1083- issues = append (issues , & Issue {
1084- Description : assetDiscordConnectionFieldEmpty ,
1085- })
1086- continue
1087- }
1001+ for _ , check := range asset .CustomChecks {
1002+ issues = append (issues , validateNotifications (check .Notifications , []string {"custom_check:" + check .Name })... )
1003+ }
10881004
1089- if isStringInArray (discordConnections , notification .Connection ) {
1090- issues = append (issues , & Issue {
1091- Description : assetDiscordConnectionFieldNotUnique ,
1092- })
1005+ for _ , col := range asset .Columns {
1006+ for _ , check := range col .Checks {
1007+ issues = append (issues , validateNotifications (check .Notifications , []string {col .Name + "." + check .Name })... )
10931008 }
1094-
1095- discordConnections = append (discordConnections , notification .Connection )
10961009 }
10971010
1011+ if len (issues ) == 0 {
1012+ return []* Issue {}, nil
1013+ }
10981014 return issues , nil
10991015}
11001016
0 commit comments