Skip to content

Commit

Permalink
Added functionality to load properties from mix of files and urls
Browse files Browse the repository at this point in the history
  • Loading branch information
Roman Khokhla committed Jul 1, 2016
1 parent c265cfa commit 180430e
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 39 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -2,3 +2,5 @@
*.sublime-workspace
*.un~
*.swp
.idea/
*.iml
155 changes: 116 additions & 39 deletions load.go
Expand Up @@ -66,6 +66,24 @@ func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) {
return loadURLs(urls, ignoreMissing)
}

// LoadResource reads the content of the resource into Properties struct.
//
// If resource name starts with http:// or https:// it is treated as URL.
// Encoding specifies encoding of the files. For URLs encoding is determined
// via the Content-Type header.
func LoadResource(name string, enc Encoding) (*Properties, error) {
return loadResources([]string{name}, enc, false)
}

// LoadResources reads the content of multiple resources in the given order
// into a Properties struct. If resource name starts with http:// or https://
// it is treated as URL otherwise as a file. Encoding specifies encoding of the files.
// For URLs encoding is determined via the Content-Type header. If 'ignoreMissing'
// is true then resources that cannot be found are ignored.
func LoadResources(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
return loadResources(names, enc, ignoreMissing)
}

// MustLoadString reads an UTF8 string into a Properties struct and
// panics on error.
func MustLoadString(s string) *Properties {
Expand Down Expand Up @@ -98,6 +116,27 @@ func MustLoadURLs(urls []string, ignoreMissing bool) *Properties {
return must(LoadURLs(urls, ignoreMissing))
}

// MustLoadResource reads the content of the resource into Properties struct
// and panics on error.
//
// If resource name starts with http:// or https:// it is treated as URL.
// Encoding specifies encoding of the files. For URLs encoding is determined
// via the Content-Type header.
func MustLoadResource(name string, enc Encoding) *Properties {
return must(LoadResource(name, enc))
}

// MustLoadResources reads the content of multiple resources in the given order
// into a Properties struct and panics on error.
//
// If resource name starts with http:// or https:// it is treated as URL otherwise as a file.
// Encoding specifies encoding of the files.
// For URLs encoding is determined via the Content-Type header. If 'ignoreMissing'
// is true then resources that cannot be found are ignored.
func MustLoadResources(names []string, enc Encoding, ignoreMissing bool) *Properties {
return must(LoadResources(names, enc, ignoreMissing))
}

func loadBuf(buf []byte, enc Encoding) (*Properties, error) {
p, err := parse(convert(buf, enc))
if err != nil {
Expand All @@ -109,61 +148,99 @@ func loadBuf(buf []byte, enc Encoding) (*Properties, error) {
func loadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
var buf bytes.Buffer
for _, filename := range filenames {
f, err := expandFilename(filename)
f, err := expandName(filename)
if err != nil {
return nil, err
}

data, err := ioutil.ReadFile(f)
if err != nil {
if ignoreMissing && os.IsNotExist(err) {
LogPrintf("properties: %s not found. skipping", filename)
continue
}
if err := loadFileIntoBuffer(f, enc, ignoreMissing, &buf); err != nil {
return nil, err
}
}
p, err := loadBuf(buf.Bytes(), UTF8)
return p, err
}

// concatenate the buffers and add a new line in case
// the previous file didn't end with a new line
buf.Write(data)
buf.WriteRune('\n')
func loadFileIntoBuffer(filename string, enc Encoding, ignoreMissing bool, buf *bytes.Buffer) error {
data, err := ioutil.ReadFile(filename)
if err != nil {
if ignoreMissing && os.IsNotExist(err) {
LogPrintf("properties: %s not found. skipping", filename)
return nil
}
return err
}
return loadBuf(buf.Bytes(), enc)

// concatenate the buffers and add a new line in case
// the previous file didn't end with a new line
buf.WriteString(convert(data, enc))
buf.WriteRune('\n')
return nil
}

func loadURLs(urls []string, ignoreMissing bool) (*Properties, error) {
var buf bytes.Buffer
for _, u := range urls {
resp, err := http.Get(u)
url, err := expandName(u)
if err != nil {
return nil, fmt.Errorf("properties: error fetching %q. %s", u, err)
}
if resp.StatusCode == 404 && ignoreMissing {
LogPrintf("properties: %s returned %d. skipping", u, resp.StatusCode)
continue
return nil, err
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("properties: %s returned %d", u, resp.StatusCode)
if err := loadUrlIntoBuffer(url, ignoreMissing, &buf); err != nil {
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
}
return loadBuf(buf.Bytes(), UTF8)
}

func loadUrlIntoBuffer(url string, ignoreMissing bool, buf *bytes.Buffer) error {
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("properties: error fetching %q. %s", url, err)
}
if resp.StatusCode == 404 && ignoreMissing {
LogPrintf("properties: %s returned %d. skipping", url, resp.StatusCode)
return nil
}
if resp.StatusCode != 200 {
return fmt.Errorf("properties: %s returned %d", url, resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return fmt.Errorf("properties: %s error reading response. %s", url, err)
}

ct := resp.Header.Get("Content-Type")
var enc Encoding
switch strings.ToLower(ct) {
case "text/plain", "text/plain; charset=iso-8859-1", "text/plain; charset=latin1":
enc = ISO_8859_1
case "", "text/plain; charset=utf-8":
enc = UTF8
default:
return fmt.Errorf("properties: invalid content type %s", ct)
}

buf.WriteString(convert(body, enc))
buf.WriteRune('\n')
return nil
}

func loadResources(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
var buf bytes.Buffer
for _, n := range names {
name, err := expandName(n)
if err != nil {
return nil, fmt.Errorf("properties: %s error reading response. %s", u, err)
return nil, err
}

ct := resp.Header.Get("Content-Type")
var enc Encoding
switch strings.ToLower(ct) {
case "text/plain", "text/plain; charset=iso-8859-1", "text/plain; charset=latin1":
enc = ISO_8859_1
case "", "text/plain; charset=utf-8":
enc = UTF8
default:
return nil, fmt.Errorf("properties: invalid content type %s", ct)
if strings.HasPrefix(name, "http://") || strings.HasPrefix(name, "https://") {
if err := loadUrlIntoBuffer(name, ignoreMissing, &buf); err != nil {
return nil, err
}
} else {
if err := loadFileIntoBuffer(name, enc, ignoreMissing, &buf); err != nil {
return nil, err
}
}

buf.WriteString(convert(body, enc))
buf.WriteRune('\n')
}
return loadBuf(buf.Bytes(), UTF8)
}
Expand All @@ -179,8 +256,8 @@ func must(p *Properties, err error) *Properties {
// If the environment variable does not exist then it will be replaced
// with an empty string. Malformed expressions like "${ENV_VAR" will
// be reported as error.
func expandFilename(filename string) (string, error) {
return expand(filename, make(map[string]bool), "${", "}", make(map[string]string))
func expandName(name string) (string, error) {
return expand(name, make(map[string]bool), "${", "}", make(map[string]string))
}

// Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string.
Expand Down
17 changes: 17 additions & 0 deletions load_test.go
Expand Up @@ -89,6 +89,23 @@ func (s *LoadSuite) TestLoadURLs(c *C) {
assertKeyValues(c, "", p, "key", "value", "key2", "value2")
}

func (s *LoadSuite) TestLoadResource(c *C) {
srv := testServer()
defer srv.Close()
p := MustLoadResource(srv.URL + "/a", UTF8)
assertKeyValues(c, "", p, "key", "value")
}

func (s *LoadSuite) TestLoadResources(c *C) {
filename := s.makeFile(c, "key=value")
filename2 := s.makeFile(c, "key2=value3")
filename3 := s.makeFile(c, "key=value4")
srv := testServer()
defer srv.Close()
p := MustLoadResources([]string{filename, filename2, srv.URL + "/a", srv.URL + "/b", filename3}, UTF8, false)
assertKeyValues(c, "", p, "key", "value4", "key2", "value2")
}

func (s *LoadSuite) TestLoadURLsAndFailMissing(c *C) {
srv := testServer()
defer srv.Close()
Expand Down

0 comments on commit 180430e

Please sign in to comment.