Browse Source

lapi: fix ipv6 operations (#567)

AlteredCoder 4 years ago
parent
commit
5544000d38
83 changed files with 3706 additions and 2445 deletions
  1. 1 1
      .github/workflows/ci_functests-install.yml
  2. 31 0
      .github/workflows/ci_functests-ip_mgmt.yml
  3. 7 0
      cmd/crowdsec-cli/alerts.go
  4. 8 19
      cmd/crowdsec-cli/decisions.go
  5. 4 4
      go.mod
  6. 17 13
      go.sum
  7. 2 0
      pkg/apiclient/alerts_service.go
  8. 0 6
      pkg/apiclient/alerts_service_test.go
  9. 3 0
      pkg/apiclient/decisions_service.go
  10. 2 6
      pkg/apiclient/decisions_service_test.go
  11. 15 15
      pkg/apiserver/alerts_test.go
  12. 14 13
      pkg/apiserver/apic.go
  13. 0 65
      pkg/apiserver/controllers/utils.go
  14. 1 11
      pkg/apiserver/controllers/v1/alerts.go
  15. 0 2
      pkg/apiserver/controllers/v1/decisions.go
  16. 14 14
      pkg/apiserver/decisions_test.go
  17. 0 2
      pkg/apiserver/tests/alertWithInvalidMachineID_sample.json
  18. 0 2
      pkg/apiserver/tests/alert_sample.json
  19. 0 2
      pkg/apiserver/tests/invalidAlert_sample.json
  20. 0 54
      pkg/apiserver/utils_test.go
  21. 0 33
      pkg/csprofiles/csprofiles.go
  22. 91 25
      pkg/database/alerts.go
  23. 240 83
      pkg/database/decisions.go
  24. 176 164
      pkg/database/ent/alert.go
  25. 8 8
      pkg/database/ent/alert/alert.go
  26. 3 3
      pkg/database/ent/alert/where.go
  27. 54 54
      pkg/database/ent/alert_create.go
  28. 5 6
      pkg/database/ent/alert_delete.go
  29. 98 79
      pkg/database/ent/alert_query.go
  30. 132 133
      pkg/database/ent/alert_update.go
  31. 90 73
      pkg/database/ent/bouncer.go
  32. 5 5
      pkg/database/ent/bouncer/bouncer.go
  33. 3 3
      pkg/database/ent/bouncer/where.go
  34. 19 19
      pkg/database/ent/bouncer_create.go
  35. 5 6
      pkg/database/ent/bouncer_delete.go
  36. 74 59
      pkg/database/ent/bouncer_query.go
  37. 50 51
      pkg/database/ent/bouncer_update.go
  38. 7 7
      pkg/database/ent/client.go
  39. 3 3
      pkg/database/ent/context.go
  40. 135 95
      pkg/database/ent/decision.go
  41. 12 3
      pkg/database/ent/decision/decision.go
  42. 294 3
      pkg/database/ent/decision/where.go
  43. 87 21
      pkg/database/ent/decision_create.go
  44. 5 6
      pkg/database/ent/decision_delete.go
  45. 78 65
      pkg/database/ent/decision_query.go
  46. 338 57
      pkg/database/ent/decision_update.go
  47. 3 3
      pkg/database/ent/ent.go
  48. 61 53
      pkg/database/ent/event.go
  49. 2 2
      pkg/database/ent/event/event.go
  50. 3 3
      pkg/database/ent/event/where.go
  51. 11 11
      pkg/database/ent/event_create.go
  52. 5 6
      pkg/database/ent/event_delete.go
  53. 78 65
      pkg/database/ent/event_query.go
  54. 28 29
      pkg/database/ent/event_update.go
  55. 10 5
      pkg/database/ent/hook/hook.go
  56. 85 68
      pkg/database/ent/machine.go
  57. 3 3
      pkg/database/ent/machine/machine.go
  58. 3 3
      pkg/database/ent/machine/where.go
  59. 19 19
      pkg/database/ent/machine_create.go
  60. 5 6
      pkg/database/ent/machine_delete.go
  61. 79 62
      pkg/database/ent/machine_query.go
  62. 54 55
      pkg/database/ent/machine_update.go
  63. 61 53
      pkg/database/ent/meta.go
  64. 2 2
      pkg/database/ent/meta/meta.go
  65. 3 3
      pkg/database/ent/meta/where.go
  66. 11 11
      pkg/database/ent/meta_create.go
  67. 5 6
      pkg/database/ent/meta_delete.go
  68. 78 65
      pkg/database/ent/meta_query.go
  69. 28 29
      pkg/database/ent/meta_update.go
  70. 2 0
      pkg/database/ent/migrate/migrate.go
  71. 4 1
      pkg/database/ent/migrate/schema.go
  72. 163 179
      pkg/database/ent/mutation.go
  73. 0 355
      pkg/database/ent/privacy/privacy.go
  74. 3 3
      pkg/database/ent/runtime.go
  75. 2 2
      pkg/database/ent/runtime/runtime.go
  76. 3 0
      pkg/database/ent/schema/decision.go
  77. 0 6
      pkg/models/decision.go
  78. 0 6
      pkg/models/localapi_swagger.yaml
  79. 106 0
      pkg/types/ip.go
  80. 220 0
      pkg/types/ip_test.go
  81. 0 37
      pkg/types/utils.go
  82. 434 0
      scripts/test_ip_management.sh
  83. 1 1
      scripts/test_wizard_upgrade.sh

+ 1 - 1
.github/workflows/ci_functests-install.yml

@@ -53,7 +53,7 @@ jobs:
       run: |
       run: |
         sudo cscli decisions list
         sudo cscli decisions list
         sudo cscli decisions list -o=json | jq -e '.[].decisions[0].value == "1.1.1.172"'
         sudo cscli decisions list -o=json | jq -e '.[].decisions[0].value == "1.1.1.172"'
-        sudo cscli decisions list -r 1.1.1.0/24 -o=json | jq -e '.[].decisions[0].value == "1.1.1.172"'
+        sudo cscli decisions list -r 1.1.1.0/24 -o=json --contained | jq -e '.[].decisions[0].value == "1.1.1.172"'
         sudo cscli decisions list -r 1.1.2.0/24 -o=json | jq -e '. == null'
         sudo cscli decisions list -r 1.1.2.0/24 -o=json | jq -e '. == null'
         sudo cscli decisions list -i 1.1.1.172 -o=json | jq -e '.[].decisions[0].value == "1.1.1.172"'
         sudo cscli decisions list -i 1.1.1.172 -o=json | jq -e '.[].decisions[0].value == "1.1.1.172"'
         sudo cscli decisions list -i 1.1.1.173 -o=json | jq -e '. == null'
         sudo cscli decisions list -i 1.1.1.173 -o=json | jq -e '. == null'

+ 31 - 0
.github/workflows/ci_functests-ip_mgmt.yml

@@ -0,0 +1,31 @@
+name: Functionnal tests for IP management
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - master
+
+jobs:
+  build:
+    name: Install generated release and perform basic tests
+    runs-on: ubuntu-latest
+    steps:
+    - name: Set up Go 1.13
+      uses: actions/setup-go@v1
+      with:
+        go-version: 1.13
+      id: go
+    - name: Check out code into the Go module directory
+      uses: actions/checkout@v2
+    - name: install ipset for bouncer
+      run: |
+        sudo apt update
+        sudo apt install jq
+    - name: run tests
+      run: |
+        cd scripts/
+        sudo ./test_ip_management.sh
+

+ 7 - 0
cmd/crowdsec-cli/alerts.go

@@ -21,6 +21,7 @@ import (
 )
 )
 
 
 var printMachine bool
 var printMachine bool
+var contained bool
 var limit *int
 var limit *int
 
 
 func DecisionsFromAlert(alert *models.Alert) string {
 func DecisionsFromAlert(alert *models.Alert) string {
@@ -232,6 +233,7 @@ func NewAlertsCmd() *cobra.Command {
 		Since:          new(string),
 		Since:          new(string),
 		Until:          new(string),
 		Until:          new(string),
 		TypeEquals:     new(string),
 		TypeEquals:     new(string),
+		Contains:       new(bool),
 	}
 	}
 	limit = new(int)
 	limit = new(int)
 	var cmdAlertsList = &cobra.Command{
 	var cmdAlertsList = &cobra.Command{
@@ -300,6 +302,7 @@ cscli alerts list --type ban`,
 			if *alertListFilter.RangeEquals == "" {
 			if *alertListFilter.RangeEquals == "" {
 				alertListFilter.RangeEquals = nil
 				alertListFilter.RangeEquals = nil
 			}
 			}
+			*alertListFilter.Contains = !contained
 			alerts, _, err := Client.Alerts.List(context.Background(), alertListFilter)
 			alerts, _, err := Client.Alerts.List(context.Background(), alertListFilter)
 			if err != nil {
 			if err != nil {
 				log.Fatalf("Unable to list alerts : %v", err.Error())
 				log.Fatalf("Unable to list alerts : %v", err.Error())
@@ -320,6 +323,7 @@ cscli alerts list --type ban`,
 	cmdAlertsList.Flags().StringVar(alertListFilter.TypeEquals, "type", "", "restrict to alerts with given decision type (ie. ban, captcha)")
 	cmdAlertsList.Flags().StringVar(alertListFilter.TypeEquals, "type", "", "restrict to alerts with given decision type (ie. ban, captcha)")
 	cmdAlertsList.Flags().StringVar(alertListFilter.ScopeEquals, "scope", "", "restrict to alerts of this scope (ie. ip,range)")
 	cmdAlertsList.Flags().StringVar(alertListFilter.ScopeEquals, "scope", "", "restrict to alerts of this scope (ie. ip,range)")
 	cmdAlertsList.Flags().StringVarP(alertListFilter.ValueEquals, "value", "v", "", "the value to match for in the specified scope")
 	cmdAlertsList.Flags().StringVarP(alertListFilter.ValueEquals, "value", "v", "", "the value to match for in the specified scope")
+	cmdAlertsList.Flags().BoolVar(&contained, "contained", false, "query decisions contained by range")
 	cmdAlertsList.Flags().BoolVarP(&printMachine, "machine", "m", false, "print machines that sended alerts")
 	cmdAlertsList.Flags().BoolVarP(&printMachine, "machine", "m", false, "print machines that sended alerts")
 	cmdAlertsList.Flags().IntVarP(limit, "limit", "l", 50, "limit size of alerts list table (0 to view all alerts)")
 	cmdAlertsList.Flags().IntVarP(limit, "limit", "l", 50, "limit size of alerts list table (0 to view all alerts)")
 	cmdAlerts.AddCommand(cmdAlertsList)
 	cmdAlerts.AddCommand(cmdAlertsList)
@@ -332,6 +336,7 @@ cscli alerts list --type ban`,
 		ScenarioEquals: new(string),
 		ScenarioEquals: new(string),
 		IPEquals:       new(string),
 		IPEquals:       new(string),
 		RangeEquals:    new(string),
 		RangeEquals:    new(string),
+		Contains:       new(bool),
 	}
 	}
 	var cmdAlertsDelete = &cobra.Command{
 	var cmdAlertsDelete = &cobra.Command{
 		Use: "delete [filters] [--all]",
 		Use: "delete [filters] [--all]",
@@ -380,6 +385,7 @@ cscli alerts delete -s crowdsecurity/ssh-bf"`,
 				if *alertDeleteFilter.RangeEquals == "" {
 				if *alertDeleteFilter.RangeEquals == "" {
 					alertDeleteFilter.RangeEquals = nil
 					alertDeleteFilter.RangeEquals = nil
 				}
 				}
+				*alertDeleteFilter.Contains = !contained
 			} else {
 			} else {
 				alertDeleteFilter = apiclient.AlertsDeleteOpts{}
 				alertDeleteFilter = apiclient.AlertsDeleteOpts{}
 			}
 			}
@@ -398,6 +404,7 @@ cscli alerts delete -s crowdsecurity/ssh-bf"`,
 	cmdAlertsDelete.Flags().StringVarP(alertDeleteFilter.IPEquals, "ip", "i", "", "Source ip (shorthand for --scope ip --value <IP>)")
 	cmdAlertsDelete.Flags().StringVarP(alertDeleteFilter.IPEquals, "ip", "i", "", "Source ip (shorthand for --scope ip --value <IP>)")
 	cmdAlertsDelete.Flags().StringVarP(alertDeleteFilter.RangeEquals, "range", "r", "", "Range source ip (shorthand for --scope range --value <RANGE>)")
 	cmdAlertsDelete.Flags().StringVarP(alertDeleteFilter.RangeEquals, "range", "r", "", "Range source ip (shorthand for --scope range --value <RANGE>)")
 	cmdAlertsDelete.Flags().BoolVarP(&AlertDeleteAll, "all", "a", false, "delete all alerts")
 	cmdAlertsDelete.Flags().BoolVarP(&AlertDeleteAll, "all", "a", false, "delete all alerts")
+	cmdAlertsDelete.Flags().BoolVar(&contained, "contained", false, "query decisions contained by range")
 
 
 	cmdAlerts.AddCommand(cmdAlertsDelete)
 	cmdAlerts.AddCommand(cmdAlertsDelete)
 
 

+ 8 - 19
cmd/crowdsec-cli/decisions.go

@@ -12,7 +12,6 @@ import (
 
 
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
-	"github.com/crowdsecurity/crowdsec/pkg/database"
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 	"github.com/go-openapi/strfmt"
 	"github.com/go-openapi/strfmt"
@@ -140,6 +139,7 @@ func NewDecisionsCmd() *cobra.Command {
 		Until:          new(string),
 		Until:          new(string),
 		TypeEquals:     new(string),
 		TypeEquals:     new(string),
 		IncludeCAPI:    new(bool),
 		IncludeCAPI:    new(bool),
+		Contains:       new(bool),
 	}
 	}
 	NoSimu := new(bool)
 	NoSimu := new(bool)
 	var cmdDecisionsList = &cobra.Command{
 	var cmdDecisionsList = &cobra.Command{
@@ -209,6 +209,7 @@ cscli decisions list -t ban
 			if *filter.RangeEquals == "" {
 			if *filter.RangeEquals == "" {
 				filter.RangeEquals = nil
 				filter.RangeEquals = nil
 			}
 			}
+			*filter.Contains = !contained
 			alerts, _, err := Client.Alerts.List(context.Background(), filter)
 			alerts, _, err := Client.Alerts.List(context.Background(), filter)
 			if err != nil {
 			if err != nil {
 				log.Fatalf("Unable to list decisions : %v", err.Error())
 				log.Fatalf("Unable to list decisions : %v", err.Error())
@@ -231,6 +232,8 @@ cscli decisions list -t ban
 	cmdDecisionsList.Flags().StringVarP(filter.IPEquals, "ip", "i", "", "restrict to alerts from this source ip (shorthand for --scope ip --value <IP>)")
 	cmdDecisionsList.Flags().StringVarP(filter.IPEquals, "ip", "i", "", "restrict to alerts from this source ip (shorthand for --scope ip --value <IP>)")
 	cmdDecisionsList.Flags().StringVarP(filter.RangeEquals, "range", "r", "", "restrict to alerts from this source range (shorthand for --scope range --value <RANGE>)")
 	cmdDecisionsList.Flags().StringVarP(filter.RangeEquals, "range", "r", "", "restrict to alerts from this source range (shorthand for --scope range --value <RANGE>)")
 	cmdDecisionsList.Flags().BoolVar(NoSimu, "no-simu", false, "exclude decisions in simulation mode")
 	cmdDecisionsList.Flags().BoolVar(NoSimu, "no-simu", false, "exclude decisions in simulation mode")
+	cmdDecisionsList.Flags().BoolVar(&contained, "contained", false, "query decisions contained by range")
+
 	cmdDecisions.AddCommand(cmdDecisionsList)
 	cmdDecisions.AddCommand(cmdDecisionsList)
 
 
 	var (
 	var (
@@ -254,7 +257,6 @@ cscli decisions add --scope username --value foobar
 		/*TBD : fix long and example*/
 		/*TBD : fix long and example*/
 		Args: cobra.ExactArgs(0),
 		Args: cobra.ExactArgs(0),
 		Run: func(cmd *cobra.Command, args []string) {
 		Run: func(cmd *cobra.Command, args []string) {
-			var startIP, endIP int64
 			var err error
 			var err error
 			var ip, ipRange string
 			var ip, ipRange string
 			alerts := models.AddAlertsRequest{}
 			alerts := models.AddAlertsRequest{}
@@ -285,20 +287,6 @@ cscli decisions add --scope username --value foobar
 				return
 				return
 			}
 			}
 
 
-			if addScope == types.Ip {
-				startIP, endIP, err = database.GetIpsFromIpRange(addValue + "/32")
-				if err != nil {
-					log.Fatalf("unable to parse IP : '%s'", addValue)
-				}
-			}
-			if addScope == types.Range {
-				startIP, endIP, err = database.GetIpsFromIpRange(addValue)
-				if err != nil {
-					log.Fatalf("unable to parse Range : '%s'", addValue)
-				}
-				ipRange = addValue
-			}
-
 			if addReason == "" {
 			if addReason == "" {
 				addReason = fmt.Sprintf("manual '%s' from '%s'", addType, csConfig.API.Client.Credentials.Login)
 				addReason = fmt.Sprintf("manual '%s' from '%s'", addType, csConfig.API.Client.Credentials.Login)
 			}
 			}
@@ -310,8 +298,6 @@ cscli decisions add --scope username --value foobar
 				Type:     &addType,
 				Type:     &addType,
 				Scenario: &addReason,
 				Scenario: &addReason,
 				Origin:   &origin,
 				Origin:   &origin,
-				StartIP:  startIP,
-				EndIP:    endIP,
 			}
 			}
 			alert := models.Alert{
 			alert := models.Alert{
 				Capacity:        &capacity,
 				Capacity:        &capacity,
@@ -364,6 +350,7 @@ cscli decisions add --scope username --value foobar
 		TypeEquals:  new(string),
 		TypeEquals:  new(string),
 		IPEquals:    new(string),
 		IPEquals:    new(string),
 		RangeEquals: new(string),
 		RangeEquals: new(string),
+		Contains:    new(bool),
 	}
 	}
 	var delDecisionId string
 	var delDecisionId string
 	var delDecisionAll bool
 	var delDecisionAll bool
@@ -414,7 +401,7 @@ cscli decisions delete --type captcha
 			if *delFilter.RangeEquals == "" {
 			if *delFilter.RangeEquals == "" {
 				delFilter.RangeEquals = nil
 				delFilter.RangeEquals = nil
 			}
 			}
-
+			*delFilter.Contains = !contained
 			if delDecisionId == "" {
 			if delDecisionId == "" {
 				decisions, _, err = Client.Decisions.Delete(context.Background(), delFilter)
 				decisions, _, err = Client.Decisions.Delete(context.Background(), delFilter)
 				if err != nil {
 				if err != nil {
@@ -437,6 +424,8 @@ cscli decisions delete --type captcha
 	cmdDecisionsDelete.Flags().StringVarP(delFilter.TypeEquals, "type", "t", "", "the decision type (ie. ban,captcha)")
 	cmdDecisionsDelete.Flags().StringVarP(delFilter.TypeEquals, "type", "t", "", "the decision type (ie. ban,captcha)")
 	cmdDecisionsDelete.Flags().StringVarP(delFilter.ValueEquals, "value", "v", "", "the value to match for in the specified scope")
 	cmdDecisionsDelete.Flags().StringVarP(delFilter.ValueEquals, "value", "v", "", "the value to match for in the specified scope")
 	cmdDecisionsDelete.Flags().BoolVar(&delDecisionAll, "all", false, "delete all decisions")
 	cmdDecisionsDelete.Flags().BoolVar(&delDecisionAll, "all", false, "delete all decisions")
+	cmdDecisionsDelete.Flags().BoolVar(&contained, "contained", false, "query decisions contained by range")
+
 	cmdDecisions.AddCommand(cmdDecisionsDelete)
 	cmdDecisions.AddCommand(cmdDecisionsDelete)
 
 
 	return cmdDecisions
 	return cmdDecisions

+ 4 - 4
go.mod

@@ -18,7 +18,7 @@ require (
 	github.com/docker/docker v20.10.2+incompatible
 	github.com/docker/docker v20.10.2+incompatible
 	github.com/docker/go-connections v0.4.0
 	github.com/docker/go-connections v0.4.0
 	github.com/enescakir/emoji v1.0.0
 	github.com/enescakir/emoji v1.0.0
-	github.com/facebook/ent v0.5.0
+	github.com/facebook/ent v0.5.4
 	github.com/gin-gonic/gin v1.6.3
 	github.com/gin-gonic/gin v1.6.3
 	github.com/go-co-op/gocron v0.3.3
 	github.com/go-co-op/gocron v0.3.3
 	github.com/go-openapi/analysis v0.19.12 // indirect
 	github.com/go-openapi/analysis v0.19.12 // indirect
@@ -35,7 +35,7 @@ require (
 	github.com/google/go-querystring v1.0.0
 	github.com/google/go-querystring v1.0.0
 	github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e
 	github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e
 	github.com/hashicorp/go-version v1.2.1
 	github.com/hashicorp/go-version v1.2.1
-	github.com/lib/pq v1.8.0
+	github.com/lib/pq v1.9.0
 	github.com/logrusorgru/grokky v0.0.0-20180829062225-47edf017d42c
 	github.com/logrusorgru/grokky v0.0.0-20180829062225-47edf017d42c
 	github.com/mailru/easyjson v0.7.6 // indirect
 	github.com/mailru/easyjson v0.7.6 // indirect
 	github.com/mattn/go-sqlite3 v2.0.3+incompatible
 	github.com/mattn/go-sqlite3 v2.0.3+incompatible
@@ -56,11 +56,11 @@ require (
 	github.com/sirupsen/logrus v1.7.0
 	github.com/sirupsen/logrus v1.7.0
 	github.com/spf13/cobra v1.1.1
 	github.com/spf13/cobra v1.1.1
 	github.com/stretchr/testify v1.6.1
 	github.com/stretchr/testify v1.6.1
-	github.com/ugorji/go/codec v1.2.0 // indirect
+	github.com/ugorji/go v1.2.0 // indirect
 	github.com/vjeantet/grok v1.0.1 // indirect
 	github.com/vjeantet/grok v1.0.1 // indirect
 	go.mongodb.org/mongo-driver v1.4.3 // indirect
 	go.mongodb.org/mongo-driver v1.4.3 // indirect
 	golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582
 	golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582
-	golang.org/x/mod v0.3.0
+	golang.org/x/mod v0.4.0
 	golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect
 	golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect
 	golang.org/x/sys v0.0.0-20201116161645-c061ba923fbb
 	golang.org/x/sys v0.0.0-20201116161645-c061ba923fbb
 	golang.org/x/text v0.3.4 // indirect
 	golang.org/x/text v0.3.4 // indirect

+ 17 - 13
go.sum

@@ -135,8 +135,8 @@ github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkK
 github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
 github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/facebook/ent v0.5.0 h1:NlDQDxJi1X6+20CCjRQgu8UqvRhQNm5ocPBCQYdxC/8=
-github.com/facebook/ent v0.5.0/go.mod h1:HrrMNGsvgZoGQ74PGBQJ9r9WNOVMqKQefcOJFXuOUlw=
+github.com/facebook/ent v0.5.4 h1:kIf2BQUdRJ7XrlTXzCyJCg69ar1K1FjFR2UQWRo/M8M=
+github.com/facebook/ent v0.5.4/go.mod h1:ZioHzZjDTB/uPABl7pff/v2+cdsEBca8roSTWW3/UaE=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
 github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
 github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
 github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
@@ -151,6 +151,7 @@ github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
 github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
 github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
 github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+github.com/go-bindata/go-bindata v1.0.1-0.20190711162640-ee3c2418e368 h1:WNHfSP1q2vuAa9vF54RrhCl4nqxCjVcXhlbsRXbGOSY=
 github.com/go-bindata/go-bindata v1.0.1-0.20190711162640-ee3c2418e368/go.mod h1:7xCgX1lzlrXPHkfvn3EhumqHkmSlzt8at9q7v0ax19c=
 github.com/go-bindata/go-bindata v1.0.1-0.20190711162640-ee3c2418e368/go.mod h1:7xCgX1lzlrXPHkfvn3EhumqHkmSlzt8at9q7v0ax19c=
 github.com/go-co-op/gocron v0.3.3 h1:QnarcMZWWKrEP25uCbtDiLsnnGw+PhCjL3wNITdWJOs=
 github.com/go-co-op/gocron v0.3.3 h1:QnarcMZWWKrEP25uCbtDiLsnnGw+PhCjL3wNITdWJOs=
 github.com/go-co-op/gocron v0.3.3/go.mod h1:Y9PWlYqDChf2Nbgg7kfS+ZsXHDTZbMZYPEQ0MILqH+M=
 github.com/go-co-op/gocron v0.3.3/go.mod h1:Y9PWlYqDChf2Nbgg7kfS+ZsXHDTZbMZYPEQ0MILqH+M=
@@ -180,6 +181,7 @@ github.com/go-openapi/errors v0.19.7 h1:Lcq+o0mSwCLKACMxZhreVHigB9ebghJ/lrmeaqAS
 github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
 github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
 github.com/go-openapi/errors v0.19.8 h1:doM+tQdZbUm9gydV9yR+iQNmztbjj7I3sW4sIcAwIzc=
 github.com/go-openapi/errors v0.19.8 h1:doM+tQdZbUm9gydV9yR+iQNmztbjj7I3sW4sIcAwIzc=
 github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
 github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
+github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
 github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
 github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
 github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
@@ -336,8 +338,8 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
-github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.4 h1:0ecGp3skIrHWPNGPJDaBIghfA6Sp7Ruo2Io8eLKzWm0=
+github.com/google/uuid v1.1.4/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40=
 github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40=
@@ -429,8 +431,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
 github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
-github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
-github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
+github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
 github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
 github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
 github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
 github.com/logrusorgru/grokky v0.0.0-20180829062225-47edf017d42c h1:S3P1IbG7Z7V2p9juEttr1oRwozZd2kxw+RQiYBYB1wQ=
 github.com/logrusorgru/grokky v0.0.0-20180829062225-47edf017d42c h1:S3P1IbG7Z7V2p9juEttr1oRwozZd2kxw+RQiYBYB1wQ=
@@ -464,7 +466,7 @@ github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuuj
 github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
 github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-sqlite3 v1.14.4/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
+github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
 github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
 github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
@@ -482,8 +484,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
 github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
 github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
-github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
+github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
 github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
 github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
 github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -737,6 +739,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
 golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
+golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -765,8 +769,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
 golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
 golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -779,7 +782,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -871,7 +874,8 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
+golang.org/x/tools v0.0.0-20210105164027-a548c3f4af2d h1:v9TQ4+tS+0r4R+9E6svkcl6ocSxeHONeVkK2y6YhzmA=
+golang.org/x/tools v0.0.0-20210105164027-a548c3f4af2d/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=

+ 2 - 0
pkg/apiclient/alerts_service.go

@@ -26,6 +26,7 @@ type AlertsListOpts struct {
 	ActiveDecisionEquals *bool   `url:"has_active_decision,omitempty"`
 	ActiveDecisionEquals *bool   `url:"has_active_decision,omitempty"`
 	IncludeCAPI          *bool   `url:"include_capi,omitempty"`
 	IncludeCAPI          *bool   `url:"include_capi,omitempty"`
 	Limit                *int    `url:"limit,omitempty"`
 	Limit                *int    `url:"limit,omitempty"`
+	Contains             *bool   `url:"contains,omitempty"`
 	ListOpts
 	ListOpts
 }
 }
 
 
@@ -39,6 +40,7 @@ type AlertsDeleteOpts struct {
 	Until                *string `url:"until,omitempty"`
 	Until                *string `url:"until,omitempty"`
 	ActiveDecisionEquals *bool   `url:"has_active_decision,omitempty"`
 	ActiveDecisionEquals *bool   `url:"has_active_decision,omitempty"`
 	SourceEquals         *string `url:"alert_source,omitempty"`
 	SourceEquals         *string `url:"alert_source,omitempty"`
+	Contains             *bool   `url:"contains,omitempty"`
 	ListOpts
 	ListOpts
 }
 }
 
 

+ 0 - 6
pkg/apiclient/alerts_service_test.go

@@ -56,13 +56,11 @@ func TestAlertsListAsMachine(t *testing.T) {
 			{"capacity":5,"created_at":"2020-11-28T10:20:47+01:00",
 			{"capacity":5,"created_at":"2020-11-28T10:20:47+01:00",
 			 "decisions":[
 			 "decisions":[
 				  {"duration":"59m49.264032632s",
 				  {"duration":"59m49.264032632s",
-				  "end_ip":16843180,
 				  "id":1,
 				  "id":1,
 				  "origin":"crowdsec",
 				  "origin":"crowdsec",
 				  "scenario":"crowdsecurity/ssh-bf",
 				  "scenario":"crowdsecurity/ssh-bf",
 				  "scope":"Ip",
 				  "scope":"Ip",
 				  "simulated":false,
 				  "simulated":false,
-				  "start_ip":16843180,
 				  "type":"ban",
 				  "type":"ban",
 				  "value":"1.1.1.172"}
 				  "value":"1.1.1.172"}
 				  ],
 				  ],
@@ -127,14 +125,12 @@ func TestAlertsListAsMachine(t *testing.T) {
 			Decisions: []*models.Decision{
 			Decisions: []*models.Decision{
 				&models.Decision{
 				&models.Decision{
 					Duration: &tduration,
 					Duration: &tduration,
-					EndIP:    16843180,
 					ID:       1,
 					ID:       1,
 					Origin:   &torigin,
 					Origin:   &torigin,
 					Scenario: &tscenario,
 					Scenario: &tscenario,
 
 
 					Scope:     &tscope,
 					Scope:     &tscope,
 					Simulated: new(bool), //false,
 					Simulated: new(bool), //false,
-					StartIP:   16843180,
 					Type:      &ttype,
 					Type:      &ttype,
 					Value:     &tvalue,
 					Value:     &tvalue,
 				},
 				},
@@ -328,14 +324,12 @@ func TestAlertsGetAsMachine(t *testing.T) {
 		Decisions: []*models.Decision{
 		Decisions: []*models.Decision{
 			&models.Decision{
 			&models.Decision{
 				Duration: &tduration,
 				Duration: &tduration,
-				EndIP:    16843180,
 				ID:       1,
 				ID:       1,
 				Origin:   &torigin,
 				Origin:   &torigin,
 				Scenario: &tscenario,
 				Scenario: &tscenario,
 
 
 				Scope:     &tscope,
 				Scope:     &tscope,
 				Simulated: new(bool), //false,
 				Simulated: new(bool), //false,
-				StartIP:   16843180,
 				Type:      &ttype,
 				Type:      &ttype,
 				Value:     &tvalue,
 				Value:     &tvalue,
 			},
 			},

+ 3 - 0
pkg/apiclient/decisions_service.go

@@ -16,6 +16,8 @@ type DecisionsListOpts struct {
 	TypeEquals  *string `url:"type,omitempty"`
 	TypeEquals  *string `url:"type,omitempty"`
 	IPEquals    *string `url:"ip,omitempty"`
 	IPEquals    *string `url:"ip,omitempty"`
 	RangeEquals *string `url:"range,omitempty"`
 	RangeEquals *string `url:"range,omitempty"`
+	Contains    *bool   `url:"contains,omitempty"`
+
 	ListOpts
 	ListOpts
 }
 }
 
 
@@ -25,6 +27,7 @@ type DecisionsDeleteOpts struct {
 	TypeEquals  *string `url:"type,omitempty"`
 	TypeEquals  *string `url:"type,omitempty"`
 	IPEquals    *string `url:"ip,omitempty"`
 	IPEquals    *string `url:"ip,omitempty"`
 	RangeEquals *string `url:"range,omitempty"`
 	RangeEquals *string `url:"range,omitempty"`
+	Contains    *bool   `url:"contains,omitempty"`
 	ListOpts
 	ListOpts
 }
 }
 
 

+ 2 - 6
pkg/apiclient/decisions_service_test.go

@@ -26,7 +26,7 @@ func TestDecisionsList(t *testing.T) {
 			assert.Equal(t, r.URL.RawQuery, "ip=1.2.3.4")
 			assert.Equal(t, r.URL.RawQuery, "ip=1.2.3.4")
 			assert.Equal(t, r.Header.Get("X-Api-Key"), "ixu")
 			assert.Equal(t, r.Header.Get("X-Api-Key"), "ixu")
 			w.WriteHeader(http.StatusOK)
 			w.WriteHeader(http.StatusOK)
-			w.Write([]byte(`[{"duration":"3h59m55.756182786s","end_ip":16909060,"id":4,"origin":"cscli","scenario":"manual 'ban' from '82929df7ee394b73b81252fe3b4e50203yaT2u6nXiaN7Ix9'","scope":"Ip","start_ip":16909060,"type":"ban","value":"1.2.3.4"}]`))
+			w.Write([]byte(`[{"duration":"3h59m55.756182786s","id":4,"origin":"cscli","scenario":"manual 'ban' from '82929df7ee394b73b81252fe3b4e50203yaT2u6nXiaN7Ix9'","scope":"Ip","type":"ban","value":"1.2.3.4"}]`))
 		} else {
 		} else {
 			w.WriteHeader(http.StatusOK)
 			w.WriteHeader(http.StatusOK)
 			w.Write([]byte(`null`))
 			w.Write([]byte(`null`))
@@ -57,12 +57,10 @@ func TestDecisionsList(t *testing.T) {
 	expected := &models.GetDecisionsResponse{
 	expected := &models.GetDecisionsResponse{
 		&models.Decision{
 		&models.Decision{
 			Duration: &tduration,
 			Duration: &tduration,
-			EndIP:    16909060,
 			ID:       4,
 			ID:       4,
 			Origin:   &torigin,
 			Origin:   &torigin,
 			Scenario: &tscenario,
 			Scenario: &tscenario,
 			Scope:    &tscope,
 			Scope:    &tscope,
-			StartIP:  16909060,
 			Type:     &ttype,
 			Type:     &ttype,
 			Value:    &tvalue,
 			Value:    &tvalue,
 		},
 		},
@@ -110,7 +108,7 @@ func TestDecisionsStream(t *testing.T) {
 
 
 			if r.URL.RawQuery == "startup=true" {
 			if r.URL.RawQuery == "startup=true" {
 				w.WriteHeader(http.StatusOK)
 				w.WriteHeader(http.StatusOK)
-				w.Write([]byte(`{"deleted":null,"new":[{"duration":"3h59m55.756182786s","end_ip":16909060,"id":4,"origin":"cscli","scenario":"manual 'ban' from '82929df7ee394b73b81252fe3b4e50203yaT2u6nXiaN7Ix9'","scope":"Ip","start_ip":16909060,"type":"ban","value":"1.2.3.4"}]}`))
+				w.Write([]byte(`{"deleted":null,"new":[{"duration":"3h59m55.756182786s","id":4,"origin":"cscli","scenario":"manual 'ban' from '82929df7ee394b73b81252fe3b4e50203yaT2u6nXiaN7Ix9'","scope":"Ip","type":"ban","value":"1.2.3.4"}]}`))
 			} else {
 			} else {
 				w.WriteHeader(http.StatusOK)
 				w.WriteHeader(http.StatusOK)
 				w.Write([]byte(`{"deleted":null,"new":null}`))
 				w.Write([]byte(`{"deleted":null,"new":null}`))
@@ -150,12 +148,10 @@ func TestDecisionsStream(t *testing.T) {
 		New: models.GetDecisionsResponse{
 		New: models.GetDecisionsResponse{
 			&models.Decision{
 			&models.Decision{
 				Duration: &tduration,
 				Duration: &tduration,
-				EndIP:    16909060,
 				ID:       4,
 				ID:       4,
 				Origin:   &torigin,
 				Origin:   &torigin,
 				Scenario: &tscenario,
 				Scenario: &tscenario,
 				Scope:    &tscope,
 				Scope:    &tscope,
-				StartIP:  16909060,
 				Type:     &ttype,
 				Type:     &ttype,
 				Value:    &tvalue,
 				Value:    &tvalue,
 			},
 			},

+ 15 - 15
pkg/apiserver/alerts_test.go

@@ -185,7 +185,7 @@ func TestAlertListFilters(t *testing.T) {
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
 	//check alert and decision
 	//check alert and decision
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
-	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"start_ip":1534676931,"type":"ban","value":"91.121.79.195"`)
+	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
 
 
 	//test decision_type filter (ok)
 	//test decision_type filter (ok)
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
@@ -195,7 +195,7 @@ func TestAlertListFilters(t *testing.T) {
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
-	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"start_ip":1534676931,"type":"ban","value":"91.121.79.195"`)
+	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
 
 
 	//test decision_type filter (bad value)
 	//test decision_type filter (bad value)
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
@@ -214,7 +214,7 @@ func TestAlertListFilters(t *testing.T) {
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
-	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"start_ip":1534676931,"type":"ban","value":"91.121.79.195"`)
+	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
 
 
 	//test scope (bad value)
 	//test scope (bad value)
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
@@ -233,7 +233,7 @@ func TestAlertListFilters(t *testing.T) {
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
-	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"start_ip":1534676931,"type":"ban","value":"91.121.79.195"`)
+	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
 
 
 	//test scenario (bad value)
 	//test scenario (bad value)
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
@@ -252,7 +252,7 @@ func TestAlertListFilters(t *testing.T) {
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
-	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"start_ip":1534676931,"type":"ban","value":"91.121.79.195"`)
+	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
 
 
 	//test ip (bad value)
 	//test ip (bad value)
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
@@ -270,21 +270,21 @@ func TestAlertListFilters(t *testing.T) {
 	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
 	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 500, w.Code)
 	assert.Equal(t, 500, w.Code)
-	assert.Equal(t, `{"message":"unable to parse 'gruueq': %!s(\u003cnil\u003e): invalid ip address / range"}`, w.Body.String())
+	assert.Equal(t, `{"message":"unable to convert 'gruueq' to int: invalid address: invalid ip address / range"}`, w.Body.String())
 
 
 	//test range (ok)
 	//test range (ok)
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
-	req, _ = http.NewRequest("GET", "/v1/alerts?range=91.121.79.0/24", nil)
+	req, _ = http.NewRequest("GET", "/v1/alerts?range=91.121.79.0/24&contains=false", nil)
 	req.Header.Add("User-Agent", UserAgent)
 	req.Header.Add("User-Agent", UserAgent)
 	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
 	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
-	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"start_ip":1534676931,"type":"ban","value":"91.121.79.195"`)
+	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
 
 
 	//test range
 	//test range
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
-	req, _ = http.NewRequest("GET", "/v1/alerts?range=99.122.77.0/24", nil)
+	req, _ = http.NewRequest("GET", "/v1/alerts?range=99.122.77.0/24&contains=false", nil)
 	req.Header.Add("User-Agent", UserAgent)
 	req.Header.Add("User-Agent", UserAgent)
 	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
 	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
@@ -298,7 +298,7 @@ func TestAlertListFilters(t *testing.T) {
 	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
 	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 500, w.Code)
 	assert.Equal(t, 500, w.Code)
-	assert.Equal(t, `{"message":"unable to convert 'ratata' to int interval: 'ratata' is not a valid CIDR: invalid ip address / range"}`, w.Body.String())
+	assert.Equal(t, `{"message":"unable to convert 'ratata' to int: invalid address: invalid ip address / range"}`, w.Body.String())
 
 
 	//test since (ok)
 	//test since (ok)
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
@@ -308,7 +308,7 @@ func TestAlertListFilters(t *testing.T) {
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
-	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"start_ip":1534676931,"type":"ban","value":"91.121.79.195"`)
+	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
 
 
 	//test since (ok but yelds no results)
 	//test since (ok but yelds no results)
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
@@ -336,7 +336,7 @@ func TestAlertListFilters(t *testing.T) {
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
-	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"start_ip":1534676931,"type":"ban","value":"91.121.79.195"`)
+	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
 
 
 	//test until (ok but no return)
 	//test until (ok but no return)
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
@@ -364,7 +364,7 @@ func TestAlertListFilters(t *testing.T) {
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
-	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"start_ip":1534676931,"type":"ban","value":"91.121.79.195"`)
+	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
 
 
 	//test simulated (ok)
 	//test simulated (ok)
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
@@ -374,7 +374,7 @@ func TestAlertListFilters(t *testing.T) {
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
-	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"start_ip":1534676931,"type":"ban","value":"91.121.79.195"`)
+	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
 
 
 	//test has active decision
 	//test has active decision
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
@@ -384,7 +384,7 @@ func TestAlertListFilters(t *testing.T) {
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
 	assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
-	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"start_ip":1534676931,"type":"ban","value":"91.121.79.195"`)
+	assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
 
 
 	//test has active decision
 	//test has active decision
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()

+ 14 - 13
pkg/apiserver/apic.go

@@ -9,7 +9,6 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
-	"github.com/crowdsecurity/crowdsec/pkg/apiserver/controllers"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
 	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/database"
 	"github.com/crowdsecurity/crowdsec/pkg/database"
@@ -269,28 +268,30 @@ func (a *apic) PullTop() error {
 
 
 	// process new decisions
 	// process new decisions
 	for _, decision := range data.New {
 	for _, decision := range data.New {
-		/*ensure scope makes sense no matter what consensus gives*/
-		if strings.ToLower(*decision.Scope) == "ip" {
-			*decision.Scope = types.Ip
-		} else if strings.ToLower(*decision.Scope) == "range" {
-			*decision.Scope = types.Range
+		var start_ip, start_sfx, end_ip, end_sfx int64
+		var sz int
+
+		/*if the scope is IP or Range, convert the value to integers */
+		if strings.ToLower(*decision.Scope) == "ip" || strings.ToLower(*decision.Scope) == "range" {
+			sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(*decision.Value)
+			if err != nil {
+				return errors.Wrapf(err, "invalid ip/range %s", *decision.Value)
+			}
 		}
 		}
 
 
 		duration, err := time.ParseDuration(*decision.Duration)
 		duration, err := time.ParseDuration(*decision.Duration)
 		if err != nil {
 		if err != nil {
 			return errors.Wrapf(err, "parse decision duration '%s':", *decision.Duration)
 			return errors.Wrapf(err, "parse decision duration '%s':", *decision.Duration)
 		}
 		}
-		startIP, endIP, err := controllers.GetIpsFromIpRange(*decision.Value)
-		if err != nil {
-			return errors.Wrapf(err, "ip to int '%s':", *decision.Value)
-		}
-
 		_, err = a.dbClient.Ent.Decision.Create().
 		_, err = a.dbClient.Ent.Decision.Create().
 			SetUntil(time.Now().Add(duration)).
 			SetUntil(time.Now().Add(duration)).
 			SetScenario(*decision.Scenario).
 			SetScenario(*decision.Scenario).
 			SetType(*decision.Type).
 			SetType(*decision.Type).
-			SetStartIP(startIP).
-			SetEndIP(endIP).
+			SetIPSize(int64(sz)).
+			SetStartIP(start_ip).
+			SetStartSuffix(start_sfx).
+			SetEndIP(end_ip).
+			SetEndSuffix(end_sfx).
 			SetValue(*decision.Value).
 			SetValue(*decision.Value).
 			SetScope(*decision.Scope).
 			SetScope(*decision.Scope).
 			SetOrigin(*decision.Origin).
 			SetOrigin(*decision.Origin).

+ 0 - 65
pkg/apiserver/controllers/utils.go

@@ -1,65 +0,0 @@
-package controllers
-
-import (
-	"encoding/binary"
-	"fmt"
-	"net"
-)
-
-func IP2Int(ip net.IP) uint32 {
-	if len(ip) == 16 {
-		return binary.BigEndian.Uint32(ip[12:16])
-	}
-	return binary.BigEndian.Uint32(ip)
-}
-
-func Int2ip(nn uint32) net.IP {
-	ip := make(net.IP, 4)
-	binary.BigEndian.PutUint32(ip, nn)
-	return ip
-}
-
-func IsIpv4(host string) bool {
-	return net.ParseIP(host) != nil
-}
-
-//Stolen from : https://github.com/llimllib/ipaddress/
-// Return the final address of a net range. Convert to IPv4 if possible,
-// otherwise return an ipv6
-func LastAddress(n *net.IPNet) net.IP {
-	ip := n.IP.To4()
-	if ip == nil {
-		ip = n.IP
-		return net.IP{
-			ip[0] | ^n.Mask[0], ip[1] | ^n.Mask[1], ip[2] | ^n.Mask[2],
-			ip[3] | ^n.Mask[3], ip[4] | ^n.Mask[4], ip[5] | ^n.Mask[5],
-			ip[6] | ^n.Mask[6], ip[7] | ^n.Mask[7], ip[8] | ^n.Mask[8],
-			ip[9] | ^n.Mask[9], ip[10] | ^n.Mask[10], ip[11] | ^n.Mask[11],
-			ip[12] | ^n.Mask[12], ip[13] | ^n.Mask[13], ip[14] | ^n.Mask[14],
-			ip[15] | ^n.Mask[15]}
-	}
-
-	return net.IPv4(
-		ip[0]|^n.Mask[0],
-		ip[1]|^n.Mask[1],
-		ip[2]|^n.Mask[2],
-		ip[3]|^n.Mask[3])
-}
-
-func GetIpsFromIpRange(host string) (int64, int64, error) {
-	var ipStart int64
-	var ipEnd int64
-	var err error
-	var parsedRange *net.IPNet
-
-	if _, parsedRange, err = net.ParseCIDR(host); err != nil {
-		return ipStart, ipEnd, fmt.Errorf("'%s' is not a valid CIDR", host)
-	}
-	if parsedRange == nil {
-		return ipStart, ipEnd, fmt.Errorf("unable to parse network : %s", err)
-	}
-	ipStart = int64(IP2Int(parsedRange.IP))
-	ipEnd = int64(IP2Int(LastAddress(parsedRange)))
-
-	return ipStart, ipEnd, nil
-}

+ 1 - 11
pkg/apiserver/controllers/v1/alerts.go

@@ -78,8 +78,6 @@ func FormatOneAlert(alert *ent.Alert) *models.Alert {
 			Duration:  &duration, // transform into time.Time ?
 			Duration:  &duration, // transform into time.Time ?
 			Scenario:  &decisionItem.Scenario,
 			Scenario:  &decisionItem.Scenario,
 			Type:      &decisionItem.Type,
 			Type:      &decisionItem.Type,
-			StartIP:   decisionItem.StartIP,
-			EndIP:     decisionItem.EndIP,
 			Scope:     &decisionItem.Scope,
 			Scope:     &decisionItem.Scope,
 			Value:     &decisionItem.Value,
 			Value:     &decisionItem.Value,
 			Origin:    &decisionItem.Origin,
 			Origin:    &decisionItem.Origin,
@@ -119,15 +117,7 @@ func (c *Controller) CreateAlert(gctx *gin.Context) {
 	}
 	}
 
 
 	for _, alert := range input {
 	for _, alert := range input {
-		if len(alert.Decisions) > 0 {
-			log.Debugf("alert %s already has decisions, don't apply profiles", *alert.Message)
-			for _, decision := range alert.Decisions {
-				if decision.EndIP != 0 && decision.StartIP != 0 && decision.EndIP < decision.StartIP {
-					gctx.JSON(http.StatusBadRequest, gin.H{"message": "'start_ip' must be lower than or equal to 'end_ip' in decisions"})
-					return
-				}
-			}
-		} else {
+		if len(alert.Decisions) == 0 {
 			decisions, err := csprofiles.EvaluateProfiles(c.Profiles, alert)
 			decisions, err := csprofiles.EvaluateProfiles(c.Profiles, alert)
 			if err != nil {
 			if err != nil {
 				gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
 				gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})

+ 0 - 2
pkg/apiserver/controllers/v1/decisions.go

@@ -21,8 +21,6 @@ func FormatDecisions(decisions []*ent.Decision) ([]*models.Decision, error) {
 		decision := models.Decision{
 		decision := models.Decision{
 			ID:       int64(dbDecision.ID),
 			ID:       int64(dbDecision.ID),
 			Duration: &duration,
 			Duration: &duration,
-			EndIP:    dbDecision.EndIP,
-			StartIP:  dbDecision.StartIP,
 			Scenario: &dbDecision.Scenario,
 			Scenario: &dbDecision.Scenario,
 			Scope:    &dbDecision.Scope,
 			Scope:    &dbDecision.Scope,
 			Value:    &dbDecision.Value,
 			Value:    &dbDecision.Value,

+ 14 - 14
pkg/apiserver/decisions_test.go

@@ -58,7 +58,7 @@ func TestDeleteDecisionRange(t *testing.T) {
 
 
 	// delete by range
 	// delete by range
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
-	req, _ = http.NewRequest("DELETE", "/v1/decisions?range=91.121.79.0/24", strings.NewReader(""))
+	req, _ = http.NewRequest("DELETE", "/v1/decisions?range=91.121.79.0/24&contains=false", strings.NewReader(""))
 	req.Header.Add("User-Agent", UserAgent)
 	req.Header.Add("User-Agent", UserAgent)
 	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
 	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
@@ -179,8 +179,8 @@ func TestGetDecisionFilters(t *testing.T) {
 	req.Header.Add("X-Api-Key", APIKey)
 	req.Header.Add("X-Api-Key", APIKey)
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
-	assert.Contains(t, w.Body.String(), `"end_ip":1534676915,"id":1,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","start_ip":1534676915,"type":"ban","value":"91.121.79.179"`)
-	assert.Contains(t, w.Body.String(), `"end_ip":1534676914,"id":2,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","start_ip":1534676914,"type":"ban","value":"91.121.79.178"`)
+	assert.Contains(t, w.Body.String(), `"id":1,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.179"`)
+	assert.Contains(t, w.Body.String(), `"id":2,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.178"`)
 
 
 	// Get Decision : type filter
 	// Get Decision : type filter
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
@@ -189,8 +189,8 @@ func TestGetDecisionFilters(t *testing.T) {
 	req.Header.Add("X-Api-Key", APIKey)
 	req.Header.Add("X-Api-Key", APIKey)
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
-	assert.Contains(t, w.Body.String(), `"end_ip":1534676915,"id":1,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","start_ip":1534676915,"type":"ban","value":"91.121.79.179"`)
-	assert.Contains(t, w.Body.String(), `"end_ip":1534676914,"id":2,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","start_ip":1534676914,"type":"ban","value":"91.121.79.178"`)
+	assert.Contains(t, w.Body.String(), `"id":1,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.179"`)
+	assert.Contains(t, w.Body.String(), `"id":2,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.178"`)
 
 
 	// Get Decision : scope/value
 	// Get Decision : scope/value
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
@@ -199,8 +199,8 @@ func TestGetDecisionFilters(t *testing.T) {
 	req.Header.Add("X-Api-Key", APIKey)
 	req.Header.Add("X-Api-Key", APIKey)
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
-	assert.Contains(t, w.Body.String(), `"end_ip":1534676915,"id":1,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","start_ip":1534676915,"type":"ban","value":"91.121.79.179"`)
-	assert.NotContains(t, w.Body.String(), `"end_ip":1534676914,"id":2,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","start_ip":1534676914,"type":"ban","value":"91.121.79.178"`)
+	assert.Contains(t, w.Body.String(), `"id":1,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.179"`)
+	assert.NotContains(t, w.Body.String(), `"id":2,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.178"`)
 
 
 	// Get Decision : ip filter
 	// Get Decision : ip filter
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
@@ -209,18 +209,18 @@ func TestGetDecisionFilters(t *testing.T) {
 	req.Header.Add("X-Api-Key", APIKey)
 	req.Header.Add("X-Api-Key", APIKey)
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
-	assert.Contains(t, w.Body.String(), `"end_ip":1534676915,"id":1,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","start_ip":1534676915,"type":"ban","value":"91.121.79.179"`)
-	assert.NotContains(t, w.Body.String(), `"end_ip":1534676914,"id":2,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","start_ip":1534676914,"type":"ban","value":"91.121.79.178"`)
+	assert.Contains(t, w.Body.String(), `"id":1,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.179"`)
+	assert.NotContains(t, w.Body.String(), `"id":2,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.178"`)
 
 
 	// Get decision : by range
 	// Get decision : by range
 	w = httptest.NewRecorder()
 	w = httptest.NewRecorder()
-	req, _ = http.NewRequest("GET", "/v1/decisions?range=91.121.79.0/24", strings.NewReader(""))
+	req, _ = http.NewRequest("GET", "/v1/decisions?range=91.121.79.0/24&contains=false", strings.NewReader(""))
 	req.Header.Add("User-Agent", UserAgent)
 	req.Header.Add("User-Agent", UserAgent)
 	req.Header.Add("X-Api-Key", APIKey)
 	req.Header.Add("X-Api-Key", APIKey)
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
-	assert.Contains(t, w.Body.String(), `"end_ip":1534676915,"id":1,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","start_ip":1534676915,"type":"ban","value":"91.121.79.179"`)
-	assert.Contains(t, w.Body.String(), `"end_ip":1534676914,"id":2,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","start_ip":1534676914,"type":"ban","value":"91.121.79.178"`)
+	assert.Contains(t, w.Body.String(), `"id":1,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.179"`)
+	assert.Contains(t, w.Body.String(), `"id":2,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.178"`)
 }
 }
 
 
 func TestGetDecision(t *testing.T) {
 func TestGetDecision(t *testing.T) {
@@ -277,7 +277,7 @@ func TestGetDecision(t *testing.T) {
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 
 
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
-	assert.Contains(t, w.Body.String(), "\"end_ip\":2130706433,\"id\":1,\"origin\":\"test\",\"scenario\":\"crowdsecurity/test\",\"scope\":\"ip\",\"start_ip\":2130706433,\"type\":\"ban\",\"value\":\"127.0.0.1\"}]")
+	assert.Contains(t, w.Body.String(), "\"id\":1,\"origin\":\"test\",\"scenario\":\"crowdsecurity/test\",\"scope\":\"ip\",\"type\":\"ban\",\"value\":\"127.0.0.1\"}]")
 
 
 }
 }
 
 
@@ -449,5 +449,5 @@ func TestStreamDecision(t *testing.T) {
 	router.ServeHTTP(w, req)
 	router.ServeHTTP(w, req)
 
 
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, 200, w.Code)
-	assert.Contains(t, w.Body.String(), "\"end_ip\":2130706433,\"id\":1,\"origin\":\"test\",\"scenario\":\"crowdsecurity/test\",\"scope\":\"ip\",\"start_ip\":2130706433,\"type\":\"ban\",\"value\":\"127.0.0.1\"}]}")
+	assert.Contains(t, w.Body.String(), "\"id\":1,\"origin\":\"test\",\"scenario\":\"crowdsecurity/test\",\"scope\":\"ip\",\"type\":\"ban\",\"value\":\"127.0.0.1\"}]}")
 }
 }

+ 0 - 2
pkg/apiserver/tests/alertWithInvalidMachineID_sample.json

@@ -8,8 +8,6 @@
             {
             {
                 "id": 1,
                 "id": 1,
                 "duration": "1h",
                 "duration": "1h",
-                "start_ip": 2130706433,
-                "end_ip": 2130706433,
                 "origin": "test",
                 "origin": "test",
                 "scenario": "crowdsecurity/test",
                 "scenario": "crowdsecurity/test",
                 "scope": "ip",
                 "scope": "ip",

+ 0 - 2
pkg/apiserver/tests/alert_sample.json

@@ -8,8 +8,6 @@
             {
             {
                 "id": 1,
                 "id": 1,
                 "duration": "1h",
                 "duration": "1h",
-                "start_ip": 2130706433,
-                "end_ip": 2130706433,
                 "origin": "test",
                 "origin": "test",
                 "scenario": "crowdsecurity/test",
                 "scenario": "crowdsecurity/test",
                 "scope": "ip",
                 "scope": "ip",

+ 0 - 2
pkg/apiserver/tests/invalidAlert_sample.json

@@ -7,8 +7,6 @@
             {
             {
                 "id": 1,
                 "id": 1,
                 "duration": "1h",
                 "duration": "1h",
-                "start_ip": 2130706433,
-                "end_ip": 2130706433,
                 "origin": "test",
                 "origin": "test",
                 "scenario": "crowdsecurity/test",
                 "scenario": "crowdsecurity/test",
                 "scope": "ip",
                 "scope": "ip",

+ 0 - 54
pkg/apiserver/utils_test.go

@@ -1,54 +0,0 @@
-package apiserver
-
-import (
-	"net"
-	"testing"
-
-	"github.com/crowdsecurity/crowdsec/pkg/apiserver/controllers"
-	log "github.com/sirupsen/logrus"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestIP2Int(t *testing.T) {
-	ipInt := controllers.IP2Int(net.ParseIP("127.0.0.1"))
-	assert.Equal(t, uint32(2130706433), ipInt)
-
-	ipInt = controllers.IP2Int([]byte{127, 0, 0, 1})
-	assert.Equal(t, uint32(2130706433), ipInt)
-}
-
-func TestInt2IP(t *testing.T) {
-	IP := controllers.Int2ip(uint32(2130706433))
-	assert.Equal(t, "127.0.0.1", IP.String())
-}
-
-func TestIsIPv4(t *testing.T) {
-	IsIpv4 := controllers.IsIpv4("127.0.0.1")
-	assert.Equal(t, true, IsIpv4)
-
-	IsIpv4 = controllers.IsIpv4("127.0.0")
-	assert.Equal(t, false, IsIpv4)
-}
-
-func TestLastAddress(t *testing.T) {
-	_, ipv4Net, err := net.ParseCIDR("192.168.0.1/24")
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	lastAddress := controllers.LastAddress(ipv4Net)
-	assert.Equal(t, "192.168.0.255", lastAddress.String())
-}
-
-func TestGetIpsFromIpRange(t *testing.T) {
-	IPStart, IPEnd, err := controllers.GetIpsFromIpRange("192.168.0.1/65")
-	assert.Equal(t, "'192.168.0.1/65' is not a valid CIDR", err.Error())
-	assert.Equal(t, int64(0), IPStart)
-	assert.Equal(t, int64(0), IPEnd)
-
-	IPStart, IPEnd, err = controllers.GetIpsFromIpRange("192.168.0.1/24")
-	assert.Equal(t, nil, err)
-	assert.Equal(t, int64(3232235520), IPStart)
-	assert.Equal(t, int64(3232235775), IPEnd)
-}

+ 0 - 33
pkg/csprofiles/csprofiles.go

@@ -2,8 +2,6 @@ package csprofiles
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"net"
-	"strings"
 
 
 	"github.com/antonmedv/expr"
 	"github.com/antonmedv/expr"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
@@ -45,37 +43,6 @@ func GenerateDecisionFromProfile(Profile *csconfig.ProfileCfg, Alert *models.Ale
 		/*for the others, let's populate it from the alert and its source*/
 		/*for the others, let's populate it from the alert and its source*/
 		decision.Value = new(string)
 		decision.Value = new(string)
 		*decision.Value = *Alert.Source.Value
 		*decision.Value = *Alert.Source.Value
-
-		if strings.EqualFold(*decision.Scope, types.Ip) {
-			srcAddr := net.ParseIP(Alert.Source.IP)
-			if srcAddr == nil {
-				return nil, fmt.Errorf("can't parse ip %s", Alert.Source.IP)
-			}
-			decision.StartIP = int64(types.IP2Int(srcAddr))
-			decision.EndIP = decision.StartIP
-		} else if strings.EqualFold(*decision.Scope, types.Range) {
-
-			/*here we're asked to ban a full range. let's keep in mind that it's not always possible :
-			- the alert is about an IP, but the geolite enrichment failed
-			- the alert is about an IP, but the geolite enrichment isn't present
-			- the alert is about a range, in this case it should succeed
-			*/
-			if Alert.Source.Range != "" {
-				srcAddr, srcRange, err := net.ParseCIDR(Alert.Source.Range)
-				if err != nil {
-					log.Warningf("Profile [%s] requires IP decision, but can't parse '%s' from '%s'",
-						Profile.Name, *Alert.Source.Value, *Alert.Scenario)
-					continue
-				}
-				decision.StartIP = int64(types.IP2Int(srcAddr))
-				decision.EndIP = int64(types.IP2Int(types.LastAddress(srcRange)))
-				decision.Value = new(string)
-				*decision.Value = Alert.Source.Range
-			} else {
-				log.Warningf("Profile [%s] requires scope decision, but information is missing from %s", Profile.Name, *Alert.Scenario)
-				continue
-			}
-		}
 		decision.Origin = new(string)
 		decision.Origin = new(string)
 		*decision.Origin = "crowdsec"
 		*decision.Origin = "crowdsec"
 		if refDecision.Origin != nil {
 		if refDecision.Origin != nil {

+ 91 - 25
pkg/database/alerts.go

@@ -191,17 +191,30 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
 		if len(alertItem.Decisions) > 0 {
 		if len(alertItem.Decisions) > 0 {
 			decisionBulk := make([]*ent.DecisionCreate, len(alertItem.Decisions))
 			decisionBulk := make([]*ent.DecisionCreate, len(alertItem.Decisions))
 			for i, decisionItem := range alertItem.Decisions {
 			for i, decisionItem := range alertItem.Decisions {
+				var start_ip, start_sfx, end_ip, end_sfx int64
+				var sz int
 
 
 				duration, err := time.ParseDuration(*decisionItem.Duration)
 				duration, err := time.ParseDuration(*decisionItem.Duration)
 				if err != nil {
 				if err != nil {
 					return []string{}, errors.Wrapf(ParseDurationFail, "decision duration '%v' : %s", decisionItem.Duration, err)
 					return []string{}, errors.Wrapf(ParseDurationFail, "decision duration '%v' : %s", decisionItem.Duration, err)
 				}
 				}
+
+				/*if the scope is IP or Range, convert the value to integers */
+				if strings.ToLower(*decisionItem.Scope) == "ip" || strings.ToLower(*decisionItem.Scope) == "range" {
+					sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(*decisionItem.Value)
+					if err != nil {
+						return []string{}, errors.Wrapf(ParseDurationFail, "invalid addr/range %s : %s", *decisionItem.Value, err)
+					}
+				}
 				decisionBulk[i] = c.Ent.Decision.Create().
 				decisionBulk[i] = c.Ent.Decision.Create().
 					SetUntil(ts.Add(duration)).
 					SetUntil(ts.Add(duration)).
 					SetScenario(*decisionItem.Scenario).
 					SetScenario(*decisionItem.Scenario).
 					SetType(*decisionItem.Type).
 					SetType(*decisionItem.Type).
-					SetStartIP(decisionItem.StartIP).
-					SetEndIP(decisionItem.EndIP).
+					SetStartIP(start_ip).
+					SetStartSuffix(start_sfx).
+					SetEndIP(end_ip).
+					SetEndSuffix(end_sfx).
+					SetIPSize(int64(sz)).
 					SetValue(*decisionItem.Value).
 					SetValue(*decisionItem.Value).
 					SetScope(*decisionItem.Scope).
 					SetScope(*decisionItem.Scope).
 					SetOrigin(*decisionItem.Origin).
 					SetOrigin(*decisionItem.Origin).
@@ -275,8 +288,12 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
 
 
 func BuildAlertRequestFromFilter(alerts *ent.AlertQuery, filter map[string][]string) (*ent.AlertQuery, error) {
 func BuildAlertRequestFromFilter(alerts *ent.AlertQuery, filter map[string][]string) (*ent.AlertQuery, error) {
 	var err error
 	var err error
-	var startIP, endIP int64
+	var start_ip, start_sfx, end_ip, end_sfx int64
 	var hasActiveDecision bool
 	var hasActiveDecision bool
+	var ip_sz int
+	var contains bool = true
+	/*if contains is true, return bans that *contains* the given value (value is the inner)
+	  else, return bans that are *contained* by the given value (value is the outer)*/
 
 
 	/*the simulated filter is a bit different : if it's not present *or* set to false, specifically exclude records with simulated to true */
 	/*the simulated filter is a bit different : if it's not present *or* set to false, specifically exclude records with simulated to true */
 	if v, ok := filter["simulated"]; ok {
 	if v, ok := filter["simulated"]; ok {
@@ -288,6 +305,11 @@ func BuildAlertRequestFromFilter(alerts *ent.AlertQuery, filter map[string][]str
 
 
 	for param, value := range filter {
 	for param, value := range filter {
 		switch param {
 		switch param {
+		case "contains":
+			contains, err = strconv.ParseBool(value[0])
+			if err != nil {
+				return nil, errors.Wrapf(InvalidFilter, "invalid contains value : %s", err)
+			}
 		case "scope":
 		case "scope":
 			var scope string = value[0]
 			var scope string = value[0]
 			if strings.ToLower(scope) == "ip" {
 			if strings.ToLower(scope) == "ip" {
@@ -300,19 +322,10 @@ func BuildAlertRequestFromFilter(alerts *ent.AlertQuery, filter map[string][]str
 			alerts = alerts.Where(alert.SourceValueEQ(value[0]))
 			alerts = alerts.Where(alert.SourceValueEQ(value[0]))
 		case "scenario":
 		case "scenario":
 			alerts = alerts.Where(alert.ScenarioEQ(value[0]))
 			alerts = alerts.Where(alert.ScenarioEQ(value[0]))
-		case "ip":
-			isValidIP := IsIpv4(value[0])
-			if !isValidIP {
-				return nil, errors.Wrapf(InvalidIPOrRange, "unable to parse '%s': %s", value[0], err)
-			}
-			startIP, endIP, err = GetIpsFromIpRange(value[0] + "/32")
-			if err != nil {
-				return nil, errors.Wrapf(InvalidIPOrRange, "unable to convert '%s' to int interval: %s", value[0], err)
-			}
-		case "range":
-			startIP, endIP, err = GetIpsFromIpRange(value[0])
+		case "ip", "range":
+			ip_sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(value[0])
 			if err != nil {
 			if err != nil {
-				return nil, errors.Wrapf(InvalidIPOrRange, "unable to convert '%s' to int interval: %s", value[0], err)
+				return nil, errors.Wrapf(InvalidIPOrRange, "unable to convert '%s' to int: %s", value[0], err)
 			}
 			}
 		case "since":
 		case "since":
 			duration, err := types.ParseDuration(value[0])
 			duration, err := types.ParseDuration(value[0])
@@ -369,21 +382,74 @@ func BuildAlertRequestFromFilter(alerts *ent.AlertQuery, filter map[string][]str
 			return nil, errors.Wrapf(InvalidFilter, "Filter parameter '%s' is unknown (=%s)", param, value[0])
 			return nil, errors.Wrapf(InvalidFilter, "Filter parameter '%s' is unknown (=%s)", param, value[0])
 		}
 		}
 	}
 	}
-	if startIP != 0 && endIP != 0 {
-		/*the user is checking for a single IP*/
-		if startIP == endIP {
-			//DECISION_START <= IP_Q >= DECISON_END
+
+	if ip_sz == 4 {
+		if contains { /*decision contains {start_ip,end_ip}*/
+			alerts = alerts.Where(alert.And(
+				alert.HasDecisionsWith(decision.StartIPLTE(start_ip)),
+				alert.HasDecisionsWith(decision.EndIPGTE(end_ip)),
+				alert.HasDecisionsWith(decision.IPSizeEQ(int64(ip_sz))),
+			))
+		} else { /*decision is contained within {start_ip,end_ip}*/
+			alerts = alerts.Where(alert.And(
+				alert.HasDecisionsWith(decision.StartIPGTE(start_ip)),
+				alert.HasDecisionsWith(decision.EndIPLTE(end_ip)),
+				alert.HasDecisionsWith(decision.IPSizeEQ(int64(ip_sz))),
+			))
+		}
+	} else if ip_sz == 16 {
+
+		if contains { /*decision contains {start_ip,end_ip}*/
 			alerts = alerts.Where(alert.And(
 			alerts = alerts.Where(alert.And(
-				alert.HasDecisionsWith(decision.StartIPLTE(startIP)),
-				alert.HasDecisionsWith(decision.EndIPGTE(endIP)),
+				//matching addr size
+				alert.HasDecisionsWith(decision.IPSizeEQ(int64(ip_sz))),
+				alert.Or(
+					//decision.start_ip < query.start_ip
+					alert.HasDecisionsWith(decision.StartIPLT(start_ip)),
+					alert.And(
+						//decision.start_ip == query.start_ip
+						alert.HasDecisionsWith(decision.StartIPEQ(start_ip)),
+						//decision.start_suffix <= query.start_suffix
+						alert.HasDecisionsWith(decision.StartSuffixLTE(start_sfx)),
+					)),
+				alert.Or(
+					//decision.end_ip > query.end_ip
+					alert.HasDecisionsWith(decision.EndIPGT(end_ip)),
+					alert.And(
+						//decision.end_ip == query.end_ip
+						alert.HasDecisionsWith(decision.EndIPEQ(end_ip)),
+						//decision.end_suffix >= query.end_suffix
+						alert.HasDecisionsWith(decision.EndSuffixGTE(end_sfx)),
+					),
+				),
 			))
 			))
-		} else { /*the user is checking for a RANGE */
-			//START_Q >= DECISION_START AND END_Q <= DECISION_END
+		} else { /*decision is contained within {start_ip,end_ip}*/
 			alerts = alerts.Where(alert.And(
 			alerts = alerts.Where(alert.And(
-				alert.HasDecisionsWith(decision.StartIPGTE(startIP)),
-				alert.HasDecisionsWith(decision.EndIPLTE(endIP)),
+				//matching addr size
+				alert.HasDecisionsWith(decision.IPSizeEQ(int64(ip_sz))),
+				alert.Or(
+					//decision.start_ip > query.start_ip
+					alert.HasDecisionsWith(decision.StartIPGT(start_ip)),
+					alert.And(
+						//decision.start_ip == query.start_ip
+						alert.HasDecisionsWith(decision.StartIPEQ(start_ip)),
+						//decision.start_suffix >= query.start_suffix
+						alert.HasDecisionsWith(decision.StartSuffixGTE(start_sfx)),
+					)),
+				alert.Or(
+					//decision.end_ip < query.end_ip
+					alert.HasDecisionsWith(decision.EndIPLT(end_ip)),
+					alert.And(
+						//decision.end_ip == query.end_ip
+						alert.HasDecisionsWith(decision.EndIPEQ(end_ip)),
+						//decision.end_suffix <= query.end_suffix
+						alert.HasDecisionsWith(decision.EndSuffixLTE(end_sfx)),
+					),
+				),
 			))
 			))
 		}
 		}
+	} else if ip_sz != 0 {
+		return nil, errors.Wrapf(InvalidFilter, "Unknown ip size %d", ip_sz)
 	}
 	}
 	return alerts, nil
 	return alerts, nil
 }
 }

+ 240 - 83
pkg/database/decisions.go

@@ -15,8 +15,14 @@ import (
 )
 )
 
 
 func BuildDecisionRequestWithFilter(query *ent.DecisionQuery, filter map[string][]string) (*ent.DecisionQuery, error) {
 func BuildDecisionRequestWithFilter(query *ent.DecisionQuery, filter map[string][]string) (*ent.DecisionQuery, error) {
+
+	//func BuildDecisionRequestWithFilter(query *ent.Query, filter map[string][]string) (*ent.DecisionQuery, error) {
 	var err error
 	var err error
-	var startIP, endIP int64
+	var start_ip, start_sfx, end_ip, end_sfx int64
+	var ip_sz int
+	var contains bool = true
+	/*if contains is true, return bans that *contains* the given value (value is the inner)
+	  else, return bans that are *contained* by the given value (value is the outer)*/
 
 
 	/*the simulated filter is a bit different : if it's not present *or* set to false, specifically exclude records with simulated to true */
 	/*the simulated filter is a bit different : if it's not present *or* set to false, specifically exclude records with simulated to true */
 	if v, ok := filter["simulated"]; ok {
 	if v, ok := filter["simulated"]; ok {
@@ -30,6 +36,11 @@ func BuildDecisionRequestWithFilter(query *ent.DecisionQuery, filter map[string]
 
 
 	for param, value := range filter {
 	for param, value := range filter {
 		switch param {
 		switch param {
+		case "contains":
+			contains, err = strconv.ParseBool(value[0])
+			if err != nil {
+				return nil, errors.Wrapf(InvalidFilter, "invalid contains value : %s", err)
+			}
 		case "scope":
 		case "scope":
 			var scope string = value[0]
 			var scope string = value[0]
 			if strings.ToLower(scope) == "ip" {
 			if strings.ToLower(scope) == "ip" {
@@ -42,40 +53,84 @@ func BuildDecisionRequestWithFilter(query *ent.DecisionQuery, filter map[string]
 			query = query.Where(decision.ValueEQ(value[0]))
 			query = query.Where(decision.ValueEQ(value[0]))
 		case "type":
 		case "type":
 			query = query.Where(decision.TypeEQ(value[0]))
 			query = query.Where(decision.TypeEQ(value[0]))
-		case "ip":
-			isValidIP := IsIpv4(value[0])
-			if !isValidIP {
-				return nil, errors.Wrapf(InvalidIPOrRange, "unable to parse '%s': %s", value[0], err)
-			}
-			startIP, endIP, err = GetIpsFromIpRange(value[0] + "/32")
-			if err != nil {
-				return nil, errors.Wrapf(InvalidIPOrRange, "unable to convert '%s' to int interval: %s", value[0], err)
-			}
-		case "range":
-			startIP, endIP, err = GetIpsFromIpRange(value[0])
+		case "ip", "range":
+			ip_sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(value[0])
 			if err != nil {
 			if err != nil {
-				return nil, errors.Wrapf(InvalidIPOrRange, "unable to convert '%s' to int interval: %s", value[0], err)
+				return nil, errors.Wrapf(InvalidIPOrRange, "unable to convert '%s' to int: %s", value[0], err)
 			}
 			}
 		default:
 		default:
 			return query, errors.Wrapf(InvalidFilter, "'%s' doesn't exist", param)
 			return query, errors.Wrapf(InvalidFilter, "'%s' doesn't exist", param)
 		}
 		}
 	}
 	}
 
 
-	if startIP != 0 && endIP != 0 {
-		/*the user is checking for a single IP*/
-		if startIP == endIP {
-			//DECISION_START <= IP_Q >= DECISON_END
+	if ip_sz == 4 {
+
+		if contains { /*decision contains {start_ip,end_ip}*/
 			query = query.Where(decision.And(
 			query = query.Where(decision.And(
-				decision.StartIPLTE(startIP),
-				decision.EndIPGTE(endIP),
+				decision.StartIPLTE(start_ip),
+				decision.EndIPGTE(end_ip),
+				decision.IPSizeEQ(int64(ip_sz)),
 			))
 			))
-		} else { /*the user is checking for a RANGE */
-			//START_Q >= DECISION_START AND END_Q <= DECISION_END
+		} else { /*decision is contained within {start_ip,end_ip}*/
 			query = query.Where(decision.And(
 			query = query.Where(decision.And(
-				decision.StartIPGTE(startIP),
-				decision.EndIPLTE(endIP),
+				decision.StartIPGTE(start_ip),
+				decision.EndIPLTE(end_ip),
+				decision.IPSizeEQ(int64(ip_sz)),
 			))
 			))
 		}
 		}
+	} else if ip_sz == 16 {
+
+		if contains { /*decision contains {start_ip,end_ip}*/
+			query = query.Where(decision.And(
+				//matching addr size
+				decision.IPSizeEQ(int64(ip_sz)),
+				decision.Or(
+					//decision.start_ip < query.start_ip
+					decision.StartIPLT(start_ip),
+					decision.And(
+						//decision.start_ip == query.start_ip
+						decision.StartIPEQ(start_ip),
+						//decision.start_suffix <= query.start_suffix
+						decision.StartSuffixLTE(start_sfx),
+					)),
+				decision.Or(
+					//decision.end_ip > query.end_ip
+					decision.EndIPGT(end_ip),
+					decision.And(
+						//decision.end_ip == query.end_ip
+						decision.EndIPEQ(end_ip),
+						//decision.end_suffix >= query.end_suffix
+						decision.EndSuffixGTE(end_sfx),
+					),
+				),
+			))
+		} else { /*decision is contained {start_ip,end_ip}*/
+			query = query.Where(decision.And(
+				//matching addr size
+				decision.IPSizeEQ(int64(ip_sz)),
+				decision.Or(
+					//decision.start_ip > query.start_ip
+					decision.StartIPGT(start_ip),
+					decision.And(
+						//decision.start_ip == query.start_ip
+						decision.StartIPEQ(start_ip),
+						//decision.start_suffix >= query.start_suffix
+						decision.StartSuffixGTE(start_sfx),
+					)),
+				decision.Or(
+					//decision.end_ip < query.end_ip
+					decision.EndIPLT(end_ip),
+					decision.And(
+						//decision.end_ip == query.end_ip
+						decision.EndIPEQ(end_ip),
+						//decision.end_suffix <= query.end_suffix
+						decision.EndSuffixLTE(end_sfx),
+					),
+				),
+			))
+		}
+	} else if ip_sz != 0 {
+		return nil, errors.Wrapf(InvalidFilter, "Unknown ip size %d", ip_sz)
 	}
 	}
 	return query, nil
 	return query, nil
 }
 }
@@ -158,52 +213,101 @@ func (c *Client) DeleteDecisionById(decisionId int) error {
 
 
 func (c *Client) DeleteDecisionsWithFilter(filter map[string][]string) (string, error) {
 func (c *Client) DeleteDecisionsWithFilter(filter map[string][]string) (string, error) {
 	var err error
 	var err error
-	var startIP, endIP int64
+	var start_ip, start_sfx, end_ip, end_sfx int64
+	var ip_sz int
+	var contains bool = true
+	/*if contains is true, return bans that *contains* the given value (value is the inner)
+	  else, return bans that are *contained* by the given value (value is the outer) */
 
 
 	decisions := c.Ent.Decision.Delete()
 	decisions := c.Ent.Decision.Delete()
-
 	for param, value := range filter {
 	for param, value := range filter {
 		switch param {
 		switch param {
+		case "contains":
+			contains, err = strconv.ParseBool(value[0])
+			if err != nil {
+				return "0", errors.Wrapf(InvalidFilter, "invalid contains value : %s", err)
+			}
 		case "scope":
 		case "scope":
 			decisions = decisions.Where(decision.ScopeEQ(value[0]))
 			decisions = decisions.Where(decision.ScopeEQ(value[0]))
 		case "value":
 		case "value":
 			decisions = decisions.Where(decision.ValueEQ(value[0]))
 			decisions = decisions.Where(decision.ValueEQ(value[0]))
 		case "type":
 		case "type":
 			decisions = decisions.Where(decision.TypeEQ(value[0]))
 			decisions = decisions.Where(decision.TypeEQ(value[0]))
-		case "ip":
-			isValidIP := IsIpv4(value[0])
-			if !isValidIP {
-				return "0", errors.Wrap(InvalidIPOrRange, fmt.Sprintf("unable to parse '%s': %s", value[0], err))
-			}
-			startIP, endIP, err = GetIpsFromIpRange(value[0] + "/32")
-			if err != nil {
-				return "0", errors.Wrap(InvalidIPOrRange, fmt.Sprintf("unable to convert '%s' to int interval: %s", value[0], err))
-			}
-		case "range":
-			startIP, endIP, err = GetIpsFromIpRange(value[0])
+		case "ip", "range":
+			ip_sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(value[0])
 			if err != nil {
 			if err != nil {
-				return "0", errors.Wrap(InvalidIPOrRange, fmt.Sprintf("unable to convert '%s' to int interval: %s", value[0], err))
+				return "0", errors.Wrapf(InvalidIPOrRange, "unable to convert '%s' to int: %s", value[0], err)
 			}
 			}
 		default:
 		default:
 			return "0", errors.Wrap(InvalidFilter, fmt.Sprintf("'%s' doesn't exist", param))
 			return "0", errors.Wrap(InvalidFilter, fmt.Sprintf("'%s' doesn't exist", param))
 		}
 		}
-
-		if startIP != 0 && endIP != 0 {
-			/*the user is checking for a single IP*/
-			if startIP == endIP {
-				//DECISION_START <= IP_Q >= DECISON_END
-				decisions = decisions.Where(decision.And(
-					decision.StartIPLTE(startIP),
-					decision.EndIPGTE(endIP),
-				))
-			} else { /*the user is checking for a RANGE */
-				//START_Q >= DECISION_START AND END_Q <= DECISION_END
-				decisions = decisions.Where(decision.And(
-					decision.StartIPGTE(startIP),
-					decision.EndIPLTE(endIP),
-				))
-			}
+	}
+	if ip_sz == 4 {
+		if contains { /*decision contains {start_ip,end_ip}*/
+			decisions = decisions.Where(decision.And(
+				decision.StartIPLTE(start_ip),
+				decision.EndIPGTE(end_ip),
+				decision.IPSizeEQ(int64(ip_sz)),
+			))
+		} else { /*decision is contained within {start_ip,end_ip}*/
+			decisions = decisions.Where(decision.And(
+				decision.StartIPGTE(start_ip),
+				decision.EndIPLTE(end_ip),
+				decision.IPSizeEQ(int64(ip_sz)),
+			))
+		}
+	} else if ip_sz == 16 {
+		if contains { /*decision contains {start_ip,end_ip}*/
+			decisions = decisions.Where(decision.And(
+				//matching addr size
+				decision.IPSizeEQ(int64(ip_sz)),
+				decision.Or(
+					//decision.start_ip < query.start_ip
+					decision.StartIPLT(start_ip),
+					decision.And(
+						//decision.start_ip == query.start_ip
+						decision.StartIPEQ(start_ip),
+						//decision.start_suffix <= query.start_suffix
+						decision.StartSuffixLTE(start_sfx),
+					)),
+				decision.Or(
+					//decision.end_ip > query.end_ip
+					decision.EndIPGT(end_ip),
+					decision.And(
+						//decision.end_ip == query.end_ip
+						decision.EndIPEQ(end_ip),
+						//decision.end_suffix >= query.end_suffix
+						decision.EndSuffixGTE(end_sfx),
+					),
+				),
+			))
+		} else {
+			decisions = decisions.Where(decision.And(
+				//matching addr size
+				decision.IPSizeEQ(int64(ip_sz)),
+				decision.Or(
+					//decision.start_ip > query.start_ip
+					decision.StartIPGT(start_ip),
+					decision.And(
+						//decision.start_ip == query.start_ip
+						decision.StartIPEQ(start_ip),
+						//decision.start_suffix >= query.start_suffix
+						decision.StartSuffixGTE(start_sfx),
+					)),
+				decision.Or(
+					//decision.end_ip < query.end_ip
+					decision.EndIPLT(end_ip),
+					decision.And(
+						//decision.end_ip == query.end_ip
+						decision.EndIPEQ(end_ip),
+						//decision.end_suffix <= query.end_suffix
+						decision.EndSuffixLTE(end_sfx),
+					),
+				),
+			))
 		}
 		}
+	} else if ip_sz != 0 {
+		return "0", errors.Wrapf(InvalidFilter, "Unknown ip size %d", ip_sz)
 	}
 	}
 
 
 	nbDeleted, err := decisions.Exec(c.CTX)
 	nbDeleted, err := decisions.Exec(c.CTX)
@@ -217,51 +321,104 @@ func (c *Client) DeleteDecisionsWithFilter(filter map[string][]string) (string,
 // SoftDeleteDecisionsWithFilter udpate the expiration time to now() for the decisions matching the filter
 // SoftDeleteDecisionsWithFilter udpate the expiration time to now() for the decisions matching the filter
 func (c *Client) SoftDeleteDecisionsWithFilter(filter map[string][]string) (string, error) {
 func (c *Client) SoftDeleteDecisionsWithFilter(filter map[string][]string) (string, error) {
 	var err error
 	var err error
-	var startIP, endIP int64
-
+	var start_ip, start_sfx, end_ip, end_sfx int64
+	var ip_sz int
+	var contains bool = true
+	/*if contains is true, return bans that *contains* the given value (value is the inner)
+	  else, return bans that are *contained* by the given value (value is the outer)*/
 	decisions := c.Ent.Decision.Update().Where(decision.UntilGT(time.Now()))
 	decisions := c.Ent.Decision.Update().Where(decision.UntilGT(time.Now()))
 	for param, value := range filter {
 	for param, value := range filter {
 		switch param {
 		switch param {
+		case "contains":
+			contains, err = strconv.ParseBool(value[0])
+			if err != nil {
+				return "0", errors.Wrapf(InvalidFilter, "invalid contains value : %s", err)
+			}
 		case "scope":
 		case "scope":
 			decisions = decisions.Where(decision.ScopeEQ(value[0]))
 			decisions = decisions.Where(decision.ScopeEQ(value[0]))
 		case "value":
 		case "value":
 			decisions = decisions.Where(decision.ValueEQ(value[0]))
 			decisions = decisions.Where(decision.ValueEQ(value[0]))
 		case "type":
 		case "type":
 			decisions = decisions.Where(decision.TypeEQ(value[0]))
 			decisions = decisions.Where(decision.TypeEQ(value[0]))
-		case "ip":
-			isValidIP := IsIpv4(value[0])
-			if !isValidIP {
-				return "0", errors.Wrapf(InvalidIPOrRange, "unable to parse '%s': %s", value[0], err)
-			}
-			startIP, endIP, err = GetIpsFromIpRange(value[0] + "/32")
+		case "ip", "range":
+			ip_sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(value[0])
 			if err != nil {
 			if err != nil {
-				return "0", errors.Wrapf(InvalidIPOrRange, "unable to convert '%s' to int interval: %s", value[0], err)
-			}
-		case "range":
-			startIP, endIP, err = GetIpsFromIpRange(value[0])
-			if err != nil {
-				return "0", errors.Wrapf(InvalidIPOrRange, "unable to convert '%s' to int interval: %s", value[0], err)
+				return "0", errors.Wrapf(InvalidIPOrRange, "unable to convert '%s' to int: %s", value[0], err)
 			}
 			}
 		default:
 		default:
 			return "0", errors.Wrapf(InvalidFilter, "'%s' doesn't exist", param)
 			return "0", errors.Wrapf(InvalidFilter, "'%s' doesn't exist", param)
 		}
 		}
-
-		if startIP != 0 && endIP != 0 {
-			/*the user is checking for a single IP*/
-			if startIP == endIP {
-				//DECISION_START <= IP_Q >= DECISON_END
-				decisions = decisions.Where(decision.And(
-					decision.StartIPLTE(startIP),
-					decision.EndIPGTE(endIP),
-				))
-			} else { /*the user is checking for a RANGE */
-				//START_Q >= DECISION_START AND END_Q <= DECISION_END
-				decisions = decisions.Where(decision.And(
-					decision.StartIPGTE(startIP),
-					decision.EndIPLTE(endIP),
-				))
-			}
+	}
+	if ip_sz == 4 {
+		if contains {
+			/*Decision contains {start_ip,end_ip}*/
+			decisions = decisions.Where(decision.And(
+				decision.StartIPLTE(start_ip),
+				decision.EndIPGTE(end_ip),
+				decision.IPSizeEQ(int64(ip_sz)),
+			))
+		} else {
+			/*Decision is contained within {start_ip,end_ip}*/
+			decisions = decisions.Where(decision.And(
+				decision.StartIPGTE(start_ip),
+				decision.EndIPLTE(end_ip),
+				decision.IPSizeEQ(int64(ip_sz)),
+			))
+		}
+	} else if ip_sz == 16 {
+		/*decision contains {start_ip,end_ip}*/
+		if contains {
+			decisions = decisions.Where(decision.And(
+				//matching addr size
+				decision.IPSizeEQ(int64(ip_sz)),
+				decision.Or(
+					//decision.start_ip < query.start_ip
+					decision.StartIPLT(start_ip),
+					decision.And(
+						//decision.start_ip == query.start_ip
+						decision.StartIPEQ(start_ip),
+						//decision.start_suffix <= query.start_suffix
+						decision.StartSuffixLTE(start_sfx),
+					)),
+				decision.Or(
+					//decision.end_ip > query.end_ip
+					decision.EndIPGT(end_ip),
+					decision.And(
+						//decision.end_ip == query.end_ip
+						decision.EndIPEQ(end_ip),
+						//decision.end_suffix >= query.end_suffix
+						decision.EndSuffixGTE(end_sfx),
+					),
+				),
+			))
+		} else {
+			/*decision is contained within {start_ip,end_ip}*/
+			decisions = decisions.Where(decision.And(
+				//matching addr size
+				decision.IPSizeEQ(int64(ip_sz)),
+				decision.Or(
+					//decision.start_ip > query.start_ip
+					decision.StartIPGT(start_ip),
+					decision.And(
+						//decision.start_ip == query.start_ip
+						decision.StartIPEQ(start_ip),
+						//decision.start_suffix >= query.start_suffix
+						decision.StartSuffixGTE(start_sfx),
+					)),
+				decision.Or(
+					//decision.end_ip < query.end_ip
+					decision.EndIPLT(end_ip),
+					decision.And(
+						//decision.end_ip == query.end_ip
+						decision.EndIPEQ(end_ip),
+						//decision.end_suffix <= query.end_suffix
+						decision.EndSuffixLTE(end_sfx),
+					),
+				),
+			))
 		}
 		}
+	} else if ip_sz != 0 {
+		return "0", errors.Wrapf(InvalidFilter, "Unknown ip size %d", ip_sz)
 	}
 	}
 	nbDeleted, err := decisions.SetUntil(time.Now()).Save(c.CTX)
 	nbDeleted, err := decisions.SetUntil(time.Now()).Save(c.CTX)
 	if err != nil {
 	if err != nil {

+ 176 - 164
pkg/database/ent/alert.go

@@ -124,204 +124,216 @@ func (e AlertEdges) MetasOrErr() ([]*Meta, error) {
 }
 }
 
 
 // scanValues returns the types for scanning values from sql.Rows.
 // scanValues returns the types for scanning values from sql.Rows.
-func (*Alert) scanValues() []interface{} {
-	return []interface{}{
-		&sql.NullInt64{},   // id
-		&sql.NullTime{},    // created_at
-		&sql.NullTime{},    // updated_at
-		&sql.NullString{},  // scenario
-		&sql.NullString{},  // bucketId
-		&sql.NullString{},  // message
-		&sql.NullInt64{},   // eventsCount
-		&sql.NullTime{},    // startedAt
-		&sql.NullTime{},    // stoppedAt
-		&sql.NullString{},  // sourceIp
-		&sql.NullString{},  // sourceRange
-		&sql.NullString{},  // sourceAsNumber
-		&sql.NullString{},  // sourceAsName
-		&sql.NullString{},  // sourceCountry
-		&sql.NullFloat64{}, // sourceLatitude
-		&sql.NullFloat64{}, // sourceLongitude
-		&sql.NullString{},  // sourceScope
-		&sql.NullString{},  // sourceValue
-		&sql.NullInt64{},   // capacity
-		&sql.NullString{},  // leakSpeed
-		&sql.NullString{},  // scenarioVersion
-		&sql.NullString{},  // scenarioHash
-		&sql.NullBool{},    // simulated
-	}
-}
-
-// fkValues returns the types for scanning foreign-keys values from sql.Rows.
-func (*Alert) fkValues() []interface{} {
-	return []interface{}{
-		&sql.NullInt64{}, // machine_alerts
+func (*Alert) scanValues(columns []string) ([]interface{}, error) {
+	values := make([]interface{}, len(columns))
+	for i := range columns {
+		switch columns[i] {
+		case alert.FieldSimulated:
+			values[i] = &sql.NullBool{}
+		case alert.FieldSourceLatitude, alert.FieldSourceLongitude:
+			values[i] = &sql.NullFloat64{}
+		case alert.FieldID, alert.FieldEventsCount, alert.FieldCapacity:
+			values[i] = &sql.NullInt64{}
+		case alert.FieldScenario, alert.FieldBucketId, alert.FieldMessage, alert.FieldSourceIp, alert.FieldSourceRange, alert.FieldSourceAsNumber, alert.FieldSourceAsName, alert.FieldSourceCountry, alert.FieldSourceScope, alert.FieldSourceValue, alert.FieldLeakSpeed, alert.FieldScenarioVersion, alert.FieldScenarioHash:
+			values[i] = &sql.NullString{}
+		case alert.FieldCreatedAt, alert.FieldUpdatedAt, alert.FieldStartedAt, alert.FieldStoppedAt:
+			values[i] = &sql.NullTime{}
+		case alert.ForeignKeys[0]: // machine_alerts
+			values[i] = &sql.NullInt64{}
+		default:
+			return nil, fmt.Errorf("unexpected column %q for type Alert", columns[i])
+		}
 	}
 	}
+	return values, nil
 }
 }
 
 
 // assignValues assigns the values that were returned from sql.Rows (after scanning)
 // assignValues assigns the values that were returned from sql.Rows (after scanning)
 // to the Alert fields.
 // to the Alert fields.
-func (a *Alert) assignValues(values ...interface{}) error {
-	if m, n := len(values), len(alert.Columns); m < n {
+func (a *Alert) assignValues(columns []string, values []interface{}) error {
+	if m, n := len(values), len(columns); m < n {
 		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
 		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
 	}
 	}
-	value, ok := values[0].(*sql.NullInt64)
-	if !ok {
-		return fmt.Errorf("unexpected type %T for field id", value)
-	}
-	a.ID = int(value.Int64)
-	values = values[1:]
-	if value, ok := values[0].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field created_at", values[0])
-	} else if value.Valid {
-		a.CreatedAt = value.Time
-	}
-	if value, ok := values[1].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field updated_at", values[1])
-	} else if value.Valid {
-		a.UpdatedAt = value.Time
-	}
-	if value, ok := values[2].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field scenario", values[2])
-	} else if value.Valid {
-		a.Scenario = value.String
-	}
-	if value, ok := values[3].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field bucketId", values[3])
-	} else if value.Valid {
-		a.BucketId = value.String
-	}
-	if value, ok := values[4].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field message", values[4])
-	} else if value.Valid {
-		a.Message = value.String
-	}
-	if value, ok := values[5].(*sql.NullInt64); !ok {
-		return fmt.Errorf("unexpected type %T for field eventsCount", values[5])
-	} else if value.Valid {
-		a.EventsCount = int32(value.Int64)
-	}
-	if value, ok := values[6].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field startedAt", values[6])
-	} else if value.Valid {
-		a.StartedAt = value.Time
-	}
-	if value, ok := values[7].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field stoppedAt", values[7])
-	} else if value.Valid {
-		a.StoppedAt = value.Time
-	}
-	if value, ok := values[8].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field sourceIp", values[8])
-	} else if value.Valid {
-		a.SourceIp = value.String
-	}
-	if value, ok := values[9].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field sourceRange", values[9])
-	} else if value.Valid {
-		a.SourceRange = value.String
-	}
-	if value, ok := values[10].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field sourceAsNumber", values[10])
-	} else if value.Valid {
-		a.SourceAsNumber = value.String
-	}
-	if value, ok := values[11].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field sourceAsName", values[11])
-	} else if value.Valid {
-		a.SourceAsName = value.String
-	}
-	if value, ok := values[12].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field sourceCountry", values[12])
-	} else if value.Valid {
-		a.SourceCountry = value.String
-	}
-	if value, ok := values[13].(*sql.NullFloat64); !ok {
-		return fmt.Errorf("unexpected type %T for field sourceLatitude", values[13])
-	} else if value.Valid {
-		a.SourceLatitude = float32(value.Float64)
-	}
-	if value, ok := values[14].(*sql.NullFloat64); !ok {
-		return fmt.Errorf("unexpected type %T for field sourceLongitude", values[14])
-	} else if value.Valid {
-		a.SourceLongitude = float32(value.Float64)
-	}
-	if value, ok := values[15].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field sourceScope", values[15])
-	} else if value.Valid {
-		a.SourceScope = value.String
-	}
-	if value, ok := values[16].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field sourceValue", values[16])
-	} else if value.Valid {
-		a.SourceValue = value.String
-	}
-	if value, ok := values[17].(*sql.NullInt64); !ok {
-		return fmt.Errorf("unexpected type %T for field capacity", values[17])
-	} else if value.Valid {
-		a.Capacity = int32(value.Int64)
-	}
-	if value, ok := values[18].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field leakSpeed", values[18])
-	} else if value.Valid {
-		a.LeakSpeed = value.String
-	}
-	if value, ok := values[19].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field scenarioVersion", values[19])
-	} else if value.Valid {
-		a.ScenarioVersion = value.String
-	}
-	if value, ok := values[20].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field scenarioHash", values[20])
-	} else if value.Valid {
-		a.ScenarioHash = value.String
-	}
-	if value, ok := values[21].(*sql.NullBool); !ok {
-		return fmt.Errorf("unexpected type %T for field simulated", values[21])
-	} else if value.Valid {
-		a.Simulated = value.Bool
-	}
-	values = values[22:]
-	if len(values) == len(alert.ForeignKeys) {
-		if value, ok := values[0].(*sql.NullInt64); !ok {
-			return fmt.Errorf("unexpected type %T for edge-field machine_alerts", value)
-		} else if value.Valid {
-			a.machine_alerts = new(int)
-			*a.machine_alerts = int(value.Int64)
+	for i := range columns {
+		switch columns[i] {
+		case alert.FieldID:
+			value, ok := values[i].(*sql.NullInt64)
+			if !ok {
+				return fmt.Errorf("unexpected type %T for field id", value)
+			}
+			a.ID = int(value.Int64)
+		case alert.FieldCreatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field created_at", values[i])
+			} else if value.Valid {
+				a.CreatedAt = value.Time
+			}
+		case alert.FieldUpdatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field updated_at", values[i])
+			} else if value.Valid {
+				a.UpdatedAt = value.Time
+			}
+		case alert.FieldScenario:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field scenario", values[i])
+			} else if value.Valid {
+				a.Scenario = value.String
+			}
+		case alert.FieldBucketId:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field bucketId", values[i])
+			} else if value.Valid {
+				a.BucketId = value.String
+			}
+		case alert.FieldMessage:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field message", values[i])
+			} else if value.Valid {
+				a.Message = value.String
+			}
+		case alert.FieldEventsCount:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field eventsCount", values[i])
+			} else if value.Valid {
+				a.EventsCount = int32(value.Int64)
+			}
+		case alert.FieldStartedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field startedAt", values[i])
+			} else if value.Valid {
+				a.StartedAt = value.Time
+			}
+		case alert.FieldStoppedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field stoppedAt", values[i])
+			} else if value.Valid {
+				a.StoppedAt = value.Time
+			}
+		case alert.FieldSourceIp:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field sourceIp", values[i])
+			} else if value.Valid {
+				a.SourceIp = value.String
+			}
+		case alert.FieldSourceRange:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field sourceRange", values[i])
+			} else if value.Valid {
+				a.SourceRange = value.String
+			}
+		case alert.FieldSourceAsNumber:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field sourceAsNumber", values[i])
+			} else if value.Valid {
+				a.SourceAsNumber = value.String
+			}
+		case alert.FieldSourceAsName:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field sourceAsName", values[i])
+			} else if value.Valid {
+				a.SourceAsName = value.String
+			}
+		case alert.FieldSourceCountry:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field sourceCountry", values[i])
+			} else if value.Valid {
+				a.SourceCountry = value.String
+			}
+		case alert.FieldSourceLatitude:
+			if value, ok := values[i].(*sql.NullFloat64); !ok {
+				return fmt.Errorf("unexpected type %T for field sourceLatitude", values[i])
+			} else if value.Valid {
+				a.SourceLatitude = float32(value.Float64)
+			}
+		case alert.FieldSourceLongitude:
+			if value, ok := values[i].(*sql.NullFloat64); !ok {
+				return fmt.Errorf("unexpected type %T for field sourceLongitude", values[i])
+			} else if value.Valid {
+				a.SourceLongitude = float32(value.Float64)
+			}
+		case alert.FieldSourceScope:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field sourceScope", values[i])
+			} else if value.Valid {
+				a.SourceScope = value.String
+			}
+		case alert.FieldSourceValue:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field sourceValue", values[i])
+			} else if value.Valid {
+				a.SourceValue = value.String
+			}
+		case alert.FieldCapacity:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field capacity", values[i])
+			} else if value.Valid {
+				a.Capacity = int32(value.Int64)
+			}
+		case alert.FieldLeakSpeed:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field leakSpeed", values[i])
+			} else if value.Valid {
+				a.LeakSpeed = value.String
+			}
+		case alert.FieldScenarioVersion:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field scenarioVersion", values[i])
+			} else if value.Valid {
+				a.ScenarioVersion = value.String
+			}
+		case alert.FieldScenarioHash:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field scenarioHash", values[i])
+			} else if value.Valid {
+				a.ScenarioHash = value.String
+			}
+		case alert.FieldSimulated:
+			if value, ok := values[i].(*sql.NullBool); !ok {
+				return fmt.Errorf("unexpected type %T for field simulated", values[i])
+			} else if value.Valid {
+				a.Simulated = value.Bool
+			}
+		case alert.ForeignKeys[0]:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for edge-field machine_alerts", value)
+			} else if value.Valid {
+				a.machine_alerts = new(int)
+				*a.machine_alerts = int(value.Int64)
+			}
 		}
 		}
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-// QueryOwner queries the owner edge of the Alert.
+// QueryOwner queries the "owner" edge of the Alert entity.
 func (a *Alert) QueryOwner() *MachineQuery {
 func (a *Alert) QueryOwner() *MachineQuery {
 	return (&AlertClient{config: a.config}).QueryOwner(a)
 	return (&AlertClient{config: a.config}).QueryOwner(a)
 }
 }
 
 
-// QueryDecisions queries the decisions edge of the Alert.
+// QueryDecisions queries the "decisions" edge of the Alert entity.
 func (a *Alert) QueryDecisions() *DecisionQuery {
 func (a *Alert) QueryDecisions() *DecisionQuery {
 	return (&AlertClient{config: a.config}).QueryDecisions(a)
 	return (&AlertClient{config: a.config}).QueryDecisions(a)
 }
 }
 
 
-// QueryEvents queries the events edge of the Alert.
+// QueryEvents queries the "events" edge of the Alert entity.
 func (a *Alert) QueryEvents() *EventQuery {
 func (a *Alert) QueryEvents() *EventQuery {
 	return (&AlertClient{config: a.config}).QueryEvents(a)
 	return (&AlertClient{config: a.config}).QueryEvents(a)
 }
 }
 
 
-// QueryMetas queries the metas edge of the Alert.
+// QueryMetas queries the "metas" edge of the Alert entity.
 func (a *Alert) QueryMetas() *MetaQuery {
 func (a *Alert) QueryMetas() *MetaQuery {
 	return (&AlertClient{config: a.config}).QueryMetas(a)
 	return (&AlertClient{config: a.config}).QueryMetas(a)
 }
 }
 
 
 // Update returns a builder for updating this Alert.
 // Update returns a builder for updating this Alert.
-// Note that, you need to call Alert.Unwrap() before calling this method, if this Alert
+// Note that you need to call Alert.Unwrap() before calling this method if this Alert
 // was returned from a transaction, and the transaction was committed or rolled back.
 // was returned from a transaction, and the transaction was committed or rolled back.
 func (a *Alert) Update() *AlertUpdateOne {
 func (a *Alert) Update() *AlertUpdateOne {
 	return (&AlertClient{config: a.config}).UpdateOne(a)
 	return (&AlertClient{config: a.config}).UpdateOne(a)
 }
 }
 
 
-// Unwrap unwraps the entity that was returned from a transaction after it was closed,
-// so that all next queries will be executed through the driver which created the transaction.
+// Unwrap unwraps the Alert entity that was returned from a transaction after it was closed,
+// so that all future queries will be executed through the driver which created the transaction.
 func (a *Alert) Unwrap() *Alert {
 func (a *Alert) Unwrap() *Alert {
 	tx, ok := a.config.driver.(*txDriver)
 	tx, ok := a.config.driver.(*txDriver)
 	if !ok {
 	if !ok {

+ 8 - 8
pkg/database/ent/alert/alert.go

@@ -145,20 +145,20 @@ func ValidColumn(column string) bool {
 }
 }
 
 
 var (
 var (
-	// DefaultCreatedAt holds the default value on creation for the created_at field.
+	// DefaultCreatedAt holds the default value on creation for the "created_at" field.
 	DefaultCreatedAt func() time.Time
 	DefaultCreatedAt func() time.Time
-	// DefaultUpdatedAt holds the default value on creation for the updated_at field.
+	// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
 	DefaultUpdatedAt func() time.Time
 	DefaultUpdatedAt func() time.Time
-	// DefaultBucketId holds the default value on creation for the bucketId field.
+	// DefaultBucketId holds the default value on creation for the "bucketId" field.
 	DefaultBucketId string
 	DefaultBucketId string
-	// DefaultMessage holds the default value on creation for the message field.
+	// DefaultMessage holds the default value on creation for the "message" field.
 	DefaultMessage string
 	DefaultMessage string
-	// DefaultEventsCount holds the default value on creation for the eventsCount field.
+	// DefaultEventsCount holds the default value on creation for the "eventsCount" field.
 	DefaultEventsCount int32
 	DefaultEventsCount int32
-	// DefaultStartedAt holds the default value on creation for the startedAt field.
+	// DefaultStartedAt holds the default value on creation for the "startedAt" field.
 	DefaultStartedAt func() time.Time
 	DefaultStartedAt func() time.Time
-	// DefaultStoppedAt holds the default value on creation for the stoppedAt field.
+	// DefaultStoppedAt holds the default value on creation for the "stoppedAt" field.
 	DefaultStoppedAt func() time.Time
 	DefaultStoppedAt func() time.Time
-	// DefaultSimulated holds the default value on creation for the simulated field.
+	// DefaultSimulated holds the default value on creation for the "simulated" field.
 	DefaultSimulated bool
 	DefaultSimulated bool
 )
 )

+ 3 - 3
pkg/database/ent/alert/where.go

@@ -10,7 +10,7 @@ import (
 	"github.com/facebook/ent/dialect/sql/sqlgraph"
 	"github.com/facebook/ent/dialect/sql/sqlgraph"
 )
 )
 
 
-// ID filters vertices based on their identifier.
+// ID filters vertices based on their ID field.
 func ID(id int) predicate.Alert {
 func ID(id int) predicate.Alert {
 	return predicate.Alert(func(s *sql.Selector) {
 	return predicate.Alert(func(s *sql.Selector) {
 		s.Where(sql.EQ(s.C(FieldID), id))
 		s.Where(sql.EQ(s.C(FieldID), id))
@@ -2676,7 +2676,7 @@ func HasMetasWith(preds ...predicate.Meta) predicate.Alert {
 	})
 	})
 }
 }
 
 
-// And groups list of predicates with the AND operator between them.
+// And groups predicates with the AND operator between them.
 func And(predicates ...predicate.Alert) predicate.Alert {
 func And(predicates ...predicate.Alert) predicate.Alert {
 	return predicate.Alert(func(s *sql.Selector) {
 	return predicate.Alert(func(s *sql.Selector) {
 		s1 := s.Clone().SetP(nil)
 		s1 := s.Clone().SetP(nil)
@@ -2687,7 +2687,7 @@ func And(predicates ...predicate.Alert) predicate.Alert {
 	})
 	})
 }
 }
 
 
-// Or groups list of predicates with the OR operator between them.
+// Or groups predicates with the OR operator between them.
 func Or(predicates ...predicate.Alert) predicate.Alert {
 func Or(predicates ...predicate.Alert) predicate.Alert {
 	return predicate.Alert(func(s *sql.Selector) {
 	return predicate.Alert(func(s *sql.Selector) {
 		s1 := s.Clone().SetP(nil)
 		s1 := s.Clone().SetP(nil)

+ 54 - 54
pkg/database/ent/alert_create.go

@@ -24,13 +24,13 @@ type AlertCreate struct {
 	hooks    []Hook
 	hooks    []Hook
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (ac *AlertCreate) SetCreatedAt(t time.Time) *AlertCreate {
 func (ac *AlertCreate) SetCreatedAt(t time.Time) *AlertCreate {
 	ac.mutation.SetCreatedAt(t)
 	ac.mutation.SetCreatedAt(t)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableCreatedAt(t *time.Time) *AlertCreate {
 func (ac *AlertCreate) SetNillableCreatedAt(t *time.Time) *AlertCreate {
 	if t != nil {
 	if t != nil {
 		ac.SetCreatedAt(*t)
 		ac.SetCreatedAt(*t)
@@ -38,13 +38,13 @@ func (ac *AlertCreate) SetNillableCreatedAt(t *time.Time) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (ac *AlertCreate) SetUpdatedAt(t time.Time) *AlertCreate {
 func (ac *AlertCreate) SetUpdatedAt(t time.Time) *AlertCreate {
 	ac.mutation.SetUpdatedAt(t)
 	ac.mutation.SetUpdatedAt(t)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableUpdatedAt(t *time.Time) *AlertCreate {
 func (ac *AlertCreate) SetNillableUpdatedAt(t *time.Time) *AlertCreate {
 	if t != nil {
 	if t != nil {
 		ac.SetUpdatedAt(*t)
 		ac.SetUpdatedAt(*t)
@@ -52,19 +52,19 @@ func (ac *AlertCreate) SetNillableUpdatedAt(t *time.Time) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetScenario sets the scenario field.
+// SetScenario sets the "scenario" field.
 func (ac *AlertCreate) SetScenario(s string) *AlertCreate {
 func (ac *AlertCreate) SetScenario(s string) *AlertCreate {
 	ac.mutation.SetScenario(s)
 	ac.mutation.SetScenario(s)
 	return ac
 	return ac
 }
 }
 
 
-// SetBucketId sets the bucketId field.
+// SetBucketId sets the "bucketId" field.
 func (ac *AlertCreate) SetBucketId(s string) *AlertCreate {
 func (ac *AlertCreate) SetBucketId(s string) *AlertCreate {
 	ac.mutation.SetBucketId(s)
 	ac.mutation.SetBucketId(s)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableBucketId sets the bucketId field if the given value is not nil.
+// SetNillableBucketId sets the "bucketId" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableBucketId(s *string) *AlertCreate {
 func (ac *AlertCreate) SetNillableBucketId(s *string) *AlertCreate {
 	if s != nil {
 	if s != nil {
 		ac.SetBucketId(*s)
 		ac.SetBucketId(*s)
@@ -72,13 +72,13 @@ func (ac *AlertCreate) SetNillableBucketId(s *string) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetMessage sets the message field.
+// SetMessage sets the "message" field.
 func (ac *AlertCreate) SetMessage(s string) *AlertCreate {
 func (ac *AlertCreate) SetMessage(s string) *AlertCreate {
 	ac.mutation.SetMessage(s)
 	ac.mutation.SetMessage(s)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableMessage sets the message field if the given value is not nil.
+// SetNillableMessage sets the "message" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableMessage(s *string) *AlertCreate {
 func (ac *AlertCreate) SetNillableMessage(s *string) *AlertCreate {
 	if s != nil {
 	if s != nil {
 		ac.SetMessage(*s)
 		ac.SetMessage(*s)
@@ -86,13 +86,13 @@ func (ac *AlertCreate) SetNillableMessage(s *string) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetEventsCount sets the eventsCount field.
+// SetEventsCount sets the "eventsCount" field.
 func (ac *AlertCreate) SetEventsCount(i int32) *AlertCreate {
 func (ac *AlertCreate) SetEventsCount(i int32) *AlertCreate {
 	ac.mutation.SetEventsCount(i)
 	ac.mutation.SetEventsCount(i)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableEventsCount sets the eventsCount field if the given value is not nil.
+// SetNillableEventsCount sets the "eventsCount" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableEventsCount(i *int32) *AlertCreate {
 func (ac *AlertCreate) SetNillableEventsCount(i *int32) *AlertCreate {
 	if i != nil {
 	if i != nil {
 		ac.SetEventsCount(*i)
 		ac.SetEventsCount(*i)
@@ -100,13 +100,13 @@ func (ac *AlertCreate) SetNillableEventsCount(i *int32) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetStartedAt sets the startedAt field.
+// SetStartedAt sets the "startedAt" field.
 func (ac *AlertCreate) SetStartedAt(t time.Time) *AlertCreate {
 func (ac *AlertCreate) SetStartedAt(t time.Time) *AlertCreate {
 	ac.mutation.SetStartedAt(t)
 	ac.mutation.SetStartedAt(t)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableStartedAt sets the startedAt field if the given value is not nil.
+// SetNillableStartedAt sets the "startedAt" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableStartedAt(t *time.Time) *AlertCreate {
 func (ac *AlertCreate) SetNillableStartedAt(t *time.Time) *AlertCreate {
 	if t != nil {
 	if t != nil {
 		ac.SetStartedAt(*t)
 		ac.SetStartedAt(*t)
@@ -114,13 +114,13 @@ func (ac *AlertCreate) SetNillableStartedAt(t *time.Time) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetStoppedAt sets the stoppedAt field.
+// SetStoppedAt sets the "stoppedAt" field.
 func (ac *AlertCreate) SetStoppedAt(t time.Time) *AlertCreate {
 func (ac *AlertCreate) SetStoppedAt(t time.Time) *AlertCreate {
 	ac.mutation.SetStoppedAt(t)
 	ac.mutation.SetStoppedAt(t)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableStoppedAt sets the stoppedAt field if the given value is not nil.
+// SetNillableStoppedAt sets the "stoppedAt" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableStoppedAt(t *time.Time) *AlertCreate {
 func (ac *AlertCreate) SetNillableStoppedAt(t *time.Time) *AlertCreate {
 	if t != nil {
 	if t != nil {
 		ac.SetStoppedAt(*t)
 		ac.SetStoppedAt(*t)
@@ -128,13 +128,13 @@ func (ac *AlertCreate) SetNillableStoppedAt(t *time.Time) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetSourceIp sets the sourceIp field.
+// SetSourceIp sets the "sourceIp" field.
 func (ac *AlertCreate) SetSourceIp(s string) *AlertCreate {
 func (ac *AlertCreate) SetSourceIp(s string) *AlertCreate {
 	ac.mutation.SetSourceIp(s)
 	ac.mutation.SetSourceIp(s)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableSourceIp sets the sourceIp field if the given value is not nil.
+// SetNillableSourceIp sets the "sourceIp" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableSourceIp(s *string) *AlertCreate {
 func (ac *AlertCreate) SetNillableSourceIp(s *string) *AlertCreate {
 	if s != nil {
 	if s != nil {
 		ac.SetSourceIp(*s)
 		ac.SetSourceIp(*s)
@@ -142,13 +142,13 @@ func (ac *AlertCreate) SetNillableSourceIp(s *string) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetSourceRange sets the sourceRange field.
+// SetSourceRange sets the "sourceRange" field.
 func (ac *AlertCreate) SetSourceRange(s string) *AlertCreate {
 func (ac *AlertCreate) SetSourceRange(s string) *AlertCreate {
 	ac.mutation.SetSourceRange(s)
 	ac.mutation.SetSourceRange(s)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableSourceRange sets the sourceRange field if the given value is not nil.
+// SetNillableSourceRange sets the "sourceRange" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableSourceRange(s *string) *AlertCreate {
 func (ac *AlertCreate) SetNillableSourceRange(s *string) *AlertCreate {
 	if s != nil {
 	if s != nil {
 		ac.SetSourceRange(*s)
 		ac.SetSourceRange(*s)
@@ -156,13 +156,13 @@ func (ac *AlertCreate) SetNillableSourceRange(s *string) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetSourceAsNumber sets the sourceAsNumber field.
+// SetSourceAsNumber sets the "sourceAsNumber" field.
 func (ac *AlertCreate) SetSourceAsNumber(s string) *AlertCreate {
 func (ac *AlertCreate) SetSourceAsNumber(s string) *AlertCreate {
 	ac.mutation.SetSourceAsNumber(s)
 	ac.mutation.SetSourceAsNumber(s)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableSourceAsNumber sets the sourceAsNumber field if the given value is not nil.
+// SetNillableSourceAsNumber sets the "sourceAsNumber" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableSourceAsNumber(s *string) *AlertCreate {
 func (ac *AlertCreate) SetNillableSourceAsNumber(s *string) *AlertCreate {
 	if s != nil {
 	if s != nil {
 		ac.SetSourceAsNumber(*s)
 		ac.SetSourceAsNumber(*s)
@@ -170,13 +170,13 @@ func (ac *AlertCreate) SetNillableSourceAsNumber(s *string) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetSourceAsName sets the sourceAsName field.
+// SetSourceAsName sets the "sourceAsName" field.
 func (ac *AlertCreate) SetSourceAsName(s string) *AlertCreate {
 func (ac *AlertCreate) SetSourceAsName(s string) *AlertCreate {
 	ac.mutation.SetSourceAsName(s)
 	ac.mutation.SetSourceAsName(s)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableSourceAsName sets the sourceAsName field if the given value is not nil.
+// SetNillableSourceAsName sets the "sourceAsName" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableSourceAsName(s *string) *AlertCreate {
 func (ac *AlertCreate) SetNillableSourceAsName(s *string) *AlertCreate {
 	if s != nil {
 	if s != nil {
 		ac.SetSourceAsName(*s)
 		ac.SetSourceAsName(*s)
@@ -184,13 +184,13 @@ func (ac *AlertCreate) SetNillableSourceAsName(s *string) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetSourceCountry sets the sourceCountry field.
+// SetSourceCountry sets the "sourceCountry" field.
 func (ac *AlertCreate) SetSourceCountry(s string) *AlertCreate {
 func (ac *AlertCreate) SetSourceCountry(s string) *AlertCreate {
 	ac.mutation.SetSourceCountry(s)
 	ac.mutation.SetSourceCountry(s)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableSourceCountry sets the sourceCountry field if the given value is not nil.
+// SetNillableSourceCountry sets the "sourceCountry" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableSourceCountry(s *string) *AlertCreate {
 func (ac *AlertCreate) SetNillableSourceCountry(s *string) *AlertCreate {
 	if s != nil {
 	if s != nil {
 		ac.SetSourceCountry(*s)
 		ac.SetSourceCountry(*s)
@@ -198,13 +198,13 @@ func (ac *AlertCreate) SetNillableSourceCountry(s *string) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetSourceLatitude sets the sourceLatitude field.
+// SetSourceLatitude sets the "sourceLatitude" field.
 func (ac *AlertCreate) SetSourceLatitude(f float32) *AlertCreate {
 func (ac *AlertCreate) SetSourceLatitude(f float32) *AlertCreate {
 	ac.mutation.SetSourceLatitude(f)
 	ac.mutation.SetSourceLatitude(f)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableSourceLatitude sets the sourceLatitude field if the given value is not nil.
+// SetNillableSourceLatitude sets the "sourceLatitude" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableSourceLatitude(f *float32) *AlertCreate {
 func (ac *AlertCreate) SetNillableSourceLatitude(f *float32) *AlertCreate {
 	if f != nil {
 	if f != nil {
 		ac.SetSourceLatitude(*f)
 		ac.SetSourceLatitude(*f)
@@ -212,13 +212,13 @@ func (ac *AlertCreate) SetNillableSourceLatitude(f *float32) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetSourceLongitude sets the sourceLongitude field.
+// SetSourceLongitude sets the "sourceLongitude" field.
 func (ac *AlertCreate) SetSourceLongitude(f float32) *AlertCreate {
 func (ac *AlertCreate) SetSourceLongitude(f float32) *AlertCreate {
 	ac.mutation.SetSourceLongitude(f)
 	ac.mutation.SetSourceLongitude(f)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableSourceLongitude sets the sourceLongitude field if the given value is not nil.
+// SetNillableSourceLongitude sets the "sourceLongitude" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableSourceLongitude(f *float32) *AlertCreate {
 func (ac *AlertCreate) SetNillableSourceLongitude(f *float32) *AlertCreate {
 	if f != nil {
 	if f != nil {
 		ac.SetSourceLongitude(*f)
 		ac.SetSourceLongitude(*f)
@@ -226,13 +226,13 @@ func (ac *AlertCreate) SetNillableSourceLongitude(f *float32) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetSourceScope sets the sourceScope field.
+// SetSourceScope sets the "sourceScope" field.
 func (ac *AlertCreate) SetSourceScope(s string) *AlertCreate {
 func (ac *AlertCreate) SetSourceScope(s string) *AlertCreate {
 	ac.mutation.SetSourceScope(s)
 	ac.mutation.SetSourceScope(s)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableSourceScope sets the sourceScope field if the given value is not nil.
+// SetNillableSourceScope sets the "sourceScope" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableSourceScope(s *string) *AlertCreate {
 func (ac *AlertCreate) SetNillableSourceScope(s *string) *AlertCreate {
 	if s != nil {
 	if s != nil {
 		ac.SetSourceScope(*s)
 		ac.SetSourceScope(*s)
@@ -240,13 +240,13 @@ func (ac *AlertCreate) SetNillableSourceScope(s *string) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetSourceValue sets the sourceValue field.
+// SetSourceValue sets the "sourceValue" field.
 func (ac *AlertCreate) SetSourceValue(s string) *AlertCreate {
 func (ac *AlertCreate) SetSourceValue(s string) *AlertCreate {
 	ac.mutation.SetSourceValue(s)
 	ac.mutation.SetSourceValue(s)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableSourceValue sets the sourceValue field if the given value is not nil.
+// SetNillableSourceValue sets the "sourceValue" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableSourceValue(s *string) *AlertCreate {
 func (ac *AlertCreate) SetNillableSourceValue(s *string) *AlertCreate {
 	if s != nil {
 	if s != nil {
 		ac.SetSourceValue(*s)
 		ac.SetSourceValue(*s)
@@ -254,13 +254,13 @@ func (ac *AlertCreate) SetNillableSourceValue(s *string) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetCapacity sets the capacity field.
+// SetCapacity sets the "capacity" field.
 func (ac *AlertCreate) SetCapacity(i int32) *AlertCreate {
 func (ac *AlertCreate) SetCapacity(i int32) *AlertCreate {
 	ac.mutation.SetCapacity(i)
 	ac.mutation.SetCapacity(i)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableCapacity sets the capacity field if the given value is not nil.
+// SetNillableCapacity sets the "capacity" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableCapacity(i *int32) *AlertCreate {
 func (ac *AlertCreate) SetNillableCapacity(i *int32) *AlertCreate {
 	if i != nil {
 	if i != nil {
 		ac.SetCapacity(*i)
 		ac.SetCapacity(*i)
@@ -268,13 +268,13 @@ func (ac *AlertCreate) SetNillableCapacity(i *int32) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetLeakSpeed sets the leakSpeed field.
+// SetLeakSpeed sets the "leakSpeed" field.
 func (ac *AlertCreate) SetLeakSpeed(s string) *AlertCreate {
 func (ac *AlertCreate) SetLeakSpeed(s string) *AlertCreate {
 	ac.mutation.SetLeakSpeed(s)
 	ac.mutation.SetLeakSpeed(s)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableLeakSpeed sets the leakSpeed field if the given value is not nil.
+// SetNillableLeakSpeed sets the "leakSpeed" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableLeakSpeed(s *string) *AlertCreate {
 func (ac *AlertCreate) SetNillableLeakSpeed(s *string) *AlertCreate {
 	if s != nil {
 	if s != nil {
 		ac.SetLeakSpeed(*s)
 		ac.SetLeakSpeed(*s)
@@ -282,13 +282,13 @@ func (ac *AlertCreate) SetNillableLeakSpeed(s *string) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetScenarioVersion sets the scenarioVersion field.
+// SetScenarioVersion sets the "scenarioVersion" field.
 func (ac *AlertCreate) SetScenarioVersion(s string) *AlertCreate {
 func (ac *AlertCreate) SetScenarioVersion(s string) *AlertCreate {
 	ac.mutation.SetScenarioVersion(s)
 	ac.mutation.SetScenarioVersion(s)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableScenarioVersion sets the scenarioVersion field if the given value is not nil.
+// SetNillableScenarioVersion sets the "scenarioVersion" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableScenarioVersion(s *string) *AlertCreate {
 func (ac *AlertCreate) SetNillableScenarioVersion(s *string) *AlertCreate {
 	if s != nil {
 	if s != nil {
 		ac.SetScenarioVersion(*s)
 		ac.SetScenarioVersion(*s)
@@ -296,13 +296,13 @@ func (ac *AlertCreate) SetNillableScenarioVersion(s *string) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetScenarioHash sets the scenarioHash field.
+// SetScenarioHash sets the "scenarioHash" field.
 func (ac *AlertCreate) SetScenarioHash(s string) *AlertCreate {
 func (ac *AlertCreate) SetScenarioHash(s string) *AlertCreate {
 	ac.mutation.SetScenarioHash(s)
 	ac.mutation.SetScenarioHash(s)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableScenarioHash sets the scenarioHash field if the given value is not nil.
+// SetNillableScenarioHash sets the "scenarioHash" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableScenarioHash(s *string) *AlertCreate {
 func (ac *AlertCreate) SetNillableScenarioHash(s *string) *AlertCreate {
 	if s != nil {
 	if s != nil {
 		ac.SetScenarioHash(*s)
 		ac.SetScenarioHash(*s)
@@ -310,13 +310,13 @@ func (ac *AlertCreate) SetNillableScenarioHash(s *string) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetSimulated sets the simulated field.
+// SetSimulated sets the "simulated" field.
 func (ac *AlertCreate) SetSimulated(b bool) *AlertCreate {
 func (ac *AlertCreate) SetSimulated(b bool) *AlertCreate {
 	ac.mutation.SetSimulated(b)
 	ac.mutation.SetSimulated(b)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableSimulated sets the simulated field if the given value is not nil.
+// SetNillableSimulated sets the "simulated" field if the given value is not nil.
 func (ac *AlertCreate) SetNillableSimulated(b *bool) *AlertCreate {
 func (ac *AlertCreate) SetNillableSimulated(b *bool) *AlertCreate {
 	if b != nil {
 	if b != nil {
 		ac.SetSimulated(*b)
 		ac.SetSimulated(*b)
@@ -324,13 +324,13 @@ func (ac *AlertCreate) SetNillableSimulated(b *bool) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetOwnerID sets the owner edge to Machine by id.
+// SetOwnerID sets the "owner" edge to the Machine entity by ID.
 func (ac *AlertCreate) SetOwnerID(id int) *AlertCreate {
 func (ac *AlertCreate) SetOwnerID(id int) *AlertCreate {
 	ac.mutation.SetOwnerID(id)
 	ac.mutation.SetOwnerID(id)
 	return ac
 	return ac
 }
 }
 
 
-// SetNillableOwnerID sets the owner edge to Machine by id if the given value is not nil.
+// SetNillableOwnerID sets the "owner" edge to the Machine entity by ID if the given value is not nil.
 func (ac *AlertCreate) SetNillableOwnerID(id *int) *AlertCreate {
 func (ac *AlertCreate) SetNillableOwnerID(id *int) *AlertCreate {
 	if id != nil {
 	if id != nil {
 		ac = ac.SetOwnerID(*id)
 		ac = ac.SetOwnerID(*id)
@@ -338,18 +338,18 @@ func (ac *AlertCreate) SetNillableOwnerID(id *int) *AlertCreate {
 	return ac
 	return ac
 }
 }
 
 
-// SetOwner sets the owner edge to Machine.
+// SetOwner sets the "owner" edge to the Machine entity.
 func (ac *AlertCreate) SetOwner(m *Machine) *AlertCreate {
 func (ac *AlertCreate) SetOwner(m *Machine) *AlertCreate {
 	return ac.SetOwnerID(m.ID)
 	return ac.SetOwnerID(m.ID)
 }
 }
 
 
-// AddDecisionIDs adds the decisions edge to Decision by ids.
+// AddDecisionIDs adds the "decisions" edge to the Decision entity by IDs.
 func (ac *AlertCreate) AddDecisionIDs(ids ...int) *AlertCreate {
 func (ac *AlertCreate) AddDecisionIDs(ids ...int) *AlertCreate {
 	ac.mutation.AddDecisionIDs(ids...)
 	ac.mutation.AddDecisionIDs(ids...)
 	return ac
 	return ac
 }
 }
 
 
-// AddDecisions adds the decisions edges to Decision.
+// AddDecisions adds the "decisions" edges to the Decision entity.
 func (ac *AlertCreate) AddDecisions(d ...*Decision) *AlertCreate {
 func (ac *AlertCreate) AddDecisions(d ...*Decision) *AlertCreate {
 	ids := make([]int, len(d))
 	ids := make([]int, len(d))
 	for i := range d {
 	for i := range d {
@@ -358,13 +358,13 @@ func (ac *AlertCreate) AddDecisions(d ...*Decision) *AlertCreate {
 	return ac.AddDecisionIDs(ids...)
 	return ac.AddDecisionIDs(ids...)
 }
 }
 
 
-// AddEventIDs adds the events edge to Event by ids.
+// AddEventIDs adds the "events" edge to the Event entity by IDs.
 func (ac *AlertCreate) AddEventIDs(ids ...int) *AlertCreate {
 func (ac *AlertCreate) AddEventIDs(ids ...int) *AlertCreate {
 	ac.mutation.AddEventIDs(ids...)
 	ac.mutation.AddEventIDs(ids...)
 	return ac
 	return ac
 }
 }
 
 
-// AddEvents adds the events edges to Event.
+// AddEvents adds the "events" edges to the Event entity.
 func (ac *AlertCreate) AddEvents(e ...*Event) *AlertCreate {
 func (ac *AlertCreate) AddEvents(e ...*Event) *AlertCreate {
 	ids := make([]int, len(e))
 	ids := make([]int, len(e))
 	for i := range e {
 	for i := range e {
@@ -373,13 +373,13 @@ func (ac *AlertCreate) AddEvents(e ...*Event) *AlertCreate {
 	return ac.AddEventIDs(ids...)
 	return ac.AddEventIDs(ids...)
 }
 }
 
 
-// AddMetaIDs adds the metas edge to Meta by ids.
+// AddMetaIDs adds the "metas" edge to the Meta entity by IDs.
 func (ac *AlertCreate) AddMetaIDs(ids ...int) *AlertCreate {
 func (ac *AlertCreate) AddMetaIDs(ids ...int) *AlertCreate {
 	ac.mutation.AddMetaIDs(ids...)
 	ac.mutation.AddMetaIDs(ids...)
 	return ac
 	return ac
 }
 }
 
 
-// AddMetas adds the metas edges to Meta.
+// AddMetas adds the "metas" edges to the Meta entity.
 func (ac *AlertCreate) AddMetas(m ...*Meta) *AlertCreate {
 func (ac *AlertCreate) AddMetas(m ...*Meta) *AlertCreate {
 	ids := make([]int, len(m))
 	ids := make([]int, len(m))
 	for i := range m {
 	for i := range m {
@@ -770,7 +770,7 @@ func (ac *AlertCreate) createSpec() (*Alert, *sqlgraph.CreateSpec) {
 	return _node, _spec
 	return _node, _spec
 }
 }
 
 
-// AlertCreateBulk is the builder for creating a bulk of Alert entities.
+// AlertCreateBulk is the builder for creating many Alert entities in bulk.
 type AlertCreateBulk struct {
 type AlertCreateBulk struct {
 	config
 	config
 	builders []*AlertCreate
 	builders []*AlertCreate
@@ -828,7 +828,7 @@ func (acb *AlertCreateBulk) Save(ctx context.Context) ([]*Alert, error) {
 	return nodes, nil
 	return nodes, nil
 }
 }
 
 
-// SaveX calls Save and panics if Save returns an error.
+// SaveX is like Save, but panics if an error occurs.
 func (acb *AlertCreateBulk) SaveX(ctx context.Context) []*Alert {
 func (acb *AlertCreateBulk) SaveX(ctx context.Context) []*Alert {
 	v, err := acb.Save(ctx)
 	v, err := acb.Save(ctx)
 	if err != nil {
 	if err != nil {

+ 5 - 6
pkg/database/ent/alert_delete.go

@@ -16,14 +16,13 @@ import (
 // AlertDelete is the builder for deleting a Alert entity.
 // AlertDelete is the builder for deleting a Alert entity.
 type AlertDelete struct {
 type AlertDelete struct {
 	config
 	config
-	hooks      []Hook
-	mutation   *AlertMutation
-	predicates []predicate.Alert
+	hooks    []Hook
+	mutation *AlertMutation
 }
 }
 
 
-// Where adds a new predicate to the delete builder.
+// Where adds a new predicate to the AlertDelete builder.
 func (ad *AlertDelete) Where(ps ...predicate.Alert) *AlertDelete {
 func (ad *AlertDelete) Where(ps ...predicate.Alert) *AlertDelete {
-	ad.predicates = append(ad.predicates, ps...)
+	ad.mutation.predicates = append(ad.mutation.predicates, ps...)
 	return ad
 	return ad
 }
 }
 
 
@@ -75,7 +74,7 @@ func (ad *AlertDelete) sqlExec(ctx context.Context) (int, error) {
 			},
 			},
 		},
 		},
 	}
 	}
-	if ps := ad.predicates; len(ps) > 0 {
+	if ps := ad.mutation.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
 				ps[i](selector)
 				ps[i](selector)

+ 98 - 79
pkg/database/ent/alert_query.go

@@ -26,7 +26,7 @@ type AlertQuery struct {
 	limit      *int
 	limit      *int
 	offset     *int
 	offset     *int
 	order      []OrderFunc
 	order      []OrderFunc
-	unique     []string
+	fields     []string
 	predicates []predicate.Alert
 	predicates []predicate.Alert
 	// eager-loading edges.
 	// eager-loading edges.
 	withOwner     *MachineQuery
 	withOwner     *MachineQuery
@@ -39,7 +39,7 @@ type AlertQuery struct {
 	path func(context.Context) (*sql.Selector, error)
 	path func(context.Context) (*sql.Selector, error)
 }
 }
 
 
-// Where adds a new predicate for the builder.
+// Where adds a new predicate for the AlertQuery builder.
 func (aq *AlertQuery) Where(ps ...predicate.Alert) *AlertQuery {
 func (aq *AlertQuery) Where(ps ...predicate.Alert) *AlertQuery {
 	aq.predicates = append(aq.predicates, ps...)
 	aq.predicates = append(aq.predicates, ps...)
 	return aq
 	return aq
@@ -63,7 +63,7 @@ func (aq *AlertQuery) Order(o ...OrderFunc) *AlertQuery {
 	return aq
 	return aq
 }
 }
 
 
-// QueryOwner chains the current query on the owner edge.
+// QueryOwner chains the current query on the "owner" edge.
 func (aq *AlertQuery) QueryOwner() *MachineQuery {
 func (aq *AlertQuery) QueryOwner() *MachineQuery {
 	query := &MachineQuery{config: aq.config}
 	query := &MachineQuery{config: aq.config}
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
@@ -85,7 +85,7 @@ func (aq *AlertQuery) QueryOwner() *MachineQuery {
 	return query
 	return query
 }
 }
 
 
-// QueryDecisions chains the current query on the decisions edge.
+// QueryDecisions chains the current query on the "decisions" edge.
 func (aq *AlertQuery) QueryDecisions() *DecisionQuery {
 func (aq *AlertQuery) QueryDecisions() *DecisionQuery {
 	query := &DecisionQuery{config: aq.config}
 	query := &DecisionQuery{config: aq.config}
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
@@ -107,7 +107,7 @@ func (aq *AlertQuery) QueryDecisions() *DecisionQuery {
 	return query
 	return query
 }
 }
 
 
-// QueryEvents chains the current query on the events edge.
+// QueryEvents chains the current query on the "events" edge.
 func (aq *AlertQuery) QueryEvents() *EventQuery {
 func (aq *AlertQuery) QueryEvents() *EventQuery {
 	query := &EventQuery{config: aq.config}
 	query := &EventQuery{config: aq.config}
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
@@ -129,7 +129,7 @@ func (aq *AlertQuery) QueryEvents() *EventQuery {
 	return query
 	return query
 }
 }
 
 
-// QueryMetas chains the current query on the metas edge.
+// QueryMetas chains the current query on the "metas" edge.
 func (aq *AlertQuery) QueryMetas() *MetaQuery {
 func (aq *AlertQuery) QueryMetas() *MetaQuery {
 	query := &MetaQuery{config: aq.config}
 	query := &MetaQuery{config: aq.config}
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
@@ -151,7 +151,8 @@ func (aq *AlertQuery) QueryMetas() *MetaQuery {
 	return query
 	return query
 }
 }
 
 
-// First returns the first Alert entity in the query. Returns *NotFoundError when no alert was found.
+// First returns the first Alert entity from the query.
+// Returns a *NotFoundError when no Alert was found.
 func (aq *AlertQuery) First(ctx context.Context) (*Alert, error) {
 func (aq *AlertQuery) First(ctx context.Context) (*Alert, error) {
 	nodes, err := aq.Limit(1).All(ctx)
 	nodes, err := aq.Limit(1).All(ctx)
 	if err != nil {
 	if err != nil {
@@ -172,7 +173,8 @@ func (aq *AlertQuery) FirstX(ctx context.Context) *Alert {
 	return node
 	return node
 }
 }
 
 
-// FirstID returns the first Alert id in the query. Returns *NotFoundError when no id was found.
+// FirstID returns the first Alert ID from the query.
+// Returns a *NotFoundError when no Alert ID was found.
 func (aq *AlertQuery) FirstID(ctx context.Context) (id int, err error) {
 func (aq *AlertQuery) FirstID(ctx context.Context) (id int, err error) {
 	var ids []int
 	var ids []int
 	if ids, err = aq.Limit(1).IDs(ctx); err != nil {
 	if ids, err = aq.Limit(1).IDs(ctx); err != nil {
@@ -185,8 +187,8 @@ func (aq *AlertQuery) FirstID(ctx context.Context) (id int, err error) {
 	return ids[0], nil
 	return ids[0], nil
 }
 }
 
 
-// FirstXID is like FirstID, but panics if an error occurs.
-func (aq *AlertQuery) FirstXID(ctx context.Context) int {
+// FirstIDX is like FirstID, but panics if an error occurs.
+func (aq *AlertQuery) FirstIDX(ctx context.Context) int {
 	id, err := aq.FirstID(ctx)
 	id, err := aq.FirstID(ctx)
 	if err != nil && !IsNotFound(err) {
 	if err != nil && !IsNotFound(err) {
 		panic(err)
 		panic(err)
@@ -194,7 +196,9 @@ func (aq *AlertQuery) FirstXID(ctx context.Context) int {
 	return id
 	return id
 }
 }
 
 
-// Only returns the only Alert entity in the query, returns an error if not exactly one entity was returned.
+// Only returns a single Alert entity found by the query, ensuring it only returns one.
+// Returns a *NotSingularError when exactly one Alert entity is not found.
+// Returns a *NotFoundError when no Alert entities are found.
 func (aq *AlertQuery) Only(ctx context.Context) (*Alert, error) {
 func (aq *AlertQuery) Only(ctx context.Context) (*Alert, error) {
 	nodes, err := aq.Limit(2).All(ctx)
 	nodes, err := aq.Limit(2).All(ctx)
 	if err != nil {
 	if err != nil {
@@ -219,7 +223,9 @@ func (aq *AlertQuery) OnlyX(ctx context.Context) *Alert {
 	return node
 	return node
 }
 }
 
 
-// OnlyID returns the only Alert id in the query, returns an error if not exactly one id was returned.
+// OnlyID is like Only, but returns the only Alert ID in the query.
+// Returns a *NotSingularError when exactly one Alert ID is not found.
+// Returns a *NotFoundError when no entities are found.
 func (aq *AlertQuery) OnlyID(ctx context.Context) (id int, err error) {
 func (aq *AlertQuery) OnlyID(ctx context.Context) (id int, err error) {
 	var ids []int
 	var ids []int
 	if ids, err = aq.Limit(2).IDs(ctx); err != nil {
 	if ids, err = aq.Limit(2).IDs(ctx); err != nil {
@@ -262,7 +268,7 @@ func (aq *AlertQuery) AllX(ctx context.Context) []*Alert {
 	return nodes
 	return nodes
 }
 }
 
 
-// IDs executes the query and returns a list of Alert ids.
+// IDs executes the query and returns a list of Alert IDs.
 func (aq *AlertQuery) IDs(ctx context.Context) ([]int, error) {
 func (aq *AlertQuery) IDs(ctx context.Context) ([]int, error) {
 	var ids []int
 	var ids []int
 	if err := aq.Select(alert.FieldID).Scan(ctx, &ids); err != nil {
 	if err := aq.Select(alert.FieldID).Scan(ctx, &ids); err != nil {
@@ -314,24 +320,30 @@ func (aq *AlertQuery) ExistX(ctx context.Context) bool {
 	return exist
 	return exist
 }
 }
 
 
-// Clone returns a duplicate of the query builder, including all associated steps. It can be
+// Clone returns a duplicate of the AlertQuery builder, including all associated steps. It can be
 // used to prepare common query builders and use them differently after the clone is made.
 // used to prepare common query builders and use them differently after the clone is made.
 func (aq *AlertQuery) Clone() *AlertQuery {
 func (aq *AlertQuery) Clone() *AlertQuery {
+	if aq == nil {
+		return nil
+	}
 	return &AlertQuery{
 	return &AlertQuery{
-		config:     aq.config,
-		limit:      aq.limit,
-		offset:     aq.offset,
-		order:      append([]OrderFunc{}, aq.order...),
-		unique:     append([]string{}, aq.unique...),
-		predicates: append([]predicate.Alert{}, aq.predicates...),
+		config:        aq.config,
+		limit:         aq.limit,
+		offset:        aq.offset,
+		order:         append([]OrderFunc{}, aq.order...),
+		predicates:    append([]predicate.Alert{}, aq.predicates...),
+		withOwner:     aq.withOwner.Clone(),
+		withDecisions: aq.withDecisions.Clone(),
+		withEvents:    aq.withEvents.Clone(),
+		withMetas:     aq.withMetas.Clone(),
 		// clone intermediate query.
 		// clone intermediate query.
 		sql:  aq.sql.Clone(),
 		sql:  aq.sql.Clone(),
 		path: aq.path,
 		path: aq.path,
 	}
 	}
 }
 }
 
 
-//  WithOwner tells the query-builder to eager-loads the nodes that are connected to
-// the "owner" edge. The optional arguments used to configure the query builder of the edge.
+// WithOwner tells the query-builder to eager-load the nodes that are connected to
+// the "owner" edge. The optional arguments are used to configure the query builder of the edge.
 func (aq *AlertQuery) WithOwner(opts ...func(*MachineQuery)) *AlertQuery {
 func (aq *AlertQuery) WithOwner(opts ...func(*MachineQuery)) *AlertQuery {
 	query := &MachineQuery{config: aq.config}
 	query := &MachineQuery{config: aq.config}
 	for _, opt := range opts {
 	for _, opt := range opts {
@@ -341,8 +353,8 @@ func (aq *AlertQuery) WithOwner(opts ...func(*MachineQuery)) *AlertQuery {
 	return aq
 	return aq
 }
 }
 
 
-//  WithDecisions tells the query-builder to eager-loads the nodes that are connected to
-// the "decisions" edge. The optional arguments used to configure the query builder of the edge.
+// WithDecisions tells the query-builder to eager-load the nodes that are connected to
+// the "decisions" edge. The optional arguments are used to configure the query builder of the edge.
 func (aq *AlertQuery) WithDecisions(opts ...func(*DecisionQuery)) *AlertQuery {
 func (aq *AlertQuery) WithDecisions(opts ...func(*DecisionQuery)) *AlertQuery {
 	query := &DecisionQuery{config: aq.config}
 	query := &DecisionQuery{config: aq.config}
 	for _, opt := range opts {
 	for _, opt := range opts {
@@ -352,8 +364,8 @@ func (aq *AlertQuery) WithDecisions(opts ...func(*DecisionQuery)) *AlertQuery {
 	return aq
 	return aq
 }
 }
 
 
-//  WithEvents tells the query-builder to eager-loads the nodes that are connected to
-// the "events" edge. The optional arguments used to configure the query builder of the edge.
+// WithEvents tells the query-builder to eager-load the nodes that are connected to
+// the "events" edge. The optional arguments are used to configure the query builder of the edge.
 func (aq *AlertQuery) WithEvents(opts ...func(*EventQuery)) *AlertQuery {
 func (aq *AlertQuery) WithEvents(opts ...func(*EventQuery)) *AlertQuery {
 	query := &EventQuery{config: aq.config}
 	query := &EventQuery{config: aq.config}
 	for _, opt := range opts {
 	for _, opt := range opts {
@@ -363,8 +375,8 @@ func (aq *AlertQuery) WithEvents(opts ...func(*EventQuery)) *AlertQuery {
 	return aq
 	return aq
 }
 }
 
 
-//  WithMetas tells the query-builder to eager-loads the nodes that are connected to
-// the "metas" edge. The optional arguments used to configure the query builder of the edge.
+// WithMetas tells the query-builder to eager-load the nodes that are connected to
+// the "metas" edge. The optional arguments are used to configure the query builder of the edge.
 func (aq *AlertQuery) WithMetas(opts ...func(*MetaQuery)) *AlertQuery {
 func (aq *AlertQuery) WithMetas(opts ...func(*MetaQuery)) *AlertQuery {
 	query := &MetaQuery{config: aq.config}
 	query := &MetaQuery{config: aq.config}
 	for _, opt := range opts {
 	for _, opt := range opts {
@@ -374,7 +386,7 @@ func (aq *AlertQuery) WithMetas(opts ...func(*MetaQuery)) *AlertQuery {
 	return aq
 	return aq
 }
 }
 
 
-// GroupBy used to group vertices by one or more fields/columns.
+// GroupBy is used to group vertices by one or more fields/columns.
 // It is often used with aggregate functions, like: count, max, mean, min, sum.
 // It is often used with aggregate functions, like: count, max, mean, min, sum.
 //
 //
 // Example:
 // Example:
@@ -401,7 +413,8 @@ func (aq *AlertQuery) GroupBy(field string, fields ...string) *AlertGroupBy {
 	return group
 	return group
 }
 }
 
 
-// Select one or more fields from the given query.
+// Select allows the selection one or more fields/columns for the given query,
+// instead of selecting all fields in the entity.
 //
 //
 // Example:
 // Example:
 //
 //
@@ -414,18 +427,16 @@ func (aq *AlertQuery) GroupBy(field string, fields ...string) *AlertGroupBy {
 //		Scan(ctx, &v)
 //		Scan(ctx, &v)
 //
 //
 func (aq *AlertQuery) Select(field string, fields ...string) *AlertSelect {
 func (aq *AlertQuery) Select(field string, fields ...string) *AlertSelect {
-	selector := &AlertSelect{config: aq.config}
-	selector.fields = append([]string{field}, fields...)
-	selector.path = func(ctx context.Context) (prev *sql.Selector, err error) {
-		if err := aq.prepareQuery(ctx); err != nil {
-			return nil, err
-		}
-		return aq.sqlQuery(), nil
-	}
-	return selector
+	aq.fields = append([]string{field}, fields...)
+	return &AlertSelect{AlertQuery: aq}
 }
 }
 
 
 func (aq *AlertQuery) prepareQuery(ctx context.Context) error {
 func (aq *AlertQuery) prepareQuery(ctx context.Context) error {
+	for _, f := range aq.fields {
+		if !alert.ValidColumn(f) {
+			return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+		}
+	}
 	if aq.path != nil {
 	if aq.path != nil {
 		prev, err := aq.path(ctx)
 		prev, err := aq.path(ctx)
 		if err != nil {
 		if err != nil {
@@ -454,22 +465,18 @@ func (aq *AlertQuery) sqlAll(ctx context.Context) ([]*Alert, error) {
 	if withFKs {
 	if withFKs {
 		_spec.Node.Columns = append(_spec.Node.Columns, alert.ForeignKeys...)
 		_spec.Node.Columns = append(_spec.Node.Columns, alert.ForeignKeys...)
 	}
 	}
-	_spec.ScanValues = func() []interface{} {
+	_spec.ScanValues = func(columns []string) ([]interface{}, error) {
 		node := &Alert{config: aq.config}
 		node := &Alert{config: aq.config}
 		nodes = append(nodes, node)
 		nodes = append(nodes, node)
-		values := node.scanValues()
-		if withFKs {
-			values = append(values, node.fkValues()...)
-		}
-		return values
+		return node.scanValues(columns)
 	}
 	}
-	_spec.Assign = func(values ...interface{}) error {
+	_spec.Assign = func(columns []string, values []interface{}) error {
 		if len(nodes) == 0 {
 		if len(nodes) == 0 {
 			return fmt.Errorf("ent: Assign called without calling ScanValues")
 			return fmt.Errorf("ent: Assign called without calling ScanValues")
 		}
 		}
 		node := nodes[len(nodes)-1]
 		node := nodes[len(nodes)-1]
 		node.Edges.loadedTypes = loadedTypes
 		node.Edges.loadedTypes = loadedTypes
-		return node.assignValues(values...)
+		return node.assignValues(columns, values)
 	}
 	}
 	if err := sqlgraph.QueryNodes(ctx, aq.driver, _spec); err != nil {
 	if err := sqlgraph.QueryNodes(ctx, aq.driver, _spec); err != nil {
 		return nil, err
 		return nil, err
@@ -509,6 +516,7 @@ func (aq *AlertQuery) sqlAll(ctx context.Context) ([]*Alert, error) {
 		for i := range nodes {
 		for i := range nodes {
 			fks = append(fks, nodes[i].ID)
 			fks = append(fks, nodes[i].ID)
 			nodeids[nodes[i].ID] = nodes[i]
 			nodeids[nodes[i].ID] = nodes[i]
+			nodes[i].Edges.Decisions = []*Decision{}
 		}
 		}
 		query.withFKs = true
 		query.withFKs = true
 		query.Where(predicate.Decision(func(s *sql.Selector) {
 		query.Where(predicate.Decision(func(s *sql.Selector) {
@@ -537,6 +545,7 @@ func (aq *AlertQuery) sqlAll(ctx context.Context) ([]*Alert, error) {
 		for i := range nodes {
 		for i := range nodes {
 			fks = append(fks, nodes[i].ID)
 			fks = append(fks, nodes[i].ID)
 			nodeids[nodes[i].ID] = nodes[i]
 			nodeids[nodes[i].ID] = nodes[i]
+			nodes[i].Edges.Events = []*Event{}
 		}
 		}
 		query.withFKs = true
 		query.withFKs = true
 		query.Where(predicate.Event(func(s *sql.Selector) {
 		query.Where(predicate.Event(func(s *sql.Selector) {
@@ -565,6 +574,7 @@ func (aq *AlertQuery) sqlAll(ctx context.Context) ([]*Alert, error) {
 		for i := range nodes {
 		for i := range nodes {
 			fks = append(fks, nodes[i].ID)
 			fks = append(fks, nodes[i].ID)
 			nodeids[nodes[i].ID] = nodes[i]
 			nodeids[nodes[i].ID] = nodes[i]
+			nodes[i].Edges.Metas = []*Meta{}
 		}
 		}
 		query.withFKs = true
 		query.withFKs = true
 		query.Where(predicate.Meta(func(s *sql.Selector) {
 		query.Where(predicate.Meta(func(s *sql.Selector) {
@@ -616,6 +626,15 @@ func (aq *AlertQuery) querySpec() *sqlgraph.QuerySpec {
 		From:   aq.sql,
 		From:   aq.sql,
 		Unique: true,
 		Unique: true,
 	}
 	}
+	if fields := aq.fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, alert.FieldID)
+		for i := range fields {
+			if fields[i] != alert.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
+			}
+		}
+	}
 	if ps := aq.predicates; len(ps) > 0 {
 	if ps := aq.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
@@ -664,7 +683,7 @@ func (aq *AlertQuery) sqlQuery() *sql.Selector {
 	return selector
 	return selector
 }
 }
 
 
-// AlertGroupBy is the builder for group-by Alert entities.
+// AlertGroupBy is the group-by builder for Alert entities.
 type AlertGroupBy struct {
 type AlertGroupBy struct {
 	config
 	config
 	fields []string
 	fields []string
@@ -680,7 +699,7 @@ func (agb *AlertGroupBy) Aggregate(fns ...AggregateFunc) *AlertGroupBy {
 	return agb
 	return agb
 }
 }
 
 
-// Scan applies the group-by query and scan the result into the given value.
+// Scan applies the group-by query and scans the result into the given value.
 func (agb *AlertGroupBy) Scan(ctx context.Context, v interface{}) error {
 func (agb *AlertGroupBy) Scan(ctx context.Context, v interface{}) error {
 	query, err := agb.path(ctx)
 	query, err := agb.path(ctx)
 	if err != nil {
 	if err != nil {
@@ -697,7 +716,8 @@ func (agb *AlertGroupBy) ScanX(ctx context.Context, v interface{}) {
 	}
 	}
 }
 }
 
 
-// Strings returns list of strings from group-by. It is only allowed when querying group-by with one field.
+// Strings returns list of strings from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (agb *AlertGroupBy) Strings(ctx context.Context) ([]string, error) {
 func (agb *AlertGroupBy) Strings(ctx context.Context) ([]string, error) {
 	if len(agb.fields) > 1 {
 	if len(agb.fields) > 1 {
 		return nil, errors.New("ent: AlertGroupBy.Strings is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: AlertGroupBy.Strings is not achievable when grouping more than 1 field")
@@ -718,7 +738,8 @@ func (agb *AlertGroupBy) StringsX(ctx context.Context) []string {
 	return v
 	return v
 }
 }
 
 
-// String returns a single string from group-by. It is only allowed when querying group-by with one field.
+// String returns a single string from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (agb *AlertGroupBy) String(ctx context.Context) (_ string, err error) {
 func (agb *AlertGroupBy) String(ctx context.Context) (_ string, err error) {
 	var v []string
 	var v []string
 	if v, err = agb.Strings(ctx); err != nil {
 	if v, err = agb.Strings(ctx); err != nil {
@@ -744,7 +765,8 @@ func (agb *AlertGroupBy) StringX(ctx context.Context) string {
 	return v
 	return v
 }
 }
 
 
-// Ints returns list of ints from group-by. It is only allowed when querying group-by with one field.
+// Ints returns list of ints from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (agb *AlertGroupBy) Ints(ctx context.Context) ([]int, error) {
 func (agb *AlertGroupBy) Ints(ctx context.Context) ([]int, error) {
 	if len(agb.fields) > 1 {
 	if len(agb.fields) > 1 {
 		return nil, errors.New("ent: AlertGroupBy.Ints is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: AlertGroupBy.Ints is not achievable when grouping more than 1 field")
@@ -765,7 +787,8 @@ func (agb *AlertGroupBy) IntsX(ctx context.Context) []int {
 	return v
 	return v
 }
 }
 
 
-// Int returns a single int from group-by. It is only allowed when querying group-by with one field.
+// Int returns a single int from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (agb *AlertGroupBy) Int(ctx context.Context) (_ int, err error) {
 func (agb *AlertGroupBy) Int(ctx context.Context) (_ int, err error) {
 	var v []int
 	var v []int
 	if v, err = agb.Ints(ctx); err != nil {
 	if v, err = agb.Ints(ctx); err != nil {
@@ -791,7 +814,8 @@ func (agb *AlertGroupBy) IntX(ctx context.Context) int {
 	return v
 	return v
 }
 }
 
 
-// Float64s returns list of float64s from group-by. It is only allowed when querying group-by with one field.
+// Float64s returns list of float64s from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (agb *AlertGroupBy) Float64s(ctx context.Context) ([]float64, error) {
 func (agb *AlertGroupBy) Float64s(ctx context.Context) ([]float64, error) {
 	if len(agb.fields) > 1 {
 	if len(agb.fields) > 1 {
 		return nil, errors.New("ent: AlertGroupBy.Float64s is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: AlertGroupBy.Float64s is not achievable when grouping more than 1 field")
@@ -812,7 +836,8 @@ func (agb *AlertGroupBy) Float64sX(ctx context.Context) []float64 {
 	return v
 	return v
 }
 }
 
 
-// Float64 returns a single float64 from group-by. It is only allowed when querying group-by with one field.
+// Float64 returns a single float64 from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (agb *AlertGroupBy) Float64(ctx context.Context) (_ float64, err error) {
 func (agb *AlertGroupBy) Float64(ctx context.Context) (_ float64, err error) {
 	var v []float64
 	var v []float64
 	if v, err = agb.Float64s(ctx); err != nil {
 	if v, err = agb.Float64s(ctx); err != nil {
@@ -838,7 +863,8 @@ func (agb *AlertGroupBy) Float64X(ctx context.Context) float64 {
 	return v
 	return v
 }
 }
 
 
-// Bools returns list of bools from group-by. It is only allowed when querying group-by with one field.
+// Bools returns list of bools from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (agb *AlertGroupBy) Bools(ctx context.Context) ([]bool, error) {
 func (agb *AlertGroupBy) Bools(ctx context.Context) ([]bool, error) {
 	if len(agb.fields) > 1 {
 	if len(agb.fields) > 1 {
 		return nil, errors.New("ent: AlertGroupBy.Bools is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: AlertGroupBy.Bools is not achievable when grouping more than 1 field")
@@ -859,7 +885,8 @@ func (agb *AlertGroupBy) BoolsX(ctx context.Context) []bool {
 	return v
 	return v
 }
 }
 
 
-// Bool returns a single bool from group-by. It is only allowed when querying group-by with one field.
+// Bool returns a single bool from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (agb *AlertGroupBy) Bool(ctx context.Context) (_ bool, err error) {
 func (agb *AlertGroupBy) Bool(ctx context.Context) (_ bool, err error) {
 	var v []bool
 	var v []bool
 	if v, err = agb.Bools(ctx); err != nil {
 	if v, err = agb.Bools(ctx); err != nil {
@@ -914,22 +941,19 @@ func (agb *AlertGroupBy) sqlQuery() *sql.Selector {
 	return selector.Select(columns...).GroupBy(agb.fields...)
 	return selector.Select(columns...).GroupBy(agb.fields...)
 }
 }
 
 
-// AlertSelect is the builder for select fields of Alert entities.
+// AlertSelect is the builder for selecting fields of Alert entities.
 type AlertSelect struct {
 type AlertSelect struct {
-	config
-	fields []string
+	*AlertQuery
 	// intermediate query (i.e. traversal path).
 	// intermediate query (i.e. traversal path).
-	sql  *sql.Selector
-	path func(context.Context) (*sql.Selector, error)
+	sql *sql.Selector
 }
 }
 
 
-// Scan applies the selector query and scan the result into the given value.
+// Scan applies the selector query and scans the result into the given value.
 func (as *AlertSelect) Scan(ctx context.Context, v interface{}) error {
 func (as *AlertSelect) Scan(ctx context.Context, v interface{}) error {
-	query, err := as.path(ctx)
-	if err != nil {
+	if err := as.prepareQuery(ctx); err != nil {
 		return err
 		return err
 	}
 	}
-	as.sql = query
+	as.sql = as.AlertQuery.sqlQuery()
 	return as.sqlScan(ctx, v)
 	return as.sqlScan(ctx, v)
 }
 }
 
 
@@ -940,7 +964,7 @@ func (as *AlertSelect) ScanX(ctx context.Context, v interface{}) {
 	}
 	}
 }
 }
 
 
-// Strings returns list of strings from selector. It is only allowed when selecting one field.
+// Strings returns list of strings from a selector. It is only allowed when selecting one field.
 func (as *AlertSelect) Strings(ctx context.Context) ([]string, error) {
 func (as *AlertSelect) Strings(ctx context.Context) ([]string, error) {
 	if len(as.fields) > 1 {
 	if len(as.fields) > 1 {
 		return nil, errors.New("ent: AlertSelect.Strings is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: AlertSelect.Strings is not achievable when selecting more than 1 field")
@@ -961,7 +985,7 @@ func (as *AlertSelect) StringsX(ctx context.Context) []string {
 	return v
 	return v
 }
 }
 
 
-// String returns a single string from selector. It is only allowed when selecting one field.
+// String returns a single string from a selector. It is only allowed when selecting one field.
 func (as *AlertSelect) String(ctx context.Context) (_ string, err error) {
 func (as *AlertSelect) String(ctx context.Context) (_ string, err error) {
 	var v []string
 	var v []string
 	if v, err = as.Strings(ctx); err != nil {
 	if v, err = as.Strings(ctx); err != nil {
@@ -987,7 +1011,7 @@ func (as *AlertSelect) StringX(ctx context.Context) string {
 	return v
 	return v
 }
 }
 
 
-// Ints returns list of ints from selector. It is only allowed when selecting one field.
+// Ints returns list of ints from a selector. It is only allowed when selecting one field.
 func (as *AlertSelect) Ints(ctx context.Context) ([]int, error) {
 func (as *AlertSelect) Ints(ctx context.Context) ([]int, error) {
 	if len(as.fields) > 1 {
 	if len(as.fields) > 1 {
 		return nil, errors.New("ent: AlertSelect.Ints is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: AlertSelect.Ints is not achievable when selecting more than 1 field")
@@ -1008,7 +1032,7 @@ func (as *AlertSelect) IntsX(ctx context.Context) []int {
 	return v
 	return v
 }
 }
 
 
-// Int returns a single int from selector. It is only allowed when selecting one field.
+// Int returns a single int from a selector. It is only allowed when selecting one field.
 func (as *AlertSelect) Int(ctx context.Context) (_ int, err error) {
 func (as *AlertSelect) Int(ctx context.Context) (_ int, err error) {
 	var v []int
 	var v []int
 	if v, err = as.Ints(ctx); err != nil {
 	if v, err = as.Ints(ctx); err != nil {
@@ -1034,7 +1058,7 @@ func (as *AlertSelect) IntX(ctx context.Context) int {
 	return v
 	return v
 }
 }
 
 
-// Float64s returns list of float64s from selector. It is only allowed when selecting one field.
+// Float64s returns list of float64s from a selector. It is only allowed when selecting one field.
 func (as *AlertSelect) Float64s(ctx context.Context) ([]float64, error) {
 func (as *AlertSelect) Float64s(ctx context.Context) ([]float64, error) {
 	if len(as.fields) > 1 {
 	if len(as.fields) > 1 {
 		return nil, errors.New("ent: AlertSelect.Float64s is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: AlertSelect.Float64s is not achievable when selecting more than 1 field")
@@ -1055,7 +1079,7 @@ func (as *AlertSelect) Float64sX(ctx context.Context) []float64 {
 	return v
 	return v
 }
 }
 
 
-// Float64 returns a single float64 from selector. It is only allowed when selecting one field.
+// Float64 returns a single float64 from a selector. It is only allowed when selecting one field.
 func (as *AlertSelect) Float64(ctx context.Context) (_ float64, err error) {
 func (as *AlertSelect) Float64(ctx context.Context) (_ float64, err error) {
 	var v []float64
 	var v []float64
 	if v, err = as.Float64s(ctx); err != nil {
 	if v, err = as.Float64s(ctx); err != nil {
@@ -1081,7 +1105,7 @@ func (as *AlertSelect) Float64X(ctx context.Context) float64 {
 	return v
 	return v
 }
 }
 
 
-// Bools returns list of bools from selector. It is only allowed when selecting one field.
+// Bools returns list of bools from a selector. It is only allowed when selecting one field.
 func (as *AlertSelect) Bools(ctx context.Context) ([]bool, error) {
 func (as *AlertSelect) Bools(ctx context.Context) ([]bool, error) {
 	if len(as.fields) > 1 {
 	if len(as.fields) > 1 {
 		return nil, errors.New("ent: AlertSelect.Bools is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: AlertSelect.Bools is not achievable when selecting more than 1 field")
@@ -1102,7 +1126,7 @@ func (as *AlertSelect) BoolsX(ctx context.Context) []bool {
 	return v
 	return v
 }
 }
 
 
-// Bool returns a single bool from selector. It is only allowed when selecting one field.
+// Bool returns a single bool from a selector. It is only allowed when selecting one field.
 func (as *AlertSelect) Bool(ctx context.Context) (_ bool, err error) {
 func (as *AlertSelect) Bool(ctx context.Context) (_ bool, err error) {
 	var v []bool
 	var v []bool
 	if v, err = as.Bools(ctx); err != nil {
 	if v, err = as.Bools(ctx); err != nil {
@@ -1129,11 +1153,6 @@ func (as *AlertSelect) BoolX(ctx context.Context) bool {
 }
 }
 
 
 func (as *AlertSelect) sqlScan(ctx context.Context, v interface{}) error {
 func (as *AlertSelect) sqlScan(ctx context.Context, v interface{}) error {
-	for _, f := range as.fields {
-		if !alert.ValidColumn(f) {
-			return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for selection", f)}
-		}
-	}
 	rows := &sql.Rows{}
 	rows := &sql.Rows{}
 	query, args := as.sqlQuery().Query()
 	query, args := as.sqlQuery().Query()
 	if err := as.driver.Query(ctx, query, args, rows); err != nil {
 	if err := as.driver.Query(ctx, query, args, rows); err != nil {

File diff suppressed because it is too large
+ 132 - 133
pkg/database/ent/alert_update.go


+ 90 - 73
pkg/database/ent/bouncer.go

@@ -39,96 +39,113 @@ type Bouncer struct {
 }
 }
 
 
 // scanValues returns the types for scanning values from sql.Rows.
 // scanValues returns the types for scanning values from sql.Rows.
-func (*Bouncer) scanValues() []interface{} {
-	return []interface{}{
-		&sql.NullInt64{},  // id
-		&sql.NullTime{},   // created_at
-		&sql.NullTime{},   // updated_at
-		&sql.NullString{}, // name
-		&sql.NullString{}, // api_key
-		&sql.NullBool{},   // revoked
-		&sql.NullString{}, // ip_address
-		&sql.NullString{}, // type
-		&sql.NullString{}, // version
-		&sql.NullTime{},   // until
-		&sql.NullTime{},   // last_pull
+func (*Bouncer) scanValues(columns []string) ([]interface{}, error) {
+	values := make([]interface{}, len(columns))
+	for i := range columns {
+		switch columns[i] {
+		case bouncer.FieldRevoked:
+			values[i] = &sql.NullBool{}
+		case bouncer.FieldID:
+			values[i] = &sql.NullInt64{}
+		case bouncer.FieldName, bouncer.FieldAPIKey, bouncer.FieldIPAddress, bouncer.FieldType, bouncer.FieldVersion:
+			values[i] = &sql.NullString{}
+		case bouncer.FieldCreatedAt, bouncer.FieldUpdatedAt, bouncer.FieldUntil, bouncer.FieldLastPull:
+			values[i] = &sql.NullTime{}
+		default:
+			return nil, fmt.Errorf("unexpected column %q for type Bouncer", columns[i])
+		}
 	}
 	}
+	return values, nil
 }
 }
 
 
 // assignValues assigns the values that were returned from sql.Rows (after scanning)
 // assignValues assigns the values that were returned from sql.Rows (after scanning)
 // to the Bouncer fields.
 // to the Bouncer fields.
-func (b *Bouncer) assignValues(values ...interface{}) error {
-	if m, n := len(values), len(bouncer.Columns); m < n {
+func (b *Bouncer) assignValues(columns []string, values []interface{}) error {
+	if m, n := len(values), len(columns); m < n {
 		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
 		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
 	}
 	}
-	value, ok := values[0].(*sql.NullInt64)
-	if !ok {
-		return fmt.Errorf("unexpected type %T for field id", value)
-	}
-	b.ID = int(value.Int64)
-	values = values[1:]
-	if value, ok := values[0].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field created_at", values[0])
-	} else if value.Valid {
-		b.CreatedAt = value.Time
-	}
-	if value, ok := values[1].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field updated_at", values[1])
-	} else if value.Valid {
-		b.UpdatedAt = value.Time
-	}
-	if value, ok := values[2].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field name", values[2])
-	} else if value.Valid {
-		b.Name = value.String
-	}
-	if value, ok := values[3].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field api_key", values[3])
-	} else if value.Valid {
-		b.APIKey = value.String
-	}
-	if value, ok := values[4].(*sql.NullBool); !ok {
-		return fmt.Errorf("unexpected type %T for field revoked", values[4])
-	} else if value.Valid {
-		b.Revoked = value.Bool
-	}
-	if value, ok := values[5].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field ip_address", values[5])
-	} else if value.Valid {
-		b.IPAddress = value.String
-	}
-	if value, ok := values[6].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field type", values[6])
-	} else if value.Valid {
-		b.Type = value.String
-	}
-	if value, ok := values[7].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field version", values[7])
-	} else if value.Valid {
-		b.Version = value.String
-	}
-	if value, ok := values[8].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field until", values[8])
-	} else if value.Valid {
-		b.Until = value.Time
-	}
-	if value, ok := values[9].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field last_pull", values[9])
-	} else if value.Valid {
-		b.LastPull = value.Time
+	for i := range columns {
+		switch columns[i] {
+		case bouncer.FieldID:
+			value, ok := values[i].(*sql.NullInt64)
+			if !ok {
+				return fmt.Errorf("unexpected type %T for field id", value)
+			}
+			b.ID = int(value.Int64)
+		case bouncer.FieldCreatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field created_at", values[i])
+			} else if value.Valid {
+				b.CreatedAt = value.Time
+			}
+		case bouncer.FieldUpdatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field updated_at", values[i])
+			} else if value.Valid {
+				b.UpdatedAt = value.Time
+			}
+		case bouncer.FieldName:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field name", values[i])
+			} else if value.Valid {
+				b.Name = value.String
+			}
+		case bouncer.FieldAPIKey:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field api_key", values[i])
+			} else if value.Valid {
+				b.APIKey = value.String
+			}
+		case bouncer.FieldRevoked:
+			if value, ok := values[i].(*sql.NullBool); !ok {
+				return fmt.Errorf("unexpected type %T for field revoked", values[i])
+			} else if value.Valid {
+				b.Revoked = value.Bool
+			}
+		case bouncer.FieldIPAddress:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field ip_address", values[i])
+			} else if value.Valid {
+				b.IPAddress = value.String
+			}
+		case bouncer.FieldType:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field type", values[i])
+			} else if value.Valid {
+				b.Type = value.String
+			}
+		case bouncer.FieldVersion:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field version", values[i])
+			} else if value.Valid {
+				b.Version = value.String
+			}
+		case bouncer.FieldUntil:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field until", values[i])
+			} else if value.Valid {
+				b.Until = value.Time
+			}
+		case bouncer.FieldLastPull:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field last_pull", values[i])
+			} else if value.Valid {
+				b.LastPull = value.Time
+			}
+		}
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
 // Update returns a builder for updating this Bouncer.
 // Update returns a builder for updating this Bouncer.
-// Note that, you need to call Bouncer.Unwrap() before calling this method, if this Bouncer
+// Note that you need to call Bouncer.Unwrap() before calling this method if this Bouncer
 // was returned from a transaction, and the transaction was committed or rolled back.
 // was returned from a transaction, and the transaction was committed or rolled back.
 func (b *Bouncer) Update() *BouncerUpdateOne {
 func (b *Bouncer) Update() *BouncerUpdateOne {
 	return (&BouncerClient{config: b.config}).UpdateOne(b)
 	return (&BouncerClient{config: b.config}).UpdateOne(b)
 }
 }
 
 
-// Unwrap unwraps the entity that was returned from a transaction after it was closed,
-// so that all next queries will be executed through the driver which created the transaction.
+// Unwrap unwraps the Bouncer entity that was returned from a transaction after it was closed,
+// so that all future queries will be executed through the driver which created the transaction.
 func (b *Bouncer) Unwrap() *Bouncer {
 func (b *Bouncer) Unwrap() *Bouncer {
 	tx, ok := b.config.driver.(*txDriver)
 	tx, ok := b.config.driver.(*txDriver)
 	if !ok {
 	if !ok {

+ 5 - 5
pkg/database/ent/bouncer/bouncer.go

@@ -62,14 +62,14 @@ func ValidColumn(column string) bool {
 }
 }
 
 
 var (
 var (
-	// DefaultCreatedAt holds the default value on creation for the created_at field.
+	// DefaultCreatedAt holds the default value on creation for the "created_at" field.
 	DefaultCreatedAt func() time.Time
 	DefaultCreatedAt func() time.Time
-	// DefaultUpdatedAt holds the default value on creation for the updated_at field.
+	// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
 	DefaultUpdatedAt func() time.Time
 	DefaultUpdatedAt func() time.Time
-	// DefaultIPAddress holds the default value on creation for the ip_address field.
+	// DefaultIPAddress holds the default value on creation for the "ip_address" field.
 	DefaultIPAddress string
 	DefaultIPAddress string
-	// DefaultUntil holds the default value on creation for the until field.
+	// DefaultUntil holds the default value on creation for the "until" field.
 	DefaultUntil func() time.Time
 	DefaultUntil func() time.Time
-	// DefaultLastPull holds the default value on creation for the last_pull field.
+	// DefaultLastPull holds the default value on creation for the "last_pull" field.
 	DefaultLastPull func() time.Time
 	DefaultLastPull func() time.Time
 )
 )

+ 3 - 3
pkg/database/ent/bouncer/where.go

@@ -9,7 +9,7 @@ import (
 	"github.com/facebook/ent/dialect/sql"
 	"github.com/facebook/ent/dialect/sql"
 )
 )
 
 
-// ID filters vertices based on their identifier.
+// ID filters vertices based on their ID field.
 func ID(id int) predicate.Bouncer {
 func ID(id int) predicate.Bouncer {
 	return predicate.Bouncer(func(s *sql.Selector) {
 	return predicate.Bouncer(func(s *sql.Selector) {
 		s.Where(sql.EQ(s.C(FieldID), id))
 		s.Where(sql.EQ(s.C(FieldID), id))
@@ -1091,7 +1091,7 @@ func LastPullLTE(v time.Time) predicate.Bouncer {
 	})
 	})
 }
 }
 
 
-// And groups list of predicates with the AND operator between them.
+// And groups predicates with the AND operator between them.
 func And(predicates ...predicate.Bouncer) predicate.Bouncer {
 func And(predicates ...predicate.Bouncer) predicate.Bouncer {
 	return predicate.Bouncer(func(s *sql.Selector) {
 	return predicate.Bouncer(func(s *sql.Selector) {
 		s1 := s.Clone().SetP(nil)
 		s1 := s.Clone().SetP(nil)
@@ -1102,7 +1102,7 @@ func And(predicates ...predicate.Bouncer) predicate.Bouncer {
 	})
 	})
 }
 }
 
 
-// Or groups list of predicates with the OR operator between them.
+// Or groups predicates with the OR operator between them.
 func Or(predicates ...predicate.Bouncer) predicate.Bouncer {
 func Or(predicates ...predicate.Bouncer) predicate.Bouncer {
 	return predicate.Bouncer(func(s *sql.Selector) {
 	return predicate.Bouncer(func(s *sql.Selector) {
 		s1 := s.Clone().SetP(nil)
 		s1 := s.Clone().SetP(nil)

+ 19 - 19
pkg/database/ent/bouncer_create.go

@@ -20,13 +20,13 @@ type BouncerCreate struct {
 	hooks    []Hook
 	hooks    []Hook
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (bc *BouncerCreate) SetCreatedAt(t time.Time) *BouncerCreate {
 func (bc *BouncerCreate) SetCreatedAt(t time.Time) *BouncerCreate {
 	bc.mutation.SetCreatedAt(t)
 	bc.mutation.SetCreatedAt(t)
 	return bc
 	return bc
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (bc *BouncerCreate) SetNillableCreatedAt(t *time.Time) *BouncerCreate {
 func (bc *BouncerCreate) SetNillableCreatedAt(t *time.Time) *BouncerCreate {
 	if t != nil {
 	if t != nil {
 		bc.SetCreatedAt(*t)
 		bc.SetCreatedAt(*t)
@@ -34,13 +34,13 @@ func (bc *BouncerCreate) SetNillableCreatedAt(t *time.Time) *BouncerCreate {
 	return bc
 	return bc
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (bc *BouncerCreate) SetUpdatedAt(t time.Time) *BouncerCreate {
 func (bc *BouncerCreate) SetUpdatedAt(t time.Time) *BouncerCreate {
 	bc.mutation.SetUpdatedAt(t)
 	bc.mutation.SetUpdatedAt(t)
 	return bc
 	return bc
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (bc *BouncerCreate) SetNillableUpdatedAt(t *time.Time) *BouncerCreate {
 func (bc *BouncerCreate) SetNillableUpdatedAt(t *time.Time) *BouncerCreate {
 	if t != nil {
 	if t != nil {
 		bc.SetUpdatedAt(*t)
 		bc.SetUpdatedAt(*t)
@@ -48,31 +48,31 @@ func (bc *BouncerCreate) SetNillableUpdatedAt(t *time.Time) *BouncerCreate {
 	return bc
 	return bc
 }
 }
 
 
-// SetName sets the name field.
+// SetName sets the "name" field.
 func (bc *BouncerCreate) SetName(s string) *BouncerCreate {
 func (bc *BouncerCreate) SetName(s string) *BouncerCreate {
 	bc.mutation.SetName(s)
 	bc.mutation.SetName(s)
 	return bc
 	return bc
 }
 }
 
 
-// SetAPIKey sets the api_key field.
+// SetAPIKey sets the "api_key" field.
 func (bc *BouncerCreate) SetAPIKey(s string) *BouncerCreate {
 func (bc *BouncerCreate) SetAPIKey(s string) *BouncerCreate {
 	bc.mutation.SetAPIKey(s)
 	bc.mutation.SetAPIKey(s)
 	return bc
 	return bc
 }
 }
 
 
-// SetRevoked sets the revoked field.
+// SetRevoked sets the "revoked" field.
 func (bc *BouncerCreate) SetRevoked(b bool) *BouncerCreate {
 func (bc *BouncerCreate) SetRevoked(b bool) *BouncerCreate {
 	bc.mutation.SetRevoked(b)
 	bc.mutation.SetRevoked(b)
 	return bc
 	return bc
 }
 }
 
 
-// SetIPAddress sets the ip_address field.
+// SetIPAddress sets the "ip_address" field.
 func (bc *BouncerCreate) SetIPAddress(s string) *BouncerCreate {
 func (bc *BouncerCreate) SetIPAddress(s string) *BouncerCreate {
 	bc.mutation.SetIPAddress(s)
 	bc.mutation.SetIPAddress(s)
 	return bc
 	return bc
 }
 }
 
 
-// SetNillableIPAddress sets the ip_address field if the given value is not nil.
+// SetNillableIPAddress sets the "ip_address" field if the given value is not nil.
 func (bc *BouncerCreate) SetNillableIPAddress(s *string) *BouncerCreate {
 func (bc *BouncerCreate) SetNillableIPAddress(s *string) *BouncerCreate {
 	if s != nil {
 	if s != nil {
 		bc.SetIPAddress(*s)
 		bc.SetIPAddress(*s)
@@ -80,13 +80,13 @@ func (bc *BouncerCreate) SetNillableIPAddress(s *string) *BouncerCreate {
 	return bc
 	return bc
 }
 }
 
 
-// SetType sets the type field.
+// SetType sets the "type" field.
 func (bc *BouncerCreate) SetType(s string) *BouncerCreate {
 func (bc *BouncerCreate) SetType(s string) *BouncerCreate {
 	bc.mutation.SetType(s)
 	bc.mutation.SetType(s)
 	return bc
 	return bc
 }
 }
 
 
-// SetNillableType sets the type field if the given value is not nil.
+// SetNillableType sets the "type" field if the given value is not nil.
 func (bc *BouncerCreate) SetNillableType(s *string) *BouncerCreate {
 func (bc *BouncerCreate) SetNillableType(s *string) *BouncerCreate {
 	if s != nil {
 	if s != nil {
 		bc.SetType(*s)
 		bc.SetType(*s)
@@ -94,13 +94,13 @@ func (bc *BouncerCreate) SetNillableType(s *string) *BouncerCreate {
 	return bc
 	return bc
 }
 }
 
 
-// SetVersion sets the version field.
+// SetVersion sets the "version" field.
 func (bc *BouncerCreate) SetVersion(s string) *BouncerCreate {
 func (bc *BouncerCreate) SetVersion(s string) *BouncerCreate {
 	bc.mutation.SetVersion(s)
 	bc.mutation.SetVersion(s)
 	return bc
 	return bc
 }
 }
 
 
-// SetNillableVersion sets the version field if the given value is not nil.
+// SetNillableVersion sets the "version" field if the given value is not nil.
 func (bc *BouncerCreate) SetNillableVersion(s *string) *BouncerCreate {
 func (bc *BouncerCreate) SetNillableVersion(s *string) *BouncerCreate {
 	if s != nil {
 	if s != nil {
 		bc.SetVersion(*s)
 		bc.SetVersion(*s)
@@ -108,13 +108,13 @@ func (bc *BouncerCreate) SetNillableVersion(s *string) *BouncerCreate {
 	return bc
 	return bc
 }
 }
 
 
-// SetUntil sets the until field.
+// SetUntil sets the "until" field.
 func (bc *BouncerCreate) SetUntil(t time.Time) *BouncerCreate {
 func (bc *BouncerCreate) SetUntil(t time.Time) *BouncerCreate {
 	bc.mutation.SetUntil(t)
 	bc.mutation.SetUntil(t)
 	return bc
 	return bc
 }
 }
 
 
-// SetNillableUntil sets the until field if the given value is not nil.
+// SetNillableUntil sets the "until" field if the given value is not nil.
 func (bc *BouncerCreate) SetNillableUntil(t *time.Time) *BouncerCreate {
 func (bc *BouncerCreate) SetNillableUntil(t *time.Time) *BouncerCreate {
 	if t != nil {
 	if t != nil {
 		bc.SetUntil(*t)
 		bc.SetUntil(*t)
@@ -122,13 +122,13 @@ func (bc *BouncerCreate) SetNillableUntil(t *time.Time) *BouncerCreate {
 	return bc
 	return bc
 }
 }
 
 
-// SetLastPull sets the last_pull field.
+// SetLastPull sets the "last_pull" field.
 func (bc *BouncerCreate) SetLastPull(t time.Time) *BouncerCreate {
 func (bc *BouncerCreate) SetLastPull(t time.Time) *BouncerCreate {
 	bc.mutation.SetLastPull(t)
 	bc.mutation.SetLastPull(t)
 	return bc
 	return bc
 }
 }
 
 
-// SetNillableLastPull sets the last_pull field if the given value is not nil.
+// SetNillableLastPull sets the "last_pull" field if the given value is not nil.
 func (bc *BouncerCreate) SetNillableLastPull(t *time.Time) *BouncerCreate {
 func (bc *BouncerCreate) SetNillableLastPull(t *time.Time) *BouncerCreate {
 	if t != nil {
 	if t != nil {
 		bc.SetLastPull(*t)
 		bc.SetLastPull(*t)
@@ -340,7 +340,7 @@ func (bc *BouncerCreate) createSpec() (*Bouncer, *sqlgraph.CreateSpec) {
 	return _node, _spec
 	return _node, _spec
 }
 }
 
 
-// BouncerCreateBulk is the builder for creating a bulk of Bouncer entities.
+// BouncerCreateBulk is the builder for creating many Bouncer entities in bulk.
 type BouncerCreateBulk struct {
 type BouncerCreateBulk struct {
 	config
 	config
 	builders []*BouncerCreate
 	builders []*BouncerCreate
@@ -398,7 +398,7 @@ func (bcb *BouncerCreateBulk) Save(ctx context.Context) ([]*Bouncer, error) {
 	return nodes, nil
 	return nodes, nil
 }
 }
 
 
-// SaveX calls Save and panics if Save returns an error.
+// SaveX is like Save, but panics if an error occurs.
 func (bcb *BouncerCreateBulk) SaveX(ctx context.Context) []*Bouncer {
 func (bcb *BouncerCreateBulk) SaveX(ctx context.Context) []*Bouncer {
 	v, err := bcb.Save(ctx)
 	v, err := bcb.Save(ctx)
 	if err != nil {
 	if err != nil {

+ 5 - 6
pkg/database/ent/bouncer_delete.go

@@ -16,14 +16,13 @@ import (
 // BouncerDelete is the builder for deleting a Bouncer entity.
 // BouncerDelete is the builder for deleting a Bouncer entity.
 type BouncerDelete struct {
 type BouncerDelete struct {
 	config
 	config
-	hooks      []Hook
-	mutation   *BouncerMutation
-	predicates []predicate.Bouncer
+	hooks    []Hook
+	mutation *BouncerMutation
 }
 }
 
 
-// Where adds a new predicate to the delete builder.
+// Where adds a new predicate to the BouncerDelete builder.
 func (bd *BouncerDelete) Where(ps ...predicate.Bouncer) *BouncerDelete {
 func (bd *BouncerDelete) Where(ps ...predicate.Bouncer) *BouncerDelete {
-	bd.predicates = append(bd.predicates, ps...)
+	bd.mutation.predicates = append(bd.mutation.predicates, ps...)
 	return bd
 	return bd
 }
 }
 
 
@@ -75,7 +74,7 @@ func (bd *BouncerDelete) sqlExec(ctx context.Context) (int, error) {
 			},
 			},
 		},
 		},
 	}
 	}
-	if ps := bd.predicates; len(ps) > 0 {
+	if ps := bd.mutation.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
 				ps[i](selector)
 				ps[i](selector)

+ 74 - 59
pkg/database/ent/bouncer_query.go

@@ -21,14 +21,14 @@ type BouncerQuery struct {
 	limit      *int
 	limit      *int
 	offset     *int
 	offset     *int
 	order      []OrderFunc
 	order      []OrderFunc
-	unique     []string
+	fields     []string
 	predicates []predicate.Bouncer
 	predicates []predicate.Bouncer
 	// intermediate query (i.e. traversal path).
 	// intermediate query (i.e. traversal path).
 	sql  *sql.Selector
 	sql  *sql.Selector
 	path func(context.Context) (*sql.Selector, error)
 	path func(context.Context) (*sql.Selector, error)
 }
 }
 
 
-// Where adds a new predicate for the builder.
+// Where adds a new predicate for the BouncerQuery builder.
 func (bq *BouncerQuery) Where(ps ...predicate.Bouncer) *BouncerQuery {
 func (bq *BouncerQuery) Where(ps ...predicate.Bouncer) *BouncerQuery {
 	bq.predicates = append(bq.predicates, ps...)
 	bq.predicates = append(bq.predicates, ps...)
 	return bq
 	return bq
@@ -52,7 +52,8 @@ func (bq *BouncerQuery) Order(o ...OrderFunc) *BouncerQuery {
 	return bq
 	return bq
 }
 }
 
 
-// First returns the first Bouncer entity in the query. Returns *NotFoundError when no bouncer was found.
+// First returns the first Bouncer entity from the query.
+// Returns a *NotFoundError when no Bouncer was found.
 func (bq *BouncerQuery) First(ctx context.Context) (*Bouncer, error) {
 func (bq *BouncerQuery) First(ctx context.Context) (*Bouncer, error) {
 	nodes, err := bq.Limit(1).All(ctx)
 	nodes, err := bq.Limit(1).All(ctx)
 	if err != nil {
 	if err != nil {
@@ -73,7 +74,8 @@ func (bq *BouncerQuery) FirstX(ctx context.Context) *Bouncer {
 	return node
 	return node
 }
 }
 
 
-// FirstID returns the first Bouncer id in the query. Returns *NotFoundError when no id was found.
+// FirstID returns the first Bouncer ID from the query.
+// Returns a *NotFoundError when no Bouncer ID was found.
 func (bq *BouncerQuery) FirstID(ctx context.Context) (id int, err error) {
 func (bq *BouncerQuery) FirstID(ctx context.Context) (id int, err error) {
 	var ids []int
 	var ids []int
 	if ids, err = bq.Limit(1).IDs(ctx); err != nil {
 	if ids, err = bq.Limit(1).IDs(ctx); err != nil {
@@ -86,8 +88,8 @@ func (bq *BouncerQuery) FirstID(ctx context.Context) (id int, err error) {
 	return ids[0], nil
 	return ids[0], nil
 }
 }
 
 
-// FirstXID is like FirstID, but panics if an error occurs.
-func (bq *BouncerQuery) FirstXID(ctx context.Context) int {
+// FirstIDX is like FirstID, but panics if an error occurs.
+func (bq *BouncerQuery) FirstIDX(ctx context.Context) int {
 	id, err := bq.FirstID(ctx)
 	id, err := bq.FirstID(ctx)
 	if err != nil && !IsNotFound(err) {
 	if err != nil && !IsNotFound(err) {
 		panic(err)
 		panic(err)
@@ -95,7 +97,9 @@ func (bq *BouncerQuery) FirstXID(ctx context.Context) int {
 	return id
 	return id
 }
 }
 
 
-// Only returns the only Bouncer entity in the query, returns an error if not exactly one entity was returned.
+// Only returns a single Bouncer entity found by the query, ensuring it only returns one.
+// Returns a *NotSingularError when exactly one Bouncer entity is not found.
+// Returns a *NotFoundError when no Bouncer entities are found.
 func (bq *BouncerQuery) Only(ctx context.Context) (*Bouncer, error) {
 func (bq *BouncerQuery) Only(ctx context.Context) (*Bouncer, error) {
 	nodes, err := bq.Limit(2).All(ctx)
 	nodes, err := bq.Limit(2).All(ctx)
 	if err != nil {
 	if err != nil {
@@ -120,7 +124,9 @@ func (bq *BouncerQuery) OnlyX(ctx context.Context) *Bouncer {
 	return node
 	return node
 }
 }
 
 
-// OnlyID returns the only Bouncer id in the query, returns an error if not exactly one id was returned.
+// OnlyID is like Only, but returns the only Bouncer ID in the query.
+// Returns a *NotSingularError when exactly one Bouncer ID is not found.
+// Returns a *NotFoundError when no entities are found.
 func (bq *BouncerQuery) OnlyID(ctx context.Context) (id int, err error) {
 func (bq *BouncerQuery) OnlyID(ctx context.Context) (id int, err error) {
 	var ids []int
 	var ids []int
 	if ids, err = bq.Limit(2).IDs(ctx); err != nil {
 	if ids, err = bq.Limit(2).IDs(ctx); err != nil {
@@ -163,7 +169,7 @@ func (bq *BouncerQuery) AllX(ctx context.Context) []*Bouncer {
 	return nodes
 	return nodes
 }
 }
 
 
-// IDs executes the query and returns a list of Bouncer ids.
+// IDs executes the query and returns a list of Bouncer IDs.
 func (bq *BouncerQuery) IDs(ctx context.Context) ([]int, error) {
 func (bq *BouncerQuery) IDs(ctx context.Context) ([]int, error) {
 	var ids []int
 	var ids []int
 	if err := bq.Select(bouncer.FieldID).Scan(ctx, &ids); err != nil {
 	if err := bq.Select(bouncer.FieldID).Scan(ctx, &ids); err != nil {
@@ -215,15 +221,17 @@ func (bq *BouncerQuery) ExistX(ctx context.Context) bool {
 	return exist
 	return exist
 }
 }
 
 
-// Clone returns a duplicate of the query builder, including all associated steps. It can be
+// Clone returns a duplicate of the BouncerQuery builder, including all associated steps. It can be
 // used to prepare common query builders and use them differently after the clone is made.
 // used to prepare common query builders and use them differently after the clone is made.
 func (bq *BouncerQuery) Clone() *BouncerQuery {
 func (bq *BouncerQuery) Clone() *BouncerQuery {
+	if bq == nil {
+		return nil
+	}
 	return &BouncerQuery{
 	return &BouncerQuery{
 		config:     bq.config,
 		config:     bq.config,
 		limit:      bq.limit,
 		limit:      bq.limit,
 		offset:     bq.offset,
 		offset:     bq.offset,
 		order:      append([]OrderFunc{}, bq.order...),
 		order:      append([]OrderFunc{}, bq.order...),
-		unique:     append([]string{}, bq.unique...),
 		predicates: append([]predicate.Bouncer{}, bq.predicates...),
 		predicates: append([]predicate.Bouncer{}, bq.predicates...),
 		// clone intermediate query.
 		// clone intermediate query.
 		sql:  bq.sql.Clone(),
 		sql:  bq.sql.Clone(),
@@ -231,7 +239,7 @@ func (bq *BouncerQuery) Clone() *BouncerQuery {
 	}
 	}
 }
 }
 
 
-// GroupBy used to group vertices by one or more fields/columns.
+// GroupBy is used to group vertices by one or more fields/columns.
 // It is often used with aggregate functions, like: count, max, mean, min, sum.
 // It is often used with aggregate functions, like: count, max, mean, min, sum.
 //
 //
 // Example:
 // Example:
@@ -258,7 +266,8 @@ func (bq *BouncerQuery) GroupBy(field string, fields ...string) *BouncerGroupBy
 	return group
 	return group
 }
 }
 
 
-// Select one or more fields from the given query.
+// Select allows the selection one or more fields/columns for the given query,
+// instead of selecting all fields in the entity.
 //
 //
 // Example:
 // Example:
 //
 //
@@ -271,18 +280,16 @@ func (bq *BouncerQuery) GroupBy(field string, fields ...string) *BouncerGroupBy
 //		Scan(ctx, &v)
 //		Scan(ctx, &v)
 //
 //
 func (bq *BouncerQuery) Select(field string, fields ...string) *BouncerSelect {
 func (bq *BouncerQuery) Select(field string, fields ...string) *BouncerSelect {
-	selector := &BouncerSelect{config: bq.config}
-	selector.fields = append([]string{field}, fields...)
-	selector.path = func(ctx context.Context) (prev *sql.Selector, err error) {
-		if err := bq.prepareQuery(ctx); err != nil {
-			return nil, err
-		}
-		return bq.sqlQuery(), nil
-	}
-	return selector
+	bq.fields = append([]string{field}, fields...)
+	return &BouncerSelect{BouncerQuery: bq}
 }
 }
 
 
 func (bq *BouncerQuery) prepareQuery(ctx context.Context) error {
 func (bq *BouncerQuery) prepareQuery(ctx context.Context) error {
+	for _, f := range bq.fields {
+		if !bouncer.ValidColumn(f) {
+			return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+		}
+	}
 	if bq.path != nil {
 	if bq.path != nil {
 		prev, err := bq.path(ctx)
 		prev, err := bq.path(ctx)
 		if err != nil {
 		if err != nil {
@@ -298,18 +305,17 @@ func (bq *BouncerQuery) sqlAll(ctx context.Context) ([]*Bouncer, error) {
 		nodes = []*Bouncer{}
 		nodes = []*Bouncer{}
 		_spec = bq.querySpec()
 		_spec = bq.querySpec()
 	)
 	)
-	_spec.ScanValues = func() []interface{} {
+	_spec.ScanValues = func(columns []string) ([]interface{}, error) {
 		node := &Bouncer{config: bq.config}
 		node := &Bouncer{config: bq.config}
 		nodes = append(nodes, node)
 		nodes = append(nodes, node)
-		values := node.scanValues()
-		return values
+		return node.scanValues(columns)
 	}
 	}
-	_spec.Assign = func(values ...interface{}) error {
+	_spec.Assign = func(columns []string, values []interface{}) error {
 		if len(nodes) == 0 {
 		if len(nodes) == 0 {
 			return fmt.Errorf("ent: Assign called without calling ScanValues")
 			return fmt.Errorf("ent: Assign called without calling ScanValues")
 		}
 		}
 		node := nodes[len(nodes)-1]
 		node := nodes[len(nodes)-1]
-		return node.assignValues(values...)
+		return node.assignValues(columns, values)
 	}
 	}
 	if err := sqlgraph.QueryNodes(ctx, bq.driver, _spec); err != nil {
 	if err := sqlgraph.QueryNodes(ctx, bq.driver, _spec); err != nil {
 		return nil, err
 		return nil, err
@@ -346,6 +352,15 @@ func (bq *BouncerQuery) querySpec() *sqlgraph.QuerySpec {
 		From:   bq.sql,
 		From:   bq.sql,
 		Unique: true,
 		Unique: true,
 	}
 	}
+	if fields := bq.fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, bouncer.FieldID)
+		for i := range fields {
+			if fields[i] != bouncer.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
+			}
+		}
+	}
 	if ps := bq.predicates; len(ps) > 0 {
 	if ps := bq.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
@@ -394,7 +409,7 @@ func (bq *BouncerQuery) sqlQuery() *sql.Selector {
 	return selector
 	return selector
 }
 }
 
 
-// BouncerGroupBy is the builder for group-by Bouncer entities.
+// BouncerGroupBy is the group-by builder for Bouncer entities.
 type BouncerGroupBy struct {
 type BouncerGroupBy struct {
 	config
 	config
 	fields []string
 	fields []string
@@ -410,7 +425,7 @@ func (bgb *BouncerGroupBy) Aggregate(fns ...AggregateFunc) *BouncerGroupBy {
 	return bgb
 	return bgb
 }
 }
 
 
-// Scan applies the group-by query and scan the result into the given value.
+// Scan applies the group-by query and scans the result into the given value.
 func (bgb *BouncerGroupBy) Scan(ctx context.Context, v interface{}) error {
 func (bgb *BouncerGroupBy) Scan(ctx context.Context, v interface{}) error {
 	query, err := bgb.path(ctx)
 	query, err := bgb.path(ctx)
 	if err != nil {
 	if err != nil {
@@ -427,7 +442,8 @@ func (bgb *BouncerGroupBy) ScanX(ctx context.Context, v interface{}) {
 	}
 	}
 }
 }
 
 
-// Strings returns list of strings from group-by. It is only allowed when querying group-by with one field.
+// Strings returns list of strings from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (bgb *BouncerGroupBy) Strings(ctx context.Context) ([]string, error) {
 func (bgb *BouncerGroupBy) Strings(ctx context.Context) ([]string, error) {
 	if len(bgb.fields) > 1 {
 	if len(bgb.fields) > 1 {
 		return nil, errors.New("ent: BouncerGroupBy.Strings is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: BouncerGroupBy.Strings is not achievable when grouping more than 1 field")
@@ -448,7 +464,8 @@ func (bgb *BouncerGroupBy) StringsX(ctx context.Context) []string {
 	return v
 	return v
 }
 }
 
 
-// String returns a single string from group-by. It is only allowed when querying group-by with one field.
+// String returns a single string from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (bgb *BouncerGroupBy) String(ctx context.Context) (_ string, err error) {
 func (bgb *BouncerGroupBy) String(ctx context.Context) (_ string, err error) {
 	var v []string
 	var v []string
 	if v, err = bgb.Strings(ctx); err != nil {
 	if v, err = bgb.Strings(ctx); err != nil {
@@ -474,7 +491,8 @@ func (bgb *BouncerGroupBy) StringX(ctx context.Context) string {
 	return v
 	return v
 }
 }
 
 
-// Ints returns list of ints from group-by. It is only allowed when querying group-by with one field.
+// Ints returns list of ints from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (bgb *BouncerGroupBy) Ints(ctx context.Context) ([]int, error) {
 func (bgb *BouncerGroupBy) Ints(ctx context.Context) ([]int, error) {
 	if len(bgb.fields) > 1 {
 	if len(bgb.fields) > 1 {
 		return nil, errors.New("ent: BouncerGroupBy.Ints is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: BouncerGroupBy.Ints is not achievable when grouping more than 1 field")
@@ -495,7 +513,8 @@ func (bgb *BouncerGroupBy) IntsX(ctx context.Context) []int {
 	return v
 	return v
 }
 }
 
 
-// Int returns a single int from group-by. It is only allowed when querying group-by with one field.
+// Int returns a single int from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (bgb *BouncerGroupBy) Int(ctx context.Context) (_ int, err error) {
 func (bgb *BouncerGroupBy) Int(ctx context.Context) (_ int, err error) {
 	var v []int
 	var v []int
 	if v, err = bgb.Ints(ctx); err != nil {
 	if v, err = bgb.Ints(ctx); err != nil {
@@ -521,7 +540,8 @@ func (bgb *BouncerGroupBy) IntX(ctx context.Context) int {
 	return v
 	return v
 }
 }
 
 
-// Float64s returns list of float64s from group-by. It is only allowed when querying group-by with one field.
+// Float64s returns list of float64s from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (bgb *BouncerGroupBy) Float64s(ctx context.Context) ([]float64, error) {
 func (bgb *BouncerGroupBy) Float64s(ctx context.Context) ([]float64, error) {
 	if len(bgb.fields) > 1 {
 	if len(bgb.fields) > 1 {
 		return nil, errors.New("ent: BouncerGroupBy.Float64s is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: BouncerGroupBy.Float64s is not achievable when grouping more than 1 field")
@@ -542,7 +562,8 @@ func (bgb *BouncerGroupBy) Float64sX(ctx context.Context) []float64 {
 	return v
 	return v
 }
 }
 
 
-// Float64 returns a single float64 from group-by. It is only allowed when querying group-by with one field.
+// Float64 returns a single float64 from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (bgb *BouncerGroupBy) Float64(ctx context.Context) (_ float64, err error) {
 func (bgb *BouncerGroupBy) Float64(ctx context.Context) (_ float64, err error) {
 	var v []float64
 	var v []float64
 	if v, err = bgb.Float64s(ctx); err != nil {
 	if v, err = bgb.Float64s(ctx); err != nil {
@@ -568,7 +589,8 @@ func (bgb *BouncerGroupBy) Float64X(ctx context.Context) float64 {
 	return v
 	return v
 }
 }
 
 
-// Bools returns list of bools from group-by. It is only allowed when querying group-by with one field.
+// Bools returns list of bools from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (bgb *BouncerGroupBy) Bools(ctx context.Context) ([]bool, error) {
 func (bgb *BouncerGroupBy) Bools(ctx context.Context) ([]bool, error) {
 	if len(bgb.fields) > 1 {
 	if len(bgb.fields) > 1 {
 		return nil, errors.New("ent: BouncerGroupBy.Bools is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: BouncerGroupBy.Bools is not achievable when grouping more than 1 field")
@@ -589,7 +611,8 @@ func (bgb *BouncerGroupBy) BoolsX(ctx context.Context) []bool {
 	return v
 	return v
 }
 }
 
 
-// Bool returns a single bool from group-by. It is only allowed when querying group-by with one field.
+// Bool returns a single bool from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (bgb *BouncerGroupBy) Bool(ctx context.Context) (_ bool, err error) {
 func (bgb *BouncerGroupBy) Bool(ctx context.Context) (_ bool, err error) {
 	var v []bool
 	var v []bool
 	if v, err = bgb.Bools(ctx); err != nil {
 	if v, err = bgb.Bools(ctx); err != nil {
@@ -644,22 +667,19 @@ func (bgb *BouncerGroupBy) sqlQuery() *sql.Selector {
 	return selector.Select(columns...).GroupBy(bgb.fields...)
 	return selector.Select(columns...).GroupBy(bgb.fields...)
 }
 }
 
 
-// BouncerSelect is the builder for select fields of Bouncer entities.
+// BouncerSelect is the builder for selecting fields of Bouncer entities.
 type BouncerSelect struct {
 type BouncerSelect struct {
-	config
-	fields []string
+	*BouncerQuery
 	// intermediate query (i.e. traversal path).
 	// intermediate query (i.e. traversal path).
-	sql  *sql.Selector
-	path func(context.Context) (*sql.Selector, error)
+	sql *sql.Selector
 }
 }
 
 
-// Scan applies the selector query and scan the result into the given value.
+// Scan applies the selector query and scans the result into the given value.
 func (bs *BouncerSelect) Scan(ctx context.Context, v interface{}) error {
 func (bs *BouncerSelect) Scan(ctx context.Context, v interface{}) error {
-	query, err := bs.path(ctx)
-	if err != nil {
+	if err := bs.prepareQuery(ctx); err != nil {
 		return err
 		return err
 	}
 	}
-	bs.sql = query
+	bs.sql = bs.BouncerQuery.sqlQuery()
 	return bs.sqlScan(ctx, v)
 	return bs.sqlScan(ctx, v)
 }
 }
 
 
@@ -670,7 +690,7 @@ func (bs *BouncerSelect) ScanX(ctx context.Context, v interface{}) {
 	}
 	}
 }
 }
 
 
-// Strings returns list of strings from selector. It is only allowed when selecting one field.
+// Strings returns list of strings from a selector. It is only allowed when selecting one field.
 func (bs *BouncerSelect) Strings(ctx context.Context) ([]string, error) {
 func (bs *BouncerSelect) Strings(ctx context.Context) ([]string, error) {
 	if len(bs.fields) > 1 {
 	if len(bs.fields) > 1 {
 		return nil, errors.New("ent: BouncerSelect.Strings is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: BouncerSelect.Strings is not achievable when selecting more than 1 field")
@@ -691,7 +711,7 @@ func (bs *BouncerSelect) StringsX(ctx context.Context) []string {
 	return v
 	return v
 }
 }
 
 
-// String returns a single string from selector. It is only allowed when selecting one field.
+// String returns a single string from a selector. It is only allowed when selecting one field.
 func (bs *BouncerSelect) String(ctx context.Context) (_ string, err error) {
 func (bs *BouncerSelect) String(ctx context.Context) (_ string, err error) {
 	var v []string
 	var v []string
 	if v, err = bs.Strings(ctx); err != nil {
 	if v, err = bs.Strings(ctx); err != nil {
@@ -717,7 +737,7 @@ func (bs *BouncerSelect) StringX(ctx context.Context) string {
 	return v
 	return v
 }
 }
 
 
-// Ints returns list of ints from selector. It is only allowed when selecting one field.
+// Ints returns list of ints from a selector. It is only allowed when selecting one field.
 func (bs *BouncerSelect) Ints(ctx context.Context) ([]int, error) {
 func (bs *BouncerSelect) Ints(ctx context.Context) ([]int, error) {
 	if len(bs.fields) > 1 {
 	if len(bs.fields) > 1 {
 		return nil, errors.New("ent: BouncerSelect.Ints is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: BouncerSelect.Ints is not achievable when selecting more than 1 field")
@@ -738,7 +758,7 @@ func (bs *BouncerSelect) IntsX(ctx context.Context) []int {
 	return v
 	return v
 }
 }
 
 
-// Int returns a single int from selector. It is only allowed when selecting one field.
+// Int returns a single int from a selector. It is only allowed when selecting one field.
 func (bs *BouncerSelect) Int(ctx context.Context) (_ int, err error) {
 func (bs *BouncerSelect) Int(ctx context.Context) (_ int, err error) {
 	var v []int
 	var v []int
 	if v, err = bs.Ints(ctx); err != nil {
 	if v, err = bs.Ints(ctx); err != nil {
@@ -764,7 +784,7 @@ func (bs *BouncerSelect) IntX(ctx context.Context) int {
 	return v
 	return v
 }
 }
 
 
-// Float64s returns list of float64s from selector. It is only allowed when selecting one field.
+// Float64s returns list of float64s from a selector. It is only allowed when selecting one field.
 func (bs *BouncerSelect) Float64s(ctx context.Context) ([]float64, error) {
 func (bs *BouncerSelect) Float64s(ctx context.Context) ([]float64, error) {
 	if len(bs.fields) > 1 {
 	if len(bs.fields) > 1 {
 		return nil, errors.New("ent: BouncerSelect.Float64s is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: BouncerSelect.Float64s is not achievable when selecting more than 1 field")
@@ -785,7 +805,7 @@ func (bs *BouncerSelect) Float64sX(ctx context.Context) []float64 {
 	return v
 	return v
 }
 }
 
 
-// Float64 returns a single float64 from selector. It is only allowed when selecting one field.
+// Float64 returns a single float64 from a selector. It is only allowed when selecting one field.
 func (bs *BouncerSelect) Float64(ctx context.Context) (_ float64, err error) {
 func (bs *BouncerSelect) Float64(ctx context.Context) (_ float64, err error) {
 	var v []float64
 	var v []float64
 	if v, err = bs.Float64s(ctx); err != nil {
 	if v, err = bs.Float64s(ctx); err != nil {
@@ -811,7 +831,7 @@ func (bs *BouncerSelect) Float64X(ctx context.Context) float64 {
 	return v
 	return v
 }
 }
 
 
-// Bools returns list of bools from selector. It is only allowed when selecting one field.
+// Bools returns list of bools from a selector. It is only allowed when selecting one field.
 func (bs *BouncerSelect) Bools(ctx context.Context) ([]bool, error) {
 func (bs *BouncerSelect) Bools(ctx context.Context) ([]bool, error) {
 	if len(bs.fields) > 1 {
 	if len(bs.fields) > 1 {
 		return nil, errors.New("ent: BouncerSelect.Bools is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: BouncerSelect.Bools is not achievable when selecting more than 1 field")
@@ -832,7 +852,7 @@ func (bs *BouncerSelect) BoolsX(ctx context.Context) []bool {
 	return v
 	return v
 }
 }
 
 
-// Bool returns a single bool from selector. It is only allowed when selecting one field.
+// Bool returns a single bool from a selector. It is only allowed when selecting one field.
 func (bs *BouncerSelect) Bool(ctx context.Context) (_ bool, err error) {
 func (bs *BouncerSelect) Bool(ctx context.Context) (_ bool, err error) {
 	var v []bool
 	var v []bool
 	if v, err = bs.Bools(ctx); err != nil {
 	if v, err = bs.Bools(ctx); err != nil {
@@ -859,11 +879,6 @@ func (bs *BouncerSelect) BoolX(ctx context.Context) bool {
 }
 }
 
 
 func (bs *BouncerSelect) sqlScan(ctx context.Context, v interface{}) error {
 func (bs *BouncerSelect) sqlScan(ctx context.Context, v interface{}) error {
-	for _, f := range bs.fields {
-		if !bouncer.ValidColumn(f) {
-			return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for selection", f)}
-		}
-	}
 	rows := &sql.Rows{}
 	rows := &sql.Rows{}
 	query, args := bs.sqlQuery().Query()
 	query, args := bs.sqlQuery().Query()
 	if err := bs.driver.Query(ctx, query, args, rows); err != nil {
 	if err := bs.driver.Query(ctx, query, args, rows); err != nil {

+ 50 - 51
pkg/database/ent/bouncer_update.go

@@ -17,24 +17,23 @@ import (
 // BouncerUpdate is the builder for updating Bouncer entities.
 // BouncerUpdate is the builder for updating Bouncer entities.
 type BouncerUpdate struct {
 type BouncerUpdate struct {
 	config
 	config
-	hooks      []Hook
-	mutation   *BouncerMutation
-	predicates []predicate.Bouncer
+	hooks    []Hook
+	mutation *BouncerMutation
 }
 }
 
 
-// Where adds a new predicate for the builder.
+// Where adds a new predicate for the BouncerUpdate builder.
 func (bu *BouncerUpdate) Where(ps ...predicate.Bouncer) *BouncerUpdate {
 func (bu *BouncerUpdate) Where(ps ...predicate.Bouncer) *BouncerUpdate {
-	bu.predicates = append(bu.predicates, ps...)
+	bu.mutation.predicates = append(bu.mutation.predicates, ps...)
 	return bu
 	return bu
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (bu *BouncerUpdate) SetCreatedAt(t time.Time) *BouncerUpdate {
 func (bu *BouncerUpdate) SetCreatedAt(t time.Time) *BouncerUpdate {
 	bu.mutation.SetCreatedAt(t)
 	bu.mutation.SetCreatedAt(t)
 	return bu
 	return bu
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (bu *BouncerUpdate) SetNillableCreatedAt(t *time.Time) *BouncerUpdate {
 func (bu *BouncerUpdate) SetNillableCreatedAt(t *time.Time) *BouncerUpdate {
 	if t != nil {
 	if t != nil {
 		bu.SetCreatedAt(*t)
 		bu.SetCreatedAt(*t)
@@ -42,13 +41,13 @@ func (bu *BouncerUpdate) SetNillableCreatedAt(t *time.Time) *BouncerUpdate {
 	return bu
 	return bu
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (bu *BouncerUpdate) SetUpdatedAt(t time.Time) *BouncerUpdate {
 func (bu *BouncerUpdate) SetUpdatedAt(t time.Time) *BouncerUpdate {
 	bu.mutation.SetUpdatedAt(t)
 	bu.mutation.SetUpdatedAt(t)
 	return bu
 	return bu
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (bu *BouncerUpdate) SetNillableUpdatedAt(t *time.Time) *BouncerUpdate {
 func (bu *BouncerUpdate) SetNillableUpdatedAt(t *time.Time) *BouncerUpdate {
 	if t != nil {
 	if t != nil {
 		bu.SetUpdatedAt(*t)
 		bu.SetUpdatedAt(*t)
@@ -56,31 +55,31 @@ func (bu *BouncerUpdate) SetNillableUpdatedAt(t *time.Time) *BouncerUpdate {
 	return bu
 	return bu
 }
 }
 
 
-// SetName sets the name field.
+// SetName sets the "name" field.
 func (bu *BouncerUpdate) SetName(s string) *BouncerUpdate {
 func (bu *BouncerUpdate) SetName(s string) *BouncerUpdate {
 	bu.mutation.SetName(s)
 	bu.mutation.SetName(s)
 	return bu
 	return bu
 }
 }
 
 
-// SetAPIKey sets the api_key field.
+// SetAPIKey sets the "api_key" field.
 func (bu *BouncerUpdate) SetAPIKey(s string) *BouncerUpdate {
 func (bu *BouncerUpdate) SetAPIKey(s string) *BouncerUpdate {
 	bu.mutation.SetAPIKey(s)
 	bu.mutation.SetAPIKey(s)
 	return bu
 	return bu
 }
 }
 
 
-// SetRevoked sets the revoked field.
+// SetRevoked sets the "revoked" field.
 func (bu *BouncerUpdate) SetRevoked(b bool) *BouncerUpdate {
 func (bu *BouncerUpdate) SetRevoked(b bool) *BouncerUpdate {
 	bu.mutation.SetRevoked(b)
 	bu.mutation.SetRevoked(b)
 	return bu
 	return bu
 }
 }
 
 
-// SetIPAddress sets the ip_address field.
+// SetIPAddress sets the "ip_address" field.
 func (bu *BouncerUpdate) SetIPAddress(s string) *BouncerUpdate {
 func (bu *BouncerUpdate) SetIPAddress(s string) *BouncerUpdate {
 	bu.mutation.SetIPAddress(s)
 	bu.mutation.SetIPAddress(s)
 	return bu
 	return bu
 }
 }
 
 
-// SetNillableIPAddress sets the ip_address field if the given value is not nil.
+// SetNillableIPAddress sets the "ip_address" field if the given value is not nil.
 func (bu *BouncerUpdate) SetNillableIPAddress(s *string) *BouncerUpdate {
 func (bu *BouncerUpdate) SetNillableIPAddress(s *string) *BouncerUpdate {
 	if s != nil {
 	if s != nil {
 		bu.SetIPAddress(*s)
 		bu.SetIPAddress(*s)
@@ -88,19 +87,19 @@ func (bu *BouncerUpdate) SetNillableIPAddress(s *string) *BouncerUpdate {
 	return bu
 	return bu
 }
 }
 
 
-// ClearIPAddress clears the value of ip_address.
+// ClearIPAddress clears the value of the "ip_address" field.
 func (bu *BouncerUpdate) ClearIPAddress() *BouncerUpdate {
 func (bu *BouncerUpdate) ClearIPAddress() *BouncerUpdate {
 	bu.mutation.ClearIPAddress()
 	bu.mutation.ClearIPAddress()
 	return bu
 	return bu
 }
 }
 
 
-// SetType sets the type field.
+// SetType sets the "type" field.
 func (bu *BouncerUpdate) SetType(s string) *BouncerUpdate {
 func (bu *BouncerUpdate) SetType(s string) *BouncerUpdate {
 	bu.mutation.SetType(s)
 	bu.mutation.SetType(s)
 	return bu
 	return bu
 }
 }
 
 
-// SetNillableType sets the type field if the given value is not nil.
+// SetNillableType sets the "type" field if the given value is not nil.
 func (bu *BouncerUpdate) SetNillableType(s *string) *BouncerUpdate {
 func (bu *BouncerUpdate) SetNillableType(s *string) *BouncerUpdate {
 	if s != nil {
 	if s != nil {
 		bu.SetType(*s)
 		bu.SetType(*s)
@@ -108,19 +107,19 @@ func (bu *BouncerUpdate) SetNillableType(s *string) *BouncerUpdate {
 	return bu
 	return bu
 }
 }
 
 
-// ClearType clears the value of type.
+// ClearType clears the value of the "type" field.
 func (bu *BouncerUpdate) ClearType() *BouncerUpdate {
 func (bu *BouncerUpdate) ClearType() *BouncerUpdate {
 	bu.mutation.ClearType()
 	bu.mutation.ClearType()
 	return bu
 	return bu
 }
 }
 
 
-// SetVersion sets the version field.
+// SetVersion sets the "version" field.
 func (bu *BouncerUpdate) SetVersion(s string) *BouncerUpdate {
 func (bu *BouncerUpdate) SetVersion(s string) *BouncerUpdate {
 	bu.mutation.SetVersion(s)
 	bu.mutation.SetVersion(s)
 	return bu
 	return bu
 }
 }
 
 
-// SetNillableVersion sets the version field if the given value is not nil.
+// SetNillableVersion sets the "version" field if the given value is not nil.
 func (bu *BouncerUpdate) SetNillableVersion(s *string) *BouncerUpdate {
 func (bu *BouncerUpdate) SetNillableVersion(s *string) *BouncerUpdate {
 	if s != nil {
 	if s != nil {
 		bu.SetVersion(*s)
 		bu.SetVersion(*s)
@@ -128,19 +127,19 @@ func (bu *BouncerUpdate) SetNillableVersion(s *string) *BouncerUpdate {
 	return bu
 	return bu
 }
 }
 
 
-// ClearVersion clears the value of version.
+// ClearVersion clears the value of the "version" field.
 func (bu *BouncerUpdate) ClearVersion() *BouncerUpdate {
 func (bu *BouncerUpdate) ClearVersion() *BouncerUpdate {
 	bu.mutation.ClearVersion()
 	bu.mutation.ClearVersion()
 	return bu
 	return bu
 }
 }
 
 
-// SetUntil sets the until field.
+// SetUntil sets the "until" field.
 func (bu *BouncerUpdate) SetUntil(t time.Time) *BouncerUpdate {
 func (bu *BouncerUpdate) SetUntil(t time.Time) *BouncerUpdate {
 	bu.mutation.SetUntil(t)
 	bu.mutation.SetUntil(t)
 	return bu
 	return bu
 }
 }
 
 
-// SetNillableUntil sets the until field if the given value is not nil.
+// SetNillableUntil sets the "until" field if the given value is not nil.
 func (bu *BouncerUpdate) SetNillableUntil(t *time.Time) *BouncerUpdate {
 func (bu *BouncerUpdate) SetNillableUntil(t *time.Time) *BouncerUpdate {
 	if t != nil {
 	if t != nil {
 		bu.SetUntil(*t)
 		bu.SetUntil(*t)
@@ -148,19 +147,19 @@ func (bu *BouncerUpdate) SetNillableUntil(t *time.Time) *BouncerUpdate {
 	return bu
 	return bu
 }
 }
 
 
-// ClearUntil clears the value of until.
+// ClearUntil clears the value of the "until" field.
 func (bu *BouncerUpdate) ClearUntil() *BouncerUpdate {
 func (bu *BouncerUpdate) ClearUntil() *BouncerUpdate {
 	bu.mutation.ClearUntil()
 	bu.mutation.ClearUntil()
 	return bu
 	return bu
 }
 }
 
 
-// SetLastPull sets the last_pull field.
+// SetLastPull sets the "last_pull" field.
 func (bu *BouncerUpdate) SetLastPull(t time.Time) *BouncerUpdate {
 func (bu *BouncerUpdate) SetLastPull(t time.Time) *BouncerUpdate {
 	bu.mutation.SetLastPull(t)
 	bu.mutation.SetLastPull(t)
 	return bu
 	return bu
 }
 }
 
 
-// SetNillableLastPull sets the last_pull field if the given value is not nil.
+// SetNillableLastPull sets the "last_pull" field if the given value is not nil.
 func (bu *BouncerUpdate) SetNillableLastPull(t *time.Time) *BouncerUpdate {
 func (bu *BouncerUpdate) SetNillableLastPull(t *time.Time) *BouncerUpdate {
 	if t != nil {
 	if t != nil {
 		bu.SetLastPull(*t)
 		bu.SetLastPull(*t)
@@ -173,7 +172,7 @@ func (bu *BouncerUpdate) Mutation() *BouncerMutation {
 	return bu.mutation
 	return bu.mutation
 }
 }
 
 
-// Save executes the query and returns the number of rows/vertices matched by this operation.
+// Save executes the query and returns the number of nodes affected by the update operation.
 func (bu *BouncerUpdate) Save(ctx context.Context) (int, error) {
 func (bu *BouncerUpdate) Save(ctx context.Context) (int, error) {
 	var (
 	var (
 		err      error
 		err      error
@@ -235,7 +234,7 @@ func (bu *BouncerUpdate) sqlSave(ctx context.Context) (n int, err error) {
 			},
 			},
 		},
 		},
 	}
 	}
-	if ps := bu.predicates; len(ps) > 0 {
+	if ps := bu.mutation.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
 				ps[i](selector)
 				ps[i](selector)
@@ -354,13 +353,13 @@ type BouncerUpdateOne struct {
 	mutation *BouncerMutation
 	mutation *BouncerMutation
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (buo *BouncerUpdateOne) SetCreatedAt(t time.Time) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetCreatedAt(t time.Time) *BouncerUpdateOne {
 	buo.mutation.SetCreatedAt(t)
 	buo.mutation.SetCreatedAt(t)
 	return buo
 	return buo
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (buo *BouncerUpdateOne) SetNillableCreatedAt(t *time.Time) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetNillableCreatedAt(t *time.Time) *BouncerUpdateOne {
 	if t != nil {
 	if t != nil {
 		buo.SetCreatedAt(*t)
 		buo.SetCreatedAt(*t)
@@ -368,13 +367,13 @@ func (buo *BouncerUpdateOne) SetNillableCreatedAt(t *time.Time) *BouncerUpdateOn
 	return buo
 	return buo
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (buo *BouncerUpdateOne) SetUpdatedAt(t time.Time) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetUpdatedAt(t time.Time) *BouncerUpdateOne {
 	buo.mutation.SetUpdatedAt(t)
 	buo.mutation.SetUpdatedAt(t)
 	return buo
 	return buo
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (buo *BouncerUpdateOne) SetNillableUpdatedAt(t *time.Time) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetNillableUpdatedAt(t *time.Time) *BouncerUpdateOne {
 	if t != nil {
 	if t != nil {
 		buo.SetUpdatedAt(*t)
 		buo.SetUpdatedAt(*t)
@@ -382,31 +381,31 @@ func (buo *BouncerUpdateOne) SetNillableUpdatedAt(t *time.Time) *BouncerUpdateOn
 	return buo
 	return buo
 }
 }
 
 
-// SetName sets the name field.
+// SetName sets the "name" field.
 func (buo *BouncerUpdateOne) SetName(s string) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetName(s string) *BouncerUpdateOne {
 	buo.mutation.SetName(s)
 	buo.mutation.SetName(s)
 	return buo
 	return buo
 }
 }
 
 
-// SetAPIKey sets the api_key field.
+// SetAPIKey sets the "api_key" field.
 func (buo *BouncerUpdateOne) SetAPIKey(s string) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetAPIKey(s string) *BouncerUpdateOne {
 	buo.mutation.SetAPIKey(s)
 	buo.mutation.SetAPIKey(s)
 	return buo
 	return buo
 }
 }
 
 
-// SetRevoked sets the revoked field.
+// SetRevoked sets the "revoked" field.
 func (buo *BouncerUpdateOne) SetRevoked(b bool) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetRevoked(b bool) *BouncerUpdateOne {
 	buo.mutation.SetRevoked(b)
 	buo.mutation.SetRevoked(b)
 	return buo
 	return buo
 }
 }
 
 
-// SetIPAddress sets the ip_address field.
+// SetIPAddress sets the "ip_address" field.
 func (buo *BouncerUpdateOne) SetIPAddress(s string) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetIPAddress(s string) *BouncerUpdateOne {
 	buo.mutation.SetIPAddress(s)
 	buo.mutation.SetIPAddress(s)
 	return buo
 	return buo
 }
 }
 
 
-// SetNillableIPAddress sets the ip_address field if the given value is not nil.
+// SetNillableIPAddress sets the "ip_address" field if the given value is not nil.
 func (buo *BouncerUpdateOne) SetNillableIPAddress(s *string) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetNillableIPAddress(s *string) *BouncerUpdateOne {
 	if s != nil {
 	if s != nil {
 		buo.SetIPAddress(*s)
 		buo.SetIPAddress(*s)
@@ -414,19 +413,19 @@ func (buo *BouncerUpdateOne) SetNillableIPAddress(s *string) *BouncerUpdateOne {
 	return buo
 	return buo
 }
 }
 
 
-// ClearIPAddress clears the value of ip_address.
+// ClearIPAddress clears the value of the "ip_address" field.
 func (buo *BouncerUpdateOne) ClearIPAddress() *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) ClearIPAddress() *BouncerUpdateOne {
 	buo.mutation.ClearIPAddress()
 	buo.mutation.ClearIPAddress()
 	return buo
 	return buo
 }
 }
 
 
-// SetType sets the type field.
+// SetType sets the "type" field.
 func (buo *BouncerUpdateOne) SetType(s string) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetType(s string) *BouncerUpdateOne {
 	buo.mutation.SetType(s)
 	buo.mutation.SetType(s)
 	return buo
 	return buo
 }
 }
 
 
-// SetNillableType sets the type field if the given value is not nil.
+// SetNillableType sets the "type" field if the given value is not nil.
 func (buo *BouncerUpdateOne) SetNillableType(s *string) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetNillableType(s *string) *BouncerUpdateOne {
 	if s != nil {
 	if s != nil {
 		buo.SetType(*s)
 		buo.SetType(*s)
@@ -434,19 +433,19 @@ func (buo *BouncerUpdateOne) SetNillableType(s *string) *BouncerUpdateOne {
 	return buo
 	return buo
 }
 }
 
 
-// ClearType clears the value of type.
+// ClearType clears the value of the "type" field.
 func (buo *BouncerUpdateOne) ClearType() *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) ClearType() *BouncerUpdateOne {
 	buo.mutation.ClearType()
 	buo.mutation.ClearType()
 	return buo
 	return buo
 }
 }
 
 
-// SetVersion sets the version field.
+// SetVersion sets the "version" field.
 func (buo *BouncerUpdateOne) SetVersion(s string) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetVersion(s string) *BouncerUpdateOne {
 	buo.mutation.SetVersion(s)
 	buo.mutation.SetVersion(s)
 	return buo
 	return buo
 }
 }
 
 
-// SetNillableVersion sets the version field if the given value is not nil.
+// SetNillableVersion sets the "version" field if the given value is not nil.
 func (buo *BouncerUpdateOne) SetNillableVersion(s *string) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetNillableVersion(s *string) *BouncerUpdateOne {
 	if s != nil {
 	if s != nil {
 		buo.SetVersion(*s)
 		buo.SetVersion(*s)
@@ -454,19 +453,19 @@ func (buo *BouncerUpdateOne) SetNillableVersion(s *string) *BouncerUpdateOne {
 	return buo
 	return buo
 }
 }
 
 
-// ClearVersion clears the value of version.
+// ClearVersion clears the value of the "version" field.
 func (buo *BouncerUpdateOne) ClearVersion() *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) ClearVersion() *BouncerUpdateOne {
 	buo.mutation.ClearVersion()
 	buo.mutation.ClearVersion()
 	return buo
 	return buo
 }
 }
 
 
-// SetUntil sets the until field.
+// SetUntil sets the "until" field.
 func (buo *BouncerUpdateOne) SetUntil(t time.Time) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetUntil(t time.Time) *BouncerUpdateOne {
 	buo.mutation.SetUntil(t)
 	buo.mutation.SetUntil(t)
 	return buo
 	return buo
 }
 }
 
 
-// SetNillableUntil sets the until field if the given value is not nil.
+// SetNillableUntil sets the "until" field if the given value is not nil.
 func (buo *BouncerUpdateOne) SetNillableUntil(t *time.Time) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetNillableUntil(t *time.Time) *BouncerUpdateOne {
 	if t != nil {
 	if t != nil {
 		buo.SetUntil(*t)
 		buo.SetUntil(*t)
@@ -474,19 +473,19 @@ func (buo *BouncerUpdateOne) SetNillableUntil(t *time.Time) *BouncerUpdateOne {
 	return buo
 	return buo
 }
 }
 
 
-// ClearUntil clears the value of until.
+// ClearUntil clears the value of the "until" field.
 func (buo *BouncerUpdateOne) ClearUntil() *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) ClearUntil() *BouncerUpdateOne {
 	buo.mutation.ClearUntil()
 	buo.mutation.ClearUntil()
 	return buo
 	return buo
 }
 }
 
 
-// SetLastPull sets the last_pull field.
+// SetLastPull sets the "last_pull" field.
 func (buo *BouncerUpdateOne) SetLastPull(t time.Time) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetLastPull(t time.Time) *BouncerUpdateOne {
 	buo.mutation.SetLastPull(t)
 	buo.mutation.SetLastPull(t)
 	return buo
 	return buo
 }
 }
 
 
-// SetNillableLastPull sets the last_pull field if the given value is not nil.
+// SetNillableLastPull sets the "last_pull" field if the given value is not nil.
 func (buo *BouncerUpdateOne) SetNillableLastPull(t *time.Time) *BouncerUpdateOne {
 func (buo *BouncerUpdateOne) SetNillableLastPull(t *time.Time) *BouncerUpdateOne {
 	if t != nil {
 	if t != nil {
 		buo.SetLastPull(*t)
 		buo.SetLastPull(*t)
@@ -499,7 +498,7 @@ func (buo *BouncerUpdateOne) Mutation() *BouncerMutation {
 	return buo.mutation
 	return buo.mutation
 }
 }
 
 
-// Save executes the query and returns the updated entity.
+// Save executes the query and returns the updated Bouncer entity.
 func (buo *BouncerUpdateOne) Save(ctx context.Context) (*Bouncer, error) {
 func (buo *BouncerUpdateOne) Save(ctx context.Context) (*Bouncer, error) {
 	var (
 	var (
 		err  error
 		err  error
@@ -662,7 +661,7 @@ func (buo *BouncerUpdateOne) sqlSave(ctx context.Context) (_node *Bouncer, err e
 	}
 	}
 	_node = &Bouncer{config: buo.config}
 	_node = &Bouncer{config: buo.config}
 	_spec.Assign = _node.assignValues
 	_spec.Assign = _node.assignValues
-	_spec.ScanValues = _node.scanValues()
+	_spec.ScanValues = _node.scanValues
 	if err = sqlgraph.UpdateNode(ctx, buo.driver, _spec); err != nil {
 	if err = sqlgraph.UpdateNode(ctx, buo.driver, _spec); err != nil {
 		if _, ok := err.(*sqlgraph.NotFoundError); ok {
 		if _, ok := err.(*sqlgraph.NotFoundError); ok {
 			err = &NotFoundError{bouncer.Label}
 			err = &NotFoundError{bouncer.Label}

+ 7 - 7
pkg/database/ent/client.go

@@ -98,7 +98,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
 	}, nil
 	}, nil
 }
 }
 
 
-// BeginTx returns a transactional client with options.
+// BeginTx returns a transactional client with specified options.
 func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
 func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
 	if _, ok := c.driver.(*txDriver); ok {
 	if _, ok := c.driver.(*txDriver); ok {
 		return nil, fmt.Errorf("ent: cannot start a transaction within a transaction")
 		return nil, fmt.Errorf("ent: cannot start a transaction within a transaction")
@@ -174,7 +174,7 @@ func (c *AlertClient) Create() *AlertCreate {
 	return &AlertCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
 	return &AlertCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
 }
 }
 
 
-// BulkCreate returns a builder for creating a bulk of Alert entities.
+// CreateBulk returns a builder for creating a bulk of Alert entities.
 func (c *AlertClient) CreateBulk(builders ...*AlertCreate) *AlertCreateBulk {
 func (c *AlertClient) CreateBulk(builders ...*AlertCreate) *AlertCreateBulk {
 	return &AlertCreateBulk{config: c.config, builders: builders}
 	return &AlertCreateBulk{config: c.config, builders: builders}
 }
 }
@@ -326,7 +326,7 @@ func (c *BouncerClient) Create() *BouncerCreate {
 	return &BouncerCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
 	return &BouncerCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
 }
 }
 
 
-// BulkCreate returns a builder for creating a bulk of Bouncer entities.
+// CreateBulk returns a builder for creating a bulk of Bouncer entities.
 func (c *BouncerClient) CreateBulk(builders ...*BouncerCreate) *BouncerCreateBulk {
 func (c *BouncerClient) CreateBulk(builders ...*BouncerCreate) *BouncerCreateBulk {
 	return &BouncerCreateBulk{config: c.config, builders: builders}
 	return &BouncerCreateBulk{config: c.config, builders: builders}
 }
 }
@@ -414,7 +414,7 @@ func (c *DecisionClient) Create() *DecisionCreate {
 	return &DecisionCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
 	return &DecisionCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
 }
 }
 
 
-// BulkCreate returns a builder for creating a bulk of Decision entities.
+// CreateBulk returns a builder for creating a bulk of Decision entities.
 func (c *DecisionClient) CreateBulk(builders ...*DecisionCreate) *DecisionCreateBulk {
 func (c *DecisionClient) CreateBulk(builders ...*DecisionCreate) *DecisionCreateBulk {
 	return &DecisionCreateBulk{config: c.config, builders: builders}
 	return &DecisionCreateBulk{config: c.config, builders: builders}
 }
 }
@@ -518,7 +518,7 @@ func (c *EventClient) Create() *EventCreate {
 	return &EventCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
 	return &EventCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
 }
 }
 
 
-// BulkCreate returns a builder for creating a bulk of Event entities.
+// CreateBulk returns a builder for creating a bulk of Event entities.
 func (c *EventClient) CreateBulk(builders ...*EventCreate) *EventCreateBulk {
 func (c *EventClient) CreateBulk(builders ...*EventCreate) *EventCreateBulk {
 	return &EventCreateBulk{config: c.config, builders: builders}
 	return &EventCreateBulk{config: c.config, builders: builders}
 }
 }
@@ -622,7 +622,7 @@ func (c *MachineClient) Create() *MachineCreate {
 	return &MachineCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
 	return &MachineCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
 }
 }
 
 
-// BulkCreate returns a builder for creating a bulk of Machine entities.
+// CreateBulk returns a builder for creating a bulk of Machine entities.
 func (c *MachineClient) CreateBulk(builders ...*MachineCreate) *MachineCreateBulk {
 func (c *MachineClient) CreateBulk(builders ...*MachineCreate) *MachineCreateBulk {
 	return &MachineCreateBulk{config: c.config, builders: builders}
 	return &MachineCreateBulk{config: c.config, builders: builders}
 }
 }
@@ -726,7 +726,7 @@ func (c *MetaClient) Create() *MetaCreate {
 	return &MetaCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
 	return &MetaCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
 }
 }
 
 
-// BulkCreate returns a builder for creating a bulk of Meta entities.
+// CreateBulk returns a builder for creating a bulk of Meta entities.
 func (c *MetaClient) CreateBulk(builders ...*MetaCreate) *MetaCreateBulk {
 func (c *MetaClient) CreateBulk(builders ...*MetaCreate) *MetaCreateBulk {
 	return &MetaCreateBulk{config: c.config, builders: builders}
 	return &MetaCreateBulk{config: c.config, builders: builders}
 }
 }

+ 3 - 3
pkg/database/ent/context.go

@@ -8,7 +8,7 @@ import (
 
 
 type clientCtxKey struct{}
 type clientCtxKey struct{}
 
 
-// FromContext returns the Client stored in a context, or nil if there isn't one.
+// FromContext returns a Client stored inside a context, or nil if there isn't one.
 func FromContext(ctx context.Context) *Client {
 func FromContext(ctx context.Context) *Client {
 	c, _ := ctx.Value(clientCtxKey{}).(*Client)
 	c, _ := ctx.Value(clientCtxKey{}).(*Client)
 	return c
 	return c
@@ -21,13 +21,13 @@ func NewContext(parent context.Context, c *Client) context.Context {
 
 
 type txCtxKey struct{}
 type txCtxKey struct{}
 
 
-// TxFromContext returns the Tx stored in a context, or nil if there isn't one.
+// TxFromContext returns a Tx stored inside a context, or nil if there isn't one.
 func TxFromContext(ctx context.Context) *Tx {
 func TxFromContext(ctx context.Context) *Tx {
 	tx, _ := ctx.Value(txCtxKey{}).(*Tx)
 	tx, _ := ctx.Value(txCtxKey{}).(*Tx)
 	return tx
 	return tx
 }
 }
 
 
-// NewTxContext returns a new context with the given Client attached.
+// NewTxContext returns a new context with the given Tx attached.
 func NewTxContext(parent context.Context, tx *Tx) context.Context {
 func NewTxContext(parent context.Context, tx *Tx) context.Context {
 	return context.WithValue(parent, txCtxKey{}, tx)
 	return context.WithValue(parent, txCtxKey{}, tx)
 }
 }

+ 135 - 95
pkg/database/ent/decision.go

@@ -31,6 +31,12 @@ type Decision struct {
 	StartIP int64 `json:"start_ip,omitempty"`
 	StartIP int64 `json:"start_ip,omitempty"`
 	// EndIP holds the value of the "end_ip" field.
 	// EndIP holds the value of the "end_ip" field.
 	EndIP int64 `json:"end_ip,omitempty"`
 	EndIP int64 `json:"end_ip,omitempty"`
+	// StartSuffix holds the value of the "start_suffix" field.
+	StartSuffix int64 `json:"start_suffix,omitempty"`
+	// EndSuffix holds the value of the "end_suffix" field.
+	EndSuffix int64 `json:"end_suffix,omitempty"`
+	// IPSize holds the value of the "ip_size" field.
+	IPSize int64 `json:"ip_size,omitempty"`
 	// Scope holds the value of the "scope" field.
 	// Scope holds the value of the "scope" field.
 	Scope string `json:"scope,omitempty"`
 	Scope string `json:"scope,omitempty"`
 	// Value holds the value of the "value" field.
 	// Value holds the value of the "value" field.
@@ -69,123 +75,151 @@ func (e DecisionEdges) OwnerOrErr() (*Alert, error) {
 }
 }
 
 
 // scanValues returns the types for scanning values from sql.Rows.
 // scanValues returns the types for scanning values from sql.Rows.
-func (*Decision) scanValues() []interface{} {
-	return []interface{}{
-		&sql.NullInt64{},  // id
-		&sql.NullTime{},   // created_at
-		&sql.NullTime{},   // updated_at
-		&sql.NullTime{},   // until
-		&sql.NullString{}, // scenario
-		&sql.NullString{}, // type
-		&sql.NullInt64{},  // start_ip
-		&sql.NullInt64{},  // end_ip
-		&sql.NullString{}, // scope
-		&sql.NullString{}, // value
-		&sql.NullString{}, // origin
-		&sql.NullBool{},   // simulated
-	}
-}
-
-// fkValues returns the types for scanning foreign-keys values from sql.Rows.
-func (*Decision) fkValues() []interface{} {
-	return []interface{}{
-		&sql.NullInt64{}, // alert_decisions
+func (*Decision) scanValues(columns []string) ([]interface{}, error) {
+	values := make([]interface{}, len(columns))
+	for i := range columns {
+		switch columns[i] {
+		case decision.FieldSimulated:
+			values[i] = &sql.NullBool{}
+		case decision.FieldID, decision.FieldStartIP, decision.FieldEndIP, decision.FieldStartSuffix, decision.FieldEndSuffix, decision.FieldIPSize:
+			values[i] = &sql.NullInt64{}
+		case decision.FieldScenario, decision.FieldType, decision.FieldScope, decision.FieldValue, decision.FieldOrigin:
+			values[i] = &sql.NullString{}
+		case decision.FieldCreatedAt, decision.FieldUpdatedAt, decision.FieldUntil:
+			values[i] = &sql.NullTime{}
+		case decision.ForeignKeys[0]: // alert_decisions
+			values[i] = &sql.NullInt64{}
+		default:
+			return nil, fmt.Errorf("unexpected column %q for type Decision", columns[i])
+		}
 	}
 	}
+	return values, nil
 }
 }
 
 
 // assignValues assigns the values that were returned from sql.Rows (after scanning)
 // assignValues assigns the values that were returned from sql.Rows (after scanning)
 // to the Decision fields.
 // to the Decision fields.
-func (d *Decision) assignValues(values ...interface{}) error {
-	if m, n := len(values), len(decision.Columns); m < n {
+func (d *Decision) assignValues(columns []string, values []interface{}) error {
+	if m, n := len(values), len(columns); m < n {
 		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
 		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
 	}
 	}
-	value, ok := values[0].(*sql.NullInt64)
-	if !ok {
-		return fmt.Errorf("unexpected type %T for field id", value)
-	}
-	d.ID = int(value.Int64)
-	values = values[1:]
-	if value, ok := values[0].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field created_at", values[0])
-	} else if value.Valid {
-		d.CreatedAt = value.Time
-	}
-	if value, ok := values[1].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field updated_at", values[1])
-	} else if value.Valid {
-		d.UpdatedAt = value.Time
-	}
-	if value, ok := values[2].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field until", values[2])
-	} else if value.Valid {
-		d.Until = value.Time
-	}
-	if value, ok := values[3].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field scenario", values[3])
-	} else if value.Valid {
-		d.Scenario = value.String
-	}
-	if value, ok := values[4].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field type", values[4])
-	} else if value.Valid {
-		d.Type = value.String
-	}
-	if value, ok := values[5].(*sql.NullInt64); !ok {
-		return fmt.Errorf("unexpected type %T for field start_ip", values[5])
-	} else if value.Valid {
-		d.StartIP = value.Int64
-	}
-	if value, ok := values[6].(*sql.NullInt64); !ok {
-		return fmt.Errorf("unexpected type %T for field end_ip", values[6])
-	} else if value.Valid {
-		d.EndIP = value.Int64
-	}
-	if value, ok := values[7].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field scope", values[7])
-	} else if value.Valid {
-		d.Scope = value.String
-	}
-	if value, ok := values[8].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field value", values[8])
-	} else if value.Valid {
-		d.Value = value.String
-	}
-	if value, ok := values[9].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field origin", values[9])
-	} else if value.Valid {
-		d.Origin = value.String
-	}
-	if value, ok := values[10].(*sql.NullBool); !ok {
-		return fmt.Errorf("unexpected type %T for field simulated", values[10])
-	} else if value.Valid {
-		d.Simulated = value.Bool
-	}
-	values = values[11:]
-	if len(values) == len(decision.ForeignKeys) {
-		if value, ok := values[0].(*sql.NullInt64); !ok {
-			return fmt.Errorf("unexpected type %T for edge-field alert_decisions", value)
-		} else if value.Valid {
-			d.alert_decisions = new(int)
-			*d.alert_decisions = int(value.Int64)
+	for i := range columns {
+		switch columns[i] {
+		case decision.FieldID:
+			value, ok := values[i].(*sql.NullInt64)
+			if !ok {
+				return fmt.Errorf("unexpected type %T for field id", value)
+			}
+			d.ID = int(value.Int64)
+		case decision.FieldCreatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field created_at", values[i])
+			} else if value.Valid {
+				d.CreatedAt = value.Time
+			}
+		case decision.FieldUpdatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field updated_at", values[i])
+			} else if value.Valid {
+				d.UpdatedAt = value.Time
+			}
+		case decision.FieldUntil:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field until", values[i])
+			} else if value.Valid {
+				d.Until = value.Time
+			}
+		case decision.FieldScenario:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field scenario", values[i])
+			} else if value.Valid {
+				d.Scenario = value.String
+			}
+		case decision.FieldType:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field type", values[i])
+			} else if value.Valid {
+				d.Type = value.String
+			}
+		case decision.FieldStartIP:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field start_ip", values[i])
+			} else if value.Valid {
+				d.StartIP = value.Int64
+			}
+		case decision.FieldEndIP:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field end_ip", values[i])
+			} else if value.Valid {
+				d.EndIP = value.Int64
+			}
+		case decision.FieldStartSuffix:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field start_suffix", values[i])
+			} else if value.Valid {
+				d.StartSuffix = value.Int64
+			}
+		case decision.FieldEndSuffix:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field end_suffix", values[i])
+			} else if value.Valid {
+				d.EndSuffix = value.Int64
+			}
+		case decision.FieldIPSize:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field ip_size", values[i])
+			} else if value.Valid {
+				d.IPSize = value.Int64
+			}
+		case decision.FieldScope:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field scope", values[i])
+			} else if value.Valid {
+				d.Scope = value.String
+			}
+		case decision.FieldValue:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field value", values[i])
+			} else if value.Valid {
+				d.Value = value.String
+			}
+		case decision.FieldOrigin:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field origin", values[i])
+			} else if value.Valid {
+				d.Origin = value.String
+			}
+		case decision.FieldSimulated:
+			if value, ok := values[i].(*sql.NullBool); !ok {
+				return fmt.Errorf("unexpected type %T for field simulated", values[i])
+			} else if value.Valid {
+				d.Simulated = value.Bool
+			}
+		case decision.ForeignKeys[0]:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for edge-field alert_decisions", value)
+			} else if value.Valid {
+				d.alert_decisions = new(int)
+				*d.alert_decisions = int(value.Int64)
+			}
 		}
 		}
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-// QueryOwner queries the owner edge of the Decision.
+// QueryOwner queries the "owner" edge of the Decision entity.
 func (d *Decision) QueryOwner() *AlertQuery {
 func (d *Decision) QueryOwner() *AlertQuery {
 	return (&DecisionClient{config: d.config}).QueryOwner(d)
 	return (&DecisionClient{config: d.config}).QueryOwner(d)
 }
 }
 
 
 // Update returns a builder for updating this Decision.
 // Update returns a builder for updating this Decision.
-// Note that, you need to call Decision.Unwrap() before calling this method, if this Decision
+// Note that you need to call Decision.Unwrap() before calling this method if this Decision
 // was returned from a transaction, and the transaction was committed or rolled back.
 // was returned from a transaction, and the transaction was committed or rolled back.
 func (d *Decision) Update() *DecisionUpdateOne {
 func (d *Decision) Update() *DecisionUpdateOne {
 	return (&DecisionClient{config: d.config}).UpdateOne(d)
 	return (&DecisionClient{config: d.config}).UpdateOne(d)
 }
 }
 
 
-// Unwrap unwraps the entity that was returned from a transaction after it was closed,
-// so that all next queries will be executed through the driver which created the transaction.
+// Unwrap unwraps the Decision entity that was returned from a transaction after it was closed,
+// so that all future queries will be executed through the driver which created the transaction.
 func (d *Decision) Unwrap() *Decision {
 func (d *Decision) Unwrap() *Decision {
 	tx, ok := d.config.driver.(*txDriver)
 	tx, ok := d.config.driver.(*txDriver)
 	if !ok {
 	if !ok {
@@ -214,6 +248,12 @@ func (d *Decision) String() string {
 	builder.WriteString(fmt.Sprintf("%v", d.StartIP))
 	builder.WriteString(fmt.Sprintf("%v", d.StartIP))
 	builder.WriteString(", end_ip=")
 	builder.WriteString(", end_ip=")
 	builder.WriteString(fmt.Sprintf("%v", d.EndIP))
 	builder.WriteString(fmt.Sprintf("%v", d.EndIP))
+	builder.WriteString(", start_suffix=")
+	builder.WriteString(fmt.Sprintf("%v", d.StartSuffix))
+	builder.WriteString(", end_suffix=")
+	builder.WriteString(fmt.Sprintf("%v", d.EndSuffix))
+	builder.WriteString(", ip_size=")
+	builder.WriteString(fmt.Sprintf("%v", d.IPSize))
 	builder.WriteString(", scope=")
 	builder.WriteString(", scope=")
 	builder.WriteString(d.Scope)
 	builder.WriteString(d.Scope)
 	builder.WriteString(", value=")
 	builder.WriteString(", value=")

+ 12 - 3
pkg/database/ent/decision/decision.go

@@ -25,6 +25,12 @@ const (
 	FieldStartIP = "start_ip"
 	FieldStartIP = "start_ip"
 	// FieldEndIP holds the string denoting the end_ip field in the database.
 	// FieldEndIP holds the string denoting the end_ip field in the database.
 	FieldEndIP = "end_ip"
 	FieldEndIP = "end_ip"
+	// FieldStartSuffix holds the string denoting the start_suffix field in the database.
+	FieldStartSuffix = "start_suffix"
+	// FieldEndSuffix holds the string denoting the end_suffix field in the database.
+	FieldEndSuffix = "end_suffix"
+	// FieldIPSize holds the string denoting the ip_size field in the database.
+	FieldIPSize = "ip_size"
 	// FieldScope holds the string denoting the scope field in the database.
 	// FieldScope holds the string denoting the scope field in the database.
 	FieldScope = "scope"
 	FieldScope = "scope"
 	// FieldValue holds the string denoting the value field in the database.
 	// FieldValue holds the string denoting the value field in the database.
@@ -58,6 +64,9 @@ var Columns = []string{
 	FieldType,
 	FieldType,
 	FieldStartIP,
 	FieldStartIP,
 	FieldEndIP,
 	FieldEndIP,
+	FieldStartSuffix,
+	FieldEndSuffix,
+	FieldIPSize,
 	FieldScope,
 	FieldScope,
 	FieldValue,
 	FieldValue,
 	FieldOrigin,
 	FieldOrigin,
@@ -85,10 +94,10 @@ func ValidColumn(column string) bool {
 }
 }
 
 
 var (
 var (
-	// DefaultCreatedAt holds the default value on creation for the created_at field.
+	// DefaultCreatedAt holds the default value on creation for the "created_at" field.
 	DefaultCreatedAt func() time.Time
 	DefaultCreatedAt func() time.Time
-	// DefaultUpdatedAt holds the default value on creation for the updated_at field.
+	// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
 	DefaultUpdatedAt func() time.Time
 	DefaultUpdatedAt func() time.Time
-	// DefaultSimulated holds the default value on creation for the simulated field.
+	// DefaultSimulated holds the default value on creation for the "simulated" field.
 	DefaultSimulated bool
 	DefaultSimulated bool
 )
 )

+ 294 - 3
pkg/database/ent/decision/where.go

@@ -10,7 +10,7 @@ import (
 	"github.com/facebook/ent/dialect/sql/sqlgraph"
 	"github.com/facebook/ent/dialect/sql/sqlgraph"
 )
 )
 
 
-// ID filters vertices based on their identifier.
+// ID filters vertices based on their ID field.
 func ID(id int) predicate.Decision {
 func ID(id int) predicate.Decision {
 	return predicate.Decision(func(s *sql.Selector) {
 	return predicate.Decision(func(s *sql.Selector) {
 		s.Where(sql.EQ(s.C(FieldID), id))
 		s.Where(sql.EQ(s.C(FieldID), id))
@@ -142,6 +142,27 @@ func EndIP(v int64) predicate.Decision {
 	})
 	})
 }
 }
 
 
+// StartSuffix applies equality check predicate on the "start_suffix" field. It's identical to StartSuffixEQ.
+func StartSuffix(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldStartSuffix), v))
+	})
+}
+
+// EndSuffix applies equality check predicate on the "end_suffix" field. It's identical to EndSuffixEQ.
+func EndSuffix(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldEndSuffix), v))
+	})
+}
+
+// IPSize applies equality check predicate on the "ip_size" field. It's identical to IPSizeEQ.
+func IPSize(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldIPSize), v))
+	})
+}
+
 // Scope applies equality check predicate on the "scope" field. It's identical to ScopeEQ.
 // Scope applies equality check predicate on the "scope" field. It's identical to ScopeEQ.
 func Scope(v string) predicate.Decision {
 func Scope(v string) predicate.Decision {
 	return predicate.Decision(func(s *sql.Selector) {
 	return predicate.Decision(func(s *sql.Selector) {
@@ -800,6 +821,276 @@ func EndIPNotNil() predicate.Decision {
 	})
 	})
 }
 }
 
 
+// StartSuffixEQ applies the EQ predicate on the "start_suffix" field.
+func StartSuffixEQ(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldStartSuffix), v))
+	})
+}
+
+// StartSuffixNEQ applies the NEQ predicate on the "start_suffix" field.
+func StartSuffixNEQ(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.NEQ(s.C(FieldStartSuffix), v))
+	})
+}
+
+// StartSuffixIn applies the In predicate on the "start_suffix" field.
+func StartSuffixIn(vs ...int64) predicate.Decision {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.Decision(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(v) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.In(s.C(FieldStartSuffix), v...))
+	})
+}
+
+// StartSuffixNotIn applies the NotIn predicate on the "start_suffix" field.
+func StartSuffixNotIn(vs ...int64) predicate.Decision {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.Decision(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(v) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.NotIn(s.C(FieldStartSuffix), v...))
+	})
+}
+
+// StartSuffixGT applies the GT predicate on the "start_suffix" field.
+func StartSuffixGT(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.GT(s.C(FieldStartSuffix), v))
+	})
+}
+
+// StartSuffixGTE applies the GTE predicate on the "start_suffix" field.
+func StartSuffixGTE(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.GTE(s.C(FieldStartSuffix), v))
+	})
+}
+
+// StartSuffixLT applies the LT predicate on the "start_suffix" field.
+func StartSuffixLT(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.LT(s.C(FieldStartSuffix), v))
+	})
+}
+
+// StartSuffixLTE applies the LTE predicate on the "start_suffix" field.
+func StartSuffixLTE(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.LTE(s.C(FieldStartSuffix), v))
+	})
+}
+
+// StartSuffixIsNil applies the IsNil predicate on the "start_suffix" field.
+func StartSuffixIsNil() predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.IsNull(s.C(FieldStartSuffix)))
+	})
+}
+
+// StartSuffixNotNil applies the NotNil predicate on the "start_suffix" field.
+func StartSuffixNotNil() predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.NotNull(s.C(FieldStartSuffix)))
+	})
+}
+
+// EndSuffixEQ applies the EQ predicate on the "end_suffix" field.
+func EndSuffixEQ(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldEndSuffix), v))
+	})
+}
+
+// EndSuffixNEQ applies the NEQ predicate on the "end_suffix" field.
+func EndSuffixNEQ(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.NEQ(s.C(FieldEndSuffix), v))
+	})
+}
+
+// EndSuffixIn applies the In predicate on the "end_suffix" field.
+func EndSuffixIn(vs ...int64) predicate.Decision {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.Decision(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(v) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.In(s.C(FieldEndSuffix), v...))
+	})
+}
+
+// EndSuffixNotIn applies the NotIn predicate on the "end_suffix" field.
+func EndSuffixNotIn(vs ...int64) predicate.Decision {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.Decision(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(v) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.NotIn(s.C(FieldEndSuffix), v...))
+	})
+}
+
+// EndSuffixGT applies the GT predicate on the "end_suffix" field.
+func EndSuffixGT(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.GT(s.C(FieldEndSuffix), v))
+	})
+}
+
+// EndSuffixGTE applies the GTE predicate on the "end_suffix" field.
+func EndSuffixGTE(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.GTE(s.C(FieldEndSuffix), v))
+	})
+}
+
+// EndSuffixLT applies the LT predicate on the "end_suffix" field.
+func EndSuffixLT(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.LT(s.C(FieldEndSuffix), v))
+	})
+}
+
+// EndSuffixLTE applies the LTE predicate on the "end_suffix" field.
+func EndSuffixLTE(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.LTE(s.C(FieldEndSuffix), v))
+	})
+}
+
+// EndSuffixIsNil applies the IsNil predicate on the "end_suffix" field.
+func EndSuffixIsNil() predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.IsNull(s.C(FieldEndSuffix)))
+	})
+}
+
+// EndSuffixNotNil applies the NotNil predicate on the "end_suffix" field.
+func EndSuffixNotNil() predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.NotNull(s.C(FieldEndSuffix)))
+	})
+}
+
+// IPSizeEQ applies the EQ predicate on the "ip_size" field.
+func IPSizeEQ(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldIPSize), v))
+	})
+}
+
+// IPSizeNEQ applies the NEQ predicate on the "ip_size" field.
+func IPSizeNEQ(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.NEQ(s.C(FieldIPSize), v))
+	})
+}
+
+// IPSizeIn applies the In predicate on the "ip_size" field.
+func IPSizeIn(vs ...int64) predicate.Decision {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.Decision(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(v) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.In(s.C(FieldIPSize), v...))
+	})
+}
+
+// IPSizeNotIn applies the NotIn predicate on the "ip_size" field.
+func IPSizeNotIn(vs ...int64) predicate.Decision {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.Decision(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(v) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.NotIn(s.C(FieldIPSize), v...))
+	})
+}
+
+// IPSizeGT applies the GT predicate on the "ip_size" field.
+func IPSizeGT(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.GT(s.C(FieldIPSize), v))
+	})
+}
+
+// IPSizeGTE applies the GTE predicate on the "ip_size" field.
+func IPSizeGTE(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.GTE(s.C(FieldIPSize), v))
+	})
+}
+
+// IPSizeLT applies the LT predicate on the "ip_size" field.
+func IPSizeLT(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.LT(s.C(FieldIPSize), v))
+	})
+}
+
+// IPSizeLTE applies the LTE predicate on the "ip_size" field.
+func IPSizeLTE(v int64) predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.LTE(s.C(FieldIPSize), v))
+	})
+}
+
+// IPSizeIsNil applies the IsNil predicate on the "ip_size" field.
+func IPSizeIsNil() predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.IsNull(s.C(FieldIPSize)))
+	})
+}
+
+// IPSizeNotNil applies the NotNil predicate on the "ip_size" field.
+func IPSizeNotNil() predicate.Decision {
+	return predicate.Decision(func(s *sql.Selector) {
+		s.Where(sql.NotNull(s.C(FieldIPSize)))
+	})
+}
+
 // ScopeEQ applies the EQ predicate on the "scope" field.
 // ScopeEQ applies the EQ predicate on the "scope" field.
 func ScopeEQ(v string) predicate.Decision {
 func ScopeEQ(v string) predicate.Decision {
 	return predicate.Decision(func(s *sql.Selector) {
 	return predicate.Decision(func(s *sql.Selector) {
@@ -1175,7 +1466,7 @@ func HasOwnerWith(preds ...predicate.Alert) predicate.Decision {
 	})
 	})
 }
 }
 
 
-// And groups list of predicates with the AND operator between them.
+// And groups predicates with the AND operator between them.
 func And(predicates ...predicate.Decision) predicate.Decision {
 func And(predicates ...predicate.Decision) predicate.Decision {
 	return predicate.Decision(func(s *sql.Selector) {
 	return predicate.Decision(func(s *sql.Selector) {
 		s1 := s.Clone().SetP(nil)
 		s1 := s.Clone().SetP(nil)
@@ -1186,7 +1477,7 @@ func And(predicates ...predicate.Decision) predicate.Decision {
 	})
 	})
 }
 }
 
 
-// Or groups list of predicates with the OR operator between them.
+// Or groups predicates with the OR operator between them.
 func Or(predicates ...predicate.Decision) predicate.Decision {
 func Or(predicates ...predicate.Decision) predicate.Decision {
 	return predicate.Decision(func(s *sql.Selector) {
 	return predicate.Decision(func(s *sql.Selector) {
 		s1 := s.Clone().SetP(nil)
 		s1 := s.Clone().SetP(nil)

+ 87 - 21
pkg/database/ent/decision_create.go

@@ -21,13 +21,13 @@ type DecisionCreate struct {
 	hooks    []Hook
 	hooks    []Hook
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (dc *DecisionCreate) SetCreatedAt(t time.Time) *DecisionCreate {
 func (dc *DecisionCreate) SetCreatedAt(t time.Time) *DecisionCreate {
 	dc.mutation.SetCreatedAt(t)
 	dc.mutation.SetCreatedAt(t)
 	return dc
 	return dc
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (dc *DecisionCreate) SetNillableCreatedAt(t *time.Time) *DecisionCreate {
 func (dc *DecisionCreate) SetNillableCreatedAt(t *time.Time) *DecisionCreate {
 	if t != nil {
 	if t != nil {
 		dc.SetCreatedAt(*t)
 		dc.SetCreatedAt(*t)
@@ -35,13 +35,13 @@ func (dc *DecisionCreate) SetNillableCreatedAt(t *time.Time) *DecisionCreate {
 	return dc
 	return dc
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (dc *DecisionCreate) SetUpdatedAt(t time.Time) *DecisionCreate {
 func (dc *DecisionCreate) SetUpdatedAt(t time.Time) *DecisionCreate {
 	dc.mutation.SetUpdatedAt(t)
 	dc.mutation.SetUpdatedAt(t)
 	return dc
 	return dc
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (dc *DecisionCreate) SetNillableUpdatedAt(t *time.Time) *DecisionCreate {
 func (dc *DecisionCreate) SetNillableUpdatedAt(t *time.Time) *DecisionCreate {
 	if t != nil {
 	if t != nil {
 		dc.SetUpdatedAt(*t)
 		dc.SetUpdatedAt(*t)
@@ -49,31 +49,31 @@ func (dc *DecisionCreate) SetNillableUpdatedAt(t *time.Time) *DecisionCreate {
 	return dc
 	return dc
 }
 }
 
 
-// SetUntil sets the until field.
+// SetUntil sets the "until" field.
 func (dc *DecisionCreate) SetUntil(t time.Time) *DecisionCreate {
 func (dc *DecisionCreate) SetUntil(t time.Time) *DecisionCreate {
 	dc.mutation.SetUntil(t)
 	dc.mutation.SetUntil(t)
 	return dc
 	return dc
 }
 }
 
 
-// SetScenario sets the scenario field.
+// SetScenario sets the "scenario" field.
 func (dc *DecisionCreate) SetScenario(s string) *DecisionCreate {
 func (dc *DecisionCreate) SetScenario(s string) *DecisionCreate {
 	dc.mutation.SetScenario(s)
 	dc.mutation.SetScenario(s)
 	return dc
 	return dc
 }
 }
 
 
-// SetType sets the type field.
+// SetType sets the "type" field.
 func (dc *DecisionCreate) SetType(s string) *DecisionCreate {
 func (dc *DecisionCreate) SetType(s string) *DecisionCreate {
 	dc.mutation.SetType(s)
 	dc.mutation.SetType(s)
 	return dc
 	return dc
 }
 }
 
 
-// SetStartIP sets the start_ip field.
+// SetStartIP sets the "start_ip" field.
 func (dc *DecisionCreate) SetStartIP(i int64) *DecisionCreate {
 func (dc *DecisionCreate) SetStartIP(i int64) *DecisionCreate {
 	dc.mutation.SetStartIP(i)
 	dc.mutation.SetStartIP(i)
 	return dc
 	return dc
 }
 }
 
 
-// SetNillableStartIP sets the start_ip field if the given value is not nil.
+// SetNillableStartIP sets the "start_ip" field if the given value is not nil.
 func (dc *DecisionCreate) SetNillableStartIP(i *int64) *DecisionCreate {
 func (dc *DecisionCreate) SetNillableStartIP(i *int64) *DecisionCreate {
 	if i != nil {
 	if i != nil {
 		dc.SetStartIP(*i)
 		dc.SetStartIP(*i)
@@ -81,13 +81,13 @@ func (dc *DecisionCreate) SetNillableStartIP(i *int64) *DecisionCreate {
 	return dc
 	return dc
 }
 }
 
 
-// SetEndIP sets the end_ip field.
+// SetEndIP sets the "end_ip" field.
 func (dc *DecisionCreate) SetEndIP(i int64) *DecisionCreate {
 func (dc *DecisionCreate) SetEndIP(i int64) *DecisionCreate {
 	dc.mutation.SetEndIP(i)
 	dc.mutation.SetEndIP(i)
 	return dc
 	return dc
 }
 }
 
 
-// SetNillableEndIP sets the end_ip field if the given value is not nil.
+// SetNillableEndIP sets the "end_ip" field if the given value is not nil.
 func (dc *DecisionCreate) SetNillableEndIP(i *int64) *DecisionCreate {
 func (dc *DecisionCreate) SetNillableEndIP(i *int64) *DecisionCreate {
 	if i != nil {
 	if i != nil {
 		dc.SetEndIP(*i)
 		dc.SetEndIP(*i)
@@ -95,31 +95,73 @@ func (dc *DecisionCreate) SetNillableEndIP(i *int64) *DecisionCreate {
 	return dc
 	return dc
 }
 }
 
 
-// SetScope sets the scope field.
+// SetStartSuffix sets the "start_suffix" field.
+func (dc *DecisionCreate) SetStartSuffix(i int64) *DecisionCreate {
+	dc.mutation.SetStartSuffix(i)
+	return dc
+}
+
+// SetNillableStartSuffix sets the "start_suffix" field if the given value is not nil.
+func (dc *DecisionCreate) SetNillableStartSuffix(i *int64) *DecisionCreate {
+	if i != nil {
+		dc.SetStartSuffix(*i)
+	}
+	return dc
+}
+
+// SetEndSuffix sets the "end_suffix" field.
+func (dc *DecisionCreate) SetEndSuffix(i int64) *DecisionCreate {
+	dc.mutation.SetEndSuffix(i)
+	return dc
+}
+
+// SetNillableEndSuffix sets the "end_suffix" field if the given value is not nil.
+func (dc *DecisionCreate) SetNillableEndSuffix(i *int64) *DecisionCreate {
+	if i != nil {
+		dc.SetEndSuffix(*i)
+	}
+	return dc
+}
+
+// SetIPSize sets the "ip_size" field.
+func (dc *DecisionCreate) SetIPSize(i int64) *DecisionCreate {
+	dc.mutation.SetIPSize(i)
+	return dc
+}
+
+// SetNillableIPSize sets the "ip_size" field if the given value is not nil.
+func (dc *DecisionCreate) SetNillableIPSize(i *int64) *DecisionCreate {
+	if i != nil {
+		dc.SetIPSize(*i)
+	}
+	return dc
+}
+
+// SetScope sets the "scope" field.
 func (dc *DecisionCreate) SetScope(s string) *DecisionCreate {
 func (dc *DecisionCreate) SetScope(s string) *DecisionCreate {
 	dc.mutation.SetScope(s)
 	dc.mutation.SetScope(s)
 	return dc
 	return dc
 }
 }
 
 
-// SetValue sets the value field.
+// SetValue sets the "value" field.
 func (dc *DecisionCreate) SetValue(s string) *DecisionCreate {
 func (dc *DecisionCreate) SetValue(s string) *DecisionCreate {
 	dc.mutation.SetValue(s)
 	dc.mutation.SetValue(s)
 	return dc
 	return dc
 }
 }
 
 
-// SetOrigin sets the origin field.
+// SetOrigin sets the "origin" field.
 func (dc *DecisionCreate) SetOrigin(s string) *DecisionCreate {
 func (dc *DecisionCreate) SetOrigin(s string) *DecisionCreate {
 	dc.mutation.SetOrigin(s)
 	dc.mutation.SetOrigin(s)
 	return dc
 	return dc
 }
 }
 
 
-// SetSimulated sets the simulated field.
+// SetSimulated sets the "simulated" field.
 func (dc *DecisionCreate) SetSimulated(b bool) *DecisionCreate {
 func (dc *DecisionCreate) SetSimulated(b bool) *DecisionCreate {
 	dc.mutation.SetSimulated(b)
 	dc.mutation.SetSimulated(b)
 	return dc
 	return dc
 }
 }
 
 
-// SetNillableSimulated sets the simulated field if the given value is not nil.
+// SetNillableSimulated sets the "simulated" field if the given value is not nil.
 func (dc *DecisionCreate) SetNillableSimulated(b *bool) *DecisionCreate {
 func (dc *DecisionCreate) SetNillableSimulated(b *bool) *DecisionCreate {
 	if b != nil {
 	if b != nil {
 		dc.SetSimulated(*b)
 		dc.SetSimulated(*b)
@@ -127,13 +169,13 @@ func (dc *DecisionCreate) SetNillableSimulated(b *bool) *DecisionCreate {
 	return dc
 	return dc
 }
 }
 
 
-// SetOwnerID sets the owner edge to Alert by id.
+// SetOwnerID sets the "owner" edge to the Alert entity by ID.
 func (dc *DecisionCreate) SetOwnerID(id int) *DecisionCreate {
 func (dc *DecisionCreate) SetOwnerID(id int) *DecisionCreate {
 	dc.mutation.SetOwnerID(id)
 	dc.mutation.SetOwnerID(id)
 	return dc
 	return dc
 }
 }
 
 
-// SetNillableOwnerID sets the owner edge to Alert by id if the given value is not nil.
+// SetNillableOwnerID sets the "owner" edge to the Alert entity by ID if the given value is not nil.
 func (dc *DecisionCreate) SetNillableOwnerID(id *int) *DecisionCreate {
 func (dc *DecisionCreate) SetNillableOwnerID(id *int) *DecisionCreate {
 	if id != nil {
 	if id != nil {
 		dc = dc.SetOwnerID(*id)
 		dc = dc.SetOwnerID(*id)
@@ -141,7 +183,7 @@ func (dc *DecisionCreate) SetNillableOwnerID(id *int) *DecisionCreate {
 	return dc
 	return dc
 }
 }
 
 
-// SetOwner sets the owner edge to Alert.
+// SetOwner sets the "owner" edge to the Alert entity.
 func (dc *DecisionCreate) SetOwner(a *Alert) *DecisionCreate {
 func (dc *DecisionCreate) SetOwner(a *Alert) *DecisionCreate {
 	return dc.SetOwnerID(a.ID)
 	return dc.SetOwnerID(a.ID)
 }
 }
@@ -324,6 +366,30 @@ func (dc *DecisionCreate) createSpec() (*Decision, *sqlgraph.CreateSpec) {
 		})
 		})
 		_node.EndIP = value
 		_node.EndIP = value
 	}
 	}
+	if value, ok := dc.mutation.StartSuffix(); ok {
+		_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldStartSuffix,
+		})
+		_node.StartSuffix = value
+	}
+	if value, ok := dc.mutation.EndSuffix(); ok {
+		_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldEndSuffix,
+		})
+		_node.EndSuffix = value
+	}
+	if value, ok := dc.mutation.IPSize(); ok {
+		_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldIPSize,
+		})
+		_node.IPSize = value
+	}
 	if value, ok := dc.mutation.Scope(); ok {
 	if value, ok := dc.mutation.Scope(); ok {
 		_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
 		_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
 			Type:   field.TypeString,
 			Type:   field.TypeString,
@@ -378,7 +444,7 @@ func (dc *DecisionCreate) createSpec() (*Decision, *sqlgraph.CreateSpec) {
 	return _node, _spec
 	return _node, _spec
 }
 }
 
 
-// DecisionCreateBulk is the builder for creating a bulk of Decision entities.
+// DecisionCreateBulk is the builder for creating many Decision entities in bulk.
 type DecisionCreateBulk struct {
 type DecisionCreateBulk struct {
 	config
 	config
 	builders []*DecisionCreate
 	builders []*DecisionCreate
@@ -436,7 +502,7 @@ func (dcb *DecisionCreateBulk) Save(ctx context.Context) ([]*Decision, error) {
 	return nodes, nil
 	return nodes, nil
 }
 }
 
 
-// SaveX calls Save and panics if Save returns an error.
+// SaveX is like Save, but panics if an error occurs.
 func (dcb *DecisionCreateBulk) SaveX(ctx context.Context) []*Decision {
 func (dcb *DecisionCreateBulk) SaveX(ctx context.Context) []*Decision {
 	v, err := dcb.Save(ctx)
 	v, err := dcb.Save(ctx)
 	if err != nil {
 	if err != nil {

+ 5 - 6
pkg/database/ent/decision_delete.go

@@ -16,14 +16,13 @@ import (
 // DecisionDelete is the builder for deleting a Decision entity.
 // DecisionDelete is the builder for deleting a Decision entity.
 type DecisionDelete struct {
 type DecisionDelete struct {
 	config
 	config
-	hooks      []Hook
-	mutation   *DecisionMutation
-	predicates []predicate.Decision
+	hooks    []Hook
+	mutation *DecisionMutation
 }
 }
 
 
-// Where adds a new predicate to the delete builder.
+// Where adds a new predicate to the DecisionDelete builder.
 func (dd *DecisionDelete) Where(ps ...predicate.Decision) *DecisionDelete {
 func (dd *DecisionDelete) Where(ps ...predicate.Decision) *DecisionDelete {
-	dd.predicates = append(dd.predicates, ps...)
+	dd.mutation.predicates = append(dd.mutation.predicates, ps...)
 	return dd
 	return dd
 }
 }
 
 
@@ -75,7 +74,7 @@ func (dd *DecisionDelete) sqlExec(ctx context.Context) (int, error) {
 			},
 			},
 		},
 		},
 	}
 	}
-	if ps := dd.predicates; len(ps) > 0 {
+	if ps := dd.mutation.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
 				ps[i](selector)
 				ps[i](selector)

+ 78 - 65
pkg/database/ent/decision_query.go

@@ -22,7 +22,7 @@ type DecisionQuery struct {
 	limit      *int
 	limit      *int
 	offset     *int
 	offset     *int
 	order      []OrderFunc
 	order      []OrderFunc
-	unique     []string
+	fields     []string
 	predicates []predicate.Decision
 	predicates []predicate.Decision
 	// eager-loading edges.
 	// eager-loading edges.
 	withOwner *AlertQuery
 	withOwner *AlertQuery
@@ -32,7 +32,7 @@ type DecisionQuery struct {
 	path func(context.Context) (*sql.Selector, error)
 	path func(context.Context) (*sql.Selector, error)
 }
 }
 
 
-// Where adds a new predicate for the builder.
+// Where adds a new predicate for the DecisionQuery builder.
 func (dq *DecisionQuery) Where(ps ...predicate.Decision) *DecisionQuery {
 func (dq *DecisionQuery) Where(ps ...predicate.Decision) *DecisionQuery {
 	dq.predicates = append(dq.predicates, ps...)
 	dq.predicates = append(dq.predicates, ps...)
 	return dq
 	return dq
@@ -56,7 +56,7 @@ func (dq *DecisionQuery) Order(o ...OrderFunc) *DecisionQuery {
 	return dq
 	return dq
 }
 }
 
 
-// QueryOwner chains the current query on the owner edge.
+// QueryOwner chains the current query on the "owner" edge.
 func (dq *DecisionQuery) QueryOwner() *AlertQuery {
 func (dq *DecisionQuery) QueryOwner() *AlertQuery {
 	query := &AlertQuery{config: dq.config}
 	query := &AlertQuery{config: dq.config}
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
@@ -78,7 +78,8 @@ func (dq *DecisionQuery) QueryOwner() *AlertQuery {
 	return query
 	return query
 }
 }
 
 
-// First returns the first Decision entity in the query. Returns *NotFoundError when no decision was found.
+// First returns the first Decision entity from the query.
+// Returns a *NotFoundError when no Decision was found.
 func (dq *DecisionQuery) First(ctx context.Context) (*Decision, error) {
 func (dq *DecisionQuery) First(ctx context.Context) (*Decision, error) {
 	nodes, err := dq.Limit(1).All(ctx)
 	nodes, err := dq.Limit(1).All(ctx)
 	if err != nil {
 	if err != nil {
@@ -99,7 +100,8 @@ func (dq *DecisionQuery) FirstX(ctx context.Context) *Decision {
 	return node
 	return node
 }
 }
 
 
-// FirstID returns the first Decision id in the query. Returns *NotFoundError when no id was found.
+// FirstID returns the first Decision ID from the query.
+// Returns a *NotFoundError when no Decision ID was found.
 func (dq *DecisionQuery) FirstID(ctx context.Context) (id int, err error) {
 func (dq *DecisionQuery) FirstID(ctx context.Context) (id int, err error) {
 	var ids []int
 	var ids []int
 	if ids, err = dq.Limit(1).IDs(ctx); err != nil {
 	if ids, err = dq.Limit(1).IDs(ctx); err != nil {
@@ -112,8 +114,8 @@ func (dq *DecisionQuery) FirstID(ctx context.Context) (id int, err error) {
 	return ids[0], nil
 	return ids[0], nil
 }
 }
 
 
-// FirstXID is like FirstID, but panics if an error occurs.
-func (dq *DecisionQuery) FirstXID(ctx context.Context) int {
+// FirstIDX is like FirstID, but panics if an error occurs.
+func (dq *DecisionQuery) FirstIDX(ctx context.Context) int {
 	id, err := dq.FirstID(ctx)
 	id, err := dq.FirstID(ctx)
 	if err != nil && !IsNotFound(err) {
 	if err != nil && !IsNotFound(err) {
 		panic(err)
 		panic(err)
@@ -121,7 +123,9 @@ func (dq *DecisionQuery) FirstXID(ctx context.Context) int {
 	return id
 	return id
 }
 }
 
 
-// Only returns the only Decision entity in the query, returns an error if not exactly one entity was returned.
+// Only returns a single Decision entity found by the query, ensuring it only returns one.
+// Returns a *NotSingularError when exactly one Decision entity is not found.
+// Returns a *NotFoundError when no Decision entities are found.
 func (dq *DecisionQuery) Only(ctx context.Context) (*Decision, error) {
 func (dq *DecisionQuery) Only(ctx context.Context) (*Decision, error) {
 	nodes, err := dq.Limit(2).All(ctx)
 	nodes, err := dq.Limit(2).All(ctx)
 	if err != nil {
 	if err != nil {
@@ -146,7 +150,9 @@ func (dq *DecisionQuery) OnlyX(ctx context.Context) *Decision {
 	return node
 	return node
 }
 }
 
 
-// OnlyID returns the only Decision id in the query, returns an error if not exactly one id was returned.
+// OnlyID is like Only, but returns the only Decision ID in the query.
+// Returns a *NotSingularError when exactly one Decision ID is not found.
+// Returns a *NotFoundError when no entities are found.
 func (dq *DecisionQuery) OnlyID(ctx context.Context) (id int, err error) {
 func (dq *DecisionQuery) OnlyID(ctx context.Context) (id int, err error) {
 	var ids []int
 	var ids []int
 	if ids, err = dq.Limit(2).IDs(ctx); err != nil {
 	if ids, err = dq.Limit(2).IDs(ctx); err != nil {
@@ -189,7 +195,7 @@ func (dq *DecisionQuery) AllX(ctx context.Context) []*Decision {
 	return nodes
 	return nodes
 }
 }
 
 
-// IDs executes the query and returns a list of Decision ids.
+// IDs executes the query and returns a list of Decision IDs.
 func (dq *DecisionQuery) IDs(ctx context.Context) ([]int, error) {
 func (dq *DecisionQuery) IDs(ctx context.Context) ([]int, error) {
 	var ids []int
 	var ids []int
 	if err := dq.Select(decision.FieldID).Scan(ctx, &ids); err != nil {
 	if err := dq.Select(decision.FieldID).Scan(ctx, &ids); err != nil {
@@ -241,24 +247,27 @@ func (dq *DecisionQuery) ExistX(ctx context.Context) bool {
 	return exist
 	return exist
 }
 }
 
 
-// Clone returns a duplicate of the query builder, including all associated steps. It can be
+// Clone returns a duplicate of the DecisionQuery builder, including all associated steps. It can be
 // used to prepare common query builders and use them differently after the clone is made.
 // used to prepare common query builders and use them differently after the clone is made.
 func (dq *DecisionQuery) Clone() *DecisionQuery {
 func (dq *DecisionQuery) Clone() *DecisionQuery {
+	if dq == nil {
+		return nil
+	}
 	return &DecisionQuery{
 	return &DecisionQuery{
 		config:     dq.config,
 		config:     dq.config,
 		limit:      dq.limit,
 		limit:      dq.limit,
 		offset:     dq.offset,
 		offset:     dq.offset,
 		order:      append([]OrderFunc{}, dq.order...),
 		order:      append([]OrderFunc{}, dq.order...),
-		unique:     append([]string{}, dq.unique...),
 		predicates: append([]predicate.Decision{}, dq.predicates...),
 		predicates: append([]predicate.Decision{}, dq.predicates...),
+		withOwner:  dq.withOwner.Clone(),
 		// clone intermediate query.
 		// clone intermediate query.
 		sql:  dq.sql.Clone(),
 		sql:  dq.sql.Clone(),
 		path: dq.path,
 		path: dq.path,
 	}
 	}
 }
 }
 
 
-//  WithOwner tells the query-builder to eager-loads the nodes that are connected to
-// the "owner" edge. The optional arguments used to configure the query builder of the edge.
+// WithOwner tells the query-builder to eager-load the nodes that are connected to
+// the "owner" edge. The optional arguments are used to configure the query builder of the edge.
 func (dq *DecisionQuery) WithOwner(opts ...func(*AlertQuery)) *DecisionQuery {
 func (dq *DecisionQuery) WithOwner(opts ...func(*AlertQuery)) *DecisionQuery {
 	query := &AlertQuery{config: dq.config}
 	query := &AlertQuery{config: dq.config}
 	for _, opt := range opts {
 	for _, opt := range opts {
@@ -268,7 +277,7 @@ func (dq *DecisionQuery) WithOwner(opts ...func(*AlertQuery)) *DecisionQuery {
 	return dq
 	return dq
 }
 }
 
 
-// GroupBy used to group vertices by one or more fields/columns.
+// GroupBy is used to group vertices by one or more fields/columns.
 // It is often used with aggregate functions, like: count, max, mean, min, sum.
 // It is often used with aggregate functions, like: count, max, mean, min, sum.
 //
 //
 // Example:
 // Example:
@@ -295,7 +304,8 @@ func (dq *DecisionQuery) GroupBy(field string, fields ...string) *DecisionGroupB
 	return group
 	return group
 }
 }
 
 
-// Select one or more fields from the given query.
+// Select allows the selection one or more fields/columns for the given query,
+// instead of selecting all fields in the entity.
 //
 //
 // Example:
 // Example:
 //
 //
@@ -308,18 +318,16 @@ func (dq *DecisionQuery) GroupBy(field string, fields ...string) *DecisionGroupB
 //		Scan(ctx, &v)
 //		Scan(ctx, &v)
 //
 //
 func (dq *DecisionQuery) Select(field string, fields ...string) *DecisionSelect {
 func (dq *DecisionQuery) Select(field string, fields ...string) *DecisionSelect {
-	selector := &DecisionSelect{config: dq.config}
-	selector.fields = append([]string{field}, fields...)
-	selector.path = func(ctx context.Context) (prev *sql.Selector, err error) {
-		if err := dq.prepareQuery(ctx); err != nil {
-			return nil, err
-		}
-		return dq.sqlQuery(), nil
-	}
-	return selector
+	dq.fields = append([]string{field}, fields...)
+	return &DecisionSelect{DecisionQuery: dq}
 }
 }
 
 
 func (dq *DecisionQuery) prepareQuery(ctx context.Context) error {
 func (dq *DecisionQuery) prepareQuery(ctx context.Context) error {
+	for _, f := range dq.fields {
+		if !decision.ValidColumn(f) {
+			return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+		}
+	}
 	if dq.path != nil {
 	if dq.path != nil {
 		prev, err := dq.path(ctx)
 		prev, err := dq.path(ctx)
 		if err != nil {
 		if err != nil {
@@ -345,22 +353,18 @@ func (dq *DecisionQuery) sqlAll(ctx context.Context) ([]*Decision, error) {
 	if withFKs {
 	if withFKs {
 		_spec.Node.Columns = append(_spec.Node.Columns, decision.ForeignKeys...)
 		_spec.Node.Columns = append(_spec.Node.Columns, decision.ForeignKeys...)
 	}
 	}
-	_spec.ScanValues = func() []interface{} {
+	_spec.ScanValues = func(columns []string) ([]interface{}, error) {
 		node := &Decision{config: dq.config}
 		node := &Decision{config: dq.config}
 		nodes = append(nodes, node)
 		nodes = append(nodes, node)
-		values := node.scanValues()
-		if withFKs {
-			values = append(values, node.fkValues()...)
-		}
-		return values
+		return node.scanValues(columns)
 	}
 	}
-	_spec.Assign = func(values ...interface{}) error {
+	_spec.Assign = func(columns []string, values []interface{}) error {
 		if len(nodes) == 0 {
 		if len(nodes) == 0 {
 			return fmt.Errorf("ent: Assign called without calling ScanValues")
 			return fmt.Errorf("ent: Assign called without calling ScanValues")
 		}
 		}
 		node := nodes[len(nodes)-1]
 		node := nodes[len(nodes)-1]
 		node.Edges.loadedTypes = loadedTypes
 		node.Edges.loadedTypes = loadedTypes
-		return node.assignValues(values...)
+		return node.assignValues(columns, values)
 	}
 	}
 	if err := sqlgraph.QueryNodes(ctx, dq.driver, _spec); err != nil {
 	if err := sqlgraph.QueryNodes(ctx, dq.driver, _spec); err != nil {
 		return nil, err
 		return nil, err
@@ -423,6 +427,15 @@ func (dq *DecisionQuery) querySpec() *sqlgraph.QuerySpec {
 		From:   dq.sql,
 		From:   dq.sql,
 		Unique: true,
 		Unique: true,
 	}
 	}
+	if fields := dq.fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, decision.FieldID)
+		for i := range fields {
+			if fields[i] != decision.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
+			}
+		}
+	}
 	if ps := dq.predicates; len(ps) > 0 {
 	if ps := dq.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
@@ -471,7 +484,7 @@ func (dq *DecisionQuery) sqlQuery() *sql.Selector {
 	return selector
 	return selector
 }
 }
 
 
-// DecisionGroupBy is the builder for group-by Decision entities.
+// DecisionGroupBy is the group-by builder for Decision entities.
 type DecisionGroupBy struct {
 type DecisionGroupBy struct {
 	config
 	config
 	fields []string
 	fields []string
@@ -487,7 +500,7 @@ func (dgb *DecisionGroupBy) Aggregate(fns ...AggregateFunc) *DecisionGroupBy {
 	return dgb
 	return dgb
 }
 }
 
 
-// Scan applies the group-by query and scan the result into the given value.
+// Scan applies the group-by query and scans the result into the given value.
 func (dgb *DecisionGroupBy) Scan(ctx context.Context, v interface{}) error {
 func (dgb *DecisionGroupBy) Scan(ctx context.Context, v interface{}) error {
 	query, err := dgb.path(ctx)
 	query, err := dgb.path(ctx)
 	if err != nil {
 	if err != nil {
@@ -504,7 +517,8 @@ func (dgb *DecisionGroupBy) ScanX(ctx context.Context, v interface{}) {
 	}
 	}
 }
 }
 
 
-// Strings returns list of strings from group-by. It is only allowed when querying group-by with one field.
+// Strings returns list of strings from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (dgb *DecisionGroupBy) Strings(ctx context.Context) ([]string, error) {
 func (dgb *DecisionGroupBy) Strings(ctx context.Context) ([]string, error) {
 	if len(dgb.fields) > 1 {
 	if len(dgb.fields) > 1 {
 		return nil, errors.New("ent: DecisionGroupBy.Strings is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: DecisionGroupBy.Strings is not achievable when grouping more than 1 field")
@@ -525,7 +539,8 @@ func (dgb *DecisionGroupBy) StringsX(ctx context.Context) []string {
 	return v
 	return v
 }
 }
 
 
-// String returns a single string from group-by. It is only allowed when querying group-by with one field.
+// String returns a single string from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (dgb *DecisionGroupBy) String(ctx context.Context) (_ string, err error) {
 func (dgb *DecisionGroupBy) String(ctx context.Context) (_ string, err error) {
 	var v []string
 	var v []string
 	if v, err = dgb.Strings(ctx); err != nil {
 	if v, err = dgb.Strings(ctx); err != nil {
@@ -551,7 +566,8 @@ func (dgb *DecisionGroupBy) StringX(ctx context.Context) string {
 	return v
 	return v
 }
 }
 
 
-// Ints returns list of ints from group-by. It is only allowed when querying group-by with one field.
+// Ints returns list of ints from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (dgb *DecisionGroupBy) Ints(ctx context.Context) ([]int, error) {
 func (dgb *DecisionGroupBy) Ints(ctx context.Context) ([]int, error) {
 	if len(dgb.fields) > 1 {
 	if len(dgb.fields) > 1 {
 		return nil, errors.New("ent: DecisionGroupBy.Ints is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: DecisionGroupBy.Ints is not achievable when grouping more than 1 field")
@@ -572,7 +588,8 @@ func (dgb *DecisionGroupBy) IntsX(ctx context.Context) []int {
 	return v
 	return v
 }
 }
 
 
-// Int returns a single int from group-by. It is only allowed when querying group-by with one field.
+// Int returns a single int from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (dgb *DecisionGroupBy) Int(ctx context.Context) (_ int, err error) {
 func (dgb *DecisionGroupBy) Int(ctx context.Context) (_ int, err error) {
 	var v []int
 	var v []int
 	if v, err = dgb.Ints(ctx); err != nil {
 	if v, err = dgb.Ints(ctx); err != nil {
@@ -598,7 +615,8 @@ func (dgb *DecisionGroupBy) IntX(ctx context.Context) int {
 	return v
 	return v
 }
 }
 
 
-// Float64s returns list of float64s from group-by. It is only allowed when querying group-by with one field.
+// Float64s returns list of float64s from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (dgb *DecisionGroupBy) Float64s(ctx context.Context) ([]float64, error) {
 func (dgb *DecisionGroupBy) Float64s(ctx context.Context) ([]float64, error) {
 	if len(dgb.fields) > 1 {
 	if len(dgb.fields) > 1 {
 		return nil, errors.New("ent: DecisionGroupBy.Float64s is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: DecisionGroupBy.Float64s is not achievable when grouping more than 1 field")
@@ -619,7 +637,8 @@ func (dgb *DecisionGroupBy) Float64sX(ctx context.Context) []float64 {
 	return v
 	return v
 }
 }
 
 
-// Float64 returns a single float64 from group-by. It is only allowed when querying group-by with one field.
+// Float64 returns a single float64 from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (dgb *DecisionGroupBy) Float64(ctx context.Context) (_ float64, err error) {
 func (dgb *DecisionGroupBy) Float64(ctx context.Context) (_ float64, err error) {
 	var v []float64
 	var v []float64
 	if v, err = dgb.Float64s(ctx); err != nil {
 	if v, err = dgb.Float64s(ctx); err != nil {
@@ -645,7 +664,8 @@ func (dgb *DecisionGroupBy) Float64X(ctx context.Context) float64 {
 	return v
 	return v
 }
 }
 
 
-// Bools returns list of bools from group-by. It is only allowed when querying group-by with one field.
+// Bools returns list of bools from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (dgb *DecisionGroupBy) Bools(ctx context.Context) ([]bool, error) {
 func (dgb *DecisionGroupBy) Bools(ctx context.Context) ([]bool, error) {
 	if len(dgb.fields) > 1 {
 	if len(dgb.fields) > 1 {
 		return nil, errors.New("ent: DecisionGroupBy.Bools is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: DecisionGroupBy.Bools is not achievable when grouping more than 1 field")
@@ -666,7 +686,8 @@ func (dgb *DecisionGroupBy) BoolsX(ctx context.Context) []bool {
 	return v
 	return v
 }
 }
 
 
-// Bool returns a single bool from group-by. It is only allowed when querying group-by with one field.
+// Bool returns a single bool from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (dgb *DecisionGroupBy) Bool(ctx context.Context) (_ bool, err error) {
 func (dgb *DecisionGroupBy) Bool(ctx context.Context) (_ bool, err error) {
 	var v []bool
 	var v []bool
 	if v, err = dgb.Bools(ctx); err != nil {
 	if v, err = dgb.Bools(ctx); err != nil {
@@ -721,22 +742,19 @@ func (dgb *DecisionGroupBy) sqlQuery() *sql.Selector {
 	return selector.Select(columns...).GroupBy(dgb.fields...)
 	return selector.Select(columns...).GroupBy(dgb.fields...)
 }
 }
 
 
-// DecisionSelect is the builder for select fields of Decision entities.
+// DecisionSelect is the builder for selecting fields of Decision entities.
 type DecisionSelect struct {
 type DecisionSelect struct {
-	config
-	fields []string
+	*DecisionQuery
 	// intermediate query (i.e. traversal path).
 	// intermediate query (i.e. traversal path).
-	sql  *sql.Selector
-	path func(context.Context) (*sql.Selector, error)
+	sql *sql.Selector
 }
 }
 
 
-// Scan applies the selector query and scan the result into the given value.
+// Scan applies the selector query and scans the result into the given value.
 func (ds *DecisionSelect) Scan(ctx context.Context, v interface{}) error {
 func (ds *DecisionSelect) Scan(ctx context.Context, v interface{}) error {
-	query, err := ds.path(ctx)
-	if err != nil {
+	if err := ds.prepareQuery(ctx); err != nil {
 		return err
 		return err
 	}
 	}
-	ds.sql = query
+	ds.sql = ds.DecisionQuery.sqlQuery()
 	return ds.sqlScan(ctx, v)
 	return ds.sqlScan(ctx, v)
 }
 }
 
 
@@ -747,7 +765,7 @@ func (ds *DecisionSelect) ScanX(ctx context.Context, v interface{}) {
 	}
 	}
 }
 }
 
 
-// Strings returns list of strings from selector. It is only allowed when selecting one field.
+// Strings returns list of strings from a selector. It is only allowed when selecting one field.
 func (ds *DecisionSelect) Strings(ctx context.Context) ([]string, error) {
 func (ds *DecisionSelect) Strings(ctx context.Context) ([]string, error) {
 	if len(ds.fields) > 1 {
 	if len(ds.fields) > 1 {
 		return nil, errors.New("ent: DecisionSelect.Strings is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: DecisionSelect.Strings is not achievable when selecting more than 1 field")
@@ -768,7 +786,7 @@ func (ds *DecisionSelect) StringsX(ctx context.Context) []string {
 	return v
 	return v
 }
 }
 
 
-// String returns a single string from selector. It is only allowed when selecting one field.
+// String returns a single string from a selector. It is only allowed when selecting one field.
 func (ds *DecisionSelect) String(ctx context.Context) (_ string, err error) {
 func (ds *DecisionSelect) String(ctx context.Context) (_ string, err error) {
 	var v []string
 	var v []string
 	if v, err = ds.Strings(ctx); err != nil {
 	if v, err = ds.Strings(ctx); err != nil {
@@ -794,7 +812,7 @@ func (ds *DecisionSelect) StringX(ctx context.Context) string {
 	return v
 	return v
 }
 }
 
 
-// Ints returns list of ints from selector. It is only allowed when selecting one field.
+// Ints returns list of ints from a selector. It is only allowed when selecting one field.
 func (ds *DecisionSelect) Ints(ctx context.Context) ([]int, error) {
 func (ds *DecisionSelect) Ints(ctx context.Context) ([]int, error) {
 	if len(ds.fields) > 1 {
 	if len(ds.fields) > 1 {
 		return nil, errors.New("ent: DecisionSelect.Ints is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: DecisionSelect.Ints is not achievable when selecting more than 1 field")
@@ -815,7 +833,7 @@ func (ds *DecisionSelect) IntsX(ctx context.Context) []int {
 	return v
 	return v
 }
 }
 
 
-// Int returns a single int from selector. It is only allowed when selecting one field.
+// Int returns a single int from a selector. It is only allowed when selecting one field.
 func (ds *DecisionSelect) Int(ctx context.Context) (_ int, err error) {
 func (ds *DecisionSelect) Int(ctx context.Context) (_ int, err error) {
 	var v []int
 	var v []int
 	if v, err = ds.Ints(ctx); err != nil {
 	if v, err = ds.Ints(ctx); err != nil {
@@ -841,7 +859,7 @@ func (ds *DecisionSelect) IntX(ctx context.Context) int {
 	return v
 	return v
 }
 }
 
 
-// Float64s returns list of float64s from selector. It is only allowed when selecting one field.
+// Float64s returns list of float64s from a selector. It is only allowed when selecting one field.
 func (ds *DecisionSelect) Float64s(ctx context.Context) ([]float64, error) {
 func (ds *DecisionSelect) Float64s(ctx context.Context) ([]float64, error) {
 	if len(ds.fields) > 1 {
 	if len(ds.fields) > 1 {
 		return nil, errors.New("ent: DecisionSelect.Float64s is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: DecisionSelect.Float64s is not achievable when selecting more than 1 field")
@@ -862,7 +880,7 @@ func (ds *DecisionSelect) Float64sX(ctx context.Context) []float64 {
 	return v
 	return v
 }
 }
 
 
-// Float64 returns a single float64 from selector. It is only allowed when selecting one field.
+// Float64 returns a single float64 from a selector. It is only allowed when selecting one field.
 func (ds *DecisionSelect) Float64(ctx context.Context) (_ float64, err error) {
 func (ds *DecisionSelect) Float64(ctx context.Context) (_ float64, err error) {
 	var v []float64
 	var v []float64
 	if v, err = ds.Float64s(ctx); err != nil {
 	if v, err = ds.Float64s(ctx); err != nil {
@@ -888,7 +906,7 @@ func (ds *DecisionSelect) Float64X(ctx context.Context) float64 {
 	return v
 	return v
 }
 }
 
 
-// Bools returns list of bools from selector. It is only allowed when selecting one field.
+// Bools returns list of bools from a selector. It is only allowed when selecting one field.
 func (ds *DecisionSelect) Bools(ctx context.Context) ([]bool, error) {
 func (ds *DecisionSelect) Bools(ctx context.Context) ([]bool, error) {
 	if len(ds.fields) > 1 {
 	if len(ds.fields) > 1 {
 		return nil, errors.New("ent: DecisionSelect.Bools is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: DecisionSelect.Bools is not achievable when selecting more than 1 field")
@@ -909,7 +927,7 @@ func (ds *DecisionSelect) BoolsX(ctx context.Context) []bool {
 	return v
 	return v
 }
 }
 
 
-// Bool returns a single bool from selector. It is only allowed when selecting one field.
+// Bool returns a single bool from a selector. It is only allowed when selecting one field.
 func (ds *DecisionSelect) Bool(ctx context.Context) (_ bool, err error) {
 func (ds *DecisionSelect) Bool(ctx context.Context) (_ bool, err error) {
 	var v []bool
 	var v []bool
 	if v, err = ds.Bools(ctx); err != nil {
 	if v, err = ds.Bools(ctx); err != nil {
@@ -936,11 +954,6 @@ func (ds *DecisionSelect) BoolX(ctx context.Context) bool {
 }
 }
 
 
 func (ds *DecisionSelect) sqlScan(ctx context.Context, v interface{}) error {
 func (ds *DecisionSelect) sqlScan(ctx context.Context, v interface{}) error {
-	for _, f := range ds.fields {
-		if !decision.ValidColumn(f) {
-			return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for selection", f)}
-		}
-	}
 	rows := &sql.Rows{}
 	rows := &sql.Rows{}
 	query, args := ds.sqlQuery().Query()
 	query, args := ds.sqlQuery().Query()
 	if err := ds.driver.Query(ctx, query, args, rows); err != nil {
 	if err := ds.driver.Query(ctx, query, args, rows); err != nil {

+ 338 - 57
pkg/database/ent/decision_update.go

@@ -18,24 +18,23 @@ import (
 // DecisionUpdate is the builder for updating Decision entities.
 // DecisionUpdate is the builder for updating Decision entities.
 type DecisionUpdate struct {
 type DecisionUpdate struct {
 	config
 	config
-	hooks      []Hook
-	mutation   *DecisionMutation
-	predicates []predicate.Decision
+	hooks    []Hook
+	mutation *DecisionMutation
 }
 }
 
 
-// Where adds a new predicate for the builder.
+// Where adds a new predicate for the DecisionUpdate builder.
 func (du *DecisionUpdate) Where(ps ...predicate.Decision) *DecisionUpdate {
 func (du *DecisionUpdate) Where(ps ...predicate.Decision) *DecisionUpdate {
-	du.predicates = append(du.predicates, ps...)
+	du.mutation.predicates = append(du.mutation.predicates, ps...)
 	return du
 	return du
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (du *DecisionUpdate) SetCreatedAt(t time.Time) *DecisionUpdate {
 func (du *DecisionUpdate) SetCreatedAt(t time.Time) *DecisionUpdate {
 	du.mutation.SetCreatedAt(t)
 	du.mutation.SetCreatedAt(t)
 	return du
 	return du
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (du *DecisionUpdate) SetNillableCreatedAt(t *time.Time) *DecisionUpdate {
 func (du *DecisionUpdate) SetNillableCreatedAt(t *time.Time) *DecisionUpdate {
 	if t != nil {
 	if t != nil {
 		du.SetCreatedAt(*t)
 		du.SetCreatedAt(*t)
@@ -43,13 +42,13 @@ func (du *DecisionUpdate) SetNillableCreatedAt(t *time.Time) *DecisionUpdate {
 	return du
 	return du
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (du *DecisionUpdate) SetUpdatedAt(t time.Time) *DecisionUpdate {
 func (du *DecisionUpdate) SetUpdatedAt(t time.Time) *DecisionUpdate {
 	du.mutation.SetUpdatedAt(t)
 	du.mutation.SetUpdatedAt(t)
 	return du
 	return du
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (du *DecisionUpdate) SetNillableUpdatedAt(t *time.Time) *DecisionUpdate {
 func (du *DecisionUpdate) SetNillableUpdatedAt(t *time.Time) *DecisionUpdate {
 	if t != nil {
 	if t != nil {
 		du.SetUpdatedAt(*t)
 		du.SetUpdatedAt(*t)
@@ -57,32 +56,32 @@ func (du *DecisionUpdate) SetNillableUpdatedAt(t *time.Time) *DecisionUpdate {
 	return du
 	return du
 }
 }
 
 
-// SetUntil sets the until field.
+// SetUntil sets the "until" field.
 func (du *DecisionUpdate) SetUntil(t time.Time) *DecisionUpdate {
 func (du *DecisionUpdate) SetUntil(t time.Time) *DecisionUpdate {
 	du.mutation.SetUntil(t)
 	du.mutation.SetUntil(t)
 	return du
 	return du
 }
 }
 
 
-// SetScenario sets the scenario field.
+// SetScenario sets the "scenario" field.
 func (du *DecisionUpdate) SetScenario(s string) *DecisionUpdate {
 func (du *DecisionUpdate) SetScenario(s string) *DecisionUpdate {
 	du.mutation.SetScenario(s)
 	du.mutation.SetScenario(s)
 	return du
 	return du
 }
 }
 
 
-// SetType sets the type field.
+// SetType sets the "type" field.
 func (du *DecisionUpdate) SetType(s string) *DecisionUpdate {
 func (du *DecisionUpdate) SetType(s string) *DecisionUpdate {
 	du.mutation.SetType(s)
 	du.mutation.SetType(s)
 	return du
 	return du
 }
 }
 
 
-// SetStartIP sets the start_ip field.
+// SetStartIP sets the "start_ip" field.
 func (du *DecisionUpdate) SetStartIP(i int64) *DecisionUpdate {
 func (du *DecisionUpdate) SetStartIP(i int64) *DecisionUpdate {
 	du.mutation.ResetStartIP()
 	du.mutation.ResetStartIP()
 	du.mutation.SetStartIP(i)
 	du.mutation.SetStartIP(i)
 	return du
 	return du
 }
 }
 
 
-// SetNillableStartIP sets the start_ip field if the given value is not nil.
+// SetNillableStartIP sets the "start_ip" field if the given value is not nil.
 func (du *DecisionUpdate) SetNillableStartIP(i *int64) *DecisionUpdate {
 func (du *DecisionUpdate) SetNillableStartIP(i *int64) *DecisionUpdate {
 	if i != nil {
 	if i != nil {
 		du.SetStartIP(*i)
 		du.SetStartIP(*i)
@@ -90,26 +89,26 @@ func (du *DecisionUpdate) SetNillableStartIP(i *int64) *DecisionUpdate {
 	return du
 	return du
 }
 }
 
 
-// AddStartIP adds i to start_ip.
+// AddStartIP adds i to the "start_ip" field.
 func (du *DecisionUpdate) AddStartIP(i int64) *DecisionUpdate {
 func (du *DecisionUpdate) AddStartIP(i int64) *DecisionUpdate {
 	du.mutation.AddStartIP(i)
 	du.mutation.AddStartIP(i)
 	return du
 	return du
 }
 }
 
 
-// ClearStartIP clears the value of start_ip.
+// ClearStartIP clears the value of the "start_ip" field.
 func (du *DecisionUpdate) ClearStartIP() *DecisionUpdate {
 func (du *DecisionUpdate) ClearStartIP() *DecisionUpdate {
 	du.mutation.ClearStartIP()
 	du.mutation.ClearStartIP()
 	return du
 	return du
 }
 }
 
 
-// SetEndIP sets the end_ip field.
+// SetEndIP sets the "end_ip" field.
 func (du *DecisionUpdate) SetEndIP(i int64) *DecisionUpdate {
 func (du *DecisionUpdate) SetEndIP(i int64) *DecisionUpdate {
 	du.mutation.ResetEndIP()
 	du.mutation.ResetEndIP()
 	du.mutation.SetEndIP(i)
 	du.mutation.SetEndIP(i)
 	return du
 	return du
 }
 }
 
 
-// SetNillableEndIP sets the end_ip field if the given value is not nil.
+// SetNillableEndIP sets the "end_ip" field if the given value is not nil.
 func (du *DecisionUpdate) SetNillableEndIP(i *int64) *DecisionUpdate {
 func (du *DecisionUpdate) SetNillableEndIP(i *int64) *DecisionUpdate {
 	if i != nil {
 	if i != nil {
 		du.SetEndIP(*i)
 		du.SetEndIP(*i)
@@ -117,43 +116,124 @@ func (du *DecisionUpdate) SetNillableEndIP(i *int64) *DecisionUpdate {
 	return du
 	return du
 }
 }
 
 
-// AddEndIP adds i to end_ip.
+// AddEndIP adds i to the "end_ip" field.
 func (du *DecisionUpdate) AddEndIP(i int64) *DecisionUpdate {
 func (du *DecisionUpdate) AddEndIP(i int64) *DecisionUpdate {
 	du.mutation.AddEndIP(i)
 	du.mutation.AddEndIP(i)
 	return du
 	return du
 }
 }
 
 
-// ClearEndIP clears the value of end_ip.
+// ClearEndIP clears the value of the "end_ip" field.
 func (du *DecisionUpdate) ClearEndIP() *DecisionUpdate {
 func (du *DecisionUpdate) ClearEndIP() *DecisionUpdate {
 	du.mutation.ClearEndIP()
 	du.mutation.ClearEndIP()
 	return du
 	return du
 }
 }
 
 
-// SetScope sets the scope field.
+// SetStartSuffix sets the "start_suffix" field.
+func (du *DecisionUpdate) SetStartSuffix(i int64) *DecisionUpdate {
+	du.mutation.ResetStartSuffix()
+	du.mutation.SetStartSuffix(i)
+	return du
+}
+
+// SetNillableStartSuffix sets the "start_suffix" field if the given value is not nil.
+func (du *DecisionUpdate) SetNillableStartSuffix(i *int64) *DecisionUpdate {
+	if i != nil {
+		du.SetStartSuffix(*i)
+	}
+	return du
+}
+
+// AddStartSuffix adds i to the "start_suffix" field.
+func (du *DecisionUpdate) AddStartSuffix(i int64) *DecisionUpdate {
+	du.mutation.AddStartSuffix(i)
+	return du
+}
+
+// ClearStartSuffix clears the value of the "start_suffix" field.
+func (du *DecisionUpdate) ClearStartSuffix() *DecisionUpdate {
+	du.mutation.ClearStartSuffix()
+	return du
+}
+
+// SetEndSuffix sets the "end_suffix" field.
+func (du *DecisionUpdate) SetEndSuffix(i int64) *DecisionUpdate {
+	du.mutation.ResetEndSuffix()
+	du.mutation.SetEndSuffix(i)
+	return du
+}
+
+// SetNillableEndSuffix sets the "end_suffix" field if the given value is not nil.
+func (du *DecisionUpdate) SetNillableEndSuffix(i *int64) *DecisionUpdate {
+	if i != nil {
+		du.SetEndSuffix(*i)
+	}
+	return du
+}
+
+// AddEndSuffix adds i to the "end_suffix" field.
+func (du *DecisionUpdate) AddEndSuffix(i int64) *DecisionUpdate {
+	du.mutation.AddEndSuffix(i)
+	return du
+}
+
+// ClearEndSuffix clears the value of the "end_suffix" field.
+func (du *DecisionUpdate) ClearEndSuffix() *DecisionUpdate {
+	du.mutation.ClearEndSuffix()
+	return du
+}
+
+// SetIPSize sets the "ip_size" field.
+func (du *DecisionUpdate) SetIPSize(i int64) *DecisionUpdate {
+	du.mutation.ResetIPSize()
+	du.mutation.SetIPSize(i)
+	return du
+}
+
+// SetNillableIPSize sets the "ip_size" field if the given value is not nil.
+func (du *DecisionUpdate) SetNillableIPSize(i *int64) *DecisionUpdate {
+	if i != nil {
+		du.SetIPSize(*i)
+	}
+	return du
+}
+
+// AddIPSize adds i to the "ip_size" field.
+func (du *DecisionUpdate) AddIPSize(i int64) *DecisionUpdate {
+	du.mutation.AddIPSize(i)
+	return du
+}
+
+// ClearIPSize clears the value of the "ip_size" field.
+func (du *DecisionUpdate) ClearIPSize() *DecisionUpdate {
+	du.mutation.ClearIPSize()
+	return du
+}
+
+// SetScope sets the "scope" field.
 func (du *DecisionUpdate) SetScope(s string) *DecisionUpdate {
 func (du *DecisionUpdate) SetScope(s string) *DecisionUpdate {
 	du.mutation.SetScope(s)
 	du.mutation.SetScope(s)
 	return du
 	return du
 }
 }
 
 
-// SetValue sets the value field.
+// SetValue sets the "value" field.
 func (du *DecisionUpdate) SetValue(s string) *DecisionUpdate {
 func (du *DecisionUpdate) SetValue(s string) *DecisionUpdate {
 	du.mutation.SetValue(s)
 	du.mutation.SetValue(s)
 	return du
 	return du
 }
 }
 
 
-// SetOrigin sets the origin field.
+// SetOrigin sets the "origin" field.
 func (du *DecisionUpdate) SetOrigin(s string) *DecisionUpdate {
 func (du *DecisionUpdate) SetOrigin(s string) *DecisionUpdate {
 	du.mutation.SetOrigin(s)
 	du.mutation.SetOrigin(s)
 	return du
 	return du
 }
 }
 
 
-// SetSimulated sets the simulated field.
+// SetSimulated sets the "simulated" field.
 func (du *DecisionUpdate) SetSimulated(b bool) *DecisionUpdate {
 func (du *DecisionUpdate) SetSimulated(b bool) *DecisionUpdate {
 	du.mutation.SetSimulated(b)
 	du.mutation.SetSimulated(b)
 	return du
 	return du
 }
 }
 
 
-// SetNillableSimulated sets the simulated field if the given value is not nil.
+// SetNillableSimulated sets the "simulated" field if the given value is not nil.
 func (du *DecisionUpdate) SetNillableSimulated(b *bool) *DecisionUpdate {
 func (du *DecisionUpdate) SetNillableSimulated(b *bool) *DecisionUpdate {
 	if b != nil {
 	if b != nil {
 		du.SetSimulated(*b)
 		du.SetSimulated(*b)
@@ -161,13 +241,13 @@ func (du *DecisionUpdate) SetNillableSimulated(b *bool) *DecisionUpdate {
 	return du
 	return du
 }
 }
 
 
-// SetOwnerID sets the owner edge to Alert by id.
+// SetOwnerID sets the "owner" edge to the Alert entity by ID.
 func (du *DecisionUpdate) SetOwnerID(id int) *DecisionUpdate {
 func (du *DecisionUpdate) SetOwnerID(id int) *DecisionUpdate {
 	du.mutation.SetOwnerID(id)
 	du.mutation.SetOwnerID(id)
 	return du
 	return du
 }
 }
 
 
-// SetNillableOwnerID sets the owner edge to Alert by id if the given value is not nil.
+// SetNillableOwnerID sets the "owner" edge to the Alert entity by ID if the given value is not nil.
 func (du *DecisionUpdate) SetNillableOwnerID(id *int) *DecisionUpdate {
 func (du *DecisionUpdate) SetNillableOwnerID(id *int) *DecisionUpdate {
 	if id != nil {
 	if id != nil {
 		du = du.SetOwnerID(*id)
 		du = du.SetOwnerID(*id)
@@ -175,7 +255,7 @@ func (du *DecisionUpdate) SetNillableOwnerID(id *int) *DecisionUpdate {
 	return du
 	return du
 }
 }
 
 
-// SetOwner sets the owner edge to Alert.
+// SetOwner sets the "owner" edge to the Alert entity.
 func (du *DecisionUpdate) SetOwner(a *Alert) *DecisionUpdate {
 func (du *DecisionUpdate) SetOwner(a *Alert) *DecisionUpdate {
 	return du.SetOwnerID(a.ID)
 	return du.SetOwnerID(a.ID)
 }
 }
@@ -185,13 +265,13 @@ func (du *DecisionUpdate) Mutation() *DecisionMutation {
 	return du.mutation
 	return du.mutation
 }
 }
 
 
-// ClearOwner clears the "owner" edge to type Alert.
+// ClearOwner clears the "owner" edge to the Alert entity.
 func (du *DecisionUpdate) ClearOwner() *DecisionUpdate {
 func (du *DecisionUpdate) ClearOwner() *DecisionUpdate {
 	du.mutation.ClearOwner()
 	du.mutation.ClearOwner()
 	return du
 	return du
 }
 }
 
 
-// Save executes the query and returns the number of rows/vertices matched by this operation.
+// Save executes the query and returns the number of nodes affected by the update operation.
 func (du *DecisionUpdate) Save(ctx context.Context) (int, error) {
 func (du *DecisionUpdate) Save(ctx context.Context) (int, error) {
 	var (
 	var (
 		err      error
 		err      error
@@ -253,7 +333,7 @@ func (du *DecisionUpdate) sqlSave(ctx context.Context) (n int, err error) {
 			},
 			},
 		},
 		},
 	}
 	}
-	if ps := du.predicates; len(ps) > 0 {
+	if ps := du.mutation.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
 				ps[i](selector)
 				ps[i](selector)
@@ -335,6 +415,66 @@ func (du *DecisionUpdate) sqlSave(ctx context.Context) (n int, err error) {
 			Column: decision.FieldEndIP,
 			Column: decision.FieldEndIP,
 		})
 		})
 	}
 	}
+	if value, ok := du.mutation.StartSuffix(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldStartSuffix,
+		})
+	}
+	if value, ok := du.mutation.AddedStartSuffix(); ok {
+		_spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldStartSuffix,
+		})
+	}
+	if du.mutation.StartSuffixCleared() {
+		_spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Column: decision.FieldStartSuffix,
+		})
+	}
+	if value, ok := du.mutation.EndSuffix(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldEndSuffix,
+		})
+	}
+	if value, ok := du.mutation.AddedEndSuffix(); ok {
+		_spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldEndSuffix,
+		})
+	}
+	if du.mutation.EndSuffixCleared() {
+		_spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Column: decision.FieldEndSuffix,
+		})
+	}
+	if value, ok := du.mutation.IPSize(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldIPSize,
+		})
+	}
+	if value, ok := du.mutation.AddedIPSize(); ok {
+		_spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldIPSize,
+		})
+	}
+	if du.mutation.IPSizeCleared() {
+		_spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Column: decision.FieldIPSize,
+		})
+	}
 	if value, ok := du.mutation.Scope(); ok {
 	if value, ok := du.mutation.Scope(); ok {
 		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
 		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
 			Type:   field.TypeString,
 			Type:   field.TypeString,
@@ -416,13 +556,13 @@ type DecisionUpdateOne struct {
 	mutation *DecisionMutation
 	mutation *DecisionMutation
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (duo *DecisionUpdateOne) SetCreatedAt(t time.Time) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetCreatedAt(t time.Time) *DecisionUpdateOne {
 	duo.mutation.SetCreatedAt(t)
 	duo.mutation.SetCreatedAt(t)
 	return duo
 	return duo
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (duo *DecisionUpdateOne) SetNillableCreatedAt(t *time.Time) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetNillableCreatedAt(t *time.Time) *DecisionUpdateOne {
 	if t != nil {
 	if t != nil {
 		duo.SetCreatedAt(*t)
 		duo.SetCreatedAt(*t)
@@ -430,13 +570,13 @@ func (duo *DecisionUpdateOne) SetNillableCreatedAt(t *time.Time) *DecisionUpdate
 	return duo
 	return duo
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (duo *DecisionUpdateOne) SetUpdatedAt(t time.Time) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetUpdatedAt(t time.Time) *DecisionUpdateOne {
 	duo.mutation.SetUpdatedAt(t)
 	duo.mutation.SetUpdatedAt(t)
 	return duo
 	return duo
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (duo *DecisionUpdateOne) SetNillableUpdatedAt(t *time.Time) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetNillableUpdatedAt(t *time.Time) *DecisionUpdateOne {
 	if t != nil {
 	if t != nil {
 		duo.SetUpdatedAt(*t)
 		duo.SetUpdatedAt(*t)
@@ -444,32 +584,32 @@ func (duo *DecisionUpdateOne) SetNillableUpdatedAt(t *time.Time) *DecisionUpdate
 	return duo
 	return duo
 }
 }
 
 
-// SetUntil sets the until field.
+// SetUntil sets the "until" field.
 func (duo *DecisionUpdateOne) SetUntil(t time.Time) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetUntil(t time.Time) *DecisionUpdateOne {
 	duo.mutation.SetUntil(t)
 	duo.mutation.SetUntil(t)
 	return duo
 	return duo
 }
 }
 
 
-// SetScenario sets the scenario field.
+// SetScenario sets the "scenario" field.
 func (duo *DecisionUpdateOne) SetScenario(s string) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetScenario(s string) *DecisionUpdateOne {
 	duo.mutation.SetScenario(s)
 	duo.mutation.SetScenario(s)
 	return duo
 	return duo
 }
 }
 
 
-// SetType sets the type field.
+// SetType sets the "type" field.
 func (duo *DecisionUpdateOne) SetType(s string) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetType(s string) *DecisionUpdateOne {
 	duo.mutation.SetType(s)
 	duo.mutation.SetType(s)
 	return duo
 	return duo
 }
 }
 
 
-// SetStartIP sets the start_ip field.
+// SetStartIP sets the "start_ip" field.
 func (duo *DecisionUpdateOne) SetStartIP(i int64) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetStartIP(i int64) *DecisionUpdateOne {
 	duo.mutation.ResetStartIP()
 	duo.mutation.ResetStartIP()
 	duo.mutation.SetStartIP(i)
 	duo.mutation.SetStartIP(i)
 	return duo
 	return duo
 }
 }
 
 
-// SetNillableStartIP sets the start_ip field if the given value is not nil.
+// SetNillableStartIP sets the "start_ip" field if the given value is not nil.
 func (duo *DecisionUpdateOne) SetNillableStartIP(i *int64) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetNillableStartIP(i *int64) *DecisionUpdateOne {
 	if i != nil {
 	if i != nil {
 		duo.SetStartIP(*i)
 		duo.SetStartIP(*i)
@@ -477,26 +617,26 @@ func (duo *DecisionUpdateOne) SetNillableStartIP(i *int64) *DecisionUpdateOne {
 	return duo
 	return duo
 }
 }
 
 
-// AddStartIP adds i to start_ip.
+// AddStartIP adds i to the "start_ip" field.
 func (duo *DecisionUpdateOne) AddStartIP(i int64) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) AddStartIP(i int64) *DecisionUpdateOne {
 	duo.mutation.AddStartIP(i)
 	duo.mutation.AddStartIP(i)
 	return duo
 	return duo
 }
 }
 
 
-// ClearStartIP clears the value of start_ip.
+// ClearStartIP clears the value of the "start_ip" field.
 func (duo *DecisionUpdateOne) ClearStartIP() *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) ClearStartIP() *DecisionUpdateOne {
 	duo.mutation.ClearStartIP()
 	duo.mutation.ClearStartIP()
 	return duo
 	return duo
 }
 }
 
 
-// SetEndIP sets the end_ip field.
+// SetEndIP sets the "end_ip" field.
 func (duo *DecisionUpdateOne) SetEndIP(i int64) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetEndIP(i int64) *DecisionUpdateOne {
 	duo.mutation.ResetEndIP()
 	duo.mutation.ResetEndIP()
 	duo.mutation.SetEndIP(i)
 	duo.mutation.SetEndIP(i)
 	return duo
 	return duo
 }
 }
 
 
-// SetNillableEndIP sets the end_ip field if the given value is not nil.
+// SetNillableEndIP sets the "end_ip" field if the given value is not nil.
 func (duo *DecisionUpdateOne) SetNillableEndIP(i *int64) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetNillableEndIP(i *int64) *DecisionUpdateOne {
 	if i != nil {
 	if i != nil {
 		duo.SetEndIP(*i)
 		duo.SetEndIP(*i)
@@ -504,43 +644,124 @@ func (duo *DecisionUpdateOne) SetNillableEndIP(i *int64) *DecisionUpdateOne {
 	return duo
 	return duo
 }
 }
 
 
-// AddEndIP adds i to end_ip.
+// AddEndIP adds i to the "end_ip" field.
 func (duo *DecisionUpdateOne) AddEndIP(i int64) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) AddEndIP(i int64) *DecisionUpdateOne {
 	duo.mutation.AddEndIP(i)
 	duo.mutation.AddEndIP(i)
 	return duo
 	return duo
 }
 }
 
 
-// ClearEndIP clears the value of end_ip.
+// ClearEndIP clears the value of the "end_ip" field.
 func (duo *DecisionUpdateOne) ClearEndIP() *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) ClearEndIP() *DecisionUpdateOne {
 	duo.mutation.ClearEndIP()
 	duo.mutation.ClearEndIP()
 	return duo
 	return duo
 }
 }
 
 
-// SetScope sets the scope field.
+// SetStartSuffix sets the "start_suffix" field.
+func (duo *DecisionUpdateOne) SetStartSuffix(i int64) *DecisionUpdateOne {
+	duo.mutation.ResetStartSuffix()
+	duo.mutation.SetStartSuffix(i)
+	return duo
+}
+
+// SetNillableStartSuffix sets the "start_suffix" field if the given value is not nil.
+func (duo *DecisionUpdateOne) SetNillableStartSuffix(i *int64) *DecisionUpdateOne {
+	if i != nil {
+		duo.SetStartSuffix(*i)
+	}
+	return duo
+}
+
+// AddStartSuffix adds i to the "start_suffix" field.
+func (duo *DecisionUpdateOne) AddStartSuffix(i int64) *DecisionUpdateOne {
+	duo.mutation.AddStartSuffix(i)
+	return duo
+}
+
+// ClearStartSuffix clears the value of the "start_suffix" field.
+func (duo *DecisionUpdateOne) ClearStartSuffix() *DecisionUpdateOne {
+	duo.mutation.ClearStartSuffix()
+	return duo
+}
+
+// SetEndSuffix sets the "end_suffix" field.
+func (duo *DecisionUpdateOne) SetEndSuffix(i int64) *DecisionUpdateOne {
+	duo.mutation.ResetEndSuffix()
+	duo.mutation.SetEndSuffix(i)
+	return duo
+}
+
+// SetNillableEndSuffix sets the "end_suffix" field if the given value is not nil.
+func (duo *DecisionUpdateOne) SetNillableEndSuffix(i *int64) *DecisionUpdateOne {
+	if i != nil {
+		duo.SetEndSuffix(*i)
+	}
+	return duo
+}
+
+// AddEndSuffix adds i to the "end_suffix" field.
+func (duo *DecisionUpdateOne) AddEndSuffix(i int64) *DecisionUpdateOne {
+	duo.mutation.AddEndSuffix(i)
+	return duo
+}
+
+// ClearEndSuffix clears the value of the "end_suffix" field.
+func (duo *DecisionUpdateOne) ClearEndSuffix() *DecisionUpdateOne {
+	duo.mutation.ClearEndSuffix()
+	return duo
+}
+
+// SetIPSize sets the "ip_size" field.
+func (duo *DecisionUpdateOne) SetIPSize(i int64) *DecisionUpdateOne {
+	duo.mutation.ResetIPSize()
+	duo.mutation.SetIPSize(i)
+	return duo
+}
+
+// SetNillableIPSize sets the "ip_size" field if the given value is not nil.
+func (duo *DecisionUpdateOne) SetNillableIPSize(i *int64) *DecisionUpdateOne {
+	if i != nil {
+		duo.SetIPSize(*i)
+	}
+	return duo
+}
+
+// AddIPSize adds i to the "ip_size" field.
+func (duo *DecisionUpdateOne) AddIPSize(i int64) *DecisionUpdateOne {
+	duo.mutation.AddIPSize(i)
+	return duo
+}
+
+// ClearIPSize clears the value of the "ip_size" field.
+func (duo *DecisionUpdateOne) ClearIPSize() *DecisionUpdateOne {
+	duo.mutation.ClearIPSize()
+	return duo
+}
+
+// SetScope sets the "scope" field.
 func (duo *DecisionUpdateOne) SetScope(s string) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetScope(s string) *DecisionUpdateOne {
 	duo.mutation.SetScope(s)
 	duo.mutation.SetScope(s)
 	return duo
 	return duo
 }
 }
 
 
-// SetValue sets the value field.
+// SetValue sets the "value" field.
 func (duo *DecisionUpdateOne) SetValue(s string) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetValue(s string) *DecisionUpdateOne {
 	duo.mutation.SetValue(s)
 	duo.mutation.SetValue(s)
 	return duo
 	return duo
 }
 }
 
 
-// SetOrigin sets the origin field.
+// SetOrigin sets the "origin" field.
 func (duo *DecisionUpdateOne) SetOrigin(s string) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetOrigin(s string) *DecisionUpdateOne {
 	duo.mutation.SetOrigin(s)
 	duo.mutation.SetOrigin(s)
 	return duo
 	return duo
 }
 }
 
 
-// SetSimulated sets the simulated field.
+// SetSimulated sets the "simulated" field.
 func (duo *DecisionUpdateOne) SetSimulated(b bool) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetSimulated(b bool) *DecisionUpdateOne {
 	duo.mutation.SetSimulated(b)
 	duo.mutation.SetSimulated(b)
 	return duo
 	return duo
 }
 }
 
 
-// SetNillableSimulated sets the simulated field if the given value is not nil.
+// SetNillableSimulated sets the "simulated" field if the given value is not nil.
 func (duo *DecisionUpdateOne) SetNillableSimulated(b *bool) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetNillableSimulated(b *bool) *DecisionUpdateOne {
 	if b != nil {
 	if b != nil {
 		duo.SetSimulated(*b)
 		duo.SetSimulated(*b)
@@ -548,13 +769,13 @@ func (duo *DecisionUpdateOne) SetNillableSimulated(b *bool) *DecisionUpdateOne {
 	return duo
 	return duo
 }
 }
 
 
-// SetOwnerID sets the owner edge to Alert by id.
+// SetOwnerID sets the "owner" edge to the Alert entity by ID.
 func (duo *DecisionUpdateOne) SetOwnerID(id int) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetOwnerID(id int) *DecisionUpdateOne {
 	duo.mutation.SetOwnerID(id)
 	duo.mutation.SetOwnerID(id)
 	return duo
 	return duo
 }
 }
 
 
-// SetNillableOwnerID sets the owner edge to Alert by id if the given value is not nil.
+// SetNillableOwnerID sets the "owner" edge to the Alert entity by ID if the given value is not nil.
 func (duo *DecisionUpdateOne) SetNillableOwnerID(id *int) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetNillableOwnerID(id *int) *DecisionUpdateOne {
 	if id != nil {
 	if id != nil {
 		duo = duo.SetOwnerID(*id)
 		duo = duo.SetOwnerID(*id)
@@ -562,7 +783,7 @@ func (duo *DecisionUpdateOne) SetNillableOwnerID(id *int) *DecisionUpdateOne {
 	return duo
 	return duo
 }
 }
 
 
-// SetOwner sets the owner edge to Alert.
+// SetOwner sets the "owner" edge to the Alert entity.
 func (duo *DecisionUpdateOne) SetOwner(a *Alert) *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) SetOwner(a *Alert) *DecisionUpdateOne {
 	return duo.SetOwnerID(a.ID)
 	return duo.SetOwnerID(a.ID)
 }
 }
@@ -572,13 +793,13 @@ func (duo *DecisionUpdateOne) Mutation() *DecisionMutation {
 	return duo.mutation
 	return duo.mutation
 }
 }
 
 
-// ClearOwner clears the "owner" edge to type Alert.
+// ClearOwner clears the "owner" edge to the Alert entity.
 func (duo *DecisionUpdateOne) ClearOwner() *DecisionUpdateOne {
 func (duo *DecisionUpdateOne) ClearOwner() *DecisionUpdateOne {
 	duo.mutation.ClearOwner()
 	duo.mutation.ClearOwner()
 	return duo
 	return duo
 }
 }
 
 
-// Save executes the query and returns the updated entity.
+// Save executes the query and returns the updated Decision entity.
 func (duo *DecisionUpdateOne) Save(ctx context.Context) (*Decision, error) {
 func (duo *DecisionUpdateOne) Save(ctx context.Context) (*Decision, error) {
 	var (
 	var (
 		err  error
 		err  error
@@ -720,6 +941,66 @@ func (duo *DecisionUpdateOne) sqlSave(ctx context.Context) (_node *Decision, err
 			Column: decision.FieldEndIP,
 			Column: decision.FieldEndIP,
 		})
 		})
 	}
 	}
+	if value, ok := duo.mutation.StartSuffix(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldStartSuffix,
+		})
+	}
+	if value, ok := duo.mutation.AddedStartSuffix(); ok {
+		_spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldStartSuffix,
+		})
+	}
+	if duo.mutation.StartSuffixCleared() {
+		_spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Column: decision.FieldStartSuffix,
+		})
+	}
+	if value, ok := duo.mutation.EndSuffix(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldEndSuffix,
+		})
+	}
+	if value, ok := duo.mutation.AddedEndSuffix(); ok {
+		_spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldEndSuffix,
+		})
+	}
+	if duo.mutation.EndSuffixCleared() {
+		_spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Column: decision.FieldEndSuffix,
+		})
+	}
+	if value, ok := duo.mutation.IPSize(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldIPSize,
+		})
+	}
+	if value, ok := duo.mutation.AddedIPSize(); ok {
+		_spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Value:  value,
+			Column: decision.FieldIPSize,
+		})
+	}
+	if duo.mutation.IPSizeCleared() {
+		_spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt64,
+			Column: decision.FieldIPSize,
+		})
+	}
 	if value, ok := duo.mutation.Scope(); ok {
 	if value, ok := duo.mutation.Scope(); ok {
 		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
 		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
 			Type:   field.TypeString,
 			Type:   field.TypeString,
@@ -785,7 +1066,7 @@ func (duo *DecisionUpdateOne) sqlSave(ctx context.Context) (_node *Decision, err
 	}
 	}
 	_node = &Decision{config: duo.config}
 	_node = &Decision{config: duo.config}
 	_spec.Assign = _node.assignValues
 	_spec.Assign = _node.assignValues
-	_spec.ScanValues = _node.scanValues()
+	_spec.ScanValues = _node.scanValues
 	if err = sqlgraph.UpdateNode(ctx, duo.driver, _spec); err != nil {
 	if err = sqlgraph.UpdateNode(ctx, duo.driver, _spec); err != nil {
 		if _, ok := err.(*sqlgraph.NotFoundError); ok {
 		if _, ok := err.(*sqlgraph.NotFoundError); ok {
 			err = &NotFoundError{decision.Label}
 			err = &NotFoundError{decision.Label}

+ 3 - 3
pkg/database/ent/ent.go

@@ -13,7 +13,7 @@ import (
 	"github.com/facebook/ent/dialect/sql/sqlgraph"
 	"github.com/facebook/ent/dialect/sql/sqlgraph"
 )
 )
 
 
-// ent aliases to avoid import conflict in user's code.
+// ent aliases to avoid import conflicts in user's code.
 type (
 type (
 	Op         = ent.Op
 	Op         = ent.Op
 	Hook       = ent.Hook
 	Hook       = ent.Hook
@@ -164,7 +164,7 @@ func IsNotFound(err error) bool {
 	return errors.As(err, &e)
 	return errors.As(err, &e)
 }
 }
 
 
-// MaskNotFound masks nor found error.
+// MaskNotFound masks not found error.
 func MaskNotFound(err error) error {
 func MaskNotFound(err error) error {
 	if IsNotFound(err) {
 	if IsNotFound(err) {
 		return nil
 		return nil
@@ -258,7 +258,7 @@ func isSQLConstraintError(err error) (*ConstraintError, bool) {
 	return nil, false
 	return nil, false
 }
 }
 
 
-// rollback calls to tx.Rollback and wraps the given error with the rollback error if occurred.
+// rollback calls tx.Rollback and wraps the given error with the rollback error if present.
 func rollback(tx dialect.Tx, err error) error {
 func rollback(tx dialect.Tx, err error) error {
 	if rerr := tx.Rollback(); rerr != nil {
 	if rerr := tx.Rollback(); rerr != nil {
 		err = fmt.Errorf("%s: %v", err.Error(), rerr)
 		err = fmt.Errorf("%s: %v", err.Error(), rerr)

+ 61 - 53
pkg/database/ent/event.go

@@ -55,81 +55,89 @@ func (e EventEdges) OwnerOrErr() (*Alert, error) {
 }
 }
 
 
 // scanValues returns the types for scanning values from sql.Rows.
 // scanValues returns the types for scanning values from sql.Rows.
-func (*Event) scanValues() []interface{} {
-	return []interface{}{
-		&sql.NullInt64{},  // id
-		&sql.NullTime{},   // created_at
-		&sql.NullTime{},   // updated_at
-		&sql.NullTime{},   // time
-		&sql.NullString{}, // serialized
-	}
-}
-
-// fkValues returns the types for scanning foreign-keys values from sql.Rows.
-func (*Event) fkValues() []interface{} {
-	return []interface{}{
-		&sql.NullInt64{}, // alert_events
+func (*Event) scanValues(columns []string) ([]interface{}, error) {
+	values := make([]interface{}, len(columns))
+	for i := range columns {
+		switch columns[i] {
+		case event.FieldID:
+			values[i] = &sql.NullInt64{}
+		case event.FieldSerialized:
+			values[i] = &sql.NullString{}
+		case event.FieldCreatedAt, event.FieldUpdatedAt, event.FieldTime:
+			values[i] = &sql.NullTime{}
+		case event.ForeignKeys[0]: // alert_events
+			values[i] = &sql.NullInt64{}
+		default:
+			return nil, fmt.Errorf("unexpected column %q for type Event", columns[i])
+		}
 	}
 	}
+	return values, nil
 }
 }
 
 
 // assignValues assigns the values that were returned from sql.Rows (after scanning)
 // assignValues assigns the values that were returned from sql.Rows (after scanning)
 // to the Event fields.
 // to the Event fields.
-func (e *Event) assignValues(values ...interface{}) error {
-	if m, n := len(values), len(event.Columns); m < n {
+func (e *Event) assignValues(columns []string, values []interface{}) error {
+	if m, n := len(values), len(columns); m < n {
 		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
 		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
 	}
 	}
-	value, ok := values[0].(*sql.NullInt64)
-	if !ok {
-		return fmt.Errorf("unexpected type %T for field id", value)
-	}
-	e.ID = int(value.Int64)
-	values = values[1:]
-	if value, ok := values[0].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field created_at", values[0])
-	} else if value.Valid {
-		e.CreatedAt = value.Time
-	}
-	if value, ok := values[1].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field updated_at", values[1])
-	} else if value.Valid {
-		e.UpdatedAt = value.Time
-	}
-	if value, ok := values[2].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field time", values[2])
-	} else if value.Valid {
-		e.Time = value.Time
-	}
-	if value, ok := values[3].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field serialized", values[3])
-	} else if value.Valid {
-		e.Serialized = value.String
-	}
-	values = values[4:]
-	if len(values) == len(event.ForeignKeys) {
-		if value, ok := values[0].(*sql.NullInt64); !ok {
-			return fmt.Errorf("unexpected type %T for edge-field alert_events", value)
-		} else if value.Valid {
-			e.alert_events = new(int)
-			*e.alert_events = int(value.Int64)
+	for i := range columns {
+		switch columns[i] {
+		case event.FieldID:
+			value, ok := values[i].(*sql.NullInt64)
+			if !ok {
+				return fmt.Errorf("unexpected type %T for field id", value)
+			}
+			e.ID = int(value.Int64)
+		case event.FieldCreatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field created_at", values[i])
+			} else if value.Valid {
+				e.CreatedAt = value.Time
+			}
+		case event.FieldUpdatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field updated_at", values[i])
+			} else if value.Valid {
+				e.UpdatedAt = value.Time
+			}
+		case event.FieldTime:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field time", values[i])
+			} else if value.Valid {
+				e.Time = value.Time
+			}
+		case event.FieldSerialized:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field serialized", values[i])
+			} else if value.Valid {
+				e.Serialized = value.String
+			}
+		case event.ForeignKeys[0]:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for edge-field alert_events", value)
+			} else if value.Valid {
+				e.alert_events = new(int)
+				*e.alert_events = int(value.Int64)
+			}
 		}
 		}
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-// QueryOwner queries the owner edge of the Event.
+// QueryOwner queries the "owner" edge of the Event entity.
 func (e *Event) QueryOwner() *AlertQuery {
 func (e *Event) QueryOwner() *AlertQuery {
 	return (&EventClient{config: e.config}).QueryOwner(e)
 	return (&EventClient{config: e.config}).QueryOwner(e)
 }
 }
 
 
 // Update returns a builder for updating this Event.
 // Update returns a builder for updating this Event.
-// Note that, you need to call Event.Unwrap() before calling this method, if this Event
+// Note that you need to call Event.Unwrap() before calling this method if this Event
 // was returned from a transaction, and the transaction was committed or rolled back.
 // was returned from a transaction, and the transaction was committed or rolled back.
 func (e *Event) Update() *EventUpdateOne {
 func (e *Event) Update() *EventUpdateOne {
 	return (&EventClient{config: e.config}).UpdateOne(e)
 	return (&EventClient{config: e.config}).UpdateOne(e)
 }
 }
 
 
-// Unwrap unwraps the entity that was returned from a transaction after it was closed,
-// so that all next queries will be executed through the driver which created the transaction.
+// Unwrap unwraps the Event entity that was returned from a transaction after it was closed,
+// so that all future queries will be executed through the driver which created the transaction.
 func (e *Event) Unwrap() *Event {
 func (e *Event) Unwrap() *Event {
 	tx, ok := e.config.driver.(*txDriver)
 	tx, ok := e.config.driver.(*txDriver)
 	if !ok {
 	if !ok {

+ 2 - 2
pkg/database/ent/event/event.go

@@ -64,9 +64,9 @@ func ValidColumn(column string) bool {
 }
 }
 
 
 var (
 var (
-	// DefaultCreatedAt holds the default value on creation for the created_at field.
+	// DefaultCreatedAt holds the default value on creation for the "created_at" field.
 	DefaultCreatedAt func() time.Time
 	DefaultCreatedAt func() time.Time
-	// DefaultUpdatedAt holds the default value on creation for the updated_at field.
+	// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
 	DefaultUpdatedAt func() time.Time
 	DefaultUpdatedAt func() time.Time
 	// SerializedValidator is a validator for the "serialized" field. It is called by the builders before save.
 	// SerializedValidator is a validator for the "serialized" field. It is called by the builders before save.
 	SerializedValidator func(string) error
 	SerializedValidator func(string) error

+ 3 - 3
pkg/database/ent/event/where.go

@@ -10,7 +10,7 @@ import (
 	"github.com/facebook/ent/dialect/sql/sqlgraph"
 	"github.com/facebook/ent/dialect/sql/sqlgraph"
 )
 )
 
 
-// ID filters vertices based on their identifier.
+// ID filters vertices based on their ID field.
 func ID(id int) predicate.Event {
 func ID(id int) predicate.Event {
 	return predicate.Event(func(s *sql.Selector) {
 	return predicate.Event(func(s *sql.Selector) {
 		s.Where(sql.EQ(s.C(FieldID), id))
 		s.Where(sql.EQ(s.C(FieldID), id))
@@ -488,7 +488,7 @@ func HasOwnerWith(preds ...predicate.Alert) predicate.Event {
 	})
 	})
 }
 }
 
 
-// And groups list of predicates with the AND operator between them.
+// And groups predicates with the AND operator between them.
 func And(predicates ...predicate.Event) predicate.Event {
 func And(predicates ...predicate.Event) predicate.Event {
 	return predicate.Event(func(s *sql.Selector) {
 	return predicate.Event(func(s *sql.Selector) {
 		s1 := s.Clone().SetP(nil)
 		s1 := s.Clone().SetP(nil)
@@ -499,7 +499,7 @@ func And(predicates ...predicate.Event) predicate.Event {
 	})
 	})
 }
 }
 
 
-// Or groups list of predicates with the OR operator between them.
+// Or groups predicates with the OR operator between them.
 func Or(predicates ...predicate.Event) predicate.Event {
 func Or(predicates ...predicate.Event) predicate.Event {
 	return predicate.Event(func(s *sql.Selector) {
 	return predicate.Event(func(s *sql.Selector) {
 		s1 := s.Clone().SetP(nil)
 		s1 := s.Clone().SetP(nil)

+ 11 - 11
pkg/database/ent/event_create.go

@@ -21,13 +21,13 @@ type EventCreate struct {
 	hooks    []Hook
 	hooks    []Hook
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (ec *EventCreate) SetCreatedAt(t time.Time) *EventCreate {
 func (ec *EventCreate) SetCreatedAt(t time.Time) *EventCreate {
 	ec.mutation.SetCreatedAt(t)
 	ec.mutation.SetCreatedAt(t)
 	return ec
 	return ec
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (ec *EventCreate) SetNillableCreatedAt(t *time.Time) *EventCreate {
 func (ec *EventCreate) SetNillableCreatedAt(t *time.Time) *EventCreate {
 	if t != nil {
 	if t != nil {
 		ec.SetCreatedAt(*t)
 		ec.SetCreatedAt(*t)
@@ -35,13 +35,13 @@ func (ec *EventCreate) SetNillableCreatedAt(t *time.Time) *EventCreate {
 	return ec
 	return ec
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (ec *EventCreate) SetUpdatedAt(t time.Time) *EventCreate {
 func (ec *EventCreate) SetUpdatedAt(t time.Time) *EventCreate {
 	ec.mutation.SetUpdatedAt(t)
 	ec.mutation.SetUpdatedAt(t)
 	return ec
 	return ec
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (ec *EventCreate) SetNillableUpdatedAt(t *time.Time) *EventCreate {
 func (ec *EventCreate) SetNillableUpdatedAt(t *time.Time) *EventCreate {
 	if t != nil {
 	if t != nil {
 		ec.SetUpdatedAt(*t)
 		ec.SetUpdatedAt(*t)
@@ -49,25 +49,25 @@ func (ec *EventCreate) SetNillableUpdatedAt(t *time.Time) *EventCreate {
 	return ec
 	return ec
 }
 }
 
 
-// SetTime sets the time field.
+// SetTime sets the "time" field.
 func (ec *EventCreate) SetTime(t time.Time) *EventCreate {
 func (ec *EventCreate) SetTime(t time.Time) *EventCreate {
 	ec.mutation.SetTime(t)
 	ec.mutation.SetTime(t)
 	return ec
 	return ec
 }
 }
 
 
-// SetSerialized sets the serialized field.
+// SetSerialized sets the "serialized" field.
 func (ec *EventCreate) SetSerialized(s string) *EventCreate {
 func (ec *EventCreate) SetSerialized(s string) *EventCreate {
 	ec.mutation.SetSerialized(s)
 	ec.mutation.SetSerialized(s)
 	return ec
 	return ec
 }
 }
 
 
-// SetOwnerID sets the owner edge to Alert by id.
+// SetOwnerID sets the "owner" edge to the Alert entity by ID.
 func (ec *EventCreate) SetOwnerID(id int) *EventCreate {
 func (ec *EventCreate) SetOwnerID(id int) *EventCreate {
 	ec.mutation.SetOwnerID(id)
 	ec.mutation.SetOwnerID(id)
 	return ec
 	return ec
 }
 }
 
 
-// SetNillableOwnerID sets the owner edge to Alert by id if the given value is not nil.
+// SetNillableOwnerID sets the "owner" edge to the Alert entity by ID if the given value is not nil.
 func (ec *EventCreate) SetNillableOwnerID(id *int) *EventCreate {
 func (ec *EventCreate) SetNillableOwnerID(id *int) *EventCreate {
 	if id != nil {
 	if id != nil {
 		ec = ec.SetOwnerID(*id)
 		ec = ec.SetOwnerID(*id)
@@ -75,7 +75,7 @@ func (ec *EventCreate) SetNillableOwnerID(id *int) *EventCreate {
 	return ec
 	return ec
 }
 }
 
 
-// SetOwner sets the owner edge to Alert.
+// SetOwner sets the "owner" edge to the Alert entity.
 func (ec *EventCreate) SetOwner(a *Alert) *EventCreate {
 func (ec *EventCreate) SetOwner(a *Alert) *EventCreate {
 	return ec.SetOwnerID(a.ID)
 	return ec.SetOwnerID(a.ID)
 }
 }
@@ -242,7 +242,7 @@ func (ec *EventCreate) createSpec() (*Event, *sqlgraph.CreateSpec) {
 	return _node, _spec
 	return _node, _spec
 }
 }
 
 
-// EventCreateBulk is the builder for creating a bulk of Event entities.
+// EventCreateBulk is the builder for creating many Event entities in bulk.
 type EventCreateBulk struct {
 type EventCreateBulk struct {
 	config
 	config
 	builders []*EventCreate
 	builders []*EventCreate
@@ -300,7 +300,7 @@ func (ecb *EventCreateBulk) Save(ctx context.Context) ([]*Event, error) {
 	return nodes, nil
 	return nodes, nil
 }
 }
 
 
-// SaveX calls Save and panics if Save returns an error.
+// SaveX is like Save, but panics if an error occurs.
 func (ecb *EventCreateBulk) SaveX(ctx context.Context) []*Event {
 func (ecb *EventCreateBulk) SaveX(ctx context.Context) []*Event {
 	v, err := ecb.Save(ctx)
 	v, err := ecb.Save(ctx)
 	if err != nil {
 	if err != nil {

+ 5 - 6
pkg/database/ent/event_delete.go

@@ -16,14 +16,13 @@ import (
 // EventDelete is the builder for deleting a Event entity.
 // EventDelete is the builder for deleting a Event entity.
 type EventDelete struct {
 type EventDelete struct {
 	config
 	config
-	hooks      []Hook
-	mutation   *EventMutation
-	predicates []predicate.Event
+	hooks    []Hook
+	mutation *EventMutation
 }
 }
 
 
-// Where adds a new predicate to the delete builder.
+// Where adds a new predicate to the EventDelete builder.
 func (ed *EventDelete) Where(ps ...predicate.Event) *EventDelete {
 func (ed *EventDelete) Where(ps ...predicate.Event) *EventDelete {
-	ed.predicates = append(ed.predicates, ps...)
+	ed.mutation.predicates = append(ed.mutation.predicates, ps...)
 	return ed
 	return ed
 }
 }
 
 
@@ -75,7 +74,7 @@ func (ed *EventDelete) sqlExec(ctx context.Context) (int, error) {
 			},
 			},
 		},
 		},
 	}
 	}
-	if ps := ed.predicates; len(ps) > 0 {
+	if ps := ed.mutation.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
 				ps[i](selector)
 				ps[i](selector)

+ 78 - 65
pkg/database/ent/event_query.go

@@ -22,7 +22,7 @@ type EventQuery struct {
 	limit      *int
 	limit      *int
 	offset     *int
 	offset     *int
 	order      []OrderFunc
 	order      []OrderFunc
-	unique     []string
+	fields     []string
 	predicates []predicate.Event
 	predicates []predicate.Event
 	// eager-loading edges.
 	// eager-loading edges.
 	withOwner *AlertQuery
 	withOwner *AlertQuery
@@ -32,7 +32,7 @@ type EventQuery struct {
 	path func(context.Context) (*sql.Selector, error)
 	path func(context.Context) (*sql.Selector, error)
 }
 }
 
 
-// Where adds a new predicate for the builder.
+// Where adds a new predicate for the EventQuery builder.
 func (eq *EventQuery) Where(ps ...predicate.Event) *EventQuery {
 func (eq *EventQuery) Where(ps ...predicate.Event) *EventQuery {
 	eq.predicates = append(eq.predicates, ps...)
 	eq.predicates = append(eq.predicates, ps...)
 	return eq
 	return eq
@@ -56,7 +56,7 @@ func (eq *EventQuery) Order(o ...OrderFunc) *EventQuery {
 	return eq
 	return eq
 }
 }
 
 
-// QueryOwner chains the current query on the owner edge.
+// QueryOwner chains the current query on the "owner" edge.
 func (eq *EventQuery) QueryOwner() *AlertQuery {
 func (eq *EventQuery) QueryOwner() *AlertQuery {
 	query := &AlertQuery{config: eq.config}
 	query := &AlertQuery{config: eq.config}
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
@@ -78,7 +78,8 @@ func (eq *EventQuery) QueryOwner() *AlertQuery {
 	return query
 	return query
 }
 }
 
 
-// First returns the first Event entity in the query. Returns *NotFoundError when no event was found.
+// First returns the first Event entity from the query.
+// Returns a *NotFoundError when no Event was found.
 func (eq *EventQuery) First(ctx context.Context) (*Event, error) {
 func (eq *EventQuery) First(ctx context.Context) (*Event, error) {
 	nodes, err := eq.Limit(1).All(ctx)
 	nodes, err := eq.Limit(1).All(ctx)
 	if err != nil {
 	if err != nil {
@@ -99,7 +100,8 @@ func (eq *EventQuery) FirstX(ctx context.Context) *Event {
 	return node
 	return node
 }
 }
 
 
-// FirstID returns the first Event id in the query. Returns *NotFoundError when no id was found.
+// FirstID returns the first Event ID from the query.
+// Returns a *NotFoundError when no Event ID was found.
 func (eq *EventQuery) FirstID(ctx context.Context) (id int, err error) {
 func (eq *EventQuery) FirstID(ctx context.Context) (id int, err error) {
 	var ids []int
 	var ids []int
 	if ids, err = eq.Limit(1).IDs(ctx); err != nil {
 	if ids, err = eq.Limit(1).IDs(ctx); err != nil {
@@ -112,8 +114,8 @@ func (eq *EventQuery) FirstID(ctx context.Context) (id int, err error) {
 	return ids[0], nil
 	return ids[0], nil
 }
 }
 
 
-// FirstXID is like FirstID, but panics if an error occurs.
-func (eq *EventQuery) FirstXID(ctx context.Context) int {
+// FirstIDX is like FirstID, but panics if an error occurs.
+func (eq *EventQuery) FirstIDX(ctx context.Context) int {
 	id, err := eq.FirstID(ctx)
 	id, err := eq.FirstID(ctx)
 	if err != nil && !IsNotFound(err) {
 	if err != nil && !IsNotFound(err) {
 		panic(err)
 		panic(err)
@@ -121,7 +123,9 @@ func (eq *EventQuery) FirstXID(ctx context.Context) int {
 	return id
 	return id
 }
 }
 
 
-// Only returns the only Event entity in the query, returns an error if not exactly one entity was returned.
+// Only returns a single Event entity found by the query, ensuring it only returns one.
+// Returns a *NotSingularError when exactly one Event entity is not found.
+// Returns a *NotFoundError when no Event entities are found.
 func (eq *EventQuery) Only(ctx context.Context) (*Event, error) {
 func (eq *EventQuery) Only(ctx context.Context) (*Event, error) {
 	nodes, err := eq.Limit(2).All(ctx)
 	nodes, err := eq.Limit(2).All(ctx)
 	if err != nil {
 	if err != nil {
@@ -146,7 +150,9 @@ func (eq *EventQuery) OnlyX(ctx context.Context) *Event {
 	return node
 	return node
 }
 }
 
 
-// OnlyID returns the only Event id in the query, returns an error if not exactly one id was returned.
+// OnlyID is like Only, but returns the only Event ID in the query.
+// Returns a *NotSingularError when exactly one Event ID is not found.
+// Returns a *NotFoundError when no entities are found.
 func (eq *EventQuery) OnlyID(ctx context.Context) (id int, err error) {
 func (eq *EventQuery) OnlyID(ctx context.Context) (id int, err error) {
 	var ids []int
 	var ids []int
 	if ids, err = eq.Limit(2).IDs(ctx); err != nil {
 	if ids, err = eq.Limit(2).IDs(ctx); err != nil {
@@ -189,7 +195,7 @@ func (eq *EventQuery) AllX(ctx context.Context) []*Event {
 	return nodes
 	return nodes
 }
 }
 
 
-// IDs executes the query and returns a list of Event ids.
+// IDs executes the query and returns a list of Event IDs.
 func (eq *EventQuery) IDs(ctx context.Context) ([]int, error) {
 func (eq *EventQuery) IDs(ctx context.Context) ([]int, error) {
 	var ids []int
 	var ids []int
 	if err := eq.Select(event.FieldID).Scan(ctx, &ids); err != nil {
 	if err := eq.Select(event.FieldID).Scan(ctx, &ids); err != nil {
@@ -241,24 +247,27 @@ func (eq *EventQuery) ExistX(ctx context.Context) bool {
 	return exist
 	return exist
 }
 }
 
 
-// Clone returns a duplicate of the query builder, including all associated steps. It can be
+// Clone returns a duplicate of the EventQuery builder, including all associated steps. It can be
 // used to prepare common query builders and use them differently after the clone is made.
 // used to prepare common query builders and use them differently after the clone is made.
 func (eq *EventQuery) Clone() *EventQuery {
 func (eq *EventQuery) Clone() *EventQuery {
+	if eq == nil {
+		return nil
+	}
 	return &EventQuery{
 	return &EventQuery{
 		config:     eq.config,
 		config:     eq.config,
 		limit:      eq.limit,
 		limit:      eq.limit,
 		offset:     eq.offset,
 		offset:     eq.offset,
 		order:      append([]OrderFunc{}, eq.order...),
 		order:      append([]OrderFunc{}, eq.order...),
-		unique:     append([]string{}, eq.unique...),
 		predicates: append([]predicate.Event{}, eq.predicates...),
 		predicates: append([]predicate.Event{}, eq.predicates...),
+		withOwner:  eq.withOwner.Clone(),
 		// clone intermediate query.
 		// clone intermediate query.
 		sql:  eq.sql.Clone(),
 		sql:  eq.sql.Clone(),
 		path: eq.path,
 		path: eq.path,
 	}
 	}
 }
 }
 
 
-//  WithOwner tells the query-builder to eager-loads the nodes that are connected to
-// the "owner" edge. The optional arguments used to configure the query builder of the edge.
+// WithOwner tells the query-builder to eager-load the nodes that are connected to
+// the "owner" edge. The optional arguments are used to configure the query builder of the edge.
 func (eq *EventQuery) WithOwner(opts ...func(*AlertQuery)) *EventQuery {
 func (eq *EventQuery) WithOwner(opts ...func(*AlertQuery)) *EventQuery {
 	query := &AlertQuery{config: eq.config}
 	query := &AlertQuery{config: eq.config}
 	for _, opt := range opts {
 	for _, opt := range opts {
@@ -268,7 +277,7 @@ func (eq *EventQuery) WithOwner(opts ...func(*AlertQuery)) *EventQuery {
 	return eq
 	return eq
 }
 }
 
 
-// GroupBy used to group vertices by one or more fields/columns.
+// GroupBy is used to group vertices by one or more fields/columns.
 // It is often used with aggregate functions, like: count, max, mean, min, sum.
 // It is often used with aggregate functions, like: count, max, mean, min, sum.
 //
 //
 // Example:
 // Example:
@@ -295,7 +304,8 @@ func (eq *EventQuery) GroupBy(field string, fields ...string) *EventGroupBy {
 	return group
 	return group
 }
 }
 
 
-// Select one or more fields from the given query.
+// Select allows the selection one or more fields/columns for the given query,
+// instead of selecting all fields in the entity.
 //
 //
 // Example:
 // Example:
 //
 //
@@ -308,18 +318,16 @@ func (eq *EventQuery) GroupBy(field string, fields ...string) *EventGroupBy {
 //		Scan(ctx, &v)
 //		Scan(ctx, &v)
 //
 //
 func (eq *EventQuery) Select(field string, fields ...string) *EventSelect {
 func (eq *EventQuery) Select(field string, fields ...string) *EventSelect {
-	selector := &EventSelect{config: eq.config}
-	selector.fields = append([]string{field}, fields...)
-	selector.path = func(ctx context.Context) (prev *sql.Selector, err error) {
-		if err := eq.prepareQuery(ctx); err != nil {
-			return nil, err
-		}
-		return eq.sqlQuery(), nil
-	}
-	return selector
+	eq.fields = append([]string{field}, fields...)
+	return &EventSelect{EventQuery: eq}
 }
 }
 
 
 func (eq *EventQuery) prepareQuery(ctx context.Context) error {
 func (eq *EventQuery) prepareQuery(ctx context.Context) error {
+	for _, f := range eq.fields {
+		if !event.ValidColumn(f) {
+			return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+		}
+	}
 	if eq.path != nil {
 	if eq.path != nil {
 		prev, err := eq.path(ctx)
 		prev, err := eq.path(ctx)
 		if err != nil {
 		if err != nil {
@@ -345,22 +353,18 @@ func (eq *EventQuery) sqlAll(ctx context.Context) ([]*Event, error) {
 	if withFKs {
 	if withFKs {
 		_spec.Node.Columns = append(_spec.Node.Columns, event.ForeignKeys...)
 		_spec.Node.Columns = append(_spec.Node.Columns, event.ForeignKeys...)
 	}
 	}
-	_spec.ScanValues = func() []interface{} {
+	_spec.ScanValues = func(columns []string) ([]interface{}, error) {
 		node := &Event{config: eq.config}
 		node := &Event{config: eq.config}
 		nodes = append(nodes, node)
 		nodes = append(nodes, node)
-		values := node.scanValues()
-		if withFKs {
-			values = append(values, node.fkValues()...)
-		}
-		return values
+		return node.scanValues(columns)
 	}
 	}
-	_spec.Assign = func(values ...interface{}) error {
+	_spec.Assign = func(columns []string, values []interface{}) error {
 		if len(nodes) == 0 {
 		if len(nodes) == 0 {
 			return fmt.Errorf("ent: Assign called without calling ScanValues")
 			return fmt.Errorf("ent: Assign called without calling ScanValues")
 		}
 		}
 		node := nodes[len(nodes)-1]
 		node := nodes[len(nodes)-1]
 		node.Edges.loadedTypes = loadedTypes
 		node.Edges.loadedTypes = loadedTypes
-		return node.assignValues(values...)
+		return node.assignValues(columns, values)
 	}
 	}
 	if err := sqlgraph.QueryNodes(ctx, eq.driver, _spec); err != nil {
 	if err := sqlgraph.QueryNodes(ctx, eq.driver, _spec); err != nil {
 		return nil, err
 		return nil, err
@@ -423,6 +427,15 @@ func (eq *EventQuery) querySpec() *sqlgraph.QuerySpec {
 		From:   eq.sql,
 		From:   eq.sql,
 		Unique: true,
 		Unique: true,
 	}
 	}
+	if fields := eq.fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, event.FieldID)
+		for i := range fields {
+			if fields[i] != event.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
+			}
+		}
+	}
 	if ps := eq.predicates; len(ps) > 0 {
 	if ps := eq.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
@@ -471,7 +484,7 @@ func (eq *EventQuery) sqlQuery() *sql.Selector {
 	return selector
 	return selector
 }
 }
 
 
-// EventGroupBy is the builder for group-by Event entities.
+// EventGroupBy is the group-by builder for Event entities.
 type EventGroupBy struct {
 type EventGroupBy struct {
 	config
 	config
 	fields []string
 	fields []string
@@ -487,7 +500,7 @@ func (egb *EventGroupBy) Aggregate(fns ...AggregateFunc) *EventGroupBy {
 	return egb
 	return egb
 }
 }
 
 
-// Scan applies the group-by query and scan the result into the given value.
+// Scan applies the group-by query and scans the result into the given value.
 func (egb *EventGroupBy) Scan(ctx context.Context, v interface{}) error {
 func (egb *EventGroupBy) Scan(ctx context.Context, v interface{}) error {
 	query, err := egb.path(ctx)
 	query, err := egb.path(ctx)
 	if err != nil {
 	if err != nil {
@@ -504,7 +517,8 @@ func (egb *EventGroupBy) ScanX(ctx context.Context, v interface{}) {
 	}
 	}
 }
 }
 
 
-// Strings returns list of strings from group-by. It is only allowed when querying group-by with one field.
+// Strings returns list of strings from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (egb *EventGroupBy) Strings(ctx context.Context) ([]string, error) {
 func (egb *EventGroupBy) Strings(ctx context.Context) ([]string, error) {
 	if len(egb.fields) > 1 {
 	if len(egb.fields) > 1 {
 		return nil, errors.New("ent: EventGroupBy.Strings is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: EventGroupBy.Strings is not achievable when grouping more than 1 field")
@@ -525,7 +539,8 @@ func (egb *EventGroupBy) StringsX(ctx context.Context) []string {
 	return v
 	return v
 }
 }
 
 
-// String returns a single string from group-by. It is only allowed when querying group-by with one field.
+// String returns a single string from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (egb *EventGroupBy) String(ctx context.Context) (_ string, err error) {
 func (egb *EventGroupBy) String(ctx context.Context) (_ string, err error) {
 	var v []string
 	var v []string
 	if v, err = egb.Strings(ctx); err != nil {
 	if v, err = egb.Strings(ctx); err != nil {
@@ -551,7 +566,8 @@ func (egb *EventGroupBy) StringX(ctx context.Context) string {
 	return v
 	return v
 }
 }
 
 
-// Ints returns list of ints from group-by. It is only allowed when querying group-by with one field.
+// Ints returns list of ints from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (egb *EventGroupBy) Ints(ctx context.Context) ([]int, error) {
 func (egb *EventGroupBy) Ints(ctx context.Context) ([]int, error) {
 	if len(egb.fields) > 1 {
 	if len(egb.fields) > 1 {
 		return nil, errors.New("ent: EventGroupBy.Ints is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: EventGroupBy.Ints is not achievable when grouping more than 1 field")
@@ -572,7 +588,8 @@ func (egb *EventGroupBy) IntsX(ctx context.Context) []int {
 	return v
 	return v
 }
 }
 
 
-// Int returns a single int from group-by. It is only allowed when querying group-by with one field.
+// Int returns a single int from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (egb *EventGroupBy) Int(ctx context.Context) (_ int, err error) {
 func (egb *EventGroupBy) Int(ctx context.Context) (_ int, err error) {
 	var v []int
 	var v []int
 	if v, err = egb.Ints(ctx); err != nil {
 	if v, err = egb.Ints(ctx); err != nil {
@@ -598,7 +615,8 @@ func (egb *EventGroupBy) IntX(ctx context.Context) int {
 	return v
 	return v
 }
 }
 
 
-// Float64s returns list of float64s from group-by. It is only allowed when querying group-by with one field.
+// Float64s returns list of float64s from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (egb *EventGroupBy) Float64s(ctx context.Context) ([]float64, error) {
 func (egb *EventGroupBy) Float64s(ctx context.Context) ([]float64, error) {
 	if len(egb.fields) > 1 {
 	if len(egb.fields) > 1 {
 		return nil, errors.New("ent: EventGroupBy.Float64s is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: EventGroupBy.Float64s is not achievable when grouping more than 1 field")
@@ -619,7 +637,8 @@ func (egb *EventGroupBy) Float64sX(ctx context.Context) []float64 {
 	return v
 	return v
 }
 }
 
 
-// Float64 returns a single float64 from group-by. It is only allowed when querying group-by with one field.
+// Float64 returns a single float64 from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (egb *EventGroupBy) Float64(ctx context.Context) (_ float64, err error) {
 func (egb *EventGroupBy) Float64(ctx context.Context) (_ float64, err error) {
 	var v []float64
 	var v []float64
 	if v, err = egb.Float64s(ctx); err != nil {
 	if v, err = egb.Float64s(ctx); err != nil {
@@ -645,7 +664,8 @@ func (egb *EventGroupBy) Float64X(ctx context.Context) float64 {
 	return v
 	return v
 }
 }
 
 
-// Bools returns list of bools from group-by. It is only allowed when querying group-by with one field.
+// Bools returns list of bools from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (egb *EventGroupBy) Bools(ctx context.Context) ([]bool, error) {
 func (egb *EventGroupBy) Bools(ctx context.Context) ([]bool, error) {
 	if len(egb.fields) > 1 {
 	if len(egb.fields) > 1 {
 		return nil, errors.New("ent: EventGroupBy.Bools is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: EventGroupBy.Bools is not achievable when grouping more than 1 field")
@@ -666,7 +686,8 @@ func (egb *EventGroupBy) BoolsX(ctx context.Context) []bool {
 	return v
 	return v
 }
 }
 
 
-// Bool returns a single bool from group-by. It is only allowed when querying group-by with one field.
+// Bool returns a single bool from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (egb *EventGroupBy) Bool(ctx context.Context) (_ bool, err error) {
 func (egb *EventGroupBy) Bool(ctx context.Context) (_ bool, err error) {
 	var v []bool
 	var v []bool
 	if v, err = egb.Bools(ctx); err != nil {
 	if v, err = egb.Bools(ctx); err != nil {
@@ -721,22 +742,19 @@ func (egb *EventGroupBy) sqlQuery() *sql.Selector {
 	return selector.Select(columns...).GroupBy(egb.fields...)
 	return selector.Select(columns...).GroupBy(egb.fields...)
 }
 }
 
 
-// EventSelect is the builder for select fields of Event entities.
+// EventSelect is the builder for selecting fields of Event entities.
 type EventSelect struct {
 type EventSelect struct {
-	config
-	fields []string
+	*EventQuery
 	// intermediate query (i.e. traversal path).
 	// intermediate query (i.e. traversal path).
-	sql  *sql.Selector
-	path func(context.Context) (*sql.Selector, error)
+	sql *sql.Selector
 }
 }
 
 
-// Scan applies the selector query and scan the result into the given value.
+// Scan applies the selector query and scans the result into the given value.
 func (es *EventSelect) Scan(ctx context.Context, v interface{}) error {
 func (es *EventSelect) Scan(ctx context.Context, v interface{}) error {
-	query, err := es.path(ctx)
-	if err != nil {
+	if err := es.prepareQuery(ctx); err != nil {
 		return err
 		return err
 	}
 	}
-	es.sql = query
+	es.sql = es.EventQuery.sqlQuery()
 	return es.sqlScan(ctx, v)
 	return es.sqlScan(ctx, v)
 }
 }
 
 
@@ -747,7 +765,7 @@ func (es *EventSelect) ScanX(ctx context.Context, v interface{}) {
 	}
 	}
 }
 }
 
 
-// Strings returns list of strings from selector. It is only allowed when selecting one field.
+// Strings returns list of strings from a selector. It is only allowed when selecting one field.
 func (es *EventSelect) Strings(ctx context.Context) ([]string, error) {
 func (es *EventSelect) Strings(ctx context.Context) ([]string, error) {
 	if len(es.fields) > 1 {
 	if len(es.fields) > 1 {
 		return nil, errors.New("ent: EventSelect.Strings is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: EventSelect.Strings is not achievable when selecting more than 1 field")
@@ -768,7 +786,7 @@ func (es *EventSelect) StringsX(ctx context.Context) []string {
 	return v
 	return v
 }
 }
 
 
-// String returns a single string from selector. It is only allowed when selecting one field.
+// String returns a single string from a selector. It is only allowed when selecting one field.
 func (es *EventSelect) String(ctx context.Context) (_ string, err error) {
 func (es *EventSelect) String(ctx context.Context) (_ string, err error) {
 	var v []string
 	var v []string
 	if v, err = es.Strings(ctx); err != nil {
 	if v, err = es.Strings(ctx); err != nil {
@@ -794,7 +812,7 @@ func (es *EventSelect) StringX(ctx context.Context) string {
 	return v
 	return v
 }
 }
 
 
-// Ints returns list of ints from selector. It is only allowed when selecting one field.
+// Ints returns list of ints from a selector. It is only allowed when selecting one field.
 func (es *EventSelect) Ints(ctx context.Context) ([]int, error) {
 func (es *EventSelect) Ints(ctx context.Context) ([]int, error) {
 	if len(es.fields) > 1 {
 	if len(es.fields) > 1 {
 		return nil, errors.New("ent: EventSelect.Ints is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: EventSelect.Ints is not achievable when selecting more than 1 field")
@@ -815,7 +833,7 @@ func (es *EventSelect) IntsX(ctx context.Context) []int {
 	return v
 	return v
 }
 }
 
 
-// Int returns a single int from selector. It is only allowed when selecting one field.
+// Int returns a single int from a selector. It is only allowed when selecting one field.
 func (es *EventSelect) Int(ctx context.Context) (_ int, err error) {
 func (es *EventSelect) Int(ctx context.Context) (_ int, err error) {
 	var v []int
 	var v []int
 	if v, err = es.Ints(ctx); err != nil {
 	if v, err = es.Ints(ctx); err != nil {
@@ -841,7 +859,7 @@ func (es *EventSelect) IntX(ctx context.Context) int {
 	return v
 	return v
 }
 }
 
 
-// Float64s returns list of float64s from selector. It is only allowed when selecting one field.
+// Float64s returns list of float64s from a selector. It is only allowed when selecting one field.
 func (es *EventSelect) Float64s(ctx context.Context) ([]float64, error) {
 func (es *EventSelect) Float64s(ctx context.Context) ([]float64, error) {
 	if len(es.fields) > 1 {
 	if len(es.fields) > 1 {
 		return nil, errors.New("ent: EventSelect.Float64s is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: EventSelect.Float64s is not achievable when selecting more than 1 field")
@@ -862,7 +880,7 @@ func (es *EventSelect) Float64sX(ctx context.Context) []float64 {
 	return v
 	return v
 }
 }
 
 
-// Float64 returns a single float64 from selector. It is only allowed when selecting one field.
+// Float64 returns a single float64 from a selector. It is only allowed when selecting one field.
 func (es *EventSelect) Float64(ctx context.Context) (_ float64, err error) {
 func (es *EventSelect) Float64(ctx context.Context) (_ float64, err error) {
 	var v []float64
 	var v []float64
 	if v, err = es.Float64s(ctx); err != nil {
 	if v, err = es.Float64s(ctx); err != nil {
@@ -888,7 +906,7 @@ func (es *EventSelect) Float64X(ctx context.Context) float64 {
 	return v
 	return v
 }
 }
 
 
-// Bools returns list of bools from selector. It is only allowed when selecting one field.
+// Bools returns list of bools from a selector. It is only allowed when selecting one field.
 func (es *EventSelect) Bools(ctx context.Context) ([]bool, error) {
 func (es *EventSelect) Bools(ctx context.Context) ([]bool, error) {
 	if len(es.fields) > 1 {
 	if len(es.fields) > 1 {
 		return nil, errors.New("ent: EventSelect.Bools is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: EventSelect.Bools is not achievable when selecting more than 1 field")
@@ -909,7 +927,7 @@ func (es *EventSelect) BoolsX(ctx context.Context) []bool {
 	return v
 	return v
 }
 }
 
 
-// Bool returns a single bool from selector. It is only allowed when selecting one field.
+// Bool returns a single bool from a selector. It is only allowed when selecting one field.
 func (es *EventSelect) Bool(ctx context.Context) (_ bool, err error) {
 func (es *EventSelect) Bool(ctx context.Context) (_ bool, err error) {
 	var v []bool
 	var v []bool
 	if v, err = es.Bools(ctx); err != nil {
 	if v, err = es.Bools(ctx); err != nil {
@@ -936,11 +954,6 @@ func (es *EventSelect) BoolX(ctx context.Context) bool {
 }
 }
 
 
 func (es *EventSelect) sqlScan(ctx context.Context, v interface{}) error {
 func (es *EventSelect) sqlScan(ctx context.Context, v interface{}) error {
-	for _, f := range es.fields {
-		if !event.ValidColumn(f) {
-			return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for selection", f)}
-		}
-	}
 	rows := &sql.Rows{}
 	rows := &sql.Rows{}
 	query, args := es.sqlQuery().Query()
 	query, args := es.sqlQuery().Query()
 	if err := es.driver.Query(ctx, query, args, rows); err != nil {
 	if err := es.driver.Query(ctx, query, args, rows); err != nil {

+ 28 - 29
pkg/database/ent/event_update.go

@@ -18,24 +18,23 @@ import (
 // EventUpdate is the builder for updating Event entities.
 // EventUpdate is the builder for updating Event entities.
 type EventUpdate struct {
 type EventUpdate struct {
 	config
 	config
-	hooks      []Hook
-	mutation   *EventMutation
-	predicates []predicate.Event
+	hooks    []Hook
+	mutation *EventMutation
 }
 }
 
 
-// Where adds a new predicate for the builder.
+// Where adds a new predicate for the EventUpdate builder.
 func (eu *EventUpdate) Where(ps ...predicate.Event) *EventUpdate {
 func (eu *EventUpdate) Where(ps ...predicate.Event) *EventUpdate {
-	eu.predicates = append(eu.predicates, ps...)
+	eu.mutation.predicates = append(eu.mutation.predicates, ps...)
 	return eu
 	return eu
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (eu *EventUpdate) SetCreatedAt(t time.Time) *EventUpdate {
 func (eu *EventUpdate) SetCreatedAt(t time.Time) *EventUpdate {
 	eu.mutation.SetCreatedAt(t)
 	eu.mutation.SetCreatedAt(t)
 	return eu
 	return eu
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (eu *EventUpdate) SetNillableCreatedAt(t *time.Time) *EventUpdate {
 func (eu *EventUpdate) SetNillableCreatedAt(t *time.Time) *EventUpdate {
 	if t != nil {
 	if t != nil {
 		eu.SetCreatedAt(*t)
 		eu.SetCreatedAt(*t)
@@ -43,13 +42,13 @@ func (eu *EventUpdate) SetNillableCreatedAt(t *time.Time) *EventUpdate {
 	return eu
 	return eu
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (eu *EventUpdate) SetUpdatedAt(t time.Time) *EventUpdate {
 func (eu *EventUpdate) SetUpdatedAt(t time.Time) *EventUpdate {
 	eu.mutation.SetUpdatedAt(t)
 	eu.mutation.SetUpdatedAt(t)
 	return eu
 	return eu
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (eu *EventUpdate) SetNillableUpdatedAt(t *time.Time) *EventUpdate {
 func (eu *EventUpdate) SetNillableUpdatedAt(t *time.Time) *EventUpdate {
 	if t != nil {
 	if t != nil {
 		eu.SetUpdatedAt(*t)
 		eu.SetUpdatedAt(*t)
@@ -57,25 +56,25 @@ func (eu *EventUpdate) SetNillableUpdatedAt(t *time.Time) *EventUpdate {
 	return eu
 	return eu
 }
 }
 
 
-// SetTime sets the time field.
+// SetTime sets the "time" field.
 func (eu *EventUpdate) SetTime(t time.Time) *EventUpdate {
 func (eu *EventUpdate) SetTime(t time.Time) *EventUpdate {
 	eu.mutation.SetTime(t)
 	eu.mutation.SetTime(t)
 	return eu
 	return eu
 }
 }
 
 
-// SetSerialized sets the serialized field.
+// SetSerialized sets the "serialized" field.
 func (eu *EventUpdate) SetSerialized(s string) *EventUpdate {
 func (eu *EventUpdate) SetSerialized(s string) *EventUpdate {
 	eu.mutation.SetSerialized(s)
 	eu.mutation.SetSerialized(s)
 	return eu
 	return eu
 }
 }
 
 
-// SetOwnerID sets the owner edge to Alert by id.
+// SetOwnerID sets the "owner" edge to the Alert entity by ID.
 func (eu *EventUpdate) SetOwnerID(id int) *EventUpdate {
 func (eu *EventUpdate) SetOwnerID(id int) *EventUpdate {
 	eu.mutation.SetOwnerID(id)
 	eu.mutation.SetOwnerID(id)
 	return eu
 	return eu
 }
 }
 
 
-// SetNillableOwnerID sets the owner edge to Alert by id if the given value is not nil.
+// SetNillableOwnerID sets the "owner" edge to the Alert entity by ID if the given value is not nil.
 func (eu *EventUpdate) SetNillableOwnerID(id *int) *EventUpdate {
 func (eu *EventUpdate) SetNillableOwnerID(id *int) *EventUpdate {
 	if id != nil {
 	if id != nil {
 		eu = eu.SetOwnerID(*id)
 		eu = eu.SetOwnerID(*id)
@@ -83,7 +82,7 @@ func (eu *EventUpdate) SetNillableOwnerID(id *int) *EventUpdate {
 	return eu
 	return eu
 }
 }
 
 
-// SetOwner sets the owner edge to Alert.
+// SetOwner sets the "owner" edge to the Alert entity.
 func (eu *EventUpdate) SetOwner(a *Alert) *EventUpdate {
 func (eu *EventUpdate) SetOwner(a *Alert) *EventUpdate {
 	return eu.SetOwnerID(a.ID)
 	return eu.SetOwnerID(a.ID)
 }
 }
@@ -93,13 +92,13 @@ func (eu *EventUpdate) Mutation() *EventMutation {
 	return eu.mutation
 	return eu.mutation
 }
 }
 
 
-// ClearOwner clears the "owner" edge to type Alert.
+// ClearOwner clears the "owner" edge to the Alert entity.
 func (eu *EventUpdate) ClearOwner() *EventUpdate {
 func (eu *EventUpdate) ClearOwner() *EventUpdate {
 	eu.mutation.ClearOwner()
 	eu.mutation.ClearOwner()
 	return eu
 	return eu
 }
 }
 
 
-// Save executes the query and returns the number of rows/vertices matched by this operation.
+// Save executes the query and returns the number of nodes affected by the update operation.
 func (eu *EventUpdate) Save(ctx context.Context) (int, error) {
 func (eu *EventUpdate) Save(ctx context.Context) (int, error) {
 	var (
 	var (
 		err      error
 		err      error
@@ -177,7 +176,7 @@ func (eu *EventUpdate) sqlSave(ctx context.Context) (n int, err error) {
 			},
 			},
 		},
 		},
 	}
 	}
-	if ps := eu.predicates; len(ps) > 0 {
+	if ps := eu.mutation.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
 				ps[i](selector)
 				ps[i](selector)
@@ -265,13 +264,13 @@ type EventUpdateOne struct {
 	mutation *EventMutation
 	mutation *EventMutation
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (euo *EventUpdateOne) SetCreatedAt(t time.Time) *EventUpdateOne {
 func (euo *EventUpdateOne) SetCreatedAt(t time.Time) *EventUpdateOne {
 	euo.mutation.SetCreatedAt(t)
 	euo.mutation.SetCreatedAt(t)
 	return euo
 	return euo
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (euo *EventUpdateOne) SetNillableCreatedAt(t *time.Time) *EventUpdateOne {
 func (euo *EventUpdateOne) SetNillableCreatedAt(t *time.Time) *EventUpdateOne {
 	if t != nil {
 	if t != nil {
 		euo.SetCreatedAt(*t)
 		euo.SetCreatedAt(*t)
@@ -279,13 +278,13 @@ func (euo *EventUpdateOne) SetNillableCreatedAt(t *time.Time) *EventUpdateOne {
 	return euo
 	return euo
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (euo *EventUpdateOne) SetUpdatedAt(t time.Time) *EventUpdateOne {
 func (euo *EventUpdateOne) SetUpdatedAt(t time.Time) *EventUpdateOne {
 	euo.mutation.SetUpdatedAt(t)
 	euo.mutation.SetUpdatedAt(t)
 	return euo
 	return euo
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (euo *EventUpdateOne) SetNillableUpdatedAt(t *time.Time) *EventUpdateOne {
 func (euo *EventUpdateOne) SetNillableUpdatedAt(t *time.Time) *EventUpdateOne {
 	if t != nil {
 	if t != nil {
 		euo.SetUpdatedAt(*t)
 		euo.SetUpdatedAt(*t)
@@ -293,25 +292,25 @@ func (euo *EventUpdateOne) SetNillableUpdatedAt(t *time.Time) *EventUpdateOne {
 	return euo
 	return euo
 }
 }
 
 
-// SetTime sets the time field.
+// SetTime sets the "time" field.
 func (euo *EventUpdateOne) SetTime(t time.Time) *EventUpdateOne {
 func (euo *EventUpdateOne) SetTime(t time.Time) *EventUpdateOne {
 	euo.mutation.SetTime(t)
 	euo.mutation.SetTime(t)
 	return euo
 	return euo
 }
 }
 
 
-// SetSerialized sets the serialized field.
+// SetSerialized sets the "serialized" field.
 func (euo *EventUpdateOne) SetSerialized(s string) *EventUpdateOne {
 func (euo *EventUpdateOne) SetSerialized(s string) *EventUpdateOne {
 	euo.mutation.SetSerialized(s)
 	euo.mutation.SetSerialized(s)
 	return euo
 	return euo
 }
 }
 
 
-// SetOwnerID sets the owner edge to Alert by id.
+// SetOwnerID sets the "owner" edge to the Alert entity by ID.
 func (euo *EventUpdateOne) SetOwnerID(id int) *EventUpdateOne {
 func (euo *EventUpdateOne) SetOwnerID(id int) *EventUpdateOne {
 	euo.mutation.SetOwnerID(id)
 	euo.mutation.SetOwnerID(id)
 	return euo
 	return euo
 }
 }
 
 
-// SetNillableOwnerID sets the owner edge to Alert by id if the given value is not nil.
+// SetNillableOwnerID sets the "owner" edge to the Alert entity by ID if the given value is not nil.
 func (euo *EventUpdateOne) SetNillableOwnerID(id *int) *EventUpdateOne {
 func (euo *EventUpdateOne) SetNillableOwnerID(id *int) *EventUpdateOne {
 	if id != nil {
 	if id != nil {
 		euo = euo.SetOwnerID(*id)
 		euo = euo.SetOwnerID(*id)
@@ -319,7 +318,7 @@ func (euo *EventUpdateOne) SetNillableOwnerID(id *int) *EventUpdateOne {
 	return euo
 	return euo
 }
 }
 
 
-// SetOwner sets the owner edge to Alert.
+// SetOwner sets the "owner" edge to the Alert entity.
 func (euo *EventUpdateOne) SetOwner(a *Alert) *EventUpdateOne {
 func (euo *EventUpdateOne) SetOwner(a *Alert) *EventUpdateOne {
 	return euo.SetOwnerID(a.ID)
 	return euo.SetOwnerID(a.ID)
 }
 }
@@ -329,13 +328,13 @@ func (euo *EventUpdateOne) Mutation() *EventMutation {
 	return euo.mutation
 	return euo.mutation
 }
 }
 
 
-// ClearOwner clears the "owner" edge to type Alert.
+// ClearOwner clears the "owner" edge to the Alert entity.
 func (euo *EventUpdateOne) ClearOwner() *EventUpdateOne {
 func (euo *EventUpdateOne) ClearOwner() *EventUpdateOne {
 	euo.mutation.ClearOwner()
 	euo.mutation.ClearOwner()
 	return euo
 	return euo
 }
 }
 
 
-// Save executes the query and returns the updated entity.
+// Save executes the query and returns the updated Event entity.
 func (euo *EventUpdateOne) Save(ctx context.Context) (*Event, error) {
 func (euo *EventUpdateOne) Save(ctx context.Context) (*Event, error) {
 	var (
 	var (
 		err  error
 		err  error
@@ -483,7 +482,7 @@ func (euo *EventUpdateOne) sqlSave(ctx context.Context) (_node *Event, err error
 	}
 	}
 	_node = &Event{config: euo.config}
 	_node = &Event{config: euo.config}
 	_spec.Assign = _node.assignValues
 	_spec.Assign = _node.assignValues
-	_spec.ScanValues = _node.scanValues()
+	_spec.ScanValues = _node.scanValues
 	if err = sqlgraph.UpdateNode(ctx, euo.driver, _spec); err != nil {
 	if err = sqlgraph.UpdateNode(ctx, euo.driver, _spec); err != nil {
 		if _, ok := err.(*sqlgraph.NotFoundError); ok {
 		if _, ok := err.(*sqlgraph.NotFoundError); ok {
 			err = &NotFoundError{event.Label}
 			err = &NotFoundError{event.Label}

+ 10 - 5
pkg/database/ent/hook/hook.go

@@ -210,6 +210,15 @@ func Unless(hk ent.Hook, op ent.Op) ent.Hook {
 	return If(hk, Not(HasOp(op)))
 	return If(hk, Not(HasOp(op)))
 }
 }
 
 
+// FixedError is a hook returning a fixed error.
+func FixedError(err error) ent.Hook {
+	return func(ent.Mutator) ent.Mutator {
+		return ent.MutateFunc(func(context.Context, ent.Mutation) (ent.Value, error) {
+			return nil, err
+		})
+	}
+}
+
 // Reject returns a hook that rejects all operations that match op.
 // Reject returns a hook that rejects all operations that match op.
 //
 //
 //	func (T) Hooks() []ent.Hook {
 //	func (T) Hooks() []ent.Hook {
@@ -219,11 +228,7 @@ func Unless(hk ent.Hook, op ent.Op) ent.Hook {
 //	}
 //	}
 //
 //
 func Reject(op ent.Op) ent.Hook {
 func Reject(op ent.Op) ent.Hook {
-	hk := func(ent.Mutator) ent.Mutator {
-		return ent.MutateFunc(func(_ context.Context, m ent.Mutation) (ent.Value, error) {
-			return nil, fmt.Errorf("%s operation is not allowed", m.Op())
-		})
-	}
+	hk := FixedError(fmt.Errorf("%s operation is not allowed", op))
 	return On(hk, op)
 	return On(hk, op)
 }
 }
 
 

+ 85 - 68
pkg/database/ent/machine.go

@@ -58,95 +58,112 @@ func (e MachineEdges) AlertsOrErr() ([]*Alert, error) {
 }
 }
 
 
 // scanValues returns the types for scanning values from sql.Rows.
 // scanValues returns the types for scanning values from sql.Rows.
-func (*Machine) scanValues() []interface{} {
-	return []interface{}{
-		&sql.NullInt64{},  // id
-		&sql.NullTime{},   // created_at
-		&sql.NullTime{},   // updated_at
-		&sql.NullString{}, // machineId
-		&sql.NullString{}, // password
-		&sql.NullString{}, // ipAddress
-		&sql.NullString{}, // scenarios
-		&sql.NullString{}, // version
-		&sql.NullBool{},   // isValidated
-		&sql.NullString{}, // status
+func (*Machine) scanValues(columns []string) ([]interface{}, error) {
+	values := make([]interface{}, len(columns))
+	for i := range columns {
+		switch columns[i] {
+		case machine.FieldIsValidated:
+			values[i] = &sql.NullBool{}
+		case machine.FieldID:
+			values[i] = &sql.NullInt64{}
+		case machine.FieldMachineId, machine.FieldPassword, machine.FieldIpAddress, machine.FieldScenarios, machine.FieldVersion, machine.FieldStatus:
+			values[i] = &sql.NullString{}
+		case machine.FieldCreatedAt, machine.FieldUpdatedAt:
+			values[i] = &sql.NullTime{}
+		default:
+			return nil, fmt.Errorf("unexpected column %q for type Machine", columns[i])
+		}
 	}
 	}
+	return values, nil
 }
 }
 
 
 // assignValues assigns the values that were returned from sql.Rows (after scanning)
 // assignValues assigns the values that were returned from sql.Rows (after scanning)
 // to the Machine fields.
 // to the Machine fields.
-func (m *Machine) assignValues(values ...interface{}) error {
-	if m, n := len(values), len(machine.Columns); m < n {
+func (m *Machine) assignValues(columns []string, values []interface{}) error {
+	if m, n := len(values), len(columns); m < n {
 		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
 		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
 	}
 	}
-	value, ok := values[0].(*sql.NullInt64)
-	if !ok {
-		return fmt.Errorf("unexpected type %T for field id", value)
-	}
-	m.ID = int(value.Int64)
-	values = values[1:]
-	if value, ok := values[0].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field created_at", values[0])
-	} else if value.Valid {
-		m.CreatedAt = value.Time
-	}
-	if value, ok := values[1].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field updated_at", values[1])
-	} else if value.Valid {
-		m.UpdatedAt = value.Time
-	}
-	if value, ok := values[2].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field machineId", values[2])
-	} else if value.Valid {
-		m.MachineId = value.String
-	}
-	if value, ok := values[3].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field password", values[3])
-	} else if value.Valid {
-		m.Password = value.String
-	}
-	if value, ok := values[4].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field ipAddress", values[4])
-	} else if value.Valid {
-		m.IpAddress = value.String
-	}
-	if value, ok := values[5].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field scenarios", values[5])
-	} else if value.Valid {
-		m.Scenarios = value.String
-	}
-	if value, ok := values[6].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field version", values[6])
-	} else if value.Valid {
-		m.Version = value.String
-	}
-	if value, ok := values[7].(*sql.NullBool); !ok {
-		return fmt.Errorf("unexpected type %T for field isValidated", values[7])
-	} else if value.Valid {
-		m.IsValidated = value.Bool
-	}
-	if value, ok := values[8].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field status", values[8])
-	} else if value.Valid {
-		m.Status = value.String
+	for i := range columns {
+		switch columns[i] {
+		case machine.FieldID:
+			value, ok := values[i].(*sql.NullInt64)
+			if !ok {
+				return fmt.Errorf("unexpected type %T for field id", value)
+			}
+			m.ID = int(value.Int64)
+		case machine.FieldCreatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field created_at", values[i])
+			} else if value.Valid {
+				m.CreatedAt = value.Time
+			}
+		case machine.FieldUpdatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field updated_at", values[i])
+			} else if value.Valid {
+				m.UpdatedAt = value.Time
+			}
+		case machine.FieldMachineId:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field machineId", values[i])
+			} else if value.Valid {
+				m.MachineId = value.String
+			}
+		case machine.FieldPassword:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field password", values[i])
+			} else if value.Valid {
+				m.Password = value.String
+			}
+		case machine.FieldIpAddress:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field ipAddress", values[i])
+			} else if value.Valid {
+				m.IpAddress = value.String
+			}
+		case machine.FieldScenarios:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field scenarios", values[i])
+			} else if value.Valid {
+				m.Scenarios = value.String
+			}
+		case machine.FieldVersion:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field version", values[i])
+			} else if value.Valid {
+				m.Version = value.String
+			}
+		case machine.FieldIsValidated:
+			if value, ok := values[i].(*sql.NullBool); !ok {
+				return fmt.Errorf("unexpected type %T for field isValidated", values[i])
+			} else if value.Valid {
+				m.IsValidated = value.Bool
+			}
+		case machine.FieldStatus:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field status", values[i])
+			} else if value.Valid {
+				m.Status = value.String
+			}
+		}
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-// QueryAlerts queries the alerts edge of the Machine.
+// QueryAlerts queries the "alerts" edge of the Machine entity.
 func (m *Machine) QueryAlerts() *AlertQuery {
 func (m *Machine) QueryAlerts() *AlertQuery {
 	return (&MachineClient{config: m.config}).QueryAlerts(m)
 	return (&MachineClient{config: m.config}).QueryAlerts(m)
 }
 }
 
 
 // Update returns a builder for updating this Machine.
 // Update returns a builder for updating this Machine.
-// Note that, you need to call Machine.Unwrap() before calling this method, if this Machine
+// Note that you need to call Machine.Unwrap() before calling this method if this Machine
 // was returned from a transaction, and the transaction was committed or rolled back.
 // was returned from a transaction, and the transaction was committed or rolled back.
 func (m *Machine) Update() *MachineUpdateOne {
 func (m *Machine) Update() *MachineUpdateOne {
 	return (&MachineClient{config: m.config}).UpdateOne(m)
 	return (&MachineClient{config: m.config}).UpdateOne(m)
 }
 }
 
 
-// Unwrap unwraps the entity that was returned from a transaction after it was closed,
-// so that all next queries will be executed through the driver which created the transaction.
+// Unwrap unwraps the Machine entity that was returned from a transaction after it was closed,
+// so that all future queries will be executed through the driver which created the transaction.
 func (m *Machine) Unwrap() *Machine {
 func (m *Machine) Unwrap() *Machine {
 	tx, ok := m.config.driver.(*txDriver)
 	tx, ok := m.config.driver.(*txDriver)
 	if !ok {
 	if !ok {

+ 3 - 3
pkg/database/ent/machine/machine.go

@@ -69,12 +69,12 @@ func ValidColumn(column string) bool {
 }
 }
 
 
 var (
 var (
-	// DefaultCreatedAt holds the default value on creation for the created_at field.
+	// DefaultCreatedAt holds the default value on creation for the "created_at" field.
 	DefaultCreatedAt func() time.Time
 	DefaultCreatedAt func() time.Time
-	// DefaultUpdatedAt holds the default value on creation for the updated_at field.
+	// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
 	DefaultUpdatedAt func() time.Time
 	DefaultUpdatedAt func() time.Time
 	// ScenariosValidator is a validator for the "scenarios" field. It is called by the builders before save.
 	// ScenariosValidator is a validator for the "scenarios" field. It is called by the builders before save.
 	ScenariosValidator func(string) error
 	ScenariosValidator func(string) error
-	// DefaultIsValidated holds the default value on creation for the isValidated field.
+	// DefaultIsValidated holds the default value on creation for the "isValidated" field.
 	DefaultIsValidated bool
 	DefaultIsValidated bool
 )
 )

+ 3 - 3
pkg/database/ent/machine/where.go

@@ -10,7 +10,7 @@ import (
 	"github.com/facebook/ent/dialect/sql/sqlgraph"
 	"github.com/facebook/ent/dialect/sql/sqlgraph"
 )
 )
 
 
-// ID filters vertices based on their identifier.
+// ID filters vertices based on their ID field.
 func ID(id int) predicate.Machine {
 func ID(id int) predicate.Machine {
 	return predicate.Machine(func(s *sql.Selector) {
 	return predicate.Machine(func(s *sql.Selector) {
 		s.Where(sql.EQ(s.C(FieldID), id))
 		s.Where(sql.EQ(s.C(FieldID), id))
@@ -1058,7 +1058,7 @@ func HasAlertsWith(preds ...predicate.Alert) predicate.Machine {
 	})
 	})
 }
 }
 
 
-// And groups list of predicates with the AND operator between them.
+// And groups predicates with the AND operator between them.
 func And(predicates ...predicate.Machine) predicate.Machine {
 func And(predicates ...predicate.Machine) predicate.Machine {
 	return predicate.Machine(func(s *sql.Selector) {
 	return predicate.Machine(func(s *sql.Selector) {
 		s1 := s.Clone().SetP(nil)
 		s1 := s.Clone().SetP(nil)
@@ -1069,7 +1069,7 @@ func And(predicates ...predicate.Machine) predicate.Machine {
 	})
 	})
 }
 }
 
 
-// Or groups list of predicates with the OR operator between them.
+// Or groups predicates with the OR operator between them.
 func Or(predicates ...predicate.Machine) predicate.Machine {
 func Or(predicates ...predicate.Machine) predicate.Machine {
 	return predicate.Machine(func(s *sql.Selector) {
 	return predicate.Machine(func(s *sql.Selector) {
 		s1 := s.Clone().SetP(nil)
 		s1 := s.Clone().SetP(nil)

+ 19 - 19
pkg/database/ent/machine_create.go

@@ -21,13 +21,13 @@ type MachineCreate struct {
 	hooks    []Hook
 	hooks    []Hook
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (mc *MachineCreate) SetCreatedAt(t time.Time) *MachineCreate {
 func (mc *MachineCreate) SetCreatedAt(t time.Time) *MachineCreate {
 	mc.mutation.SetCreatedAt(t)
 	mc.mutation.SetCreatedAt(t)
 	return mc
 	return mc
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (mc *MachineCreate) SetNillableCreatedAt(t *time.Time) *MachineCreate {
 func (mc *MachineCreate) SetNillableCreatedAt(t *time.Time) *MachineCreate {
 	if t != nil {
 	if t != nil {
 		mc.SetCreatedAt(*t)
 		mc.SetCreatedAt(*t)
@@ -35,13 +35,13 @@ func (mc *MachineCreate) SetNillableCreatedAt(t *time.Time) *MachineCreate {
 	return mc
 	return mc
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (mc *MachineCreate) SetUpdatedAt(t time.Time) *MachineCreate {
 func (mc *MachineCreate) SetUpdatedAt(t time.Time) *MachineCreate {
 	mc.mutation.SetUpdatedAt(t)
 	mc.mutation.SetUpdatedAt(t)
 	return mc
 	return mc
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (mc *MachineCreate) SetNillableUpdatedAt(t *time.Time) *MachineCreate {
 func (mc *MachineCreate) SetNillableUpdatedAt(t *time.Time) *MachineCreate {
 	if t != nil {
 	if t != nil {
 		mc.SetUpdatedAt(*t)
 		mc.SetUpdatedAt(*t)
@@ -49,31 +49,31 @@ func (mc *MachineCreate) SetNillableUpdatedAt(t *time.Time) *MachineCreate {
 	return mc
 	return mc
 }
 }
 
 
-// SetMachineId sets the machineId field.
+// SetMachineId sets the "machineId" field.
 func (mc *MachineCreate) SetMachineId(s string) *MachineCreate {
 func (mc *MachineCreate) SetMachineId(s string) *MachineCreate {
 	mc.mutation.SetMachineId(s)
 	mc.mutation.SetMachineId(s)
 	return mc
 	return mc
 }
 }
 
 
-// SetPassword sets the password field.
+// SetPassword sets the "password" field.
 func (mc *MachineCreate) SetPassword(s string) *MachineCreate {
 func (mc *MachineCreate) SetPassword(s string) *MachineCreate {
 	mc.mutation.SetPassword(s)
 	mc.mutation.SetPassword(s)
 	return mc
 	return mc
 }
 }
 
 
-// SetIpAddress sets the ipAddress field.
+// SetIpAddress sets the "ipAddress" field.
 func (mc *MachineCreate) SetIpAddress(s string) *MachineCreate {
 func (mc *MachineCreate) SetIpAddress(s string) *MachineCreate {
 	mc.mutation.SetIpAddress(s)
 	mc.mutation.SetIpAddress(s)
 	return mc
 	return mc
 }
 }
 
 
-// SetScenarios sets the scenarios field.
+// SetScenarios sets the "scenarios" field.
 func (mc *MachineCreate) SetScenarios(s string) *MachineCreate {
 func (mc *MachineCreate) SetScenarios(s string) *MachineCreate {
 	mc.mutation.SetScenarios(s)
 	mc.mutation.SetScenarios(s)
 	return mc
 	return mc
 }
 }
 
 
-// SetNillableScenarios sets the scenarios field if the given value is not nil.
+// SetNillableScenarios sets the "scenarios" field if the given value is not nil.
 func (mc *MachineCreate) SetNillableScenarios(s *string) *MachineCreate {
 func (mc *MachineCreate) SetNillableScenarios(s *string) *MachineCreate {
 	if s != nil {
 	if s != nil {
 		mc.SetScenarios(*s)
 		mc.SetScenarios(*s)
@@ -81,13 +81,13 @@ func (mc *MachineCreate) SetNillableScenarios(s *string) *MachineCreate {
 	return mc
 	return mc
 }
 }
 
 
-// SetVersion sets the version field.
+// SetVersion sets the "version" field.
 func (mc *MachineCreate) SetVersion(s string) *MachineCreate {
 func (mc *MachineCreate) SetVersion(s string) *MachineCreate {
 	mc.mutation.SetVersion(s)
 	mc.mutation.SetVersion(s)
 	return mc
 	return mc
 }
 }
 
 
-// SetNillableVersion sets the version field if the given value is not nil.
+// SetNillableVersion sets the "version" field if the given value is not nil.
 func (mc *MachineCreate) SetNillableVersion(s *string) *MachineCreate {
 func (mc *MachineCreate) SetNillableVersion(s *string) *MachineCreate {
 	if s != nil {
 	if s != nil {
 		mc.SetVersion(*s)
 		mc.SetVersion(*s)
@@ -95,13 +95,13 @@ func (mc *MachineCreate) SetNillableVersion(s *string) *MachineCreate {
 	return mc
 	return mc
 }
 }
 
 
-// SetIsValidated sets the isValidated field.
+// SetIsValidated sets the "isValidated" field.
 func (mc *MachineCreate) SetIsValidated(b bool) *MachineCreate {
 func (mc *MachineCreate) SetIsValidated(b bool) *MachineCreate {
 	mc.mutation.SetIsValidated(b)
 	mc.mutation.SetIsValidated(b)
 	return mc
 	return mc
 }
 }
 
 
-// SetNillableIsValidated sets the isValidated field if the given value is not nil.
+// SetNillableIsValidated sets the "isValidated" field if the given value is not nil.
 func (mc *MachineCreate) SetNillableIsValidated(b *bool) *MachineCreate {
 func (mc *MachineCreate) SetNillableIsValidated(b *bool) *MachineCreate {
 	if b != nil {
 	if b != nil {
 		mc.SetIsValidated(*b)
 		mc.SetIsValidated(*b)
@@ -109,13 +109,13 @@ func (mc *MachineCreate) SetNillableIsValidated(b *bool) *MachineCreate {
 	return mc
 	return mc
 }
 }
 
 
-// SetStatus sets the status field.
+// SetStatus sets the "status" field.
 func (mc *MachineCreate) SetStatus(s string) *MachineCreate {
 func (mc *MachineCreate) SetStatus(s string) *MachineCreate {
 	mc.mutation.SetStatus(s)
 	mc.mutation.SetStatus(s)
 	return mc
 	return mc
 }
 }
 
 
-// SetNillableStatus sets the status field if the given value is not nil.
+// SetNillableStatus sets the "status" field if the given value is not nil.
 func (mc *MachineCreate) SetNillableStatus(s *string) *MachineCreate {
 func (mc *MachineCreate) SetNillableStatus(s *string) *MachineCreate {
 	if s != nil {
 	if s != nil {
 		mc.SetStatus(*s)
 		mc.SetStatus(*s)
@@ -123,13 +123,13 @@ func (mc *MachineCreate) SetNillableStatus(s *string) *MachineCreate {
 	return mc
 	return mc
 }
 }
 
 
-// AddAlertIDs adds the alerts edge to Alert by ids.
+// AddAlertIDs adds the "alerts" edge to the Alert entity by IDs.
 func (mc *MachineCreate) AddAlertIDs(ids ...int) *MachineCreate {
 func (mc *MachineCreate) AddAlertIDs(ids ...int) *MachineCreate {
 	mc.mutation.AddAlertIDs(ids...)
 	mc.mutation.AddAlertIDs(ids...)
 	return mc
 	return mc
 }
 }
 
 
-// AddAlerts adds the alerts edges to Alert.
+// AddAlerts adds the "alerts" edges to the Alert entity.
 func (mc *MachineCreate) AddAlerts(a ...*Alert) *MachineCreate {
 func (mc *MachineCreate) AddAlerts(a ...*Alert) *MachineCreate {
 	ids := make([]int, len(a))
 	ids := make([]int, len(a))
 	for i := range a {
 	for i := range a {
@@ -350,7 +350,7 @@ func (mc *MachineCreate) createSpec() (*Machine, *sqlgraph.CreateSpec) {
 	return _node, _spec
 	return _node, _spec
 }
 }
 
 
-// MachineCreateBulk is the builder for creating a bulk of Machine entities.
+// MachineCreateBulk is the builder for creating many Machine entities in bulk.
 type MachineCreateBulk struct {
 type MachineCreateBulk struct {
 	config
 	config
 	builders []*MachineCreate
 	builders []*MachineCreate
@@ -408,7 +408,7 @@ func (mcb *MachineCreateBulk) Save(ctx context.Context) ([]*Machine, error) {
 	return nodes, nil
 	return nodes, nil
 }
 }
 
 
-// SaveX calls Save and panics if Save returns an error.
+// SaveX is like Save, but panics if an error occurs.
 func (mcb *MachineCreateBulk) SaveX(ctx context.Context) []*Machine {
 func (mcb *MachineCreateBulk) SaveX(ctx context.Context) []*Machine {
 	v, err := mcb.Save(ctx)
 	v, err := mcb.Save(ctx)
 	if err != nil {
 	if err != nil {

+ 5 - 6
pkg/database/ent/machine_delete.go

@@ -16,14 +16,13 @@ import (
 // MachineDelete is the builder for deleting a Machine entity.
 // MachineDelete is the builder for deleting a Machine entity.
 type MachineDelete struct {
 type MachineDelete struct {
 	config
 	config
-	hooks      []Hook
-	mutation   *MachineMutation
-	predicates []predicate.Machine
+	hooks    []Hook
+	mutation *MachineMutation
 }
 }
 
 
-// Where adds a new predicate to the delete builder.
+// Where adds a new predicate to the MachineDelete builder.
 func (md *MachineDelete) Where(ps ...predicate.Machine) *MachineDelete {
 func (md *MachineDelete) Where(ps ...predicate.Machine) *MachineDelete {
-	md.predicates = append(md.predicates, ps...)
+	md.mutation.predicates = append(md.mutation.predicates, ps...)
 	return md
 	return md
 }
 }
 
 
@@ -75,7 +74,7 @@ func (md *MachineDelete) sqlExec(ctx context.Context) (int, error) {
 			},
 			},
 		},
 		},
 	}
 	}
-	if ps := md.predicates; len(ps) > 0 {
+	if ps := md.mutation.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
 				ps[i](selector)
 				ps[i](selector)

+ 79 - 62
pkg/database/ent/machine_query.go

@@ -23,7 +23,7 @@ type MachineQuery struct {
 	limit      *int
 	limit      *int
 	offset     *int
 	offset     *int
 	order      []OrderFunc
 	order      []OrderFunc
-	unique     []string
+	fields     []string
 	predicates []predicate.Machine
 	predicates []predicate.Machine
 	// eager-loading edges.
 	// eager-loading edges.
 	withAlerts *AlertQuery
 	withAlerts *AlertQuery
@@ -32,7 +32,7 @@ type MachineQuery struct {
 	path func(context.Context) (*sql.Selector, error)
 	path func(context.Context) (*sql.Selector, error)
 }
 }
 
 
-// Where adds a new predicate for the builder.
+// Where adds a new predicate for the MachineQuery builder.
 func (mq *MachineQuery) Where(ps ...predicate.Machine) *MachineQuery {
 func (mq *MachineQuery) Where(ps ...predicate.Machine) *MachineQuery {
 	mq.predicates = append(mq.predicates, ps...)
 	mq.predicates = append(mq.predicates, ps...)
 	return mq
 	return mq
@@ -56,7 +56,7 @@ func (mq *MachineQuery) Order(o ...OrderFunc) *MachineQuery {
 	return mq
 	return mq
 }
 }
 
 
-// QueryAlerts chains the current query on the alerts edge.
+// QueryAlerts chains the current query on the "alerts" edge.
 func (mq *MachineQuery) QueryAlerts() *AlertQuery {
 func (mq *MachineQuery) QueryAlerts() *AlertQuery {
 	query := &AlertQuery{config: mq.config}
 	query := &AlertQuery{config: mq.config}
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
@@ -78,7 +78,8 @@ func (mq *MachineQuery) QueryAlerts() *AlertQuery {
 	return query
 	return query
 }
 }
 
 
-// First returns the first Machine entity in the query. Returns *NotFoundError when no machine was found.
+// First returns the first Machine entity from the query.
+// Returns a *NotFoundError when no Machine was found.
 func (mq *MachineQuery) First(ctx context.Context) (*Machine, error) {
 func (mq *MachineQuery) First(ctx context.Context) (*Machine, error) {
 	nodes, err := mq.Limit(1).All(ctx)
 	nodes, err := mq.Limit(1).All(ctx)
 	if err != nil {
 	if err != nil {
@@ -99,7 +100,8 @@ func (mq *MachineQuery) FirstX(ctx context.Context) *Machine {
 	return node
 	return node
 }
 }
 
 
-// FirstID returns the first Machine id in the query. Returns *NotFoundError when no id was found.
+// FirstID returns the first Machine ID from the query.
+// Returns a *NotFoundError when no Machine ID was found.
 func (mq *MachineQuery) FirstID(ctx context.Context) (id int, err error) {
 func (mq *MachineQuery) FirstID(ctx context.Context) (id int, err error) {
 	var ids []int
 	var ids []int
 	if ids, err = mq.Limit(1).IDs(ctx); err != nil {
 	if ids, err = mq.Limit(1).IDs(ctx); err != nil {
@@ -112,8 +114,8 @@ func (mq *MachineQuery) FirstID(ctx context.Context) (id int, err error) {
 	return ids[0], nil
 	return ids[0], nil
 }
 }
 
 
-// FirstXID is like FirstID, but panics if an error occurs.
-func (mq *MachineQuery) FirstXID(ctx context.Context) int {
+// FirstIDX is like FirstID, but panics if an error occurs.
+func (mq *MachineQuery) FirstIDX(ctx context.Context) int {
 	id, err := mq.FirstID(ctx)
 	id, err := mq.FirstID(ctx)
 	if err != nil && !IsNotFound(err) {
 	if err != nil && !IsNotFound(err) {
 		panic(err)
 		panic(err)
@@ -121,7 +123,9 @@ func (mq *MachineQuery) FirstXID(ctx context.Context) int {
 	return id
 	return id
 }
 }
 
 
-// Only returns the only Machine entity in the query, returns an error if not exactly one entity was returned.
+// Only returns a single Machine entity found by the query, ensuring it only returns one.
+// Returns a *NotSingularError when exactly one Machine entity is not found.
+// Returns a *NotFoundError when no Machine entities are found.
 func (mq *MachineQuery) Only(ctx context.Context) (*Machine, error) {
 func (mq *MachineQuery) Only(ctx context.Context) (*Machine, error) {
 	nodes, err := mq.Limit(2).All(ctx)
 	nodes, err := mq.Limit(2).All(ctx)
 	if err != nil {
 	if err != nil {
@@ -146,7 +150,9 @@ func (mq *MachineQuery) OnlyX(ctx context.Context) *Machine {
 	return node
 	return node
 }
 }
 
 
-// OnlyID returns the only Machine id in the query, returns an error if not exactly one id was returned.
+// OnlyID is like Only, but returns the only Machine ID in the query.
+// Returns a *NotSingularError when exactly one Machine ID is not found.
+// Returns a *NotFoundError when no entities are found.
 func (mq *MachineQuery) OnlyID(ctx context.Context) (id int, err error) {
 func (mq *MachineQuery) OnlyID(ctx context.Context) (id int, err error) {
 	var ids []int
 	var ids []int
 	if ids, err = mq.Limit(2).IDs(ctx); err != nil {
 	if ids, err = mq.Limit(2).IDs(ctx); err != nil {
@@ -189,7 +195,7 @@ func (mq *MachineQuery) AllX(ctx context.Context) []*Machine {
 	return nodes
 	return nodes
 }
 }
 
 
-// IDs executes the query and returns a list of Machine ids.
+// IDs executes the query and returns a list of Machine IDs.
 func (mq *MachineQuery) IDs(ctx context.Context) ([]int, error) {
 func (mq *MachineQuery) IDs(ctx context.Context) ([]int, error) {
 	var ids []int
 	var ids []int
 	if err := mq.Select(machine.FieldID).Scan(ctx, &ids); err != nil {
 	if err := mq.Select(machine.FieldID).Scan(ctx, &ids); err != nil {
@@ -241,24 +247,27 @@ func (mq *MachineQuery) ExistX(ctx context.Context) bool {
 	return exist
 	return exist
 }
 }
 
 
-// Clone returns a duplicate of the query builder, including all associated steps. It can be
+// Clone returns a duplicate of the MachineQuery builder, including all associated steps. It can be
 // used to prepare common query builders and use them differently after the clone is made.
 // used to prepare common query builders and use them differently after the clone is made.
 func (mq *MachineQuery) Clone() *MachineQuery {
 func (mq *MachineQuery) Clone() *MachineQuery {
+	if mq == nil {
+		return nil
+	}
 	return &MachineQuery{
 	return &MachineQuery{
 		config:     mq.config,
 		config:     mq.config,
 		limit:      mq.limit,
 		limit:      mq.limit,
 		offset:     mq.offset,
 		offset:     mq.offset,
 		order:      append([]OrderFunc{}, mq.order...),
 		order:      append([]OrderFunc{}, mq.order...),
-		unique:     append([]string{}, mq.unique...),
 		predicates: append([]predicate.Machine{}, mq.predicates...),
 		predicates: append([]predicate.Machine{}, mq.predicates...),
+		withAlerts: mq.withAlerts.Clone(),
 		// clone intermediate query.
 		// clone intermediate query.
 		sql:  mq.sql.Clone(),
 		sql:  mq.sql.Clone(),
 		path: mq.path,
 		path: mq.path,
 	}
 	}
 }
 }
 
 
-//  WithAlerts tells the query-builder to eager-loads the nodes that are connected to
-// the "alerts" edge. The optional arguments used to configure the query builder of the edge.
+// WithAlerts tells the query-builder to eager-load the nodes that are connected to
+// the "alerts" edge. The optional arguments are used to configure the query builder of the edge.
 func (mq *MachineQuery) WithAlerts(opts ...func(*AlertQuery)) *MachineQuery {
 func (mq *MachineQuery) WithAlerts(opts ...func(*AlertQuery)) *MachineQuery {
 	query := &AlertQuery{config: mq.config}
 	query := &AlertQuery{config: mq.config}
 	for _, opt := range opts {
 	for _, opt := range opts {
@@ -268,7 +277,7 @@ func (mq *MachineQuery) WithAlerts(opts ...func(*AlertQuery)) *MachineQuery {
 	return mq
 	return mq
 }
 }
 
 
-// GroupBy used to group vertices by one or more fields/columns.
+// GroupBy is used to group vertices by one or more fields/columns.
 // It is often used with aggregate functions, like: count, max, mean, min, sum.
 // It is often used with aggregate functions, like: count, max, mean, min, sum.
 //
 //
 // Example:
 // Example:
@@ -295,7 +304,8 @@ func (mq *MachineQuery) GroupBy(field string, fields ...string) *MachineGroupBy
 	return group
 	return group
 }
 }
 
 
-// Select one or more fields from the given query.
+// Select allows the selection one or more fields/columns for the given query,
+// instead of selecting all fields in the entity.
 //
 //
 // Example:
 // Example:
 //
 //
@@ -308,18 +318,16 @@ func (mq *MachineQuery) GroupBy(field string, fields ...string) *MachineGroupBy
 //		Scan(ctx, &v)
 //		Scan(ctx, &v)
 //
 //
 func (mq *MachineQuery) Select(field string, fields ...string) *MachineSelect {
 func (mq *MachineQuery) Select(field string, fields ...string) *MachineSelect {
-	selector := &MachineSelect{config: mq.config}
-	selector.fields = append([]string{field}, fields...)
-	selector.path = func(ctx context.Context) (prev *sql.Selector, err error) {
-		if err := mq.prepareQuery(ctx); err != nil {
-			return nil, err
-		}
-		return mq.sqlQuery(), nil
-	}
-	return selector
+	mq.fields = append([]string{field}, fields...)
+	return &MachineSelect{MachineQuery: mq}
 }
 }
 
 
 func (mq *MachineQuery) prepareQuery(ctx context.Context) error {
 func (mq *MachineQuery) prepareQuery(ctx context.Context) error {
+	for _, f := range mq.fields {
+		if !machine.ValidColumn(f) {
+			return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+		}
+	}
 	if mq.path != nil {
 	if mq.path != nil {
 		prev, err := mq.path(ctx)
 		prev, err := mq.path(ctx)
 		if err != nil {
 		if err != nil {
@@ -338,19 +346,18 @@ func (mq *MachineQuery) sqlAll(ctx context.Context) ([]*Machine, error) {
 			mq.withAlerts != nil,
 			mq.withAlerts != nil,
 		}
 		}
 	)
 	)
-	_spec.ScanValues = func() []interface{} {
+	_spec.ScanValues = func(columns []string) ([]interface{}, error) {
 		node := &Machine{config: mq.config}
 		node := &Machine{config: mq.config}
 		nodes = append(nodes, node)
 		nodes = append(nodes, node)
-		values := node.scanValues()
-		return values
+		return node.scanValues(columns)
 	}
 	}
-	_spec.Assign = func(values ...interface{}) error {
+	_spec.Assign = func(columns []string, values []interface{}) error {
 		if len(nodes) == 0 {
 		if len(nodes) == 0 {
 			return fmt.Errorf("ent: Assign called without calling ScanValues")
 			return fmt.Errorf("ent: Assign called without calling ScanValues")
 		}
 		}
 		node := nodes[len(nodes)-1]
 		node := nodes[len(nodes)-1]
 		node.Edges.loadedTypes = loadedTypes
 		node.Edges.loadedTypes = loadedTypes
-		return node.assignValues(values...)
+		return node.assignValues(columns, values)
 	}
 	}
 	if err := sqlgraph.QueryNodes(ctx, mq.driver, _spec); err != nil {
 	if err := sqlgraph.QueryNodes(ctx, mq.driver, _spec); err != nil {
 		return nil, err
 		return nil, err
@@ -365,6 +372,7 @@ func (mq *MachineQuery) sqlAll(ctx context.Context) ([]*Machine, error) {
 		for i := range nodes {
 		for i := range nodes {
 			fks = append(fks, nodes[i].ID)
 			fks = append(fks, nodes[i].ID)
 			nodeids[nodes[i].ID] = nodes[i]
 			nodeids[nodes[i].ID] = nodes[i]
+			nodes[i].Edges.Alerts = []*Alert{}
 		}
 		}
 		query.withFKs = true
 		query.withFKs = true
 		query.Where(predicate.Alert(func(s *sql.Selector) {
 		query.Where(predicate.Alert(func(s *sql.Selector) {
@@ -416,6 +424,15 @@ func (mq *MachineQuery) querySpec() *sqlgraph.QuerySpec {
 		From:   mq.sql,
 		From:   mq.sql,
 		Unique: true,
 		Unique: true,
 	}
 	}
+	if fields := mq.fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, machine.FieldID)
+		for i := range fields {
+			if fields[i] != machine.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
+			}
+		}
+	}
 	if ps := mq.predicates; len(ps) > 0 {
 	if ps := mq.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
@@ -464,7 +481,7 @@ func (mq *MachineQuery) sqlQuery() *sql.Selector {
 	return selector
 	return selector
 }
 }
 
 
-// MachineGroupBy is the builder for group-by Machine entities.
+// MachineGroupBy is the group-by builder for Machine entities.
 type MachineGroupBy struct {
 type MachineGroupBy struct {
 	config
 	config
 	fields []string
 	fields []string
@@ -480,7 +497,7 @@ func (mgb *MachineGroupBy) Aggregate(fns ...AggregateFunc) *MachineGroupBy {
 	return mgb
 	return mgb
 }
 }
 
 
-// Scan applies the group-by query and scan the result into the given value.
+// Scan applies the group-by query and scans the result into the given value.
 func (mgb *MachineGroupBy) Scan(ctx context.Context, v interface{}) error {
 func (mgb *MachineGroupBy) Scan(ctx context.Context, v interface{}) error {
 	query, err := mgb.path(ctx)
 	query, err := mgb.path(ctx)
 	if err != nil {
 	if err != nil {
@@ -497,7 +514,8 @@ func (mgb *MachineGroupBy) ScanX(ctx context.Context, v interface{}) {
 	}
 	}
 }
 }
 
 
-// Strings returns list of strings from group-by. It is only allowed when querying group-by with one field.
+// Strings returns list of strings from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MachineGroupBy) Strings(ctx context.Context) ([]string, error) {
 func (mgb *MachineGroupBy) Strings(ctx context.Context) ([]string, error) {
 	if len(mgb.fields) > 1 {
 	if len(mgb.fields) > 1 {
 		return nil, errors.New("ent: MachineGroupBy.Strings is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: MachineGroupBy.Strings is not achievable when grouping more than 1 field")
@@ -518,7 +536,8 @@ func (mgb *MachineGroupBy) StringsX(ctx context.Context) []string {
 	return v
 	return v
 }
 }
 
 
-// String returns a single string from group-by. It is only allowed when querying group-by with one field.
+// String returns a single string from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MachineGroupBy) String(ctx context.Context) (_ string, err error) {
 func (mgb *MachineGroupBy) String(ctx context.Context) (_ string, err error) {
 	var v []string
 	var v []string
 	if v, err = mgb.Strings(ctx); err != nil {
 	if v, err = mgb.Strings(ctx); err != nil {
@@ -544,7 +563,8 @@ func (mgb *MachineGroupBy) StringX(ctx context.Context) string {
 	return v
 	return v
 }
 }
 
 
-// Ints returns list of ints from group-by. It is only allowed when querying group-by with one field.
+// Ints returns list of ints from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MachineGroupBy) Ints(ctx context.Context) ([]int, error) {
 func (mgb *MachineGroupBy) Ints(ctx context.Context) ([]int, error) {
 	if len(mgb.fields) > 1 {
 	if len(mgb.fields) > 1 {
 		return nil, errors.New("ent: MachineGroupBy.Ints is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: MachineGroupBy.Ints is not achievable when grouping more than 1 field")
@@ -565,7 +585,8 @@ func (mgb *MachineGroupBy) IntsX(ctx context.Context) []int {
 	return v
 	return v
 }
 }
 
 
-// Int returns a single int from group-by. It is only allowed when querying group-by with one field.
+// Int returns a single int from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MachineGroupBy) Int(ctx context.Context) (_ int, err error) {
 func (mgb *MachineGroupBy) Int(ctx context.Context) (_ int, err error) {
 	var v []int
 	var v []int
 	if v, err = mgb.Ints(ctx); err != nil {
 	if v, err = mgb.Ints(ctx); err != nil {
@@ -591,7 +612,8 @@ func (mgb *MachineGroupBy) IntX(ctx context.Context) int {
 	return v
 	return v
 }
 }
 
 
-// Float64s returns list of float64s from group-by. It is only allowed when querying group-by with one field.
+// Float64s returns list of float64s from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MachineGroupBy) Float64s(ctx context.Context) ([]float64, error) {
 func (mgb *MachineGroupBy) Float64s(ctx context.Context) ([]float64, error) {
 	if len(mgb.fields) > 1 {
 	if len(mgb.fields) > 1 {
 		return nil, errors.New("ent: MachineGroupBy.Float64s is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: MachineGroupBy.Float64s is not achievable when grouping more than 1 field")
@@ -612,7 +634,8 @@ func (mgb *MachineGroupBy) Float64sX(ctx context.Context) []float64 {
 	return v
 	return v
 }
 }
 
 
-// Float64 returns a single float64 from group-by. It is only allowed when querying group-by with one field.
+// Float64 returns a single float64 from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MachineGroupBy) Float64(ctx context.Context) (_ float64, err error) {
 func (mgb *MachineGroupBy) Float64(ctx context.Context) (_ float64, err error) {
 	var v []float64
 	var v []float64
 	if v, err = mgb.Float64s(ctx); err != nil {
 	if v, err = mgb.Float64s(ctx); err != nil {
@@ -638,7 +661,8 @@ func (mgb *MachineGroupBy) Float64X(ctx context.Context) float64 {
 	return v
 	return v
 }
 }
 
 
-// Bools returns list of bools from group-by. It is only allowed when querying group-by with one field.
+// Bools returns list of bools from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MachineGroupBy) Bools(ctx context.Context) ([]bool, error) {
 func (mgb *MachineGroupBy) Bools(ctx context.Context) ([]bool, error) {
 	if len(mgb.fields) > 1 {
 	if len(mgb.fields) > 1 {
 		return nil, errors.New("ent: MachineGroupBy.Bools is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: MachineGroupBy.Bools is not achievable when grouping more than 1 field")
@@ -659,7 +683,8 @@ func (mgb *MachineGroupBy) BoolsX(ctx context.Context) []bool {
 	return v
 	return v
 }
 }
 
 
-// Bool returns a single bool from group-by. It is only allowed when querying group-by with one field.
+// Bool returns a single bool from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MachineGroupBy) Bool(ctx context.Context) (_ bool, err error) {
 func (mgb *MachineGroupBy) Bool(ctx context.Context) (_ bool, err error) {
 	var v []bool
 	var v []bool
 	if v, err = mgb.Bools(ctx); err != nil {
 	if v, err = mgb.Bools(ctx); err != nil {
@@ -714,22 +739,19 @@ func (mgb *MachineGroupBy) sqlQuery() *sql.Selector {
 	return selector.Select(columns...).GroupBy(mgb.fields...)
 	return selector.Select(columns...).GroupBy(mgb.fields...)
 }
 }
 
 
-// MachineSelect is the builder for select fields of Machine entities.
+// MachineSelect is the builder for selecting fields of Machine entities.
 type MachineSelect struct {
 type MachineSelect struct {
-	config
-	fields []string
+	*MachineQuery
 	// intermediate query (i.e. traversal path).
 	// intermediate query (i.e. traversal path).
-	sql  *sql.Selector
-	path func(context.Context) (*sql.Selector, error)
+	sql *sql.Selector
 }
 }
 
 
-// Scan applies the selector query and scan the result into the given value.
+// Scan applies the selector query and scans the result into the given value.
 func (ms *MachineSelect) Scan(ctx context.Context, v interface{}) error {
 func (ms *MachineSelect) Scan(ctx context.Context, v interface{}) error {
-	query, err := ms.path(ctx)
-	if err != nil {
+	if err := ms.prepareQuery(ctx); err != nil {
 		return err
 		return err
 	}
 	}
-	ms.sql = query
+	ms.sql = ms.MachineQuery.sqlQuery()
 	return ms.sqlScan(ctx, v)
 	return ms.sqlScan(ctx, v)
 }
 }
 
 
@@ -740,7 +762,7 @@ func (ms *MachineSelect) ScanX(ctx context.Context, v interface{}) {
 	}
 	}
 }
 }
 
 
-// Strings returns list of strings from selector. It is only allowed when selecting one field.
+// Strings returns list of strings from a selector. It is only allowed when selecting one field.
 func (ms *MachineSelect) Strings(ctx context.Context) ([]string, error) {
 func (ms *MachineSelect) Strings(ctx context.Context) ([]string, error) {
 	if len(ms.fields) > 1 {
 	if len(ms.fields) > 1 {
 		return nil, errors.New("ent: MachineSelect.Strings is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: MachineSelect.Strings is not achievable when selecting more than 1 field")
@@ -761,7 +783,7 @@ func (ms *MachineSelect) StringsX(ctx context.Context) []string {
 	return v
 	return v
 }
 }
 
 
-// String returns a single string from selector. It is only allowed when selecting one field.
+// String returns a single string from a selector. It is only allowed when selecting one field.
 func (ms *MachineSelect) String(ctx context.Context) (_ string, err error) {
 func (ms *MachineSelect) String(ctx context.Context) (_ string, err error) {
 	var v []string
 	var v []string
 	if v, err = ms.Strings(ctx); err != nil {
 	if v, err = ms.Strings(ctx); err != nil {
@@ -787,7 +809,7 @@ func (ms *MachineSelect) StringX(ctx context.Context) string {
 	return v
 	return v
 }
 }
 
 
-// Ints returns list of ints from selector. It is only allowed when selecting one field.
+// Ints returns list of ints from a selector. It is only allowed when selecting one field.
 func (ms *MachineSelect) Ints(ctx context.Context) ([]int, error) {
 func (ms *MachineSelect) Ints(ctx context.Context) ([]int, error) {
 	if len(ms.fields) > 1 {
 	if len(ms.fields) > 1 {
 		return nil, errors.New("ent: MachineSelect.Ints is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: MachineSelect.Ints is not achievable when selecting more than 1 field")
@@ -808,7 +830,7 @@ func (ms *MachineSelect) IntsX(ctx context.Context) []int {
 	return v
 	return v
 }
 }
 
 
-// Int returns a single int from selector. It is only allowed when selecting one field.
+// Int returns a single int from a selector. It is only allowed when selecting one field.
 func (ms *MachineSelect) Int(ctx context.Context) (_ int, err error) {
 func (ms *MachineSelect) Int(ctx context.Context) (_ int, err error) {
 	var v []int
 	var v []int
 	if v, err = ms.Ints(ctx); err != nil {
 	if v, err = ms.Ints(ctx); err != nil {
@@ -834,7 +856,7 @@ func (ms *MachineSelect) IntX(ctx context.Context) int {
 	return v
 	return v
 }
 }
 
 
-// Float64s returns list of float64s from selector. It is only allowed when selecting one field.
+// Float64s returns list of float64s from a selector. It is only allowed when selecting one field.
 func (ms *MachineSelect) Float64s(ctx context.Context) ([]float64, error) {
 func (ms *MachineSelect) Float64s(ctx context.Context) ([]float64, error) {
 	if len(ms.fields) > 1 {
 	if len(ms.fields) > 1 {
 		return nil, errors.New("ent: MachineSelect.Float64s is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: MachineSelect.Float64s is not achievable when selecting more than 1 field")
@@ -855,7 +877,7 @@ func (ms *MachineSelect) Float64sX(ctx context.Context) []float64 {
 	return v
 	return v
 }
 }
 
 
-// Float64 returns a single float64 from selector. It is only allowed when selecting one field.
+// Float64 returns a single float64 from a selector. It is only allowed when selecting one field.
 func (ms *MachineSelect) Float64(ctx context.Context) (_ float64, err error) {
 func (ms *MachineSelect) Float64(ctx context.Context) (_ float64, err error) {
 	var v []float64
 	var v []float64
 	if v, err = ms.Float64s(ctx); err != nil {
 	if v, err = ms.Float64s(ctx); err != nil {
@@ -881,7 +903,7 @@ func (ms *MachineSelect) Float64X(ctx context.Context) float64 {
 	return v
 	return v
 }
 }
 
 
-// Bools returns list of bools from selector. It is only allowed when selecting one field.
+// Bools returns list of bools from a selector. It is only allowed when selecting one field.
 func (ms *MachineSelect) Bools(ctx context.Context) ([]bool, error) {
 func (ms *MachineSelect) Bools(ctx context.Context) ([]bool, error) {
 	if len(ms.fields) > 1 {
 	if len(ms.fields) > 1 {
 		return nil, errors.New("ent: MachineSelect.Bools is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: MachineSelect.Bools is not achievable when selecting more than 1 field")
@@ -902,7 +924,7 @@ func (ms *MachineSelect) BoolsX(ctx context.Context) []bool {
 	return v
 	return v
 }
 }
 
 
-// Bool returns a single bool from selector. It is only allowed when selecting one field.
+// Bool returns a single bool from a selector. It is only allowed when selecting one field.
 func (ms *MachineSelect) Bool(ctx context.Context) (_ bool, err error) {
 func (ms *MachineSelect) Bool(ctx context.Context) (_ bool, err error) {
 	var v []bool
 	var v []bool
 	if v, err = ms.Bools(ctx); err != nil {
 	if v, err = ms.Bools(ctx); err != nil {
@@ -929,11 +951,6 @@ func (ms *MachineSelect) BoolX(ctx context.Context) bool {
 }
 }
 
 
 func (ms *MachineSelect) sqlScan(ctx context.Context, v interface{}) error {
 func (ms *MachineSelect) sqlScan(ctx context.Context, v interface{}) error {
-	for _, f := range ms.fields {
-		if !machine.ValidColumn(f) {
-			return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for selection", f)}
-		}
-	}
 	rows := &sql.Rows{}
 	rows := &sql.Rows{}
 	query, args := ms.sqlQuery().Query()
 	query, args := ms.sqlQuery().Query()
 	if err := ms.driver.Query(ctx, query, args, rows); err != nil {
 	if err := ms.driver.Query(ctx, query, args, rows); err != nil {

+ 54 - 55
pkg/database/ent/machine_update.go

@@ -18,24 +18,23 @@ import (
 // MachineUpdate is the builder for updating Machine entities.
 // MachineUpdate is the builder for updating Machine entities.
 type MachineUpdate struct {
 type MachineUpdate struct {
 	config
 	config
-	hooks      []Hook
-	mutation   *MachineMutation
-	predicates []predicate.Machine
+	hooks    []Hook
+	mutation *MachineMutation
 }
 }
 
 
-// Where adds a new predicate for the builder.
+// Where adds a new predicate for the MachineUpdate builder.
 func (mu *MachineUpdate) Where(ps ...predicate.Machine) *MachineUpdate {
 func (mu *MachineUpdate) Where(ps ...predicate.Machine) *MachineUpdate {
-	mu.predicates = append(mu.predicates, ps...)
+	mu.mutation.predicates = append(mu.mutation.predicates, ps...)
 	return mu
 	return mu
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (mu *MachineUpdate) SetCreatedAt(t time.Time) *MachineUpdate {
 func (mu *MachineUpdate) SetCreatedAt(t time.Time) *MachineUpdate {
 	mu.mutation.SetCreatedAt(t)
 	mu.mutation.SetCreatedAt(t)
 	return mu
 	return mu
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (mu *MachineUpdate) SetNillableCreatedAt(t *time.Time) *MachineUpdate {
 func (mu *MachineUpdate) SetNillableCreatedAt(t *time.Time) *MachineUpdate {
 	if t != nil {
 	if t != nil {
 		mu.SetCreatedAt(*t)
 		mu.SetCreatedAt(*t)
@@ -43,13 +42,13 @@ func (mu *MachineUpdate) SetNillableCreatedAt(t *time.Time) *MachineUpdate {
 	return mu
 	return mu
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (mu *MachineUpdate) SetUpdatedAt(t time.Time) *MachineUpdate {
 func (mu *MachineUpdate) SetUpdatedAt(t time.Time) *MachineUpdate {
 	mu.mutation.SetUpdatedAt(t)
 	mu.mutation.SetUpdatedAt(t)
 	return mu
 	return mu
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (mu *MachineUpdate) SetNillableUpdatedAt(t *time.Time) *MachineUpdate {
 func (mu *MachineUpdate) SetNillableUpdatedAt(t *time.Time) *MachineUpdate {
 	if t != nil {
 	if t != nil {
 		mu.SetUpdatedAt(*t)
 		mu.SetUpdatedAt(*t)
@@ -57,31 +56,31 @@ func (mu *MachineUpdate) SetNillableUpdatedAt(t *time.Time) *MachineUpdate {
 	return mu
 	return mu
 }
 }
 
 
-// SetMachineId sets the machineId field.
+// SetMachineId sets the "machineId" field.
 func (mu *MachineUpdate) SetMachineId(s string) *MachineUpdate {
 func (mu *MachineUpdate) SetMachineId(s string) *MachineUpdate {
 	mu.mutation.SetMachineId(s)
 	mu.mutation.SetMachineId(s)
 	return mu
 	return mu
 }
 }
 
 
-// SetPassword sets the password field.
+// SetPassword sets the "password" field.
 func (mu *MachineUpdate) SetPassword(s string) *MachineUpdate {
 func (mu *MachineUpdate) SetPassword(s string) *MachineUpdate {
 	mu.mutation.SetPassword(s)
 	mu.mutation.SetPassword(s)
 	return mu
 	return mu
 }
 }
 
 
-// SetIpAddress sets the ipAddress field.
+// SetIpAddress sets the "ipAddress" field.
 func (mu *MachineUpdate) SetIpAddress(s string) *MachineUpdate {
 func (mu *MachineUpdate) SetIpAddress(s string) *MachineUpdate {
 	mu.mutation.SetIpAddress(s)
 	mu.mutation.SetIpAddress(s)
 	return mu
 	return mu
 }
 }
 
 
-// SetScenarios sets the scenarios field.
+// SetScenarios sets the "scenarios" field.
 func (mu *MachineUpdate) SetScenarios(s string) *MachineUpdate {
 func (mu *MachineUpdate) SetScenarios(s string) *MachineUpdate {
 	mu.mutation.SetScenarios(s)
 	mu.mutation.SetScenarios(s)
 	return mu
 	return mu
 }
 }
 
 
-// SetNillableScenarios sets the scenarios field if the given value is not nil.
+// SetNillableScenarios sets the "scenarios" field if the given value is not nil.
 func (mu *MachineUpdate) SetNillableScenarios(s *string) *MachineUpdate {
 func (mu *MachineUpdate) SetNillableScenarios(s *string) *MachineUpdate {
 	if s != nil {
 	if s != nil {
 		mu.SetScenarios(*s)
 		mu.SetScenarios(*s)
@@ -89,19 +88,19 @@ func (mu *MachineUpdate) SetNillableScenarios(s *string) *MachineUpdate {
 	return mu
 	return mu
 }
 }
 
 
-// ClearScenarios clears the value of scenarios.
+// ClearScenarios clears the value of the "scenarios" field.
 func (mu *MachineUpdate) ClearScenarios() *MachineUpdate {
 func (mu *MachineUpdate) ClearScenarios() *MachineUpdate {
 	mu.mutation.ClearScenarios()
 	mu.mutation.ClearScenarios()
 	return mu
 	return mu
 }
 }
 
 
-// SetVersion sets the version field.
+// SetVersion sets the "version" field.
 func (mu *MachineUpdate) SetVersion(s string) *MachineUpdate {
 func (mu *MachineUpdate) SetVersion(s string) *MachineUpdate {
 	mu.mutation.SetVersion(s)
 	mu.mutation.SetVersion(s)
 	return mu
 	return mu
 }
 }
 
 
-// SetNillableVersion sets the version field if the given value is not nil.
+// SetNillableVersion sets the "version" field if the given value is not nil.
 func (mu *MachineUpdate) SetNillableVersion(s *string) *MachineUpdate {
 func (mu *MachineUpdate) SetNillableVersion(s *string) *MachineUpdate {
 	if s != nil {
 	if s != nil {
 		mu.SetVersion(*s)
 		mu.SetVersion(*s)
@@ -109,19 +108,19 @@ func (mu *MachineUpdate) SetNillableVersion(s *string) *MachineUpdate {
 	return mu
 	return mu
 }
 }
 
 
-// ClearVersion clears the value of version.
+// ClearVersion clears the value of the "version" field.
 func (mu *MachineUpdate) ClearVersion() *MachineUpdate {
 func (mu *MachineUpdate) ClearVersion() *MachineUpdate {
 	mu.mutation.ClearVersion()
 	mu.mutation.ClearVersion()
 	return mu
 	return mu
 }
 }
 
 
-// SetIsValidated sets the isValidated field.
+// SetIsValidated sets the "isValidated" field.
 func (mu *MachineUpdate) SetIsValidated(b bool) *MachineUpdate {
 func (mu *MachineUpdate) SetIsValidated(b bool) *MachineUpdate {
 	mu.mutation.SetIsValidated(b)
 	mu.mutation.SetIsValidated(b)
 	return mu
 	return mu
 }
 }
 
 
-// SetNillableIsValidated sets the isValidated field if the given value is not nil.
+// SetNillableIsValidated sets the "isValidated" field if the given value is not nil.
 func (mu *MachineUpdate) SetNillableIsValidated(b *bool) *MachineUpdate {
 func (mu *MachineUpdate) SetNillableIsValidated(b *bool) *MachineUpdate {
 	if b != nil {
 	if b != nil {
 		mu.SetIsValidated(*b)
 		mu.SetIsValidated(*b)
@@ -129,13 +128,13 @@ func (mu *MachineUpdate) SetNillableIsValidated(b *bool) *MachineUpdate {
 	return mu
 	return mu
 }
 }
 
 
-// SetStatus sets the status field.
+// SetStatus sets the "status" field.
 func (mu *MachineUpdate) SetStatus(s string) *MachineUpdate {
 func (mu *MachineUpdate) SetStatus(s string) *MachineUpdate {
 	mu.mutation.SetStatus(s)
 	mu.mutation.SetStatus(s)
 	return mu
 	return mu
 }
 }
 
 
-// SetNillableStatus sets the status field if the given value is not nil.
+// SetNillableStatus sets the "status" field if the given value is not nil.
 func (mu *MachineUpdate) SetNillableStatus(s *string) *MachineUpdate {
 func (mu *MachineUpdate) SetNillableStatus(s *string) *MachineUpdate {
 	if s != nil {
 	if s != nil {
 		mu.SetStatus(*s)
 		mu.SetStatus(*s)
@@ -143,19 +142,19 @@ func (mu *MachineUpdate) SetNillableStatus(s *string) *MachineUpdate {
 	return mu
 	return mu
 }
 }
 
 
-// ClearStatus clears the value of status.
+// ClearStatus clears the value of the "status" field.
 func (mu *MachineUpdate) ClearStatus() *MachineUpdate {
 func (mu *MachineUpdate) ClearStatus() *MachineUpdate {
 	mu.mutation.ClearStatus()
 	mu.mutation.ClearStatus()
 	return mu
 	return mu
 }
 }
 
 
-// AddAlertIDs adds the alerts edge to Alert by ids.
+// AddAlertIDs adds the "alerts" edge to the Alert entity by IDs.
 func (mu *MachineUpdate) AddAlertIDs(ids ...int) *MachineUpdate {
 func (mu *MachineUpdate) AddAlertIDs(ids ...int) *MachineUpdate {
 	mu.mutation.AddAlertIDs(ids...)
 	mu.mutation.AddAlertIDs(ids...)
 	return mu
 	return mu
 }
 }
 
 
-// AddAlerts adds the alerts edges to Alert.
+// AddAlerts adds the "alerts" edges to the Alert entity.
 func (mu *MachineUpdate) AddAlerts(a ...*Alert) *MachineUpdate {
 func (mu *MachineUpdate) AddAlerts(a ...*Alert) *MachineUpdate {
 	ids := make([]int, len(a))
 	ids := make([]int, len(a))
 	for i := range a {
 	for i := range a {
@@ -169,19 +168,19 @@ func (mu *MachineUpdate) Mutation() *MachineMutation {
 	return mu.mutation
 	return mu.mutation
 }
 }
 
 
-// ClearAlerts clears all "alerts" edges to type Alert.
+// ClearAlerts clears all "alerts" edges to the Alert entity.
 func (mu *MachineUpdate) ClearAlerts() *MachineUpdate {
 func (mu *MachineUpdate) ClearAlerts() *MachineUpdate {
 	mu.mutation.ClearAlerts()
 	mu.mutation.ClearAlerts()
 	return mu
 	return mu
 }
 }
 
 
-// RemoveAlertIDs removes the alerts edge to Alert by ids.
+// RemoveAlertIDs removes the "alerts" edge to Alert entities by IDs.
 func (mu *MachineUpdate) RemoveAlertIDs(ids ...int) *MachineUpdate {
 func (mu *MachineUpdate) RemoveAlertIDs(ids ...int) *MachineUpdate {
 	mu.mutation.RemoveAlertIDs(ids...)
 	mu.mutation.RemoveAlertIDs(ids...)
 	return mu
 	return mu
 }
 }
 
 
-// RemoveAlerts removes alerts edges to Alert.
+// RemoveAlerts removes "alerts" edges to Alert entities.
 func (mu *MachineUpdate) RemoveAlerts(a ...*Alert) *MachineUpdate {
 func (mu *MachineUpdate) RemoveAlerts(a ...*Alert) *MachineUpdate {
 	ids := make([]int, len(a))
 	ids := make([]int, len(a))
 	for i := range a {
 	for i := range a {
@@ -190,7 +189,7 @@ func (mu *MachineUpdate) RemoveAlerts(a ...*Alert) *MachineUpdate {
 	return mu.RemoveAlertIDs(ids...)
 	return mu.RemoveAlertIDs(ids...)
 }
 }
 
 
-// Save executes the query and returns the number of rows/vertices matched by this operation.
+// Save executes the query and returns the number of nodes affected by the update operation.
 func (mu *MachineUpdate) Save(ctx context.Context) (int, error) {
 func (mu *MachineUpdate) Save(ctx context.Context) (int, error) {
 	var (
 	var (
 		err      error
 		err      error
@@ -268,7 +267,7 @@ func (mu *MachineUpdate) sqlSave(ctx context.Context) (n int, err error) {
 			},
 			},
 		},
 		},
 	}
 	}
-	if ps := mu.predicates; len(ps) > 0 {
+	if ps := mu.mutation.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
 				ps[i](selector)
 				ps[i](selector)
@@ -428,13 +427,13 @@ type MachineUpdateOne struct {
 	mutation *MachineMutation
 	mutation *MachineMutation
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (muo *MachineUpdateOne) SetCreatedAt(t time.Time) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetCreatedAt(t time.Time) *MachineUpdateOne {
 	muo.mutation.SetCreatedAt(t)
 	muo.mutation.SetCreatedAt(t)
 	return muo
 	return muo
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (muo *MachineUpdateOne) SetNillableCreatedAt(t *time.Time) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetNillableCreatedAt(t *time.Time) *MachineUpdateOne {
 	if t != nil {
 	if t != nil {
 		muo.SetCreatedAt(*t)
 		muo.SetCreatedAt(*t)
@@ -442,13 +441,13 @@ func (muo *MachineUpdateOne) SetNillableCreatedAt(t *time.Time) *MachineUpdateOn
 	return muo
 	return muo
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (muo *MachineUpdateOne) SetUpdatedAt(t time.Time) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetUpdatedAt(t time.Time) *MachineUpdateOne {
 	muo.mutation.SetUpdatedAt(t)
 	muo.mutation.SetUpdatedAt(t)
 	return muo
 	return muo
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (muo *MachineUpdateOne) SetNillableUpdatedAt(t *time.Time) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetNillableUpdatedAt(t *time.Time) *MachineUpdateOne {
 	if t != nil {
 	if t != nil {
 		muo.SetUpdatedAt(*t)
 		muo.SetUpdatedAt(*t)
@@ -456,31 +455,31 @@ func (muo *MachineUpdateOne) SetNillableUpdatedAt(t *time.Time) *MachineUpdateOn
 	return muo
 	return muo
 }
 }
 
 
-// SetMachineId sets the machineId field.
+// SetMachineId sets the "machineId" field.
 func (muo *MachineUpdateOne) SetMachineId(s string) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetMachineId(s string) *MachineUpdateOne {
 	muo.mutation.SetMachineId(s)
 	muo.mutation.SetMachineId(s)
 	return muo
 	return muo
 }
 }
 
 
-// SetPassword sets the password field.
+// SetPassword sets the "password" field.
 func (muo *MachineUpdateOne) SetPassword(s string) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetPassword(s string) *MachineUpdateOne {
 	muo.mutation.SetPassword(s)
 	muo.mutation.SetPassword(s)
 	return muo
 	return muo
 }
 }
 
 
-// SetIpAddress sets the ipAddress field.
+// SetIpAddress sets the "ipAddress" field.
 func (muo *MachineUpdateOne) SetIpAddress(s string) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetIpAddress(s string) *MachineUpdateOne {
 	muo.mutation.SetIpAddress(s)
 	muo.mutation.SetIpAddress(s)
 	return muo
 	return muo
 }
 }
 
 
-// SetScenarios sets the scenarios field.
+// SetScenarios sets the "scenarios" field.
 func (muo *MachineUpdateOne) SetScenarios(s string) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetScenarios(s string) *MachineUpdateOne {
 	muo.mutation.SetScenarios(s)
 	muo.mutation.SetScenarios(s)
 	return muo
 	return muo
 }
 }
 
 
-// SetNillableScenarios sets the scenarios field if the given value is not nil.
+// SetNillableScenarios sets the "scenarios" field if the given value is not nil.
 func (muo *MachineUpdateOne) SetNillableScenarios(s *string) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetNillableScenarios(s *string) *MachineUpdateOne {
 	if s != nil {
 	if s != nil {
 		muo.SetScenarios(*s)
 		muo.SetScenarios(*s)
@@ -488,19 +487,19 @@ func (muo *MachineUpdateOne) SetNillableScenarios(s *string) *MachineUpdateOne {
 	return muo
 	return muo
 }
 }
 
 
-// ClearScenarios clears the value of scenarios.
+// ClearScenarios clears the value of the "scenarios" field.
 func (muo *MachineUpdateOne) ClearScenarios() *MachineUpdateOne {
 func (muo *MachineUpdateOne) ClearScenarios() *MachineUpdateOne {
 	muo.mutation.ClearScenarios()
 	muo.mutation.ClearScenarios()
 	return muo
 	return muo
 }
 }
 
 
-// SetVersion sets the version field.
+// SetVersion sets the "version" field.
 func (muo *MachineUpdateOne) SetVersion(s string) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetVersion(s string) *MachineUpdateOne {
 	muo.mutation.SetVersion(s)
 	muo.mutation.SetVersion(s)
 	return muo
 	return muo
 }
 }
 
 
-// SetNillableVersion sets the version field if the given value is not nil.
+// SetNillableVersion sets the "version" field if the given value is not nil.
 func (muo *MachineUpdateOne) SetNillableVersion(s *string) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetNillableVersion(s *string) *MachineUpdateOne {
 	if s != nil {
 	if s != nil {
 		muo.SetVersion(*s)
 		muo.SetVersion(*s)
@@ -508,19 +507,19 @@ func (muo *MachineUpdateOne) SetNillableVersion(s *string) *MachineUpdateOne {
 	return muo
 	return muo
 }
 }
 
 
-// ClearVersion clears the value of version.
+// ClearVersion clears the value of the "version" field.
 func (muo *MachineUpdateOne) ClearVersion() *MachineUpdateOne {
 func (muo *MachineUpdateOne) ClearVersion() *MachineUpdateOne {
 	muo.mutation.ClearVersion()
 	muo.mutation.ClearVersion()
 	return muo
 	return muo
 }
 }
 
 
-// SetIsValidated sets the isValidated field.
+// SetIsValidated sets the "isValidated" field.
 func (muo *MachineUpdateOne) SetIsValidated(b bool) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetIsValidated(b bool) *MachineUpdateOne {
 	muo.mutation.SetIsValidated(b)
 	muo.mutation.SetIsValidated(b)
 	return muo
 	return muo
 }
 }
 
 
-// SetNillableIsValidated sets the isValidated field if the given value is not nil.
+// SetNillableIsValidated sets the "isValidated" field if the given value is not nil.
 func (muo *MachineUpdateOne) SetNillableIsValidated(b *bool) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetNillableIsValidated(b *bool) *MachineUpdateOne {
 	if b != nil {
 	if b != nil {
 		muo.SetIsValidated(*b)
 		muo.SetIsValidated(*b)
@@ -528,13 +527,13 @@ func (muo *MachineUpdateOne) SetNillableIsValidated(b *bool) *MachineUpdateOne {
 	return muo
 	return muo
 }
 }
 
 
-// SetStatus sets the status field.
+// SetStatus sets the "status" field.
 func (muo *MachineUpdateOne) SetStatus(s string) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetStatus(s string) *MachineUpdateOne {
 	muo.mutation.SetStatus(s)
 	muo.mutation.SetStatus(s)
 	return muo
 	return muo
 }
 }
 
 
-// SetNillableStatus sets the status field if the given value is not nil.
+// SetNillableStatus sets the "status" field if the given value is not nil.
 func (muo *MachineUpdateOne) SetNillableStatus(s *string) *MachineUpdateOne {
 func (muo *MachineUpdateOne) SetNillableStatus(s *string) *MachineUpdateOne {
 	if s != nil {
 	if s != nil {
 		muo.SetStatus(*s)
 		muo.SetStatus(*s)
@@ -542,19 +541,19 @@ func (muo *MachineUpdateOne) SetNillableStatus(s *string) *MachineUpdateOne {
 	return muo
 	return muo
 }
 }
 
 
-// ClearStatus clears the value of status.
+// ClearStatus clears the value of the "status" field.
 func (muo *MachineUpdateOne) ClearStatus() *MachineUpdateOne {
 func (muo *MachineUpdateOne) ClearStatus() *MachineUpdateOne {
 	muo.mutation.ClearStatus()
 	muo.mutation.ClearStatus()
 	return muo
 	return muo
 }
 }
 
 
-// AddAlertIDs adds the alerts edge to Alert by ids.
+// AddAlertIDs adds the "alerts" edge to the Alert entity by IDs.
 func (muo *MachineUpdateOne) AddAlertIDs(ids ...int) *MachineUpdateOne {
 func (muo *MachineUpdateOne) AddAlertIDs(ids ...int) *MachineUpdateOne {
 	muo.mutation.AddAlertIDs(ids...)
 	muo.mutation.AddAlertIDs(ids...)
 	return muo
 	return muo
 }
 }
 
 
-// AddAlerts adds the alerts edges to Alert.
+// AddAlerts adds the "alerts" edges to the Alert entity.
 func (muo *MachineUpdateOne) AddAlerts(a ...*Alert) *MachineUpdateOne {
 func (muo *MachineUpdateOne) AddAlerts(a ...*Alert) *MachineUpdateOne {
 	ids := make([]int, len(a))
 	ids := make([]int, len(a))
 	for i := range a {
 	for i := range a {
@@ -568,19 +567,19 @@ func (muo *MachineUpdateOne) Mutation() *MachineMutation {
 	return muo.mutation
 	return muo.mutation
 }
 }
 
 
-// ClearAlerts clears all "alerts" edges to type Alert.
+// ClearAlerts clears all "alerts" edges to the Alert entity.
 func (muo *MachineUpdateOne) ClearAlerts() *MachineUpdateOne {
 func (muo *MachineUpdateOne) ClearAlerts() *MachineUpdateOne {
 	muo.mutation.ClearAlerts()
 	muo.mutation.ClearAlerts()
 	return muo
 	return muo
 }
 }
 
 
-// RemoveAlertIDs removes the alerts edge to Alert by ids.
+// RemoveAlertIDs removes the "alerts" edge to Alert entities by IDs.
 func (muo *MachineUpdateOne) RemoveAlertIDs(ids ...int) *MachineUpdateOne {
 func (muo *MachineUpdateOne) RemoveAlertIDs(ids ...int) *MachineUpdateOne {
 	muo.mutation.RemoveAlertIDs(ids...)
 	muo.mutation.RemoveAlertIDs(ids...)
 	return muo
 	return muo
 }
 }
 
 
-// RemoveAlerts removes alerts edges to Alert.
+// RemoveAlerts removes "alerts" edges to Alert entities.
 func (muo *MachineUpdateOne) RemoveAlerts(a ...*Alert) *MachineUpdateOne {
 func (muo *MachineUpdateOne) RemoveAlerts(a ...*Alert) *MachineUpdateOne {
 	ids := make([]int, len(a))
 	ids := make([]int, len(a))
 	for i := range a {
 	for i := range a {
@@ -589,7 +588,7 @@ func (muo *MachineUpdateOne) RemoveAlerts(a ...*Alert) *MachineUpdateOne {
 	return muo.RemoveAlertIDs(ids...)
 	return muo.RemoveAlertIDs(ids...)
 }
 }
 
 
-// Save executes the query and returns the updated entity.
+// Save executes the query and returns the updated Machine entity.
 func (muo *MachineUpdateOne) Save(ctx context.Context) (*Machine, error) {
 func (muo *MachineUpdateOne) Save(ctx context.Context) (*Machine, error) {
 	var (
 	var (
 		err  error
 		err  error
@@ -809,7 +808,7 @@ func (muo *MachineUpdateOne) sqlSave(ctx context.Context) (_node *Machine, err e
 	}
 	}
 	_node = &Machine{config: muo.config}
 	_node = &Machine{config: muo.config}
 	_spec.Assign = _node.assignValues
 	_spec.Assign = _node.assignValues
-	_spec.ScanValues = _node.scanValues()
+	_spec.ScanValues = _node.scanValues
 	if err = sqlgraph.UpdateNode(ctx, muo.driver, _spec); err != nil {
 	if err = sqlgraph.UpdateNode(ctx, muo.driver, _spec); err != nil {
 		if _, ok := err.(*sqlgraph.NotFoundError); ok {
 		if _, ok := err.(*sqlgraph.NotFoundError); ok {
 			err = &NotFoundError{machine.Label}
 			err = &NotFoundError{machine.Label}

+ 61 - 53
pkg/database/ent/meta.go

@@ -55,81 +55,89 @@ func (e MetaEdges) OwnerOrErr() (*Alert, error) {
 }
 }
 
 
 // scanValues returns the types for scanning values from sql.Rows.
 // scanValues returns the types for scanning values from sql.Rows.
-func (*Meta) scanValues() []interface{} {
-	return []interface{}{
-		&sql.NullInt64{},  // id
-		&sql.NullTime{},   // created_at
-		&sql.NullTime{},   // updated_at
-		&sql.NullString{}, // key
-		&sql.NullString{}, // value
-	}
-}
-
-// fkValues returns the types for scanning foreign-keys values from sql.Rows.
-func (*Meta) fkValues() []interface{} {
-	return []interface{}{
-		&sql.NullInt64{}, // alert_metas
+func (*Meta) scanValues(columns []string) ([]interface{}, error) {
+	values := make([]interface{}, len(columns))
+	for i := range columns {
+		switch columns[i] {
+		case meta.FieldID:
+			values[i] = &sql.NullInt64{}
+		case meta.FieldKey, meta.FieldValue:
+			values[i] = &sql.NullString{}
+		case meta.FieldCreatedAt, meta.FieldUpdatedAt:
+			values[i] = &sql.NullTime{}
+		case meta.ForeignKeys[0]: // alert_metas
+			values[i] = &sql.NullInt64{}
+		default:
+			return nil, fmt.Errorf("unexpected column %q for type Meta", columns[i])
+		}
 	}
 	}
+	return values, nil
 }
 }
 
 
 // assignValues assigns the values that were returned from sql.Rows (after scanning)
 // assignValues assigns the values that were returned from sql.Rows (after scanning)
 // to the Meta fields.
 // to the Meta fields.
-func (m *Meta) assignValues(values ...interface{}) error {
-	if m, n := len(values), len(meta.Columns); m < n {
+func (m *Meta) assignValues(columns []string, values []interface{}) error {
+	if m, n := len(values), len(columns); m < n {
 		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
 		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
 	}
 	}
-	value, ok := values[0].(*sql.NullInt64)
-	if !ok {
-		return fmt.Errorf("unexpected type %T for field id", value)
-	}
-	m.ID = int(value.Int64)
-	values = values[1:]
-	if value, ok := values[0].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field created_at", values[0])
-	} else if value.Valid {
-		m.CreatedAt = value.Time
-	}
-	if value, ok := values[1].(*sql.NullTime); !ok {
-		return fmt.Errorf("unexpected type %T for field updated_at", values[1])
-	} else if value.Valid {
-		m.UpdatedAt = value.Time
-	}
-	if value, ok := values[2].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field key", values[2])
-	} else if value.Valid {
-		m.Key = value.String
-	}
-	if value, ok := values[3].(*sql.NullString); !ok {
-		return fmt.Errorf("unexpected type %T for field value", values[3])
-	} else if value.Valid {
-		m.Value = value.String
-	}
-	values = values[4:]
-	if len(values) == len(meta.ForeignKeys) {
-		if value, ok := values[0].(*sql.NullInt64); !ok {
-			return fmt.Errorf("unexpected type %T for edge-field alert_metas", value)
-		} else if value.Valid {
-			m.alert_metas = new(int)
-			*m.alert_metas = int(value.Int64)
+	for i := range columns {
+		switch columns[i] {
+		case meta.FieldID:
+			value, ok := values[i].(*sql.NullInt64)
+			if !ok {
+				return fmt.Errorf("unexpected type %T for field id", value)
+			}
+			m.ID = int(value.Int64)
+		case meta.FieldCreatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field created_at", values[i])
+			} else if value.Valid {
+				m.CreatedAt = value.Time
+			}
+		case meta.FieldUpdatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field updated_at", values[i])
+			} else if value.Valid {
+				m.UpdatedAt = value.Time
+			}
+		case meta.FieldKey:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field key", values[i])
+			} else if value.Valid {
+				m.Key = value.String
+			}
+		case meta.FieldValue:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field value", values[i])
+			} else if value.Valid {
+				m.Value = value.String
+			}
+		case meta.ForeignKeys[0]:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for edge-field alert_metas", value)
+			} else if value.Valid {
+				m.alert_metas = new(int)
+				*m.alert_metas = int(value.Int64)
+			}
 		}
 		}
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-// QueryOwner queries the owner edge of the Meta.
+// QueryOwner queries the "owner" edge of the Meta entity.
 func (m *Meta) QueryOwner() *AlertQuery {
 func (m *Meta) QueryOwner() *AlertQuery {
 	return (&MetaClient{config: m.config}).QueryOwner(m)
 	return (&MetaClient{config: m.config}).QueryOwner(m)
 }
 }
 
 
 // Update returns a builder for updating this Meta.
 // Update returns a builder for updating this Meta.
-// Note that, you need to call Meta.Unwrap() before calling this method, if this Meta
+// Note that you need to call Meta.Unwrap() before calling this method if this Meta
 // was returned from a transaction, and the transaction was committed or rolled back.
 // was returned from a transaction, and the transaction was committed or rolled back.
 func (m *Meta) Update() *MetaUpdateOne {
 func (m *Meta) Update() *MetaUpdateOne {
 	return (&MetaClient{config: m.config}).UpdateOne(m)
 	return (&MetaClient{config: m.config}).UpdateOne(m)
 }
 }
 
 
-// Unwrap unwraps the entity that was returned from a transaction after it was closed,
-// so that all next queries will be executed through the driver which created the transaction.
+// Unwrap unwraps the Meta entity that was returned from a transaction after it was closed,
+// so that all future queries will be executed through the driver which created the transaction.
 func (m *Meta) Unwrap() *Meta {
 func (m *Meta) Unwrap() *Meta {
 	tx, ok := m.config.driver.(*txDriver)
 	tx, ok := m.config.driver.(*txDriver)
 	if !ok {
 	if !ok {

+ 2 - 2
pkg/database/ent/meta/meta.go

@@ -64,9 +64,9 @@ func ValidColumn(column string) bool {
 }
 }
 
 
 var (
 var (
-	// DefaultCreatedAt holds the default value on creation for the created_at field.
+	// DefaultCreatedAt holds the default value on creation for the "created_at" field.
 	DefaultCreatedAt func() time.Time
 	DefaultCreatedAt func() time.Time
-	// DefaultUpdatedAt holds the default value on creation for the updated_at field.
+	// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
 	DefaultUpdatedAt func() time.Time
 	DefaultUpdatedAt func() time.Time
 	// ValueValidator is a validator for the "value" field. It is called by the builders before save.
 	// ValueValidator is a validator for the "value" field. It is called by the builders before save.
 	ValueValidator func(string) error
 	ValueValidator func(string) error

+ 3 - 3
pkg/database/ent/meta/where.go

@@ -10,7 +10,7 @@ import (
 	"github.com/facebook/ent/dialect/sql/sqlgraph"
 	"github.com/facebook/ent/dialect/sql/sqlgraph"
 )
 )
 
 
-// ID filters vertices based on their identifier.
+// ID filters vertices based on their ID field.
 func ID(id int) predicate.Meta {
 func ID(id int) predicate.Meta {
 	return predicate.Meta(func(s *sql.Selector) {
 	return predicate.Meta(func(s *sql.Selector) {
 		s.Where(sql.EQ(s.C(FieldID), id))
 		s.Where(sql.EQ(s.C(FieldID), id))
@@ -523,7 +523,7 @@ func HasOwnerWith(preds ...predicate.Alert) predicate.Meta {
 	})
 	})
 }
 }
 
 
-// And groups list of predicates with the AND operator between them.
+// And groups predicates with the AND operator between them.
 func And(predicates ...predicate.Meta) predicate.Meta {
 func And(predicates ...predicate.Meta) predicate.Meta {
 	return predicate.Meta(func(s *sql.Selector) {
 	return predicate.Meta(func(s *sql.Selector) {
 		s1 := s.Clone().SetP(nil)
 		s1 := s.Clone().SetP(nil)
@@ -534,7 +534,7 @@ func And(predicates ...predicate.Meta) predicate.Meta {
 	})
 	})
 }
 }
 
 
-// Or groups list of predicates with the OR operator between them.
+// Or groups predicates with the OR operator between them.
 func Or(predicates ...predicate.Meta) predicate.Meta {
 func Or(predicates ...predicate.Meta) predicate.Meta {
 	return predicate.Meta(func(s *sql.Selector) {
 	return predicate.Meta(func(s *sql.Selector) {
 		s1 := s.Clone().SetP(nil)
 		s1 := s.Clone().SetP(nil)

+ 11 - 11
pkg/database/ent/meta_create.go

@@ -21,13 +21,13 @@ type MetaCreate struct {
 	hooks    []Hook
 	hooks    []Hook
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (mc *MetaCreate) SetCreatedAt(t time.Time) *MetaCreate {
 func (mc *MetaCreate) SetCreatedAt(t time.Time) *MetaCreate {
 	mc.mutation.SetCreatedAt(t)
 	mc.mutation.SetCreatedAt(t)
 	return mc
 	return mc
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (mc *MetaCreate) SetNillableCreatedAt(t *time.Time) *MetaCreate {
 func (mc *MetaCreate) SetNillableCreatedAt(t *time.Time) *MetaCreate {
 	if t != nil {
 	if t != nil {
 		mc.SetCreatedAt(*t)
 		mc.SetCreatedAt(*t)
@@ -35,13 +35,13 @@ func (mc *MetaCreate) SetNillableCreatedAt(t *time.Time) *MetaCreate {
 	return mc
 	return mc
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (mc *MetaCreate) SetUpdatedAt(t time.Time) *MetaCreate {
 func (mc *MetaCreate) SetUpdatedAt(t time.Time) *MetaCreate {
 	mc.mutation.SetUpdatedAt(t)
 	mc.mutation.SetUpdatedAt(t)
 	return mc
 	return mc
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (mc *MetaCreate) SetNillableUpdatedAt(t *time.Time) *MetaCreate {
 func (mc *MetaCreate) SetNillableUpdatedAt(t *time.Time) *MetaCreate {
 	if t != nil {
 	if t != nil {
 		mc.SetUpdatedAt(*t)
 		mc.SetUpdatedAt(*t)
@@ -49,25 +49,25 @@ func (mc *MetaCreate) SetNillableUpdatedAt(t *time.Time) *MetaCreate {
 	return mc
 	return mc
 }
 }
 
 
-// SetKey sets the key field.
+// SetKey sets the "key" field.
 func (mc *MetaCreate) SetKey(s string) *MetaCreate {
 func (mc *MetaCreate) SetKey(s string) *MetaCreate {
 	mc.mutation.SetKey(s)
 	mc.mutation.SetKey(s)
 	return mc
 	return mc
 }
 }
 
 
-// SetValue sets the value field.
+// SetValue sets the "value" field.
 func (mc *MetaCreate) SetValue(s string) *MetaCreate {
 func (mc *MetaCreate) SetValue(s string) *MetaCreate {
 	mc.mutation.SetValue(s)
 	mc.mutation.SetValue(s)
 	return mc
 	return mc
 }
 }
 
 
-// SetOwnerID sets the owner edge to Alert by id.
+// SetOwnerID sets the "owner" edge to the Alert entity by ID.
 func (mc *MetaCreate) SetOwnerID(id int) *MetaCreate {
 func (mc *MetaCreate) SetOwnerID(id int) *MetaCreate {
 	mc.mutation.SetOwnerID(id)
 	mc.mutation.SetOwnerID(id)
 	return mc
 	return mc
 }
 }
 
 
-// SetNillableOwnerID sets the owner edge to Alert by id if the given value is not nil.
+// SetNillableOwnerID sets the "owner" edge to the Alert entity by ID if the given value is not nil.
 func (mc *MetaCreate) SetNillableOwnerID(id *int) *MetaCreate {
 func (mc *MetaCreate) SetNillableOwnerID(id *int) *MetaCreate {
 	if id != nil {
 	if id != nil {
 		mc = mc.SetOwnerID(*id)
 		mc = mc.SetOwnerID(*id)
@@ -75,7 +75,7 @@ func (mc *MetaCreate) SetNillableOwnerID(id *int) *MetaCreate {
 	return mc
 	return mc
 }
 }
 
 
-// SetOwner sets the owner edge to Alert.
+// SetOwner sets the "owner" edge to the Alert entity.
 func (mc *MetaCreate) SetOwner(a *Alert) *MetaCreate {
 func (mc *MetaCreate) SetOwner(a *Alert) *MetaCreate {
 	return mc.SetOwnerID(a.ID)
 	return mc.SetOwnerID(a.ID)
 }
 }
@@ -242,7 +242,7 @@ func (mc *MetaCreate) createSpec() (*Meta, *sqlgraph.CreateSpec) {
 	return _node, _spec
 	return _node, _spec
 }
 }
 
 
-// MetaCreateBulk is the builder for creating a bulk of Meta entities.
+// MetaCreateBulk is the builder for creating many Meta entities in bulk.
 type MetaCreateBulk struct {
 type MetaCreateBulk struct {
 	config
 	config
 	builders []*MetaCreate
 	builders []*MetaCreate
@@ -300,7 +300,7 @@ func (mcb *MetaCreateBulk) Save(ctx context.Context) ([]*Meta, error) {
 	return nodes, nil
 	return nodes, nil
 }
 }
 
 
-// SaveX calls Save and panics if Save returns an error.
+// SaveX is like Save, but panics if an error occurs.
 func (mcb *MetaCreateBulk) SaveX(ctx context.Context) []*Meta {
 func (mcb *MetaCreateBulk) SaveX(ctx context.Context) []*Meta {
 	v, err := mcb.Save(ctx)
 	v, err := mcb.Save(ctx)
 	if err != nil {
 	if err != nil {

+ 5 - 6
pkg/database/ent/meta_delete.go

@@ -16,14 +16,13 @@ import (
 // MetaDelete is the builder for deleting a Meta entity.
 // MetaDelete is the builder for deleting a Meta entity.
 type MetaDelete struct {
 type MetaDelete struct {
 	config
 	config
-	hooks      []Hook
-	mutation   *MetaMutation
-	predicates []predicate.Meta
+	hooks    []Hook
+	mutation *MetaMutation
 }
 }
 
 
-// Where adds a new predicate to the delete builder.
+// Where adds a new predicate to the MetaDelete builder.
 func (md *MetaDelete) Where(ps ...predicate.Meta) *MetaDelete {
 func (md *MetaDelete) Where(ps ...predicate.Meta) *MetaDelete {
-	md.predicates = append(md.predicates, ps...)
+	md.mutation.predicates = append(md.mutation.predicates, ps...)
 	return md
 	return md
 }
 }
 
 
@@ -75,7 +74,7 @@ func (md *MetaDelete) sqlExec(ctx context.Context) (int, error) {
 			},
 			},
 		},
 		},
 	}
 	}
-	if ps := md.predicates; len(ps) > 0 {
+	if ps := md.mutation.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
 				ps[i](selector)
 				ps[i](selector)

+ 78 - 65
pkg/database/ent/meta_query.go

@@ -22,7 +22,7 @@ type MetaQuery struct {
 	limit      *int
 	limit      *int
 	offset     *int
 	offset     *int
 	order      []OrderFunc
 	order      []OrderFunc
-	unique     []string
+	fields     []string
 	predicates []predicate.Meta
 	predicates []predicate.Meta
 	// eager-loading edges.
 	// eager-loading edges.
 	withOwner *AlertQuery
 	withOwner *AlertQuery
@@ -32,7 +32,7 @@ type MetaQuery struct {
 	path func(context.Context) (*sql.Selector, error)
 	path func(context.Context) (*sql.Selector, error)
 }
 }
 
 
-// Where adds a new predicate for the builder.
+// Where adds a new predicate for the MetaQuery builder.
 func (mq *MetaQuery) Where(ps ...predicate.Meta) *MetaQuery {
 func (mq *MetaQuery) Where(ps ...predicate.Meta) *MetaQuery {
 	mq.predicates = append(mq.predicates, ps...)
 	mq.predicates = append(mq.predicates, ps...)
 	return mq
 	return mq
@@ -56,7 +56,7 @@ func (mq *MetaQuery) Order(o ...OrderFunc) *MetaQuery {
 	return mq
 	return mq
 }
 }
 
 
-// QueryOwner chains the current query on the owner edge.
+// QueryOwner chains the current query on the "owner" edge.
 func (mq *MetaQuery) QueryOwner() *AlertQuery {
 func (mq *MetaQuery) QueryOwner() *AlertQuery {
 	query := &AlertQuery{config: mq.config}
 	query := &AlertQuery{config: mq.config}
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
 	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
@@ -78,7 +78,8 @@ func (mq *MetaQuery) QueryOwner() *AlertQuery {
 	return query
 	return query
 }
 }
 
 
-// First returns the first Meta entity in the query. Returns *NotFoundError when no meta was found.
+// First returns the first Meta entity from the query.
+// Returns a *NotFoundError when no Meta was found.
 func (mq *MetaQuery) First(ctx context.Context) (*Meta, error) {
 func (mq *MetaQuery) First(ctx context.Context) (*Meta, error) {
 	nodes, err := mq.Limit(1).All(ctx)
 	nodes, err := mq.Limit(1).All(ctx)
 	if err != nil {
 	if err != nil {
@@ -99,7 +100,8 @@ func (mq *MetaQuery) FirstX(ctx context.Context) *Meta {
 	return node
 	return node
 }
 }
 
 
-// FirstID returns the first Meta id in the query. Returns *NotFoundError when no id was found.
+// FirstID returns the first Meta ID from the query.
+// Returns a *NotFoundError when no Meta ID was found.
 func (mq *MetaQuery) FirstID(ctx context.Context) (id int, err error) {
 func (mq *MetaQuery) FirstID(ctx context.Context) (id int, err error) {
 	var ids []int
 	var ids []int
 	if ids, err = mq.Limit(1).IDs(ctx); err != nil {
 	if ids, err = mq.Limit(1).IDs(ctx); err != nil {
@@ -112,8 +114,8 @@ func (mq *MetaQuery) FirstID(ctx context.Context) (id int, err error) {
 	return ids[0], nil
 	return ids[0], nil
 }
 }
 
 
-// FirstXID is like FirstID, but panics if an error occurs.
-func (mq *MetaQuery) FirstXID(ctx context.Context) int {
+// FirstIDX is like FirstID, but panics if an error occurs.
+func (mq *MetaQuery) FirstIDX(ctx context.Context) int {
 	id, err := mq.FirstID(ctx)
 	id, err := mq.FirstID(ctx)
 	if err != nil && !IsNotFound(err) {
 	if err != nil && !IsNotFound(err) {
 		panic(err)
 		panic(err)
@@ -121,7 +123,9 @@ func (mq *MetaQuery) FirstXID(ctx context.Context) int {
 	return id
 	return id
 }
 }
 
 
-// Only returns the only Meta entity in the query, returns an error if not exactly one entity was returned.
+// Only returns a single Meta entity found by the query, ensuring it only returns one.
+// Returns a *NotSingularError when exactly one Meta entity is not found.
+// Returns a *NotFoundError when no Meta entities are found.
 func (mq *MetaQuery) Only(ctx context.Context) (*Meta, error) {
 func (mq *MetaQuery) Only(ctx context.Context) (*Meta, error) {
 	nodes, err := mq.Limit(2).All(ctx)
 	nodes, err := mq.Limit(2).All(ctx)
 	if err != nil {
 	if err != nil {
@@ -146,7 +150,9 @@ func (mq *MetaQuery) OnlyX(ctx context.Context) *Meta {
 	return node
 	return node
 }
 }
 
 
-// OnlyID returns the only Meta id in the query, returns an error if not exactly one id was returned.
+// OnlyID is like Only, but returns the only Meta ID in the query.
+// Returns a *NotSingularError when exactly one Meta ID is not found.
+// Returns a *NotFoundError when no entities are found.
 func (mq *MetaQuery) OnlyID(ctx context.Context) (id int, err error) {
 func (mq *MetaQuery) OnlyID(ctx context.Context) (id int, err error) {
 	var ids []int
 	var ids []int
 	if ids, err = mq.Limit(2).IDs(ctx); err != nil {
 	if ids, err = mq.Limit(2).IDs(ctx); err != nil {
@@ -189,7 +195,7 @@ func (mq *MetaQuery) AllX(ctx context.Context) []*Meta {
 	return nodes
 	return nodes
 }
 }
 
 
-// IDs executes the query and returns a list of Meta ids.
+// IDs executes the query and returns a list of Meta IDs.
 func (mq *MetaQuery) IDs(ctx context.Context) ([]int, error) {
 func (mq *MetaQuery) IDs(ctx context.Context) ([]int, error) {
 	var ids []int
 	var ids []int
 	if err := mq.Select(meta.FieldID).Scan(ctx, &ids); err != nil {
 	if err := mq.Select(meta.FieldID).Scan(ctx, &ids); err != nil {
@@ -241,24 +247,27 @@ func (mq *MetaQuery) ExistX(ctx context.Context) bool {
 	return exist
 	return exist
 }
 }
 
 
-// Clone returns a duplicate of the query builder, including all associated steps. It can be
+// Clone returns a duplicate of the MetaQuery builder, including all associated steps. It can be
 // used to prepare common query builders and use them differently after the clone is made.
 // used to prepare common query builders and use them differently after the clone is made.
 func (mq *MetaQuery) Clone() *MetaQuery {
 func (mq *MetaQuery) Clone() *MetaQuery {
+	if mq == nil {
+		return nil
+	}
 	return &MetaQuery{
 	return &MetaQuery{
 		config:     mq.config,
 		config:     mq.config,
 		limit:      mq.limit,
 		limit:      mq.limit,
 		offset:     mq.offset,
 		offset:     mq.offset,
 		order:      append([]OrderFunc{}, mq.order...),
 		order:      append([]OrderFunc{}, mq.order...),
-		unique:     append([]string{}, mq.unique...),
 		predicates: append([]predicate.Meta{}, mq.predicates...),
 		predicates: append([]predicate.Meta{}, mq.predicates...),
+		withOwner:  mq.withOwner.Clone(),
 		// clone intermediate query.
 		// clone intermediate query.
 		sql:  mq.sql.Clone(),
 		sql:  mq.sql.Clone(),
 		path: mq.path,
 		path: mq.path,
 	}
 	}
 }
 }
 
 
-//  WithOwner tells the query-builder to eager-loads the nodes that are connected to
-// the "owner" edge. The optional arguments used to configure the query builder of the edge.
+// WithOwner tells the query-builder to eager-load the nodes that are connected to
+// the "owner" edge. The optional arguments are used to configure the query builder of the edge.
 func (mq *MetaQuery) WithOwner(opts ...func(*AlertQuery)) *MetaQuery {
 func (mq *MetaQuery) WithOwner(opts ...func(*AlertQuery)) *MetaQuery {
 	query := &AlertQuery{config: mq.config}
 	query := &AlertQuery{config: mq.config}
 	for _, opt := range opts {
 	for _, opt := range opts {
@@ -268,7 +277,7 @@ func (mq *MetaQuery) WithOwner(opts ...func(*AlertQuery)) *MetaQuery {
 	return mq
 	return mq
 }
 }
 
 
-// GroupBy used to group vertices by one or more fields/columns.
+// GroupBy is used to group vertices by one or more fields/columns.
 // It is often used with aggregate functions, like: count, max, mean, min, sum.
 // It is often used with aggregate functions, like: count, max, mean, min, sum.
 //
 //
 // Example:
 // Example:
@@ -295,7 +304,8 @@ func (mq *MetaQuery) GroupBy(field string, fields ...string) *MetaGroupBy {
 	return group
 	return group
 }
 }
 
 
-// Select one or more fields from the given query.
+// Select allows the selection one or more fields/columns for the given query,
+// instead of selecting all fields in the entity.
 //
 //
 // Example:
 // Example:
 //
 //
@@ -308,18 +318,16 @@ func (mq *MetaQuery) GroupBy(field string, fields ...string) *MetaGroupBy {
 //		Scan(ctx, &v)
 //		Scan(ctx, &v)
 //
 //
 func (mq *MetaQuery) Select(field string, fields ...string) *MetaSelect {
 func (mq *MetaQuery) Select(field string, fields ...string) *MetaSelect {
-	selector := &MetaSelect{config: mq.config}
-	selector.fields = append([]string{field}, fields...)
-	selector.path = func(ctx context.Context) (prev *sql.Selector, err error) {
-		if err := mq.prepareQuery(ctx); err != nil {
-			return nil, err
-		}
-		return mq.sqlQuery(), nil
-	}
-	return selector
+	mq.fields = append([]string{field}, fields...)
+	return &MetaSelect{MetaQuery: mq}
 }
 }
 
 
 func (mq *MetaQuery) prepareQuery(ctx context.Context) error {
 func (mq *MetaQuery) prepareQuery(ctx context.Context) error {
+	for _, f := range mq.fields {
+		if !meta.ValidColumn(f) {
+			return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+		}
+	}
 	if mq.path != nil {
 	if mq.path != nil {
 		prev, err := mq.path(ctx)
 		prev, err := mq.path(ctx)
 		if err != nil {
 		if err != nil {
@@ -345,22 +353,18 @@ func (mq *MetaQuery) sqlAll(ctx context.Context) ([]*Meta, error) {
 	if withFKs {
 	if withFKs {
 		_spec.Node.Columns = append(_spec.Node.Columns, meta.ForeignKeys...)
 		_spec.Node.Columns = append(_spec.Node.Columns, meta.ForeignKeys...)
 	}
 	}
-	_spec.ScanValues = func() []interface{} {
+	_spec.ScanValues = func(columns []string) ([]interface{}, error) {
 		node := &Meta{config: mq.config}
 		node := &Meta{config: mq.config}
 		nodes = append(nodes, node)
 		nodes = append(nodes, node)
-		values := node.scanValues()
-		if withFKs {
-			values = append(values, node.fkValues()...)
-		}
-		return values
+		return node.scanValues(columns)
 	}
 	}
-	_spec.Assign = func(values ...interface{}) error {
+	_spec.Assign = func(columns []string, values []interface{}) error {
 		if len(nodes) == 0 {
 		if len(nodes) == 0 {
 			return fmt.Errorf("ent: Assign called without calling ScanValues")
 			return fmt.Errorf("ent: Assign called without calling ScanValues")
 		}
 		}
 		node := nodes[len(nodes)-1]
 		node := nodes[len(nodes)-1]
 		node.Edges.loadedTypes = loadedTypes
 		node.Edges.loadedTypes = loadedTypes
-		return node.assignValues(values...)
+		return node.assignValues(columns, values)
 	}
 	}
 	if err := sqlgraph.QueryNodes(ctx, mq.driver, _spec); err != nil {
 	if err := sqlgraph.QueryNodes(ctx, mq.driver, _spec); err != nil {
 		return nil, err
 		return nil, err
@@ -423,6 +427,15 @@ func (mq *MetaQuery) querySpec() *sqlgraph.QuerySpec {
 		From:   mq.sql,
 		From:   mq.sql,
 		Unique: true,
 		Unique: true,
 	}
 	}
+	if fields := mq.fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, meta.FieldID)
+		for i := range fields {
+			if fields[i] != meta.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
+			}
+		}
+	}
 	if ps := mq.predicates; len(ps) > 0 {
 	if ps := mq.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
@@ -471,7 +484,7 @@ func (mq *MetaQuery) sqlQuery() *sql.Selector {
 	return selector
 	return selector
 }
 }
 
 
-// MetaGroupBy is the builder for group-by Meta entities.
+// MetaGroupBy is the group-by builder for Meta entities.
 type MetaGroupBy struct {
 type MetaGroupBy struct {
 	config
 	config
 	fields []string
 	fields []string
@@ -487,7 +500,7 @@ func (mgb *MetaGroupBy) Aggregate(fns ...AggregateFunc) *MetaGroupBy {
 	return mgb
 	return mgb
 }
 }
 
 
-// Scan applies the group-by query and scan the result into the given value.
+// Scan applies the group-by query and scans the result into the given value.
 func (mgb *MetaGroupBy) Scan(ctx context.Context, v interface{}) error {
 func (mgb *MetaGroupBy) Scan(ctx context.Context, v interface{}) error {
 	query, err := mgb.path(ctx)
 	query, err := mgb.path(ctx)
 	if err != nil {
 	if err != nil {
@@ -504,7 +517,8 @@ func (mgb *MetaGroupBy) ScanX(ctx context.Context, v interface{}) {
 	}
 	}
 }
 }
 
 
-// Strings returns list of strings from group-by. It is only allowed when querying group-by with one field.
+// Strings returns list of strings from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MetaGroupBy) Strings(ctx context.Context) ([]string, error) {
 func (mgb *MetaGroupBy) Strings(ctx context.Context) ([]string, error) {
 	if len(mgb.fields) > 1 {
 	if len(mgb.fields) > 1 {
 		return nil, errors.New("ent: MetaGroupBy.Strings is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: MetaGroupBy.Strings is not achievable when grouping more than 1 field")
@@ -525,7 +539,8 @@ func (mgb *MetaGroupBy) StringsX(ctx context.Context) []string {
 	return v
 	return v
 }
 }
 
 
-// String returns a single string from group-by. It is only allowed when querying group-by with one field.
+// String returns a single string from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MetaGroupBy) String(ctx context.Context) (_ string, err error) {
 func (mgb *MetaGroupBy) String(ctx context.Context) (_ string, err error) {
 	var v []string
 	var v []string
 	if v, err = mgb.Strings(ctx); err != nil {
 	if v, err = mgb.Strings(ctx); err != nil {
@@ -551,7 +566,8 @@ func (mgb *MetaGroupBy) StringX(ctx context.Context) string {
 	return v
 	return v
 }
 }
 
 
-// Ints returns list of ints from group-by. It is only allowed when querying group-by with one field.
+// Ints returns list of ints from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MetaGroupBy) Ints(ctx context.Context) ([]int, error) {
 func (mgb *MetaGroupBy) Ints(ctx context.Context) ([]int, error) {
 	if len(mgb.fields) > 1 {
 	if len(mgb.fields) > 1 {
 		return nil, errors.New("ent: MetaGroupBy.Ints is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: MetaGroupBy.Ints is not achievable when grouping more than 1 field")
@@ -572,7 +588,8 @@ func (mgb *MetaGroupBy) IntsX(ctx context.Context) []int {
 	return v
 	return v
 }
 }
 
 
-// Int returns a single int from group-by. It is only allowed when querying group-by with one field.
+// Int returns a single int from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MetaGroupBy) Int(ctx context.Context) (_ int, err error) {
 func (mgb *MetaGroupBy) Int(ctx context.Context) (_ int, err error) {
 	var v []int
 	var v []int
 	if v, err = mgb.Ints(ctx); err != nil {
 	if v, err = mgb.Ints(ctx); err != nil {
@@ -598,7 +615,8 @@ func (mgb *MetaGroupBy) IntX(ctx context.Context) int {
 	return v
 	return v
 }
 }
 
 
-// Float64s returns list of float64s from group-by. It is only allowed when querying group-by with one field.
+// Float64s returns list of float64s from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MetaGroupBy) Float64s(ctx context.Context) ([]float64, error) {
 func (mgb *MetaGroupBy) Float64s(ctx context.Context) ([]float64, error) {
 	if len(mgb.fields) > 1 {
 	if len(mgb.fields) > 1 {
 		return nil, errors.New("ent: MetaGroupBy.Float64s is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: MetaGroupBy.Float64s is not achievable when grouping more than 1 field")
@@ -619,7 +637,8 @@ func (mgb *MetaGroupBy) Float64sX(ctx context.Context) []float64 {
 	return v
 	return v
 }
 }
 
 
-// Float64 returns a single float64 from group-by. It is only allowed when querying group-by with one field.
+// Float64 returns a single float64 from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MetaGroupBy) Float64(ctx context.Context) (_ float64, err error) {
 func (mgb *MetaGroupBy) Float64(ctx context.Context) (_ float64, err error) {
 	var v []float64
 	var v []float64
 	if v, err = mgb.Float64s(ctx); err != nil {
 	if v, err = mgb.Float64s(ctx); err != nil {
@@ -645,7 +664,8 @@ func (mgb *MetaGroupBy) Float64X(ctx context.Context) float64 {
 	return v
 	return v
 }
 }
 
 
-// Bools returns list of bools from group-by. It is only allowed when querying group-by with one field.
+// Bools returns list of bools from group-by.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MetaGroupBy) Bools(ctx context.Context) ([]bool, error) {
 func (mgb *MetaGroupBy) Bools(ctx context.Context) ([]bool, error) {
 	if len(mgb.fields) > 1 {
 	if len(mgb.fields) > 1 {
 		return nil, errors.New("ent: MetaGroupBy.Bools is not achievable when grouping more than 1 field")
 		return nil, errors.New("ent: MetaGroupBy.Bools is not achievable when grouping more than 1 field")
@@ -666,7 +686,8 @@ func (mgb *MetaGroupBy) BoolsX(ctx context.Context) []bool {
 	return v
 	return v
 }
 }
 
 
-// Bool returns a single bool from group-by. It is only allowed when querying group-by with one field.
+// Bool returns a single bool from a group-by query.
+// It is only allowed when executing a group-by query with one field.
 func (mgb *MetaGroupBy) Bool(ctx context.Context) (_ bool, err error) {
 func (mgb *MetaGroupBy) Bool(ctx context.Context) (_ bool, err error) {
 	var v []bool
 	var v []bool
 	if v, err = mgb.Bools(ctx); err != nil {
 	if v, err = mgb.Bools(ctx); err != nil {
@@ -721,22 +742,19 @@ func (mgb *MetaGroupBy) sqlQuery() *sql.Selector {
 	return selector.Select(columns...).GroupBy(mgb.fields...)
 	return selector.Select(columns...).GroupBy(mgb.fields...)
 }
 }
 
 
-// MetaSelect is the builder for select fields of Meta entities.
+// MetaSelect is the builder for selecting fields of Meta entities.
 type MetaSelect struct {
 type MetaSelect struct {
-	config
-	fields []string
+	*MetaQuery
 	// intermediate query (i.e. traversal path).
 	// intermediate query (i.e. traversal path).
-	sql  *sql.Selector
-	path func(context.Context) (*sql.Selector, error)
+	sql *sql.Selector
 }
 }
 
 
-// Scan applies the selector query and scan the result into the given value.
+// Scan applies the selector query and scans the result into the given value.
 func (ms *MetaSelect) Scan(ctx context.Context, v interface{}) error {
 func (ms *MetaSelect) Scan(ctx context.Context, v interface{}) error {
-	query, err := ms.path(ctx)
-	if err != nil {
+	if err := ms.prepareQuery(ctx); err != nil {
 		return err
 		return err
 	}
 	}
-	ms.sql = query
+	ms.sql = ms.MetaQuery.sqlQuery()
 	return ms.sqlScan(ctx, v)
 	return ms.sqlScan(ctx, v)
 }
 }
 
 
@@ -747,7 +765,7 @@ func (ms *MetaSelect) ScanX(ctx context.Context, v interface{}) {
 	}
 	}
 }
 }
 
 
-// Strings returns list of strings from selector. It is only allowed when selecting one field.
+// Strings returns list of strings from a selector. It is only allowed when selecting one field.
 func (ms *MetaSelect) Strings(ctx context.Context) ([]string, error) {
 func (ms *MetaSelect) Strings(ctx context.Context) ([]string, error) {
 	if len(ms.fields) > 1 {
 	if len(ms.fields) > 1 {
 		return nil, errors.New("ent: MetaSelect.Strings is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: MetaSelect.Strings is not achievable when selecting more than 1 field")
@@ -768,7 +786,7 @@ func (ms *MetaSelect) StringsX(ctx context.Context) []string {
 	return v
 	return v
 }
 }
 
 
-// String returns a single string from selector. It is only allowed when selecting one field.
+// String returns a single string from a selector. It is only allowed when selecting one field.
 func (ms *MetaSelect) String(ctx context.Context) (_ string, err error) {
 func (ms *MetaSelect) String(ctx context.Context) (_ string, err error) {
 	var v []string
 	var v []string
 	if v, err = ms.Strings(ctx); err != nil {
 	if v, err = ms.Strings(ctx); err != nil {
@@ -794,7 +812,7 @@ func (ms *MetaSelect) StringX(ctx context.Context) string {
 	return v
 	return v
 }
 }
 
 
-// Ints returns list of ints from selector. It is only allowed when selecting one field.
+// Ints returns list of ints from a selector. It is only allowed when selecting one field.
 func (ms *MetaSelect) Ints(ctx context.Context) ([]int, error) {
 func (ms *MetaSelect) Ints(ctx context.Context) ([]int, error) {
 	if len(ms.fields) > 1 {
 	if len(ms.fields) > 1 {
 		return nil, errors.New("ent: MetaSelect.Ints is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: MetaSelect.Ints is not achievable when selecting more than 1 field")
@@ -815,7 +833,7 @@ func (ms *MetaSelect) IntsX(ctx context.Context) []int {
 	return v
 	return v
 }
 }
 
 
-// Int returns a single int from selector. It is only allowed when selecting one field.
+// Int returns a single int from a selector. It is only allowed when selecting one field.
 func (ms *MetaSelect) Int(ctx context.Context) (_ int, err error) {
 func (ms *MetaSelect) Int(ctx context.Context) (_ int, err error) {
 	var v []int
 	var v []int
 	if v, err = ms.Ints(ctx); err != nil {
 	if v, err = ms.Ints(ctx); err != nil {
@@ -841,7 +859,7 @@ func (ms *MetaSelect) IntX(ctx context.Context) int {
 	return v
 	return v
 }
 }
 
 
-// Float64s returns list of float64s from selector. It is only allowed when selecting one field.
+// Float64s returns list of float64s from a selector. It is only allowed when selecting one field.
 func (ms *MetaSelect) Float64s(ctx context.Context) ([]float64, error) {
 func (ms *MetaSelect) Float64s(ctx context.Context) ([]float64, error) {
 	if len(ms.fields) > 1 {
 	if len(ms.fields) > 1 {
 		return nil, errors.New("ent: MetaSelect.Float64s is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: MetaSelect.Float64s is not achievable when selecting more than 1 field")
@@ -862,7 +880,7 @@ func (ms *MetaSelect) Float64sX(ctx context.Context) []float64 {
 	return v
 	return v
 }
 }
 
 
-// Float64 returns a single float64 from selector. It is only allowed when selecting one field.
+// Float64 returns a single float64 from a selector. It is only allowed when selecting one field.
 func (ms *MetaSelect) Float64(ctx context.Context) (_ float64, err error) {
 func (ms *MetaSelect) Float64(ctx context.Context) (_ float64, err error) {
 	var v []float64
 	var v []float64
 	if v, err = ms.Float64s(ctx); err != nil {
 	if v, err = ms.Float64s(ctx); err != nil {
@@ -888,7 +906,7 @@ func (ms *MetaSelect) Float64X(ctx context.Context) float64 {
 	return v
 	return v
 }
 }
 
 
-// Bools returns list of bools from selector. It is only allowed when selecting one field.
+// Bools returns list of bools from a selector. It is only allowed when selecting one field.
 func (ms *MetaSelect) Bools(ctx context.Context) ([]bool, error) {
 func (ms *MetaSelect) Bools(ctx context.Context) ([]bool, error) {
 	if len(ms.fields) > 1 {
 	if len(ms.fields) > 1 {
 		return nil, errors.New("ent: MetaSelect.Bools is not achievable when selecting more than 1 field")
 		return nil, errors.New("ent: MetaSelect.Bools is not achievable when selecting more than 1 field")
@@ -909,7 +927,7 @@ func (ms *MetaSelect) BoolsX(ctx context.Context) []bool {
 	return v
 	return v
 }
 }
 
 
-// Bool returns a single bool from selector. It is only allowed when selecting one field.
+// Bool returns a single bool from a selector. It is only allowed when selecting one field.
 func (ms *MetaSelect) Bool(ctx context.Context) (_ bool, err error) {
 func (ms *MetaSelect) Bool(ctx context.Context) (_ bool, err error) {
 	var v []bool
 	var v []bool
 	if v, err = ms.Bools(ctx); err != nil {
 	if v, err = ms.Bools(ctx); err != nil {
@@ -936,11 +954,6 @@ func (ms *MetaSelect) BoolX(ctx context.Context) bool {
 }
 }
 
 
 func (ms *MetaSelect) sqlScan(ctx context.Context, v interface{}) error {
 func (ms *MetaSelect) sqlScan(ctx context.Context, v interface{}) error {
-	for _, f := range ms.fields {
-		if !meta.ValidColumn(f) {
-			return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for selection", f)}
-		}
-	}
 	rows := &sql.Rows{}
 	rows := &sql.Rows{}
 	query, args := ms.sqlQuery().Query()
 	query, args := ms.sqlQuery().Query()
 	if err := ms.driver.Query(ctx, query, args, rows); err != nil {
 	if err := ms.driver.Query(ctx, query, args, rows); err != nil {

+ 28 - 29
pkg/database/ent/meta_update.go

@@ -18,24 +18,23 @@ import (
 // MetaUpdate is the builder for updating Meta entities.
 // MetaUpdate is the builder for updating Meta entities.
 type MetaUpdate struct {
 type MetaUpdate struct {
 	config
 	config
-	hooks      []Hook
-	mutation   *MetaMutation
-	predicates []predicate.Meta
+	hooks    []Hook
+	mutation *MetaMutation
 }
 }
 
 
-// Where adds a new predicate for the builder.
+// Where adds a new predicate for the MetaUpdate builder.
 func (mu *MetaUpdate) Where(ps ...predicate.Meta) *MetaUpdate {
 func (mu *MetaUpdate) Where(ps ...predicate.Meta) *MetaUpdate {
-	mu.predicates = append(mu.predicates, ps...)
+	mu.mutation.predicates = append(mu.mutation.predicates, ps...)
 	return mu
 	return mu
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (mu *MetaUpdate) SetCreatedAt(t time.Time) *MetaUpdate {
 func (mu *MetaUpdate) SetCreatedAt(t time.Time) *MetaUpdate {
 	mu.mutation.SetCreatedAt(t)
 	mu.mutation.SetCreatedAt(t)
 	return mu
 	return mu
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (mu *MetaUpdate) SetNillableCreatedAt(t *time.Time) *MetaUpdate {
 func (mu *MetaUpdate) SetNillableCreatedAt(t *time.Time) *MetaUpdate {
 	if t != nil {
 	if t != nil {
 		mu.SetCreatedAt(*t)
 		mu.SetCreatedAt(*t)
@@ -43,13 +42,13 @@ func (mu *MetaUpdate) SetNillableCreatedAt(t *time.Time) *MetaUpdate {
 	return mu
 	return mu
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (mu *MetaUpdate) SetUpdatedAt(t time.Time) *MetaUpdate {
 func (mu *MetaUpdate) SetUpdatedAt(t time.Time) *MetaUpdate {
 	mu.mutation.SetUpdatedAt(t)
 	mu.mutation.SetUpdatedAt(t)
 	return mu
 	return mu
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (mu *MetaUpdate) SetNillableUpdatedAt(t *time.Time) *MetaUpdate {
 func (mu *MetaUpdate) SetNillableUpdatedAt(t *time.Time) *MetaUpdate {
 	if t != nil {
 	if t != nil {
 		mu.SetUpdatedAt(*t)
 		mu.SetUpdatedAt(*t)
@@ -57,25 +56,25 @@ func (mu *MetaUpdate) SetNillableUpdatedAt(t *time.Time) *MetaUpdate {
 	return mu
 	return mu
 }
 }
 
 
-// SetKey sets the key field.
+// SetKey sets the "key" field.
 func (mu *MetaUpdate) SetKey(s string) *MetaUpdate {
 func (mu *MetaUpdate) SetKey(s string) *MetaUpdate {
 	mu.mutation.SetKey(s)
 	mu.mutation.SetKey(s)
 	return mu
 	return mu
 }
 }
 
 
-// SetValue sets the value field.
+// SetValue sets the "value" field.
 func (mu *MetaUpdate) SetValue(s string) *MetaUpdate {
 func (mu *MetaUpdate) SetValue(s string) *MetaUpdate {
 	mu.mutation.SetValue(s)
 	mu.mutation.SetValue(s)
 	return mu
 	return mu
 }
 }
 
 
-// SetOwnerID sets the owner edge to Alert by id.
+// SetOwnerID sets the "owner" edge to the Alert entity by ID.
 func (mu *MetaUpdate) SetOwnerID(id int) *MetaUpdate {
 func (mu *MetaUpdate) SetOwnerID(id int) *MetaUpdate {
 	mu.mutation.SetOwnerID(id)
 	mu.mutation.SetOwnerID(id)
 	return mu
 	return mu
 }
 }
 
 
-// SetNillableOwnerID sets the owner edge to Alert by id if the given value is not nil.
+// SetNillableOwnerID sets the "owner" edge to the Alert entity by ID if the given value is not nil.
 func (mu *MetaUpdate) SetNillableOwnerID(id *int) *MetaUpdate {
 func (mu *MetaUpdate) SetNillableOwnerID(id *int) *MetaUpdate {
 	if id != nil {
 	if id != nil {
 		mu = mu.SetOwnerID(*id)
 		mu = mu.SetOwnerID(*id)
@@ -83,7 +82,7 @@ func (mu *MetaUpdate) SetNillableOwnerID(id *int) *MetaUpdate {
 	return mu
 	return mu
 }
 }
 
 
-// SetOwner sets the owner edge to Alert.
+// SetOwner sets the "owner" edge to the Alert entity.
 func (mu *MetaUpdate) SetOwner(a *Alert) *MetaUpdate {
 func (mu *MetaUpdate) SetOwner(a *Alert) *MetaUpdate {
 	return mu.SetOwnerID(a.ID)
 	return mu.SetOwnerID(a.ID)
 }
 }
@@ -93,13 +92,13 @@ func (mu *MetaUpdate) Mutation() *MetaMutation {
 	return mu.mutation
 	return mu.mutation
 }
 }
 
 
-// ClearOwner clears the "owner" edge to type Alert.
+// ClearOwner clears the "owner" edge to the Alert entity.
 func (mu *MetaUpdate) ClearOwner() *MetaUpdate {
 func (mu *MetaUpdate) ClearOwner() *MetaUpdate {
 	mu.mutation.ClearOwner()
 	mu.mutation.ClearOwner()
 	return mu
 	return mu
 }
 }
 
 
-// Save executes the query and returns the number of rows/vertices matched by this operation.
+// Save executes the query and returns the number of nodes affected by the update operation.
 func (mu *MetaUpdate) Save(ctx context.Context) (int, error) {
 func (mu *MetaUpdate) Save(ctx context.Context) (int, error) {
 	var (
 	var (
 		err      error
 		err      error
@@ -177,7 +176,7 @@ func (mu *MetaUpdate) sqlSave(ctx context.Context) (n int, err error) {
 			},
 			},
 		},
 		},
 	}
 	}
-	if ps := mu.predicates; len(ps) > 0 {
+	if ps := mu.mutation.predicates; len(ps) > 0 {
 		_spec.Predicate = func(selector *sql.Selector) {
 		_spec.Predicate = func(selector *sql.Selector) {
 			for i := range ps {
 			for i := range ps {
 				ps[i](selector)
 				ps[i](selector)
@@ -265,13 +264,13 @@ type MetaUpdateOne struct {
 	mutation *MetaMutation
 	mutation *MetaMutation
 }
 }
 
 
-// SetCreatedAt sets the created_at field.
+// SetCreatedAt sets the "created_at" field.
 func (muo *MetaUpdateOne) SetCreatedAt(t time.Time) *MetaUpdateOne {
 func (muo *MetaUpdateOne) SetCreatedAt(t time.Time) *MetaUpdateOne {
 	muo.mutation.SetCreatedAt(t)
 	muo.mutation.SetCreatedAt(t)
 	return muo
 	return muo
 }
 }
 
 
-// SetNillableCreatedAt sets the created_at field if the given value is not nil.
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
 func (muo *MetaUpdateOne) SetNillableCreatedAt(t *time.Time) *MetaUpdateOne {
 func (muo *MetaUpdateOne) SetNillableCreatedAt(t *time.Time) *MetaUpdateOne {
 	if t != nil {
 	if t != nil {
 		muo.SetCreatedAt(*t)
 		muo.SetCreatedAt(*t)
@@ -279,13 +278,13 @@ func (muo *MetaUpdateOne) SetNillableCreatedAt(t *time.Time) *MetaUpdateOne {
 	return muo
 	return muo
 }
 }
 
 
-// SetUpdatedAt sets the updated_at field.
+// SetUpdatedAt sets the "updated_at" field.
 func (muo *MetaUpdateOne) SetUpdatedAt(t time.Time) *MetaUpdateOne {
 func (muo *MetaUpdateOne) SetUpdatedAt(t time.Time) *MetaUpdateOne {
 	muo.mutation.SetUpdatedAt(t)
 	muo.mutation.SetUpdatedAt(t)
 	return muo
 	return muo
 }
 }
 
 
-// SetNillableUpdatedAt sets the updated_at field if the given value is not nil.
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
 func (muo *MetaUpdateOne) SetNillableUpdatedAt(t *time.Time) *MetaUpdateOne {
 func (muo *MetaUpdateOne) SetNillableUpdatedAt(t *time.Time) *MetaUpdateOne {
 	if t != nil {
 	if t != nil {
 		muo.SetUpdatedAt(*t)
 		muo.SetUpdatedAt(*t)
@@ -293,25 +292,25 @@ func (muo *MetaUpdateOne) SetNillableUpdatedAt(t *time.Time) *MetaUpdateOne {
 	return muo
 	return muo
 }
 }
 
 
-// SetKey sets the key field.
+// SetKey sets the "key" field.
 func (muo *MetaUpdateOne) SetKey(s string) *MetaUpdateOne {
 func (muo *MetaUpdateOne) SetKey(s string) *MetaUpdateOne {
 	muo.mutation.SetKey(s)
 	muo.mutation.SetKey(s)
 	return muo
 	return muo
 }
 }
 
 
-// SetValue sets the value field.
+// SetValue sets the "value" field.
 func (muo *MetaUpdateOne) SetValue(s string) *MetaUpdateOne {
 func (muo *MetaUpdateOne) SetValue(s string) *MetaUpdateOne {
 	muo.mutation.SetValue(s)
 	muo.mutation.SetValue(s)
 	return muo
 	return muo
 }
 }
 
 
-// SetOwnerID sets the owner edge to Alert by id.
+// SetOwnerID sets the "owner" edge to the Alert entity by ID.
 func (muo *MetaUpdateOne) SetOwnerID(id int) *MetaUpdateOne {
 func (muo *MetaUpdateOne) SetOwnerID(id int) *MetaUpdateOne {
 	muo.mutation.SetOwnerID(id)
 	muo.mutation.SetOwnerID(id)
 	return muo
 	return muo
 }
 }
 
 
-// SetNillableOwnerID sets the owner edge to Alert by id if the given value is not nil.
+// SetNillableOwnerID sets the "owner" edge to the Alert entity by ID if the given value is not nil.
 func (muo *MetaUpdateOne) SetNillableOwnerID(id *int) *MetaUpdateOne {
 func (muo *MetaUpdateOne) SetNillableOwnerID(id *int) *MetaUpdateOne {
 	if id != nil {
 	if id != nil {
 		muo = muo.SetOwnerID(*id)
 		muo = muo.SetOwnerID(*id)
@@ -319,7 +318,7 @@ func (muo *MetaUpdateOne) SetNillableOwnerID(id *int) *MetaUpdateOne {
 	return muo
 	return muo
 }
 }
 
 
-// SetOwner sets the owner edge to Alert.
+// SetOwner sets the "owner" edge to the Alert entity.
 func (muo *MetaUpdateOne) SetOwner(a *Alert) *MetaUpdateOne {
 func (muo *MetaUpdateOne) SetOwner(a *Alert) *MetaUpdateOne {
 	return muo.SetOwnerID(a.ID)
 	return muo.SetOwnerID(a.ID)
 }
 }
@@ -329,13 +328,13 @@ func (muo *MetaUpdateOne) Mutation() *MetaMutation {
 	return muo.mutation
 	return muo.mutation
 }
 }
 
 
-// ClearOwner clears the "owner" edge to type Alert.
+// ClearOwner clears the "owner" edge to the Alert entity.
 func (muo *MetaUpdateOne) ClearOwner() *MetaUpdateOne {
 func (muo *MetaUpdateOne) ClearOwner() *MetaUpdateOne {
 	muo.mutation.ClearOwner()
 	muo.mutation.ClearOwner()
 	return muo
 	return muo
 }
 }
 
 
-// Save executes the query and returns the updated entity.
+// Save executes the query and returns the updated Meta entity.
 func (muo *MetaUpdateOne) Save(ctx context.Context) (*Meta, error) {
 func (muo *MetaUpdateOne) Save(ctx context.Context) (*Meta, error) {
 	var (
 	var (
 		err  error
 		err  error
@@ -483,7 +482,7 @@ func (muo *MetaUpdateOne) sqlSave(ctx context.Context) (_node *Meta, err error)
 	}
 	}
 	_node = &Meta{config: muo.config}
 	_node = &Meta{config: muo.config}
 	_spec.Assign = _node.assignValues
 	_spec.Assign = _node.assignValues
-	_spec.ScanValues = _node.scanValues()
+	_spec.ScanValues = _node.scanValues
 	if err = sqlgraph.UpdateNode(ctx, muo.driver, _spec); err != nil {
 	if err = sqlgraph.UpdateNode(ctx, muo.driver, _spec); err != nil {
 		if _, ok := err.(*sqlgraph.NotFoundError); ok {
 		if _, ok := err.(*sqlgraph.NotFoundError); ok {
 			err = &NotFoundError{meta.Label}
 			err = &NotFoundError{meta.Label}

+ 2 - 0
pkg/database/ent/migrate/migrate.go

@@ -31,6 +31,8 @@ var (
 	// WithFixture sets the foreign-key renaming option to the migration when upgrading
 	// WithFixture sets the foreign-key renaming option to the migration when upgrading
 	// ent from v0.1.0 (issue-#285). Defaults to false.
 	// ent from v0.1.0 (issue-#285). Defaults to false.
 	WithFixture = schema.WithFixture
 	WithFixture = schema.WithFixture
+	// WithForeignKeys enables creating foreign-key in schema DDL. This defaults to true.
+	WithForeignKeys = schema.WithForeignKeys
 )
 )
 
 
 // Schema is the API for creating, migrating and dropping a schema.
 // Schema is the API for creating, migrating and dropping a schema.

+ 4 - 1
pkg/database/ent/migrate/schema.go

@@ -81,6 +81,9 @@ var (
 		{Name: "type", Type: field.TypeString},
 		{Name: "type", Type: field.TypeString},
 		{Name: "start_ip", Type: field.TypeInt64, Nullable: true},
 		{Name: "start_ip", Type: field.TypeInt64, Nullable: true},
 		{Name: "end_ip", Type: field.TypeInt64, Nullable: true},
 		{Name: "end_ip", Type: field.TypeInt64, Nullable: true},
+		{Name: "start_suffix", Type: field.TypeInt64, Nullable: true},
+		{Name: "end_suffix", Type: field.TypeInt64, Nullable: true},
+		{Name: "ip_size", Type: field.TypeInt64, Nullable: true},
 		{Name: "scope", Type: field.TypeString},
 		{Name: "scope", Type: field.TypeString},
 		{Name: "value", Type: field.TypeString},
 		{Name: "value", Type: field.TypeString},
 		{Name: "origin", Type: field.TypeString},
 		{Name: "origin", Type: field.TypeString},
@@ -95,7 +98,7 @@ var (
 		ForeignKeys: []*schema.ForeignKey{
 		ForeignKeys: []*schema.ForeignKey{
 			{
 			{
 				Symbol:  "decisions_alerts_decisions",
 				Symbol:  "decisions_alerts_decisions",
-				Columns: []*schema.Column{DecisionsColumns[12]},
+				Columns: []*schema.Column{DecisionsColumns[15]},
 
 
 				RefColumns: []*schema.Column{AlertsColumns[0]},
 				RefColumns: []*schema.Column{AlertsColumns[0]},
 				OnDelete:   schema.SetNull,
 				OnDelete:   schema.SetNull,

File diff suppressed because it is too large
+ 163 - 179
pkg/database/ent/mutation.go


+ 0 - 355
pkg/database/ent/privacy/privacy.go

@@ -1,355 +0,0 @@
-// Code generated by entc, DO NOT EDIT.
-
-package privacy
-
-import (
-	"context"
-	"errors"
-	"fmt"
-
-	"github.com/crowdsecurity/crowdsec/pkg/database/ent"
-)
-
-var (
-	// Allow may be returned by rules to indicate that the policy
-	// evaluation should terminate with an allow decision.
-	Allow = errors.New("ent/privacy: allow rule")
-
-	// Deny may be returned by rules to indicate that the policy
-	// evaluation should terminate with an deny decision.
-	Deny = errors.New("ent/privacy: deny rule")
-
-	// Skip may be returned by rules to indicate that the policy
-	// evaluation should continue to the next rule.
-	Skip = errors.New("ent/privacy: skip rule")
-)
-
-// Allowf returns an formatted wrapped Allow decision.
-func Allowf(format string, a ...interface{}) error {
-	return fmt.Errorf(format+": %w", append(a, Allow)...)
-}
-
-// Denyf returns an formatted wrapped Deny decision.
-func Denyf(format string, a ...interface{}) error {
-	return fmt.Errorf(format+": %w", append(a, Deny)...)
-}
-
-// Skipf returns an formatted wrapped Skip decision.
-func Skipf(format string, a ...interface{}) error {
-	return fmt.Errorf(format+": %w", append(a, Skip)...)
-}
-
-type decisionCtxKey struct{}
-
-// DecisionContext creates a decision context.
-func DecisionContext(parent context.Context, decision error) context.Context {
-	if decision == nil || errors.Is(decision, Skip) {
-		return parent
-	}
-	return context.WithValue(parent, decisionCtxKey{}, decision)
-}
-
-func decisionFromContext(ctx context.Context) (error, bool) {
-	decision, ok := ctx.Value(decisionCtxKey{}).(error)
-	if ok && errors.Is(decision, Allow) {
-		decision = nil
-	}
-	return decision, ok
-}
-
-type (
-	// QueryPolicy combines multiple query rules into a single policy.
-	QueryPolicy []QueryRule
-
-	// QueryRule defines the interface deciding whether a
-	// query is allowed and optionally modify it.
-	QueryRule interface {
-		EvalQuery(context.Context, ent.Query) error
-	}
-)
-
-// EvalQuery evaluates a query against a query policy.
-func (policy QueryPolicy) EvalQuery(ctx context.Context, q ent.Query) error {
-	if decision, ok := decisionFromContext(ctx); ok {
-		return decision
-	}
-	for _, rule := range policy {
-		switch decision := rule.EvalQuery(ctx, q); {
-		case decision == nil || errors.Is(decision, Skip):
-		case errors.Is(decision, Allow):
-			return nil
-		default:
-			return decision
-		}
-	}
-	return nil
-}
-
-// QueryRuleFunc type is an adapter to allow the use of
-// ordinary functions as query rules.
-type QueryRuleFunc func(context.Context, ent.Query) error
-
-// Eval returns f(ctx, q).
-func (f QueryRuleFunc) EvalQuery(ctx context.Context, q ent.Query) error {
-	return f(ctx, q)
-}
-
-type (
-	// MutationPolicy combines multiple mutation rules into a single policy.
-	MutationPolicy []MutationRule
-
-	// MutationRule defines the interface deciding whether a
-	// mutation is allowed and optionally modify it.
-	MutationRule interface {
-		EvalMutation(context.Context, ent.Mutation) error
-	}
-)
-
-// EvalMutation evaluates a mutation against a mutation policy.
-func (policy MutationPolicy) EvalMutation(ctx context.Context, m ent.Mutation) error {
-	if decision, ok := decisionFromContext(ctx); ok {
-		return decision
-	}
-	for _, rule := range policy {
-		switch decision := rule.EvalMutation(ctx, m); {
-		case decision == nil || errors.Is(decision, Skip):
-		case errors.Is(decision, Allow):
-			return nil
-		default:
-			return decision
-		}
-	}
-	return nil
-}
-
-// MutationRuleFunc type is an adapter to allow the use of
-// ordinary functions as mutation rules.
-type MutationRuleFunc func(context.Context, ent.Mutation) error
-
-// EvalMutation returns f(ctx, m).
-func (f MutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation) error {
-	return f(ctx, m)
-}
-
-// Policy groups query and mutation policies.
-type Policy struct {
-	Query    QueryPolicy
-	Mutation MutationPolicy
-}
-
-// EvalQuery forwards evaluation to query policy.
-func (policy Policy) EvalQuery(ctx context.Context, q ent.Query) error {
-	return policy.Query.EvalQuery(ctx, q)
-}
-
-// EvalMutation forwards evaluation to mutation policy.
-func (policy Policy) EvalMutation(ctx context.Context, m ent.Mutation) error {
-	return policy.Mutation.EvalMutation(ctx, m)
-}
-
-// QueryMutationRule is the interface that groups query and mutation rules.
-type QueryMutationRule interface {
-	QueryRule
-	MutationRule
-}
-
-// AlwaysAllowRule returns a rule that returns an allow decision.
-func AlwaysAllowRule() QueryMutationRule {
-	return fixedDecision{Allow}
-}
-
-// AlwaysDenyRule returns a rule that returns a deny decision.
-func AlwaysDenyRule() QueryMutationRule {
-	return fixedDecision{Deny}
-}
-
-type fixedDecision struct {
-	decision error
-}
-
-func (f fixedDecision) EvalQuery(context.Context, ent.Query) error {
-	return f.decision
-}
-
-func (f fixedDecision) EvalMutation(context.Context, ent.Mutation) error {
-	return f.decision
-}
-
-type contextDecision struct {
-	eval func(context.Context) error
-}
-
-// ContextQueryMutationRule creates a query/mutation rule from a context eval func.
-func ContextQueryMutationRule(eval func(context.Context) error) QueryMutationRule {
-	return contextDecision{eval}
-}
-
-func (c contextDecision) EvalQuery(ctx context.Context, _ ent.Query) error {
-	return c.eval(ctx)
-}
-
-func (c contextDecision) EvalMutation(ctx context.Context, _ ent.Mutation) error {
-	return c.eval(ctx)
-}
-
-// OnMutationOperation evaluates the given rule only on a given mutation operation.
-func OnMutationOperation(rule MutationRule, op ent.Op) MutationRule {
-	return MutationRuleFunc(func(ctx context.Context, m ent.Mutation) error {
-		if m.Op().Is(op) {
-			return rule.EvalMutation(ctx, m)
-		}
-		return Skip
-	})
-}
-
-// DenyMutationOperationRule returns a rule denying specified mutation operation.
-func DenyMutationOperationRule(op ent.Op) MutationRule {
-	rule := MutationRuleFunc(func(_ context.Context, m ent.Mutation) error {
-		return Denyf("ent/privacy: operation %s is not allowed", m.Op())
-	})
-	return OnMutationOperation(rule, op)
-}
-
-// The AlertQueryRuleFunc type is an adapter to allow the use of ordinary
-// functions as a query rule.
-type AlertQueryRuleFunc func(context.Context, *ent.AlertQuery) error
-
-// EvalQuery return f(ctx, q).
-func (f AlertQueryRuleFunc) EvalQuery(ctx context.Context, q ent.Query) error {
-	if q, ok := q.(*ent.AlertQuery); ok {
-		return f(ctx, q)
-	}
-	return Denyf("ent/privacy: unexpected query type %T, expect *ent.AlertQuery", q)
-}
-
-// The AlertMutationRuleFunc type is an adapter to allow the use of ordinary
-// functions as a mutation rule.
-type AlertMutationRuleFunc func(context.Context, *ent.AlertMutation) error
-
-// EvalMutation calls f(ctx, m).
-func (f AlertMutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation) error {
-	if m, ok := m.(*ent.AlertMutation); ok {
-		return f(ctx, m)
-	}
-	return Denyf("ent/privacy: unexpected mutation type %T, expect *ent.AlertMutation", m)
-}
-
-// The BouncerQueryRuleFunc type is an adapter to allow the use of ordinary
-// functions as a query rule.
-type BouncerQueryRuleFunc func(context.Context, *ent.BouncerQuery) error
-
-// EvalQuery return f(ctx, q).
-func (f BouncerQueryRuleFunc) EvalQuery(ctx context.Context, q ent.Query) error {
-	if q, ok := q.(*ent.BouncerQuery); ok {
-		return f(ctx, q)
-	}
-	return Denyf("ent/privacy: unexpected query type %T, expect *ent.BouncerQuery", q)
-}
-
-// The BouncerMutationRuleFunc type is an adapter to allow the use of ordinary
-// functions as a mutation rule.
-type BouncerMutationRuleFunc func(context.Context, *ent.BouncerMutation) error
-
-// EvalMutation calls f(ctx, m).
-func (f BouncerMutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation) error {
-	if m, ok := m.(*ent.BouncerMutation); ok {
-		return f(ctx, m)
-	}
-	return Denyf("ent/privacy: unexpected mutation type %T, expect *ent.BouncerMutation", m)
-}
-
-// The DecisionQueryRuleFunc type is an adapter to allow the use of ordinary
-// functions as a query rule.
-type DecisionQueryRuleFunc func(context.Context, *ent.DecisionQuery) error
-
-// EvalQuery return f(ctx, q).
-func (f DecisionQueryRuleFunc) EvalQuery(ctx context.Context, q ent.Query) error {
-	if q, ok := q.(*ent.DecisionQuery); ok {
-		return f(ctx, q)
-	}
-	return Denyf("ent/privacy: unexpected query type %T, expect *ent.DecisionQuery", q)
-}
-
-// The DecisionMutationRuleFunc type is an adapter to allow the use of ordinary
-// functions as a mutation rule.
-type DecisionMutationRuleFunc func(context.Context, *ent.DecisionMutation) error
-
-// EvalMutation calls f(ctx, m).
-func (f DecisionMutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation) error {
-	if m, ok := m.(*ent.DecisionMutation); ok {
-		return f(ctx, m)
-	}
-	return Denyf("ent/privacy: unexpected mutation type %T, expect *ent.DecisionMutation", m)
-}
-
-// The EventQueryRuleFunc type is an adapter to allow the use of ordinary
-// functions as a query rule.
-type EventQueryRuleFunc func(context.Context, *ent.EventQuery) error
-
-// EvalQuery return f(ctx, q).
-func (f EventQueryRuleFunc) EvalQuery(ctx context.Context, q ent.Query) error {
-	if q, ok := q.(*ent.EventQuery); ok {
-		return f(ctx, q)
-	}
-	return Denyf("ent/privacy: unexpected query type %T, expect *ent.EventQuery", q)
-}
-
-// The EventMutationRuleFunc type is an adapter to allow the use of ordinary
-// functions as a mutation rule.
-type EventMutationRuleFunc func(context.Context, *ent.EventMutation) error
-
-// EvalMutation calls f(ctx, m).
-func (f EventMutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation) error {
-	if m, ok := m.(*ent.EventMutation); ok {
-		return f(ctx, m)
-	}
-	return Denyf("ent/privacy: unexpected mutation type %T, expect *ent.EventMutation", m)
-}
-
-// The MachineQueryRuleFunc type is an adapter to allow the use of ordinary
-// functions as a query rule.
-type MachineQueryRuleFunc func(context.Context, *ent.MachineQuery) error
-
-// EvalQuery return f(ctx, q).
-func (f MachineQueryRuleFunc) EvalQuery(ctx context.Context, q ent.Query) error {
-	if q, ok := q.(*ent.MachineQuery); ok {
-		return f(ctx, q)
-	}
-	return Denyf("ent/privacy: unexpected query type %T, expect *ent.MachineQuery", q)
-}
-
-// The MachineMutationRuleFunc type is an adapter to allow the use of ordinary
-// functions as a mutation rule.
-type MachineMutationRuleFunc func(context.Context, *ent.MachineMutation) error
-
-// EvalMutation calls f(ctx, m).
-func (f MachineMutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation) error {
-	if m, ok := m.(*ent.MachineMutation); ok {
-		return f(ctx, m)
-	}
-	return Denyf("ent/privacy: unexpected mutation type %T, expect *ent.MachineMutation", m)
-}
-
-// The MetaQueryRuleFunc type is an adapter to allow the use of ordinary
-// functions as a query rule.
-type MetaQueryRuleFunc func(context.Context, *ent.MetaQuery) error
-
-// EvalQuery return f(ctx, q).
-func (f MetaQueryRuleFunc) EvalQuery(ctx context.Context, q ent.Query) error {
-	if q, ok := q.(*ent.MetaQuery); ok {
-		return f(ctx, q)
-	}
-	return Denyf("ent/privacy: unexpected query type %T, expect *ent.MetaQuery", q)
-}
-
-// The MetaMutationRuleFunc type is an adapter to allow the use of ordinary
-// functions as a mutation rule.
-type MetaMutationRuleFunc func(context.Context, *ent.MetaMutation) error
-
-// EvalMutation calls f(ctx, m).
-func (f MetaMutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation) error {
-	if m, ok := m.(*ent.MetaMutation); ok {
-		return f(ctx, m)
-	}
-	return Denyf("ent/privacy: unexpected mutation type %T, expect *ent.MetaMutation", m)
-}

+ 3 - 3
pkg/database/ent/runtime.go

@@ -14,8 +14,8 @@ import (
 	"github.com/crowdsecurity/crowdsec/pkg/database/ent/schema"
 	"github.com/crowdsecurity/crowdsec/pkg/database/ent/schema"
 )
 )
 
 
-// The init function reads all schema descriptors with runtime
-// code (default values, validators or hooks) and stitches it
+// The init function reads all schema descriptors with runtime code
+// (default values, validators, hooks and policies) and stitches it
 // to their package variables.
 // to their package variables.
 func init() {
 func init() {
 	alertFields := schema.Alert{}.Fields()
 	alertFields := schema.Alert{}.Fields()
@@ -85,7 +85,7 @@ func init() {
 	// decision.DefaultUpdatedAt holds the default value on creation for the updated_at field.
 	// decision.DefaultUpdatedAt holds the default value on creation for the updated_at field.
 	decision.DefaultUpdatedAt = decisionDescUpdatedAt.Default.(func() time.Time)
 	decision.DefaultUpdatedAt = decisionDescUpdatedAt.Default.(func() time.Time)
 	// decisionDescSimulated is the schema descriptor for simulated field.
 	// decisionDescSimulated is the schema descriptor for simulated field.
-	decisionDescSimulated := decisionFields[10].Descriptor()
+	decisionDescSimulated := decisionFields[13].Descriptor()
 	// decision.DefaultSimulated holds the default value on creation for the simulated field.
 	// decision.DefaultSimulated holds the default value on creation for the simulated field.
 	decision.DefaultSimulated = decisionDescSimulated.Default.(bool)
 	decision.DefaultSimulated = decisionDescSimulated.Default.(bool)
 	eventFields := schema.Event{}.Fields()
 	eventFields := schema.Event{}.Fields()

+ 2 - 2
pkg/database/ent/runtime/runtime.go

@@ -5,6 +5,6 @@ package runtime
 // The schema-stitching logic is generated in github.com/crowdsecurity/crowdsec/pkg/database/ent/runtime.go
 // The schema-stitching logic is generated in github.com/crowdsecurity/crowdsec/pkg/database/ent/runtime.go
 
 
 const (
 const (
-	Version = "v0.4.3"                                          // Version of ent codegen.
-	Sum     = "h1:ds9HENceKzpGBgCRlkZNq6TqBIegwKcF3e5reuV9Z0M=" // Sum of ent codegen.
+	Version = "v0.5.4"                                          // Version of ent codegen.
+	Sum     = "h1:kIf2BQUdRJ7XrlTXzCyJCg69ar1K1FjFR2UQWRo/M8M=" // Sum of ent codegen.
 )
 )

+ 3 - 0
pkg/database/ent/schema/decision.go

@@ -25,6 +25,9 @@ func (Decision) Fields() []ent.Field {
 		field.String("type"),
 		field.String("type"),
 		field.Int64("start_ip").Optional(),
 		field.Int64("start_ip").Optional(),
 		field.Int64("end_ip").Optional(),
 		field.Int64("end_ip").Optional(),
+		field.Int64("start_suffix").Optional(),
+		field.Int64("end_suffix").Optional(),
+		field.Int64("ip_size").Optional(),
 		field.String("scope"),
 		field.String("scope"),
 		field.String("value"),
 		field.String("value"),
 		field.String("origin"),
 		field.String("origin"),

+ 0 - 6
pkg/models/decision.go

@@ -21,9 +21,6 @@ type Decision struct {
 	// Required: true
 	// Required: true
 	Duration *string `json:"duration"`
 	Duration *string `json:"duration"`
 
 
-	// (only relevant for GET ops) when the value is an IP or range, its numeric representation
-	EndIP int64 `json:"end_ip,omitempty"`
-
 	// (only relevant for GET ops) the unique id
 	// (only relevant for GET ops) the unique id
 	// Read Only: true
 	// Read Only: true
 	ID int64 `json:"id,omitempty"`
 	ID int64 `json:"id,omitempty"`
@@ -44,9 +41,6 @@ type Decision struct {
 	// Read Only: true
 	// Read Only: true
 	Simulated *bool `json:"simulated,omitempty"`
 	Simulated *bool `json:"simulated,omitempty"`
 
 
-	// (only relevant for GET ops) when the value is an IP or range, its numeric representation
-	StartIP int64 `json:"start_ip,omitempty"`
-
 	// the type of decision, might be 'ban', 'captcha' or something custom. Ignored when watcher (cscli/crowdsec) is pushing to APIL.
 	// the type of decision, might be 'ban', 'captcha' or something custom. Ignored when watcher (cscli/crowdsec) is pushing to APIL.
 	// Required: true
 	// Required: true
 	Type *string `json:"type"`
 	Type *string `json:"type"`

+ 0 - 6
pkg/models/localapi_swagger.yaml

@@ -802,12 +802,6 @@ definitions:
       value:
       value:
         description: 'the value of the decision scope : an IP, a range, a username, etc'
         description: 'the value of the decision scope : an IP, a range, a username, etc'
         type: string
         type: string
-      start_ip:
-        description: '(only relevant for GET ops) when the value is an IP or range, its numeric representation'
-        type: integer
-      end_ip:
-        description: '(only relevant for GET ops) when the value is an IP or range, its numeric representation'
-        type: integer
       duration:
       duration:
         type: string
         type: string
       scenario:
       scenario:

+ 106 - 0
pkg/types/ip.go

@@ -0,0 +1,106 @@
+package types
+
+import (
+	"encoding/binary"
+	"fmt"
+	"math"
+	"net"
+	"strings"
+
+	"github.com/pkg/errors"
+)
+
+func LastAddress(n net.IPNet) net.IP {
+	ip := n.IP.To4()
+	if ip == nil {
+		ip = n.IP
+		return net.IP{
+			ip[0] | ^n.Mask[0], ip[1] | ^n.Mask[1], ip[2] | ^n.Mask[2],
+			ip[3] | ^n.Mask[3], ip[4] | ^n.Mask[4], ip[5] | ^n.Mask[5],
+			ip[6] | ^n.Mask[6], ip[7] | ^n.Mask[7], ip[8] | ^n.Mask[8],
+			ip[9] | ^n.Mask[9], ip[10] | ^n.Mask[10], ip[11] | ^n.Mask[11],
+			ip[12] | ^n.Mask[12], ip[13] | ^n.Mask[13], ip[14] | ^n.Mask[14],
+			ip[15] | ^n.Mask[15]}
+	}
+
+	return net.IPv4(
+		ip[0]|^n.Mask[0],
+		ip[1]|^n.Mask[1],
+		ip[2]|^n.Mask[2],
+		ip[3]|^n.Mask[3])
+}
+
+/*returns a range for any ip or range*/
+func Addr2Ints(any string) (int, int64, int64, int64, int64, error) {
+	if strings.Contains(any, "/") {
+		_, net, err := net.ParseCIDR(any)
+		if err != nil {
+			return -1, 0, 0, 0, 0, errors.Wrapf(err, "while parsing range %s", any)
+		}
+		return Range2Ints(*net)
+	} else {
+		ip := net.ParseIP(any)
+		if ip == nil {
+			return -1, 0, 0, 0, 0, fmt.Errorf("invalid address")
+		}
+		sz, start, end, err := IP2Ints(ip)
+		if err != nil {
+			return -1, 0, 0, 0, 0, errors.Wrapf(err, "while parsing ip %s", any)
+		}
+		return sz, start, end, start, end, nil
+	}
+}
+
+/*size (16|4), nw_start, suffix_start, nw_end, suffix_end, error*/
+func Range2Ints(network net.IPNet) (int, int64, int64, int64, int64, error) {
+
+	szStart, nwStart, sfxStart, err := IP2Ints(network.IP)
+	if err != nil {
+		return -1, 0, 0, 0, 0, errors.Wrap(err, "converting first ip in range")
+	}
+	lastAddr := LastAddress(network)
+	szEnd, nwEnd, sfxEnd, err := IP2Ints(lastAddr)
+	if err != nil {
+		return -1, 0, 0, 0, 0, errors.Wrap(err, "transforming last address of range")
+	}
+	if szEnd != szStart {
+		return -1, 0, 0, 0, 0, fmt.Errorf("inconsistent size for range first(%d) and last(%d) ip", szStart, szEnd)
+	}
+	return szStart, nwStart, sfxStart, nwEnd, sfxEnd, nil
+}
+
+func uint2int(u uint64) int64 {
+	var ret int64
+	if u == math.MaxInt64 {
+		ret = 0
+	} else if u == math.MaxUint64 {
+		ret = math.MaxInt64
+	} else if u > math.MaxInt64 {
+		u -= math.MaxInt64
+		ret = int64(u)
+	} else {
+		ret = int64(u)
+		ret -= math.MaxInt64
+	}
+	return ret
+}
+
+/*size (16|4), network, suffix, error*/
+func IP2Ints(pip net.IP) (int, int64, int64, error) {
+	var ip_nw, ip_sfx uint64
+
+	pip4 := pip.To4()
+	pip16 := pip.To16()
+
+	if pip4 != nil {
+		ip_nw32 := binary.BigEndian.Uint32(pip4)
+
+		return 4, uint2int(uint64(ip_nw32)), uint2int(ip_sfx), nil
+	} else if pip16 != nil {
+		ip_nw = binary.BigEndian.Uint64(pip16[0:8])
+		ip_sfx = binary.BigEndian.Uint64(pip16[8:16])
+		return 16, uint2int(ip_nw), uint2int(ip_sfx), nil
+	} else {
+		return -1, 0, 0, fmt.Errorf("unexpected len %d for %s", len(pip), pip)
+	}
+}

+ 220 - 0
pkg/types/ip_test.go

@@ -0,0 +1,220 @@
+package types
+
+import (
+	"math"
+	"net"
+	"strings"
+	"testing"
+)
+
+func TestIP2Int(t *testing.T) {
+
+	tEmpty := net.IP{}
+	_, _, _, err := IP2Ints(tEmpty)
+	if !strings.Contains(err.Error(), "unexpected len 0 for <nil>") {
+		t.Fatalf("unexpected: %s", err.Error())
+	}
+}
+func TestRange2Int(t *testing.T) {
+	tEmpty := net.IPNet{}
+	//empty item
+	_, _, _, _, _, err := Range2Ints(tEmpty)
+	if !strings.Contains(err.Error(), "converting first ip in range") {
+		t.Fatalf("unexpected: %s", err.Error())
+	}
+
+}
+
+func TestAdd2Int(t *testing.T) {
+	tests := []struct {
+		in_addr       string
+		exp_sz        int
+		exp_start_ip  int64
+		exp_start_sfx int64
+		exp_end_ip    int64
+		exp_end_sfx   int64
+		exp_error     string
+	}{
+		{
+			in_addr: "7FFF:FFFF:FFFF:FFFF:aaaa:aaaa:aaaa:fff7",
+
+			exp_sz:        16,
+			exp_start_ip:  -math.MaxInt64 + 0x7FFFFFFFFFFFFFFF,
+			exp_start_sfx: -math.MaxInt64 + 0xaaaaaaaaaaaafff7,
+			exp_end_ip:    -math.MaxInt64 + 0x7FFFFFFFFFFFFFFF,
+			exp_end_sfx:   -math.MaxInt64 + 0xaaaaaaaaaaaafff7,
+		},
+		{
+			in_addr: "aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:fff7",
+
+			exp_sz:        16,
+			exp_start_ip:  -math.MaxInt64 + 0xaaaaaaaaaaaaaaaa,
+			exp_start_sfx: -math.MaxInt64 + 0xaaaaaaaaaaaafff7,
+			exp_end_ip:    -math.MaxInt64 + 0xaaaaaaaaaaaaaaaa,
+			exp_end_sfx:   -math.MaxInt64 + 0xaaaaaaaaaaaafff7,
+		},
+		{
+			in_addr: "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff7",
+			/*ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff*/
+
+			exp_sz:        16,
+			exp_start_ip:  math.MaxInt64,
+			exp_start_sfx: -math.MaxInt64 + 0xfffffffffffffff7,
+			exp_end_ip:    math.MaxInt64,
+			exp_end_sfx:   -math.MaxInt64 + 0xfffffffffffffff7,
+		},
+		{
+			in_addr: "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+			/*ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff*/
+
+			exp_sz:        16,
+			exp_start_ip:  math.MaxInt64,
+			exp_start_sfx: math.MaxInt64,
+			exp_end_ip:    math.MaxInt64,
+			exp_end_sfx:   math.MaxInt64,
+		},
+		{
+			in_addr: "::",
+			/*::*/
+
+			exp_sz:        16,
+			exp_start_ip:  -math.MaxInt64,
+			exp_start_sfx: -math.MaxInt64,
+			exp_end_ip:    -math.MaxInt64,
+			exp_end_sfx:   -math.MaxInt64,
+		},
+		{
+			in_addr: "2001:db8::",
+			/*2001:db8:: -> 2001:db8::*/
+			exp_sz:        16,
+			exp_start_ip:  -math.MaxInt64 + 0x20010DB800000000,
+			exp_start_sfx: -math.MaxInt64,
+			exp_end_ip:    -math.MaxInt64 + 0x20010DB800000000,
+			exp_end_sfx:   -math.MaxInt64,
+		},
+		{
+			in_addr: "2001:db8:0000:0000:0000:0000:0000:00ff",
+			/*2001:db8:0000:0000:0000:0000:0000:00ff*/
+			exp_sz:        16,
+			exp_start_ip:  -math.MaxInt64 + 0x20010DB800000000,
+			exp_start_sfx: -math.MaxInt64 + 0xFF,
+			exp_end_ip:    -math.MaxInt64 + 0x20010DB800000000,
+			exp_end_sfx:   -math.MaxInt64 + 0xFF,
+		},
+		{
+			in_addr: "1.2.3.4",
+			/*1.2.3.4*/
+			exp_sz:        4,
+			exp_start_ip:  -math.MaxInt64 + 0x01020304,
+			exp_start_sfx: 0,
+			exp_end_ip:    -math.MaxInt64 + 0x01020304,
+			exp_end_sfx:   0,
+		},
+		{
+			in_addr: "::/0",
+			/*:: -> ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff*/
+
+			exp_sz:        16,
+			exp_start_ip:  -math.MaxInt64,
+			exp_start_sfx: -math.MaxInt64,
+			exp_end_ip:    math.MaxInt64,
+			exp_end_sfx:   math.MaxInt64,
+		},
+		{
+			in_addr: "::/64",
+			/*:: -> 0000:0000:0000:0000:ffff:ffff:ffff:ffff*/
+			exp_sz:        16,
+			exp_start_ip:  -math.MaxInt64,
+			exp_start_sfx: -math.MaxInt64,
+			exp_end_ip:    -math.MaxInt64,
+			exp_end_sfx:   math.MaxInt64,
+		},
+		{
+			in_addr: "2001:db8::/109",
+			/*2001:db8:: -> 2001:db8:0000:0000:0000:0000:0007:ffff*/
+			exp_sz:        16,
+			exp_start_ip:  -math.MaxInt64 + 0x20010DB800000000,
+			exp_start_sfx: -math.MaxInt64,
+			exp_end_ip:    -math.MaxInt64 + 0x20010DB800000000,
+			exp_end_sfx:   -math.MaxInt64 + 0x7FFFF,
+		},
+		{
+			in_addr: "0.0.0.0/0",
+			/*0.0.0.0 -> 255.255.255.255*/
+			exp_sz:        4,
+			exp_start_ip:  -math.MaxInt64,
+			exp_start_sfx: 0,
+			exp_end_ip:    -math.MaxInt64 + 0xFFFFFFFF,
+			exp_end_sfx:   0,
+		},
+		{
+			in_addr: "0.0.0.0/16",
+			/*0.0.0.0 -> 0.0.255.255*/
+			exp_sz:        4,
+			exp_start_ip:  -math.MaxInt64,
+			exp_start_sfx: 0,
+			exp_end_ip:    -math.MaxInt64 + 0x0000FFFF,
+			exp_end_sfx:   0,
+		},
+		{
+			in_addr: "255.255.0.0/16",
+			/*255.255.0.0 -> 255.255.255.255*/
+			exp_sz:        4,
+			exp_start_ip:  -math.MaxInt64 + 0xFFFF0000,
+			exp_start_sfx: 0,
+			exp_end_ip:    -math.MaxInt64 + 0xFFFFFFFF,
+			exp_end_sfx:   0,
+		},
+		{
+			in_addr: "1.2.3.0/24",
+			/*1.2.3.0 -> 1.2.3.255*/
+			exp_sz:        4,
+			exp_start_ip:  -math.MaxInt64 + 0x01020300,
+			exp_start_sfx: 0,
+			exp_end_ip:    -math.MaxInt64 + 0x010203FF,
+			exp_end_sfx:   0,
+		},
+		/*errors*/
+		{
+			in_addr:   "xxx/24",
+			exp_error: "invalid CIDR address",
+		},
+		{
+			in_addr:   "xxx2",
+			exp_error: "invalid address",
+		},
+	}
+
+	for idx, test := range tests {
+		sz, start_ip, start_sfx, end_ip, end_sfx, err := Addr2Ints(test.in_addr)
+		if err != nil && test.exp_error == "" {
+			t.Fatalf("%d unexpected error : %s", idx, err)
+		}
+		if test.exp_error != "" {
+			if !strings.Contains(err.Error(), test.exp_error) {
+				t.Fatalf("%d unmatched error : %s != %s", idx, err, test.exp_error)
+			}
+			continue //we can skip this one
+		}
+		if sz != test.exp_sz {
+			t.Fatalf("%d unexpected size %d != %d", idx, sz, test.exp_sz)
+		}
+		if start_ip != test.exp_start_ip {
+			t.Fatalf("%d unexpected start_ip %d != %d", idx, start_ip, test.exp_start_ip)
+		}
+		if sz == 16 {
+			if start_sfx != test.exp_start_sfx {
+				t.Fatalf("%d unexpected start sfx %d != %d", idx, start_sfx, test.exp_start_sfx)
+			}
+		}
+		if end_ip != test.exp_end_ip {
+			t.Fatalf("%d unexpected end ip %d != %d", idx, end_ip, test.exp_end_ip)
+		}
+		if sz == 16 {
+			if end_sfx != test.exp_end_sfx {
+				t.Fatalf("%d unexpected end sfx %d != %d", idx, end_sfx, test.exp_end_sfx)
+			}
+		}
+
+	}
+}

+ 0 - 37
pkg/types/utils.go

@@ -2,7 +2,6 @@ package types
 
 
 import (
 import (
 	"bytes"
 	"bytes"
-	"encoding/binary"
 	"encoding/gob"
 	"encoding/gob"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
@@ -21,42 +20,6 @@ import (
 	"gopkg.in/natefinch/lumberjack.v2"
 	"gopkg.in/natefinch/lumberjack.v2"
 )
 )
 
 
-func IP2Int(ip net.IP) uint32 {
-	if len(ip) == 16 {
-		return binary.BigEndian.Uint32(ip[12:16])
-	}
-	return binary.BigEndian.Uint32(ip)
-}
-
-func Int2ip(nn uint32) net.IP {
-	ip := make(net.IP, 4)
-	binary.BigEndian.PutUint32(ip, nn)
-	return ip
-}
-
-//Stolen from : https://github.com/llimllib/ipaddress/
-// Return the final address of a net range. Convert to IPv4 if possible,
-// otherwise return an ipv6
-func LastAddress(n *net.IPNet) net.IP {
-	ip := n.IP.To4()
-	if ip == nil {
-		ip = n.IP
-		return net.IP{
-			ip[0] | ^n.Mask[0], ip[1] | ^n.Mask[1], ip[2] | ^n.Mask[2],
-			ip[3] | ^n.Mask[3], ip[4] | ^n.Mask[4], ip[5] | ^n.Mask[5],
-			ip[6] | ^n.Mask[6], ip[7] | ^n.Mask[7], ip[8] | ^n.Mask[8],
-			ip[9] | ^n.Mask[9], ip[10] | ^n.Mask[10], ip[11] | ^n.Mask[11],
-			ip[12] | ^n.Mask[12], ip[13] | ^n.Mask[13], ip[14] | ^n.Mask[14],
-			ip[15] | ^n.Mask[15]}
-	}
-
-	return net.IPv4(
-		ip[0]|^n.Mask[0],
-		ip[1]|^n.Mask[1],
-		ip[2]|^n.Mask[2],
-		ip[3]|^n.Mask[3])
-}
-
 var logFormatter log.Formatter
 var logFormatter log.Formatter
 var LogOutput *lumberjack.Logger //io.Writer
 var LogOutput *lumberjack.Logger //io.Writer
 var logLevel log.Level
 var logLevel log.Level

+ 434 - 0
scripts/test_ip_management.sh

@@ -0,0 +1,434 @@
+#! /usr/bin/env bash
+# -*- coding: utf-8 -*-
+
+
+# Codes
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+NC='\033[0m'
+OK_STR="${GREEN}OK${NC}"
+FAIL_STR="${RED}FAIL${NC}"
+
+CSCLI_BIN="./cscli"
+CSCLI="${CSCLI_BIN} -c dev.yaml"
+JQ="jq -e"
+CROWDSEC_API_URL="http://localhost:8081"
+CROWDSEC_VERSION=""
+API_KEY=""
+
+RELEASE_FOLDER_FULL=""
+FAILED="false"
+MUST_FAIL="false"
+
+
+get_latest_release() {
+  CROWDSEC_VERSION=$(curl --silent "https://api.github.com/repos/crowdsecurity/crowdsec/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+}
+
+### Helpers
+function docurl
+{
+    URI=$1
+    curl -s -H "X-Api-Key: ${API_KEY}" "${CROWDSEC_API_URL}${URI}"
+} 
+
+function bouncer_echo {
+    if [[ ${FAILED} == "false" ]];
+    then
+        echo -e "[bouncer] $1: ${OK_STR}"
+    else
+        echo -e "[bouncer] $1: ${FAIL_STR}"
+    fi
+    FAILED="false"
+}
+
+function cscli_echo {
+    if [[ ${FAILED} == "false" ]];
+    then
+        echo -e "[cscli]   $1: ${OK_STR}"
+    else
+        echo -e "[cscli]   $1: ${FAIL_STR}"
+    fi
+    FAILED="false"
+}
+
+function fail {
+    FAILED="true"
+    MUST_FAIL="true"
+}
+
+## End helpers ##
+
+
+function init
+{
+    if [[ ! -d ${RELEASE_FOLDER} ]];
+    then
+      cd ..
+      get_latest_release
+      BUILD_VERSION=${CROWDSEC_VERSION} make release
+      if [ $? != 0 ]; then
+        echo "Unable to make the release (make sur you have go installed), exiting"
+        exit 1
+      fi
+      RELEASE_FOLDER="crowdsec-${CROWDSEC_VERSION}"
+    fi
+    RELEASE_FOLDER_FULL="$(readlink -f ${RELEASE_FOLDER})"
+    TEST_ENV_FOLDER="${RELEASE_FOLDER_FULL}/tests/"
+    cd ${RELEASE_FOLDER}/
+    if [[ ! -d "${TEST_ENV_FOLDER}" ]];
+    then
+        echo "Installing crowdsec test environment"
+        ./test_env.sh
+    fi
+    reset
+}
+
+
+function test_ipv4_ip
+{
+    echo ""
+    echo "##########################################"
+    echo "$FUNCNAME"
+    echo "##########################################"
+    echo ""
+    
+    ${CSCLI} decisions list -o json | ${JQ} '. == null' > /dev/null || fail
+    cscli_echo "first decisions list"
+    
+    docurl /v1/decisions | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "first bouncer decisions request (must be empty)"
+
+    #add ip decision
+    echo "adding decision for 1.2.3.4"
+    ${CSCLI} decisions add -i 1.2.3.4  > /dev/null 2>&1 || fail
+    
+    ${CSCLI} decisions list -o json | ${JQ} '.[].decisions[0].value == "1.2.3.4"' > /dev/null || fail
+    cscli_echo "getting all decision"
+    
+    docurl /v1/decisions | ${JQ} '.[0].value == "1.2.3.4"' > /dev/null || fail
+    bouncer_echo "getting all decision"
+
+    #check ip match
+    ${CSCLI} decisions list -i 1.2.3.4 -o json | ${JQ} '.[].decisions[0].value == "1.2.3.4"'  > /dev/null || fail
+    cscli_echo "getting decision for 1.2.3.4"
+    
+    docurl /v1/decisions?ip=1.2.3.4 | ${JQ} '.[0].value == "1.2.3.4"' > /dev/null || fail
+    bouncer_echo "getting decision for 1.2.3.4"
+
+    ${CSCLI} decisions list -i 1.2.3.5 -o json | ${JQ} '. == null'  > /dev/null || fail
+    cscli_echo "getting decision for 1.2.3.5"
+
+    docurl /v1/decisions?ip=1.2.3.5 | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decision for 1.2.3.5"
+
+    #check outer range match
+    ${CSCLI} decisions list -r 1.2.3.0/24 -o json | ${JQ} '. == null'  > /dev/null || fail
+    cscli_echo "getting decision for 1.2.3.0/24"
+
+    docurl "/v1/decisions?range=1.2.3.0/24" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decision for 1.2.3.0/24"
+
+    ${CSCLI} decisions list -r 1.2.3.0/24 --contained -o json |${JQ} '.[].decisions[0].value == "1.2.3.4"'  > /dev/null || fail
+    cscli_echo "getting decisions where IP in 1.2.3.0/24"
+
+    docurl "/v1/decisions?range=1.2.3.0/24&contains=false" | ${JQ} '.[0].value == "1.2.3.4"' > /dev/null || fail
+    bouncer_echo "getting decisions where IP in 1.2.3.0/24"
+
+}
+
+function test_ipv4_range
+{
+    echo ""
+    echo "##########################################"
+    echo "$FUNCNAME"
+    echo "##########################################"
+    echo ""
+
+    cscli_echo "adding decision for range 4.4.4.0/24"
+    ${CSCLI} decisions add -r 4.4.4.0/24 > /dev/null 2>&1 || fail
+
+    ${CSCLI} decisions list -o json | ${JQ} '.[0].decisions[0].value == "4.4.4.0/24", .[1].decisions[0].value == "1.2.3.4"'> /dev/null || fail
+    cscli_echo "getting all decision"
+    
+    docurl ${APIK} "/v1/decisions" | ${JQ} '.[0].value == "1.2.3.4", .[1].value == "4.4.4.0/24"'> /dev/null || fail
+    bouncer_echo "getting all decision"
+
+    #check ip within/outside of range
+    ${CSCLI} decisions list -i 4.4.4.3 -o json | ${JQ} '.[].decisions[0].value == "4.4.4.0/24"' > /dev/null || fail
+    cscli_echo "getting decisions for ip 4.4.4."
+
+    docurl ${APIK} "/v1/decisions?ip=4.4.4.3" | ${JQ} '.[0].value == "4.4.4.0/24"' > /dev/null || fail
+    bouncer_echo "getting decisions for ip 4.4.4."
+
+    ${CSCLI} decisions list -i 4.4.4.4 -o json --contained | ${JQ} '. == null'> /dev/null || fail
+    cscli_echo "getting decisions for ip contained in 4.4.4."
+
+    docurl ${APIK} "/v1/decisions?ip=4.4.4.4&contains=false" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for ip contained in 4.4.4."
+
+    ${CSCLI} decisions list -i 5.4.4.3 -o json | ${JQ} '. == null' > /dev/null || fail
+    cscli_echo "getting decisions for ip 5.4.4."
+
+    docurl ${APIK} "/v1/decisions?ip=5.4.4.3" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for ip 5.4.4."
+
+    ${CSCLI} decisions list -r 4.4.0.0/16 -o json | ${JQ} '. == null' > /dev/null || fail
+    cscli_echo "getting decisions for range 4.4.0.0/1"
+
+    docurl ${APIK} "/v1/decisions?range=4.4.0.0/16" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for range 4.4.0.0/1"
+
+    ${CSCLI} decisions list -r 4.4.0.0/16 -o json --contained | ${JQ} '.[].decisions[0].value == "4.4.4.0/24"' > /dev/null || fail
+    cscli_echo "getting decisions for ip/range in 4.4.0.0/1"
+
+    docurl ${APIK} "/v1/decisions?range=4.4.0.0/16&contains=false" | ${JQ} '.[0].value == "4.4.4.0/24"' > /dev/null || fail
+    bouncer_echo "getting decisions for ip/range in 4.4.0.0/1"
+
+    #check subrange
+    ${CSCLI} decisions list -r 4.4.4.2/28 -o json | ${JQ} '.[].decisions[0].value == "4.4.4.0/24"' > /dev/null || fail
+    cscli_echo "getting decisions for range 4.4.4.2/2"
+
+    docurl ${APIK} "/v1/decisions?range=4.4.4.2/28" | ${JQ} '.[].value == "4.4.4.0/24"' > /dev/null || fail
+    bouncer_echo "getting decisions for range 4.4.4.2/2"
+
+    ${CSCLI} decisions list -r 4.4.3.2/28 -o json | ${JQ} '. == null' > /dev/null || fail
+    cscli_echo "getting decisions for range 4.4.3.2/2"
+
+    docurl ${APIK} "/v1/decisions?range=4.4.3.2/28" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for range 4.4.3.2/2"
+
+}
+
+function test_ipv6_ip
+{
+
+    echo ""
+    echo "##########################################"
+    echo "$FUNCNAME"
+    echo "##########################################"
+    echo ""
+
+    cscli_echo "adding decision for ip 1111:2222:3333:4444:5555:6666:7777:8888"
+    ${CSCLI} decisions add -i 1111:2222:3333:4444:5555:6666:7777:8888 > /dev/null 2>&1
+
+    ${CSCLI} decisions list -o json | ${JQ} '.[].decisions[0].value == "1111:2222:3333:4444:5555:6666:7777:8888"'  > /dev/null || fail
+    cscli_echo "getting all decision"
+
+    docurl ${APIK} "/v1/decisions" | ${JQ} '.[].value == "1111:2222:3333:4444:5555:6666:7777:8888"' > /dev/null || fail
+    bouncer_echo "getting all decision"
+
+    ${CSCLI} decisions list -i 1111:2222:3333:4444:5555:6666:7777:8888 -o json | ${JQ} '.[].decisions[0].value == "1111:2222:3333:4444:5555:6666:7777:8888"'  > /dev/null || fail
+    cscli_echo "getting decisions for ip 1111:2222:3333:4444:5555:6666:7777:8888"
+    
+    docurl ${APIK} "/v1/decisions?ip=1111:2222:3333:4444:5555:6666:7777:8888" | ${JQ} '.[].value == "1111:2222:3333:4444:5555:6666:7777:8888"' > /dev/null || fail
+    bouncer_echo "getting decisions for ip 1111:2222:3333:4444:5555:6666:7777:888"
+
+    ${CSCLI} decisions list -i 1211:2222:3333:4444:5555:6666:7777:8888 -o json | ${JQ} '. == null' > /dev/null || fail
+    cscli_echo "getting decisions for ip 1211:2222:3333:4444:5555:6666:7777:8888"
+
+    docurl ${APIK} "/v1/decisions?ip=1211:2222:3333:4444:5555:6666:7777:8888" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for ip 1211:2222:3333:4444:5555:6666:7777:888"
+
+    ${CSCLI} decisions list -i 1111:2222:3333:4444:5555:6666:7777:8887 -o json | ${JQ} '. == null' > /dev/null || fail
+    cscli_echo "getting decisions for ip 1111:2222:3333:4444:5555:6666:7777:8887"
+
+    docurl ${APIK} "/v1/decisions?ip=1111:2222:3333:4444:5555:6666:7777:8887" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for ip 1111:2222:3333:4444:5555:6666:7777:888"
+
+    ${CSCLI} decisions list -r 1111:2222:3333:4444:5555:6666:7777:8888/48 -o json | ${JQ} '. == null' > /dev/null || fail
+    cscli_echo "getting decisions for range 1111:2222:3333:4444:5555:6666:7777:8888/48"
+
+    docurl ${APIK} "/v1/decisions?range=1111:2222:3333:4444:5555:6666:7777:8888/48" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for range 1111:2222:3333:4444:5555:6666:7777:8888/48"
+
+    ${CSCLI} decisions list -r 1111:2222:3333:4444:5555:6666:7777:8888/48 --contained -o json | ${JQ} '.[].decisions[0].value == "1111:2222:3333:4444:5555:6666:7777:8888"' > /dev/null || fail
+    cscli_echo "getting decisions for ip/range in range 1111:2222:3333:4444:5555:6666:7777:8888/48"
+
+    docurl ${APIK} "/v1/decisions?range=1111:2222:3333:4444:5555:6666:7777:8888/48&&contains=false" | ${JQ} '.[].value == "1111:2222:3333:4444:5555:6666:7777:8888"' > /dev/null || fail
+    bouncer_echo "getting decisions for ip/range in 1111:2222:3333:4444:5555:6666:7777:8888/48"
+
+    ${CSCLI} decisions list -r 1111:2222:3333:4444:5555:6666:7777:8888/64 -o json | ${JQ} '. == null' > /dev/null || fail
+    cscli_echo "getting decisions for range 1111:2222:3333:4444:5555:6666:7777:8888/64"
+
+    docurl ${APIK} "/v1/decisions?range=1111:2222:3333:4444:5555:6666:7777:8888/64" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for range 1111:2222:3333:4444:5555:6666:7777:8888/64"
+
+    ${CSCLI} decisions list -r 1111:2222:3333:4444:5555:6666:7777:8888/64 -o json --contained | ${JQ} '.[].decisions[0].value == "1111:2222:3333:4444:5555:6666:7777:8888"'  > /dev/null || fail
+    cscli_echo "getting decisions for ip/range in 1111:2222:3333:4444:5555:6666:7777:8888/64"
+
+    docurl ${APIK} "/v1/decisions?range=1111:2222:3333:4444:5555:6666:7777:8888/64&&contains=false" | ${JQ} '.[].value == "1111:2222:3333:4444:5555:6666:7777:8888"' > /dev/null || fail
+    bouncer_echo "getting decisions for ip/range in 1111:2222:3333:4444:5555:6666:7777:8888/64"
+
+}
+
+function test_ipv6_range
+{
+    echo ""
+    echo "##########################################"
+    echo "$FUNCNAME"
+    echo "##########################################"
+    echo ""
+
+    cscli_echo "adding decision for range aaaa:2222:3333:4444::/64"
+    ${CSCLI} decisions add -r aaaa:2222:3333:4444::/64 > /dev/null 2>&1 || fail
+     
+    ${CSCLI} decisions list -o json | ${JQ} '.[0].decisions[0].value == "aaaa:2222:3333:4444::/64", .[1].decisions[0].value == "1111:2222:3333:4444:5555:6666:7777:8888"' > /dev/null || fail
+    cscli_echo "getting all decision"
+
+    docurl ${APIK} "/v1/decisions" | ${JQ} '.[0].value == "1111:2222:3333:4444:5555:6666:7777:8888", .[1].value == "aaaa:2222:3333:4444::/64"' > /dev/null || fail
+    bouncer_echo "getting all decision"
+
+    #check ip within/out of range
+    ${CSCLI} decisions list -i aaaa:2222:3333:4444:5555:6666:7777:8888 -o json | ${JQ} '.[].decisions[0].value == "aaaa:2222:3333:4444::/64"'  > /dev/null || fail
+    cscli_echo "getting decisions for ip aaaa:2222:3333:4444:5555:6666:7777:8888"
+
+    docurl ${APIK} "/v1/decisions?ip=aaaa:2222:3333:4444:5555:6666:7777:8888" | ${JQ} '.[].value == "aaaa:2222:3333:4444::/64"' > /dev/null || fail
+    bouncer_echo "getting decisions for ip aaaa:2222:3333:4444:5555:6666:7777:8888"
+
+    ${CSCLI} decisions list -i aaaa:2222:3333:4445:5555:6666:7777:8888 -o json | ${JQ} '. == null' > /dev/null || fail
+    cscli_echo "getting decisions for ip aaaa:2222:3333:4445:5555:6666:7777:8888"
+
+    docurl ${APIK} "/v1/decisions?ip=aaaa:2222:3333:4445:5555:6666:7777:8888" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for ip aaaa:2222:3333:4445:5555:6666:7777:8888"
+
+    ${CSCLI} decisions list -i aaa1:2222:3333:4444:5555:6666:7777:8887 -o json | ${JQ} '. == null' > /dev/null || fail
+    cscli_echo "getting decisions for ip aaa1:2222:3333:4444:5555:6666:7777:8887"
+
+    docurl ${APIK} "/v1/decisions?ip=aaa1:2222:3333:4444:5555:6666:7777:8887" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for ip aaa1:2222:3333:4444:5555:6666:7777:8887"
+
+    #check subrange within/out of range
+    ${CSCLI} decisions list -r aaaa:2222:3333:4444:5555::/80 -o json | ${JQ} '.[].decisions[0].value == "aaaa:2222:3333:4444::/64"'  > /dev/null || fail
+    cscli_echo "getting decisions for range aaaa:2222:3333:4444:5555::/80"
+    
+    docurl ${APIK} "/v1/decisions?range=aaaa:2222:3333:4444:5555::/80" | ${JQ} '.[].value == "aaaa:2222:3333:4444::/64"' > /dev/null || fail
+    bouncer_echo "getting decisions for range aaaa:2222:3333:4444:5555::/80"
+
+    ${CSCLI} decisions list -r aaaa:2222:3333:4441:5555::/80 -o json | ${JQ} '. == null'  > /dev/null || fail
+    cscli_echo "getting decisions for range aaaa:2222:3333:4441:5555::/80"
+    
+    docurl ${APIK} "/v1/decisions?range=aaaa:2222:3333:4441:5555::/80" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for range aaaa:2222:3333:4441:5555::/80"
+
+    ${CSCLI} decisions list -r aaa1:2222:3333:4444:5555::/80 -o json | ${JQ} '. == null'  > /dev/null || fail
+    cscli_echo "getting decisions for range aaa1:2222:3333:4444:5555::/80"
+
+    docurl ${APIK} "/v1/decisions?range=aaa1:2222:3333:4444:5555::/80" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for range aaa1:2222:3333:4444:5555::/80"
+
+    #check outer range
+    ${CSCLI} decisions list -r aaaa:2222:3333:4444:5555:6666:7777:8888/48 -o json | ${JQ} '. == null' > /dev/null || fail
+    cscli_echo "getting decisions for range aaaa:2222:3333:4444:5555:6666:7777:8888/48"
+
+    docurl ${APIK} "/v1/decisions?range=aaaa:2222:3333:4444:5555:6666:7777:8888/48" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for range aaaa:2222:3333:4444:5555:6666:7777:8888/48"
+
+    ${CSCLI} decisions list -r aaaa:2222:3333:4444:5555:6666:7777:8888/48 -o json --contained | ${JQ} '.[].decisions[0].value == "aaaa:2222:3333:4444::/64"' > /dev/null || fail
+    cscli_echo "getting decisions for ip/range in aaaa:2222:3333:4444:5555:6666:7777:8888/48"
+
+    docurl ${APIK} "/v1/decisions?range=aaaa:2222:3333:4444:5555:6666:7777:8888/48&contains=false" | ${JQ} '.[].value == "aaaa:2222:3333:4444::/64"' > /dev/null || fail
+    bouncer_echo "getting decisions for ip/range in aaaa:2222:3333:4444:5555:6666:7777:8888/48"
+
+    ${CSCLI} decisions list -r aaaa:2222:3333:4445:5555:6666:7777:8888/48 -o json | ${JQ} '. == null' > /dev/null || fail
+    cscli_echo "getting decisions for ip/range aaaa:2222:3333:4445:5555:6666:7777:8888/48"
+
+    docurl ${APIK} "/v1/decisions?range=aaaa:2222:3333:4445:5555:6666:7777:8888/48" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for ip/range in aaaa:2222:3333:4445:5555:6666:7777:8888/48"
+
+    #bbbb:db8:: -> bbbb:db8:0000:0000:0000:7fff:ffff:ffff
+    ${CSCLI} decisions add -r bbbb:db8::/81 > /dev/null 2>&1
+    cscli_echo "adding decision for range bbbb:db8::/81" > /dev/null || fail
+
+    ${CSCLI} decisions list -o json -i bbbb:db8:0000:0000:0000:6fff:ffff:ffff | ${JQ} '.[].decisions[0].value == "bbbb:db8::/81"'  > /dev/null || fail
+    cscli_echo "getting decisions for ip bbbb:db8:0000:0000:0000:6fff:ffff:ffff"
+    
+    docurl ${APIK} "/v1/decisions?ip=bbbb:db8:0000:0000:0000:6fff:ffff:ffff" | ${JQ} '.[].value == "bbbb:db8::/81"' > /dev/null || fail
+    bouncer_echo "getting decisions for ip in bbbb:db8:0000:0000:0000:6fff:ffff:ffff"
+
+    ${CSCLI} decisions list -o json -i bbbb:db8:0000:0000:0000:8fff:ffff:ffff | ${JQ} '. == null' > /dev/null || fail
+    cscli_echo "getting decisions for ip bbbb:db8:0000:0000:0000:8fff:ffff:ffff"
+
+    docurl ${APIK} "/v1/decisions?ip=bbbb:db8:0000:0000:0000:8fff:ffff:ffff" | ${JQ} '. == null' > /dev/null || fail
+    bouncer_echo "getting decisions for ip in bbbb:db8:0000:0000:0000:8fff:ffff:ffff"
+
+}
+
+
+function start_test
+{
+
+    ## ipv4 testing
+    test_ipv4_ip
+    test_ipv4_range
+
+    reset
+
+    ## ipv6 testing
+    test_ipv6_ip
+    test_ipv6_range
+}
+
+function reset 
+{
+    cd ${RELEASE_FOLDER_FULL}/tests/
+    rm data/crowdsec.db > /dev/null 2>&1 || echo ""
+    killall crowdsec > /dev/null 2>&1 || echo ""
+    ${CSCLI} hub update
+    ${CSCLI} machines add -a
+    API_KEY=`${CSCLI} bouncers add TestingBouncer -o=raw`
+    ./crowdsec -c dev.yaml> crowdsec-out.log 2>&1  &
+    sleep 2
+}
+
+function down
+{
+  cd ${RELEASE_FOLDER_FULL}/tests/
+  rm data/crowdsec.db
+  killall crowdsec
+  #rm -rf tests/
+}
+
+
+usage() {
+      echo "Usage:"
+      echo ""
+      echo "    ./ip_mgmt_tests.sh -h                                   Display this help message."
+      echo "    ./ip_mgmt_tests.sh                                      Run all the testsuite. Go must be available to make the release"
+      echo "    ./ip_mgmt_tests.sh --release <path_to_release_folder>   If go is not installed, please provide a path to the crowdsec-vX.Y.Z release folder"
+      echo ""
+      exit 0  
+}
+
+while [[ $# -gt 0 ]]
+do
+    key="${1}"
+    case ${key} in
+    --release|-r)
+        RELEASE_FOLDER="${2}"
+        shift #past argument
+        shift
+        ;;   
+    -h|--help)
+        usage
+        exit 0
+        ;;
+    *)    # unknown option
+        echo "Unknown argument ${key}."
+        usage
+        exit 1
+        ;;
+    esac
+done
+
+
+init
+start_test
+down
+
+if [[ ${MUST_FAIL} == "true" ]];
+then
+    echo ""
+    echo "One or more tests have failed !"
+    exit 1
+fi

+ 1 - 1
scripts/test_wizard_upgrade.sh

@@ -69,7 +69,7 @@ function init
     wget https://github.com/crowdsecurity/cs-firewall-bouncer/releases/download/${BOUNCER_VERSION}/cs-firewall-bouncer.tgz
     wget https://github.com/crowdsecurity/cs-firewall-bouncer/releases/download/${BOUNCER_VERSION}/cs-firewall-bouncer.tgz
     tar xzvf cs-firewall-bouncer.tgz
     tar xzvf cs-firewall-bouncer.tgz
     cd cs-firewall-bouncer-${BOUNCER_VERSION}/
     cd cs-firewall-bouncer-${BOUNCER_VERSION}/
-    echo "iptables" | sudo ./install.sh
+    (echo "iptables" | sudo ./install.sh) || (echo "Unable to install cs-firewall-bouncer" && exit 1)
     cd ${CURRENT_FOLDER}
     cd ${CURRENT_FOLDER}
 
 
     echo "[*] Tainting parser /etc/crowdsec/parsers/s01-parse/sshd-logs.yaml"
     echo "[*] Tainting parser /etc/crowdsec/parsers/s01-parse/sshd-logs.yaml"

Some files were not shown because too many files changed in this diff