Просмотр исходного кода

dashboard: generate and include uid in dashboard model. #7883

Marcus Efraimsson 8 лет назад
Родитель
Сommit
5b35c694dc

+ 7 - 1
Gopkg.lock

@@ -412,6 +412,12 @@
   revision = "9e8dc3f972df6c8fcc0375ef492c24d0bb204857"
   version = "1.6.3"
 
+[[projects]]
+  branch = "master"
+  name = "github.com/teris-io/shortid"
+  packages = ["."]
+  revision = "771a37caa5cf0c81f585d7b6df4dfc77e0615b5c"
+
 [[projects]]
   name = "github.com/uber/jaeger-client-go"
   packages = [
@@ -625,6 +631,6 @@
 [solve-meta]
   analyzer-name = "dep"
   analyzer-version = 1
-  inputs-digest = "98e8d8f5fb21fe448aeb3db41c9fed85fe3bf80400e553211cf39a9c05720e01"
+  inputs-digest = "4de68f1342ba98a637ec8ca7496aeeae2021bf9e4c7c80db7924e14709151a62"
   solver-name = "gps-cdcl"
   solver-version = 1

+ 4 - 0
Gopkg.toml

@@ -193,3 +193,7 @@ ignored = [
   non-go = true
   go-tests = true
   unused-packages = true
+
+[[constraint]]
+  branch = "master"
+  name = "github.com/teris-io/shortid"

+ 15 - 0
pkg/models/dashboards.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/gosimple/slug"
 	"github.com/grafana/grafana/pkg/components/simplejson"
+	"github.com/teris-io/shortid"
 )
 
 // Typed errors
@@ -39,6 +40,7 @@ var (
 // Dashboard model
 type Dashboard struct {
 	Id       int64
+	Uid      string
 	Slug     string
 	OrgId    int64
 	GnetId   int64
@@ -61,6 +63,7 @@ type Dashboard struct {
 // NewDashboard creates a new dashboard
 func NewDashboard(title string) *Dashboard {
 	dash := &Dashboard{}
+	dash.Uid = DashboardUid()
 	dash.Data = simplejson.New()
 	dash.Data.Set("title", title)
 	dash.Title = title
@@ -107,9 +110,21 @@ func NewDashboardFromJson(data *simplejson.Json) *Dashboard {
 		dash.GnetId = int64(gnetId)
 	}
 
+	if uid, err := dash.Data.Get("uid").String(); err == nil {
+		dash.Uid = uid
+	} else {
+		dash.Uid = DashboardUid()
+	}
+
 	return dash
 }
 
+func DashboardUid() string {
+	gen, _ := shortid.New(1, shortid.DefaultABC, 1)
+	uid, _ := gen.Generate()
+	return uid
+}
+
 // GetDashboardModel turns the command into the savable model
 func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
 	dash := NewDashboardFromJson(cmd.Dashboard)

+ 1 - 0
pkg/services/sqlstore/dashboard.go

@@ -175,6 +175,7 @@ func GetDashboard(query *m.GetDashboardQuery) error {
 	}
 
 	dashboard.Data.Set("id", dashboard.Id)
+	dashboard.Data.Set("uid", dashboard.Uid)
 	query.Result = &dashboard
 	return nil
 }

+ 18 - 0
vendor/github.com/teris-io/shortid/LICENSE

@@ -0,0 +1,18 @@
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies
+or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 362 - 0
vendor/github.com/teris-io/shortid/shortid.go

@@ -0,0 +1,362 @@
+// Copyright (c) 2016-2017. Oleg Sklyar & teris.io. All rights reserved.
+// See the LICENSE file in the project root for licensing information.
+
+// Original algorithm:
+// Copyright (c) 2015 Dylan Greene, contributors: https://github.com/dylang/shortid.
+// MIT-license as found in the LICENSE file.
+
+// Seed computation: based on The Central Randomizer 1.3
+// Copyright (c) 1997 Paul Houle (houle@msc.cornell.edu)
+
+// Package shortid enables the generation of short, unique, non-sequential and by default URL friendly
+// Ids. The package is heavily inspired by the node.js https://github.com/dylang/shortid library.
+//
+// Id Length
+//
+// The standard Id length is 9 symbols when generated at a rate of 1 Id per millisecond,
+// occasionally it reaches 11 (at the rate of a few thousand Ids per millisecond) and very-very
+// rarely it can go beyond that during continuous generation at full throttle on high-performant
+// hardware. A test generating 500k Ids at full throttle on conventional hardware generated the
+// following Ids at the head and the tail (length > 9 is expected for this test):
+//
+//  -NDveu-9Q
+//  iNove6iQ9J
+//  NVDve6-9Q
+//  VVDvc6i99J
+//  NVovc6-QQy
+//  VVoveui9QC
+//  ...
+//  tFmGc6iQQs
+//  KpTvcui99k
+//  KFTGcuiQ9p
+//  KFmGeu-Q9O
+//  tFTvcu-QQt
+//  tpTveu-99u
+//
+// Life span
+//
+// The package guarantees the generation of unique Ids with zero collisions for 34 years
+// (1/1/2016-1/1/2050) using the same worker Id within a single (although concurrent) application if
+// application restarts take longer than 1 millisecond. The package supports up to 32 works, all
+// providing unique sequences.
+//
+// Implementation details
+//
+// Although heavily inspired by the node.js shortid library this is
+// not a simple Go port. In addition it
+//
+//  - is safe to concurrency;
+//  - does not require any yearly version/epoch resets;
+//  - provides stable Id size over a long period at the rate of 1ms;
+//  - guarantees no collisions (due to guaranteed fixed size of Ids between milliseconds and because
+//    multiple requests within the same ms lead to longer Ids with the prefix unique to the ms);
+//  - supports 32 over 16 workers.
+//
+// The algorithm uses less randomness than the original node.js implementation, which permits to
+// extend the life span as well as reduce and guarantee the length. In general terms, each Id
+// has the following 3 pieces of information encoded: the millisecond (first 8 symbols), the worker
+// Id (9th symbol), running concurrent counter within the same millisecond, only if required, over
+// all remaining symbols. The element of randomness per symbol is 1/2 for the worker and the
+// millisecond and 0 for the counter. Here 0 means no randomness, i.e. every value is encoded using
+// a 64-base alphabet; 1/2 means one of two matching symbols of the supplied alphabet, 1/4 one of
+// four matching symbols. The original algorithm of the node.js module uses 1/4 throughout.
+//
+// All methods accepting the parameters that govern the randomness are exported and can be used
+// to directly implement an algorithm with e.g. more randomness, but with longer Ids and shorter
+// life spans.
+package shortid
+
+import (
+	randc "crypto/rand"
+	"errors"
+	"fmt"
+	"math"
+	randm "math/rand"
+	"sync"
+	"sync/atomic"
+	"time"
+	"unsafe"
+)
+
+// Version defined the library version.
+const Version = 1.1
+
+// DefaultABC is the default URL-friendly alphabet.
+const DefaultABC = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-"
+
+// Abc represents a shuffled alphabet used to generate the Ids and provides methods to
+// encode data.
+type Abc struct {
+	alphabet []rune
+}
+
+// Shortid type represents a short Id generator working with a given alphabet.
+type Shortid struct {
+	abc    Abc
+	worker uint
+	epoch  time.Time  // ids can be generated for 34 years since this date
+	ms     uint       // ms since epoch for the last id
+	count  uint       // request count within the same ms
+	mx     sync.Mutex // locks access to ms and count
+}
+
+var shortid *Shortid
+
+func init() {
+	shortid = MustNew(0, DefaultABC, 1)
+}
+
+// GetDefault retrieves the default short Id generator initialised with the default alphabet,
+// worker=0 and seed=1. The default can be overwritten using SetDefault.
+func GetDefault() *Shortid {
+	return (*Shortid)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&shortid))))
+}
+
+// SetDefault overwrites the default generator.
+func SetDefault(sid *Shortid) {
+	target := (*unsafe.Pointer)(unsafe.Pointer(&shortid))
+	source := unsafe.Pointer(sid)
+	atomic.SwapPointer(target, source)
+}
+
+// Generate generates an Id using the default generator.
+func Generate() (string, error) {
+	return shortid.Generate()
+}
+
+// MustGenerate acts just like Generate, but panics instead of returning errors.
+func MustGenerate() string {
+	id, err := Generate()
+	if err == nil {
+		return id
+	}
+	panic(err)
+}
+
+// New constructs an instance of the short Id generator for the given worker number [0,31], alphabet
+// (64 unique symbols) and seed value (to shuffle the alphabet). The worker number should be
+// different for multiple or distributed processes generating Ids into the same data space. The
+// seed, on contrary, should be identical.
+func New(worker uint8, alphabet string, seed uint64) (*Shortid, error) {
+	if worker > 31 {
+		return nil, errors.New("expected worker in the range [0,31]")
+	}
+	abc, err := NewAbc(alphabet, seed)
+	if err == nil {
+		sid := &Shortid{
+			abc:    abc,
+			worker: uint(worker),
+			epoch:  time.Date(2016, time.January, 1, 0, 0, 0, 0, time.UTC),
+			ms:     0,
+			count:  0,
+		}
+		return sid, nil
+	}
+	return nil, err
+}
+
+// MustNew acts just like New, but panics instead of returning errors.
+func MustNew(worker uint8, alphabet string, seed uint64) *Shortid {
+	sid, err := New(worker, alphabet, seed)
+	if err == nil {
+		return sid
+	}
+	panic(err)
+}
+
+// Generate generates a new short Id.
+func (sid *Shortid) Generate() (string, error) {
+	return sid.GenerateInternal(nil, sid.epoch)
+}
+
+// MustGenerate acts just like Generate, but panics instead of returning errors.
+func (sid *Shortid) MustGenerate() string {
+	id, err := sid.Generate()
+	if err == nil {
+		return id
+	}
+	panic(err)
+}
+
+// GenerateInternal should only be used for testing purposes.
+func (sid *Shortid) GenerateInternal(tm *time.Time, epoch time.Time) (string, error) {
+	ms, count := sid.getMsAndCounter(tm, epoch)
+	idrunes := make([]rune, 9)
+	if tmp, err := sid.abc.Encode(ms, 8, 5); err == nil {
+		copy(idrunes, tmp) // first 8 symbols
+	} else {
+		return "", err
+	}
+	if tmp, err := sid.abc.Encode(sid.worker, 1, 5); err == nil {
+		idrunes[8] = tmp[0]
+	} else {
+		return "", err
+	}
+	if count > 0 {
+		if countrunes, err := sid.abc.Encode(count, 0, 6); err == nil {
+			// only extend if really need it
+			idrunes = append(idrunes, countrunes...)
+		} else {
+			return "", err
+		}
+	}
+	return string(idrunes), nil
+}
+
+func (sid *Shortid) getMsAndCounter(tm *time.Time, epoch time.Time) (uint, uint) {
+	sid.mx.Lock()
+	defer sid.mx.Unlock()
+	var ms uint
+	if tm != nil {
+		ms = uint(tm.Sub(epoch).Nanoseconds() / 1000000)
+	} else {
+		ms = uint(time.Now().Sub(epoch).Nanoseconds() / 1000000)
+	}
+	if ms == sid.ms {
+		sid.count++
+	} else {
+		sid.count = 0
+		sid.ms = ms
+	}
+	return sid.ms, sid.count
+}
+
+// String returns a string representation of the short Id generator.
+func (sid *Shortid) String() string {
+	return fmt.Sprintf("Shortid(worker=%v, epoch=%v, abc=%v)", sid.worker, sid.epoch, sid.abc)
+}
+
+// Abc returns the instance of alphabet used for representing the Ids.
+func (sid *Shortid) Abc() Abc {
+	return sid.abc
+}
+
+// Epoch returns the value of epoch used as the beginning of millisecond counting (normally
+// 2016-01-01 00:00:00 local time)
+func (sid *Shortid) Epoch() time.Time {
+	return sid.epoch
+}
+
+// Worker returns the value of worker for this short Id generator.
+func (sid *Shortid) Worker() uint {
+	return sid.worker
+}
+
+// NewAbc constructs a new instance of shuffled alphabet to be used for Id representation.
+func NewAbc(alphabet string, seed uint64) (Abc, error) {
+	runes := []rune(alphabet)
+	if len(runes) != len(DefaultABC) {
+		return Abc{}, fmt.Errorf("alphabet must contain %v unique characters", len(DefaultABC))
+	}
+	if nonUnique(runes) {
+		return Abc{}, errors.New("alphabet must contain unique characters only")
+	}
+	abc := Abc{alphabet: nil}
+	abc.shuffle(alphabet, seed)
+	return abc, nil
+}
+
+// MustNewAbc acts just like NewAbc, but panics instead of returning errors.
+func MustNewAbc(alphabet string, seed uint64) Abc {
+	res, err := NewAbc(alphabet, seed)
+	if err == nil {
+		return res
+	}
+	panic(err)
+}
+
+func nonUnique(runes []rune) bool {
+	found := make(map[rune]struct{})
+	for _, r := range runes {
+		if _, seen := found[r]; !seen {
+			found[r] = struct{}{}
+		}
+	}
+	return len(found) < len(runes)
+}
+
+func (abc *Abc) shuffle(alphabet string, seed uint64) {
+	source := []rune(alphabet)
+	for len(source) > 1 {
+		seed = (seed*9301 + 49297) % 233280
+		i := int(seed * uint64(len(source)) / 233280)
+
+		abc.alphabet = append(abc.alphabet, source[i])
+		source = append(source[:i], source[i+1:]...)
+	}
+	abc.alphabet = append(abc.alphabet, source[0])
+}
+
+// Encode encodes a given value into a slice of runes of length nsymbols. In case nsymbols==0, the
+// length of the result is automatically computed from data. Even if fewer symbols is required to
+// encode the data than nsymbols, all positions are used encoding 0 where required to guarantee
+// uniqueness in case further data is added to the sequence. The value of digits [4,6] represents
+// represents n in 2^n, which defines how much randomness flows into the algorithm: 4 -- every value
+// can be represented by 4 symbols in the alphabet (permitting at most 16 values), 5 -- every value
+// can be represented by 2 symbols in the alphabet (permitting at most 32 values), 6 -- every value
+// is represented by exactly 1 symbol with no randomness (permitting 64 values).
+func (abc *Abc) Encode(val, nsymbols, digits uint) ([]rune, error) {
+	if digits < 4 || 6 < digits {
+		return nil, fmt.Errorf("allowed digits range [4,6], found %v", digits)
+	}
+
+	var computedSize uint = 1
+	if val >= 1 {
+		computedSize = uint(math.Log2(float64(val)))/digits + 1
+	}
+	if nsymbols == 0 {
+		nsymbols = computedSize
+	} else if nsymbols < computedSize {
+		return nil, fmt.Errorf("cannot accommodate data, need %v digits, got %v", computedSize, nsymbols)
+	}
+
+	mask := 1<<digits - 1
+
+	random := make([]int, int(nsymbols))
+	// no random component if digits == 6
+	if digits < 6 {
+		copy(random, maskedRandomInts(len(random), 0x3f-mask))
+	}
+
+	res := make([]rune, int(nsymbols))
+	for i := range res {
+		shift := digits * uint(i)
+		index := (int(val>>shift) & mask) | random[i]
+		res[i] = abc.alphabet[index]
+	}
+	return res, nil
+}
+
+// MustEncode acts just like Encode, but panics instead of returning errors.
+func (abc *Abc) MustEncode(val, size, digits uint) []rune {
+	res, err := abc.Encode(val, size, digits)
+	if err == nil {
+		return res
+	}
+	panic(err)
+}
+
+func maskedRandomInts(size, mask int) []int {
+	ints := make([]int, size)
+	bytes := make([]byte, size)
+	if _, err := randc.Read(bytes); err == nil {
+		for i, b := range bytes {
+			ints[i] = int(b) & mask
+		}
+	} else {
+		for i := range ints {
+			ints[i] = randm.Intn(0xff) & mask
+		}
+	}
+	return ints
+}
+
+// String returns a string representation of the Abc instance.
+func (abc Abc) String() string {
+	return fmt.Sprintf("Abc{alphabet='%v')", abc.Alphabet())
+}
+
+// Alphabet returns the alphabet used as an immutable string.
+func (abc Abc) Alphabet() string {
+	return string(abc.alphabet)
+}