github.com/mailgun/holster/v4@v4.20.0/mongoutil/uri.go (about) 1 package mongoutil 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "strconv" 8 "strings" 9 "unicode" 10 ) 11 12 type Config struct { 13 Servers []string `json:"servers"` 14 Database string `json:"database"` 15 URI string `json:"uri"` 16 Options []map[string]interface{} `json:"options"` 17 } 18 19 func MongoURI() string { 20 mongoURI := os.Getenv("MONGO_URI") 21 if mongoURI == "" { 22 return "mongodb://127.0.0.1:27017/mg_test" 23 } 24 return mongoURI 25 } 26 27 func (c Config) URIWithOptions() string { 28 URI := c.URI 29 30 // Create an URI using the Servers list and Database if provided 31 if len(c.Servers) != 0 && c.Database != "" { 32 URI = fmt.Sprintf("mongodb://%s/%s", strings.Join(c.Servers, ","), c.Database) 33 } else if len(c.Servers) != 0 { 34 URI = fmt.Sprintf("mongodb://%s/", strings.Join(c.Servers, ",")) 35 } 36 37 type opt struct { 38 key string 39 value string 40 } 41 baseURI := URI 42 var options []opt 43 44 // Parse options from the URI. 45 qmIdx := strings.Index(URI, "?") 46 if qmIdx > 0 { 47 baseURI = URI[:qmIdx] 48 for _, pair := range strings.Split(URI[qmIdx+1:], "&") { 49 eqIdx := strings.Index(pair, "=") 50 if eqIdx > 0 { 51 options = append(options, opt{key: pair[:eqIdx], value: pair[eqIdx+1:]}) 52 } 53 } 54 } 55 56 // NOTE: The options are an ordered list because mongo cares 57 // about the order of some options like replica tag order. 58 59 // Override URI options with config options. 60 for _, o := range c.Options { 61 for optName, optVal := range o { 62 switch optVal := optVal.(type) { 63 case int: 64 options = append(options, opt{key: toCamelCase(optName), value: strconv.Itoa(optVal)}) 65 case float64: 66 options = append(options, opt{key: toCamelCase(optName), value: strconv.Itoa(int(optVal))}) 67 case string: 68 options = append(options, opt{key: toCamelCase(optName), value: optVal}) 69 case bool: 70 options = append(options, opt{key: toCamelCase(optName), value: strconv.FormatBool(optVal)}) 71 } 72 } 73 } 74 75 // Construct a URI as recognized by mgo.Dial 76 firstOpt := true 77 var buf bytes.Buffer 78 79 // If base URI was provided but no database specified 80 if baseURI != "" { 81 // if baseURI doesn't already end with a `/` 82 if !strings.HasSuffix(baseURI, "/") { 83 // Inspect the last character 84 last := baseURI[len(baseURI)-1] 85 // If the last character is an integer then we assume that we are looking at the port number, 86 // thus no database was provided. 87 if _, err := strconv.Atoi(string(last)); err == nil { 88 // We must append a `/` to the end of the string 89 baseURI += "/" 90 } 91 } 92 } 93 94 buf.WriteString(baseURI) 95 96 for i := range options { 97 o := options[i] 98 if firstOpt { 99 buf.WriteRune('?') 100 firstOpt = false 101 } else { 102 buf.WriteRune('&') 103 } 104 buf.WriteString(o.key) 105 buf.WriteRune('=') 106 buf.WriteString(o.value) 107 } 108 return buf.String() 109 } 110 111 func toCamelCase(s string) string { 112 var buf bytes.Buffer 113 capitalize := false 114 for _, ch := range s { 115 if ch == '_' { 116 capitalize = true 117 continue 118 } 119 if capitalize { 120 capitalize = false 121 buf.WriteRune(unicode.ToUpper(ch)) 122 continue 123 } 124 buf.WriteRune(ch) 125 } 126 return buf.String() 127 }