Repository: go-playground/validator
Pull request #1023 (2022-11-04 19:03:37)

Feat: support mongodb objectID validation
## Fixes Or Enhances


**Make sure that you've checked the boxes below before you submit PR:**
- [x] Tests exist or have been written that cover this particular change.

@go-playground/validator-maintainers

I've tried adding a tag to validate mongodb objectIDs and I welcome any form of feedback.
Thanks.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/53926161/badge)](https://coveralls.io/builds/53926161)

Coverage increased (+0.007%) to 74.19% when pulling **647b13c8e457d3fd53e61fe1d31d7d76e6803847 on itzjustalan:master** into **c7e0172e0fd176bdc521afb5186818a7db6b77ac on go-playground:master**.

tomaswarynyca: @deankarn are you considering adding this?
Ks89: this pr could be very useful

Patch:
diff --git a/README.md b/README.md
index 3d52c73..ef073fa 100644
--- a/README.md
+++ b/README.md
@@ -156,6 +156,7 @@ Baked-in Validations
 | btc_addr | Bitcoin Address |
 | btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
 | credit_card | Credit Card Number |
+| mongodb | MongoDB ObjectID |
 | cron | Cron |
 | datetime | Datetime |
 | e164 | e164 formatted phone number |
diff --git a/baked_in.go b/baked_in.go
index 9ce2282..122f30f 100644
--- a/baked_in.go
+++ b/baked_in.go
@@ -219,6 +219,7 @@ var (
 		"semver":                        isSemverFormat,
 		"dns_rfc1035_label":             isDnsRFC1035LabelFormat,
 		"credit_card":                   isCreditCard,
+		"mongodb":                       isMongoDB,
 		"cron":                          isCron,
 	}
 )
@@ -2567,6 +2568,12 @@ func isDnsRFC1035LabelFormat(fl FieldLevel) bool {
 	return dnsRegexRFC1035Label.MatchString(val)
 }
 
+// isMongoDB is the validation function for validating if the current field's value is valid mongoDB objectID
+func isMongoDB(fl FieldLevel) bool {
+	val := fl.Field().String()
+	return mongodbRegex.MatchString(val)
+}
+
 // isCreditCard is the validation function for validating if the current field's value is a valid credit card number
 func isCreditCard(fl FieldLevel) bool {
 	val := fl.Field().String()
diff --git a/doc.go b/doc.go
index 2b44b53..e0b79b7 100644
--- a/doc.go
+++ b/doc.go
@@ -1332,6 +1332,13 @@ This validates that a string value contains a valid credit card number using Luh
 	Usage: credit_card
 
 
+#MongoDb ObjectID
+
+This validates that a string is a valid 24 character hexadecimal string.
+
+  Usage: mongodb
+
+
 # Cron
 
 This validates that a string value contains a valid cron expression.
diff --git a/regexes.go b/regexes.go
index 029dd91..a65ac6f 100644
--- a/regexes.go
+++ b/regexes.go
@@ -65,6 +65,7 @@ const (
 	bicRegexString                   = `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$`
 	semverRegexString                = `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$` // numbered capture groups https://semver.org/
 	dnsRegexStringRFC1035Label       = "^[a-z]([-a-z0-9]*[a-z0-9]){0,62}$"
+	mongodbRegexString               = "^[a-f\\d]{24}$"
 	cronRegexString                  = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})`
 )
 
@@ -129,5 +130,6 @@ var (
 	bicRegex                   = regexp.MustCompile(bicRegexString)
 	semverRegex                = regexp.MustCompile(semverRegexString)
 	dnsRegexRFC1035Label       = regexp.MustCompile(dnsRegexStringRFC1035Label)
+	mongodbRegex               = regexp.MustCompile(mongodbRegexString)
 	cronRegex                  = regexp.MustCompile(cronRegexString)
 )


Test patch:
diff --git a/validator_test.go b/validator_test.go
index ec7e3aa..8e1ea3e 100644
--- a/validator_test.go
+++ b/validator_test.go
@@ -12546,6 +12546,42 @@ func TestValidate_ValidateMapCtx(t *testing.T) {
 	}
 }
 
+func TestMongoDBObjectIDFormatValidation(t *testing.T) {
+	tests := []struct {
+		value    string `validate:"mongodb"`
+		tag      string
+		expected bool
+	}{
+		{"507f191e810c19729de860ea", "mongodb", true},
+		{"507f191e810c19729de860eG", "mongodb", false},
+		{"M07f191e810c19729de860eG", "mongodb", false},
+		{"07f191e810c19729de860ea", "mongodb", false},
+		{"507f191e810c19729de860e", "mongodb", false},
+		{"507f191e810c19729de860ea4", "mongodb", false},
+	}
+
+	validate := New()
+
+	for i, test := range tests {
+		errs := validate.Var(test.value, test.tag)
+
+		if test.expected {
+			if !IsEqual(errs, nil) {
+				t.Fatalf("Index: %d mongodb failed Error: %s", i, errs)
+			}
+		} else {
+			if IsEqual(errs, nil) {
+				t.Fatalf("Index: %d mongodb failed Error: %s", i, errs)
+			} else {
+				val := getError(errs, "", "")
+				if val.Tag() != "mongodb" {
+					t.Fatalf("Index: %d mongodb failed Error: %s", i, errs)
+				}
+			}
+		}
+	}
+}
+
 func TestCreditCardFormatValidation(t *testing.T) {
 	tests := []struct {
 		value    string `validate:"credit_card"`