<commit_before>// Copyright 2016 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package scopedconfig

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/db/storage"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

const (
	defaultConfigName = ""
)

type ScopedConfig struct {
	coll          string
	name          string
	AllowedPools  []string
	AllowEmpty    bool
	ShallowMerge  bool
	Jsonfy        bool
	SliceAdd      bool
	AllowMapEmpty bool
}

type scopedConfigEntry struct {
	Name string
	Pool string
	Val  bson.Raw
}

func FindScopedConfig(coll string) *ScopedConfig {
	return FindScopedConfigFor(coll, defaultConfigName)
}

func FindScopedConfigFor(coll, name string) *ScopedConfig {
	return &ScopedConfig{coll: fmt.Sprintf("scoped_%s", coll), name: name}
}

func FindAllScopedConfig(collName string) ([]*ScopedConfig, error) {
	base := FindScopedConfig(collName)
	coll, err := base.collection()
	if err != nil {
		return nil, err
	}
	defer coll.Close()
	var names []string
	err = coll.Find(nil).Distinct("name", &names)
	if err != nil {
		return nil, err
	}
	configs := make([]*ScopedConfig, len(names))
	for i, n := range names {
		configs[i] = FindScopedConfigFor(collName, n)
	}
	return configs, nil
}

func (n *ScopedConfig) GetName() string {
	return n.name
}

func (n *ScopedConfig) SetFieldAtomic(pool, name string, value interface{}) (bool, error) {
	coll, err := n.collection()
	if err != nil {
		return false, err
	}
	defer coll.Close()
	_, err = coll.Upsert(bson.M{
		"name": n.name,
		"pool": pool,
		"$or":  []bson.M{{"val." + name: ""}, {"val." + name: bson.M{"$exists": false}}},
	}, bson.M{"$set": bson.M{"val." + name: value}})
	if err == nil {
		return true, nil
	}
	if mgo.IsDup(err) {
		return false, nil
	}
	return false, err
}

func (n *ScopedConfig) SetField(pool, name string, value interface{}) error {
	coll, err := n.collection()
	if err != nil {
		return err
	}
	defer coll.Close()
	_, err = coll.Upsert(bson.M{"name": n.name, "pool": pool}, bson.M{"$set": bson.M{"val." + name: value}})
	return err
}

func (n *ScopedConfig) SaveBase(val interface{}) error {
	return n.Save("", val)
}

func (n *ScopedConfig) Save(pool string, val interface{}) error {
	if reflect.TypeOf(val).Kind() == reflect.Ptr {
		val = reflect.ValueOf(val).Elem().Interface()
	}
	if reflect.TypeOf(val).Kind() != reflect.Struct {
		return errors.New("a struct type or pointer to a struct is required as value")
	}
	coll, err := n.collection()
	if err != nil {
		return err
	}
	defer coll.Close()
	if n.Jsonfy {
		var result map[string]interface{}
		var data []byte
		data, err = json.Marshal(val)
		if err != nil {
			return err
		}
		err = json.Unmarshal(data, &result)
		if err != nil {
			return err
		}
		val = interface{}(result)
	}
	_, err = coll.Upsert(bson.M{"name": n.name, "pool": pool}, bson.M{"name": n.name, "pool": pool, "val": val})
	return err
}

