| 930 | } |
| 931 | |
| 932 | func (s *SettingService) validateEnvironments(ctx context.Context, workspaceID string, envs []*v1pb.EnvironmentSetting_Environment) error { |
| 933 | used := map[string]bool{} |
| 934 | for _, env := range envs { |
| 935 | if env.Title == "" { |
| 936 | return connect.NewError(connect.CodeInvalidArgument, errors.Errorf("environment title cannot be empty")) |
| 937 | } |
| 938 | if !isValidResourceID(env.Id) { |
| 939 | return connect.NewError(connect.CodeInvalidArgument, errors.Errorf("invalid environment ID %v", env.Id)) |
| 940 | } |
| 941 | if used[env.Id] { |
| 942 | return connect.NewError(connect.CodeInvalidArgument, errors.Errorf("duplicate environment ID %v", env.Id)) |
| 943 | } |
| 944 | if env.Color != nil && !isOpaqueColor(env.Color) { |
| 945 | return connect.NewError(connect.CodeInvalidArgument, errors.Errorf("environment color invalid")) |
| 946 | } |
| 947 | if v, ok := env.Tags["protected"]; ok && v == "protected" { |
| 948 | if err := s.licenseService.IsFeatureEnabled(ctx, workspaceID, v1pb.PlanFeature_FEATURE_ENVIRONMENT_TIERS); err != nil { |
| 949 | return connect.NewError(connect.CodePermissionDenied, err) |
| 950 | } |
| 951 | } |
| 952 | used[env.Id] = true |
| 953 | } |
| 954 | return nil |
| 955 | } |
| 956 | |
| 957 | func validateEmailSetting(setting *storepb.EmailSetting) error { |
| 958 | if setting.From == "" { |