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  }