func (n *ScopedConfig) SaveMerge(pool string, val interface{}) error {
	newValue := reflect.ValueOf(val)
	if newValue.Type().Kind() != reflect.Struct {
		return errors.New("received object must be a struct")
	}
	coll, err := n.collection()
	if err != nil {
		return err
	}
	defer coll.Close()
	var poolValues scopedConfigEntry
	previousValue := reflect.New(reflect.ValueOf(val).Type())
	err = coll.Find(bson.M{"name": n.name, "pool": pool}).One(&poolValues)
	if err == nil {
		err = n.unmarshal(&poolValues.Val, previousValue.Interface())
		if err != nil {<commit_msg>scopedconfig: only return names of existing entries in collection<commit_after>// Copyright 2016 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package scopedconfig

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/db/storage"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

const (
	defaultConfigName = ""
)

type ScopedConfig struct {
	coll          string
	name          string
	AllowedPools  []string
	AllowEmpty    bool
	ShallowMerge  bool
	Jsonfy        bool
	SliceAdd      bool
	AllowMapEmpty bool
}

type scopedConfigEntry struct {
	Name string
	Pool string
	Val  bson.Raw
}

func FindScopedConfig(coll string) *ScopedConfig {
	return FindScopedConfigFor(coll, defaultConfigName)
}

func FindScopedConfigFor(coll, name string) *ScopedConfig {
	return &ScopedConfig{coll: fmt.Sprintf("scoped_%s", coll), name: name}
}

func FindAllScopedConfigNames(collName string) ([]string, error) {
	base := FindScopedConfig(collName)
	coll, err := base.collection()
	if err != nil {
		return nil, err
	}
	defer coll.Close()
	var names []string
	err = coll.Find(nil).Distinct("name", &names)
	return names, err
}

func (n *ScopedConfig) GetName() string {
	return n.name
}

func (n *ScopedConfig) SetFieldAtomic(pool, name string, value interface{}) (bool, error) {
	coll, err := n.collection()
	if err != nil {
		return false, err
	}
	defer coll.Close()
	_, err = coll.Upsert(bson.M{
		"name": n.name,
		"pool": pool,
		"$or":  []bson.M{{"val." + name: ""}, {"val." + name: bson.M{"$exists": false}}},
	}, bson.M{"$set": bson.M{"val." + name: value}})
	if err == nil {
		return true, nil
	}
	if mgo.IsDup(err) {
		return false, nil
	}
	return false, err
}

func (n *ScopedConfig) SetField(pool, name string, value interface{}) error {
	coll, err := n.collection()
	if err != nil {
		return err
	}
	defer coll.Close()
	_, err = coll.Upsert(bson.M{"name": n.name, "pool": pool}, bson.M{"$set": bson.M{"val." + name: value}})
	return err
}

func (n *ScopedConfig) SaveBase(val interface{}) error {
	return n.Save("", val)
}

func (n *ScopedConfig) Save(pool string, val interface{}) error {
	if reflect.TypeOf(val).Kind() == reflect.Ptr {
		val = reflect.ValueOf(val).Elem().Interface()
	}
	if reflect.TypeOf(val).Kind() != reflect.Struct {
		return errors.New("a struct type or pointer to a struct is required as value")
	}
	coll, err := n.collection()
	if err != nil {
		return err
	}
	defer coll.Close()
	if n.Jsonfy {
		var result map[string]interface{}
		var data []byte
		data, err = json.Marshal(val)
		if err != nil {
			return err
		}
		err = json.Unmarshal(data, &result)
		if err != nil {
			return err
		}
		val = interface{}(result)
	}
	_, err = coll.Upsert(bson.M{"name": n.name, "pool": pool}, bson.M{"name": n.name, "pool": pool, "val": val})
	return err
}

func (n *ScopedConfig) SaveMerge(pool string, val interface{}) error {
	if reflect.TypeOf(val).Kind() == reflect.Ptr {
		val = reflect.ValueOf(val).Elem().Interface()
	}
	if reflect.TypeOf(val).Kind() != reflect.Struct {
		return errors.New("a struct type or pointer to a struct is required as value")
	}
	coll, err := n.collection()
	if err != nil {
		return err
	}
	defer coll.Close()
	var poolValues scopedConfigEntry
	previousValue := reflect.New(reflect.ValueOf(val).Type())
	err = coll.Find(bson.M{"name": n.name, "pool": pool}).One(&poolValues)
	if err == nil {
		err = n.unmarshal(&poolValues.Val, previousValue.Interface())
		if err != nil {
{'fraction_non_alphanumeric': 0.15401723658604394, 'fraction_numerical': 0.0016680567139282735, 'mean_word_length': 3.858203916272789, 'pattern_counts': {'":': 30, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2824842', 'n_tokens_mistral': 2672, 'n_tokens_neox': 2602, 'n_words': 714}