Merge branch 'master' into flux-staging

pull/10616/head
Nathaniel Cook 2018-12-12 11:41:35 -07:00
commit 896837b9e5
98 changed files with 3130 additions and 1059 deletions

View File

@ -23,9 +23,9 @@ func (c *Client) setPassword(ctx context.Context, tx *bolt.Tx, name string, pass
return err
}
u, err := c.findUserByName(ctx, tx, name)
if err != nil {
return err
u, pe := c.findUserByName(ctx, tx, name)
if pe != nil {
return pe
}
encodedID, err := u.ID.Encode()

View File

@ -124,9 +124,9 @@ func (c *Client) CreateSession(ctx context.Context, user string) (*platform.Sess
}
func (c *Client) createSession(ctx context.Context, tx *bolt.Tx, user string) (*platform.Session, error) {
u, err := c.findUserByName(ctx, tx, user)
if err != nil {
return nil, err
u, pe := c.findUserByName(ctx, tx, user)
if pe != nil {
return nil, pe
}
s := &platform.Session{}

View File

@ -6,7 +6,7 @@ import (
"fmt"
"time"
"github.com/coreos/bbolt"
bolt "github.com/coreos/bbolt"
"github.com/influxdata/platform"
platformcontext "github.com/influxdata/platform/context"
)
@ -39,25 +39,30 @@ func (c *Client) FindUserByID(ctx context.Context, id platform.ID) (*platform.Us
var u *platform.User
err := c.db.View(func(tx *bolt.Tx) error {
usr, err := c.findUserByID(ctx, tx, id)
if err != nil {
return err
usr, pe := c.findUserByID(ctx, tx, id)
if pe != nil {
return pe
}
u = usr
return nil
})
if err != nil {
return nil, err
return nil, &platform.Error{
Op: getOp(platform.OpFindUserByID),
Err: err,
}
}
return u, nil
}
func (c *Client) findUserByID(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.User, error) {
func (c *Client) findUserByID(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.User, *platform.Error) {
encodedID, err := id.Encode()
if err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
}
}
var u platform.User
@ -71,7 +76,9 @@ func (c *Client) findUserByID(ctx context.Context, tx *bolt.Tx, id platform.ID)
}
if err := json.Unmarshal(v, &u); err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
}
}
return &u, nil
@ -82,9 +89,9 @@ func (c *Client) FindUserByName(ctx context.Context, n string) (*platform.User,
var u *platform.User
err := c.db.View(func(tx *bolt.Tx) error {
usr, err := c.findUserByName(ctx, tx, n)
if err != nil {
return err
usr, pe := c.findUserByName(ctx, tx, n)
if pe != nil {
return pe
}
u = usr
return nil
@ -93,10 +100,9 @@ func (c *Client) FindUserByName(ctx context.Context, n string) (*platform.User,
return u, err
}
func (c *Client) findUserByName(ctx context.Context, tx *bolt.Tx, n string) (*platform.User, error) {
func (c *Client) findUserByName(ctx context.Context, tx *bolt.Tx, n string) (*platform.User, *platform.Error) {
u := tx.Bucket(userIndex).Get(userIndexKey(n))
if u == nil {
// TODO: Make standard error
return nil, &platform.Error{
Code: platform.ENotFound,
Msg: "user not found",
@ -105,7 +111,9 @@ func (c *Client) findUserByName(ctx context.Context, tx *bolt.Tx, n string) (*pl
var id platform.ID
if err := id.Decode(u); err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
}
}
return c.findUserByID(ctx, tx, id)
}
@ -114,18 +122,34 @@ func (c *Client) findUserByName(ctx context.Context, tx *bolt.Tx, n string) (*pl
// Filters using ID, or Name should be efficient.
// Other filters will do a linear scan across users until it finds a match.
func (c *Client) FindUser(ctx context.Context, filter platform.UserFilter) (*platform.User, error) {
var u *platform.User
var err error
op := getOp(platform.OpFindUser)
if filter.ID != nil {
return c.FindUserByID(ctx, *filter.ID)
u, err = c.FindUserByID(ctx, *filter.ID)
if err != nil {
return nil, &platform.Error{
Op: op,
Err: err,
}
}
return u, nil
}
if filter.Name != nil {
return c.FindUserByName(ctx, *filter.Name)
u, err = c.FindUserByName(ctx, *filter.Name)
if err != nil {
return nil, &platform.Error{
Op: op,
Err: err,
}
}
return u, nil
}
filterFn := filterUsersFn(filter)
var u *platform.User
err := c.db.View(func(tx *bolt.Tx) error {
err = c.db.View(func(tx *bolt.Tx) error {
return forEachUser(ctx, tx, func(usr *platform.User) bool {
if filterFn(usr) {
u = usr
@ -136,11 +160,17 @@ func (c *Client) FindUser(ctx context.Context, filter platform.UserFilter) (*pla
})
if err != nil {
return nil, err
return nil, &platform.Error{
Op: op,
Err: err,
}
}
if u == nil {
return nil, fmt.Errorf("user not found")
return nil, &platform.Error{
Code: platform.ENotFound,
Msg: "user not found",
}
}
return u, nil
@ -166,10 +196,14 @@ func filterUsersFn(filter platform.UserFilter) func(u *platform.User) bool {
// Filters using ID, or Name should be efficient.
// Other filters will do a linear scan across all users searching for a match.
func (c *Client) FindUsers(ctx context.Context, filter platform.UserFilter, opt ...platform.FindOptions) ([]*platform.User, int, error) {
op := getOp(platform.OpFindUsers)
if filter.ID != nil {
u, err := c.FindUserByID(ctx, *filter.ID)
if err != nil {
return nil, 0, err
return nil, 0, &platform.Error{
Err: err,
Op: op,
}
}
return []*platform.User{u}, 1, nil
@ -178,7 +212,10 @@ func (c *Client) FindUsers(ctx context.Context, filter platform.UserFilter, opt
if filter.Name != nil {
u, err := c.FindUserByName(ctx, *filter.Name)
if err != nil {
return nil, 0, err
return nil, 0, &platform.Error{
Err: err,
Op: op,
}
}
return []*platform.User{u}, 1, nil
@ -204,12 +241,14 @@ func (c *Client) FindUsers(ctx context.Context, filter platform.UserFilter, opt
// CreateUser creates a platform user and sets b.ID.
func (c *Client) CreateUser(ctx context.Context, u *platform.User) error {
return c.db.Update(func(tx *bolt.Tx) error {
err := c.db.Update(func(tx *bolt.Tx) error {
unique := c.uniqueUserName(ctx, tx, u)
if !unique {
// TODO: make standard error
return fmt.Errorf("user with name %s already exists", u.Name)
return &platform.Error{
Code: platform.EConflict,
Msg: fmt.Sprintf("user with name %s already exists", u.Name),
}
}
u.ID = c.IDGenerator.ID()
@ -220,6 +259,13 @@ func (c *Client) CreateUser(ctx context.Context, u *platform.User) error {
return c.putUser(ctx, tx, u)
})
if err != nil {
return &platform.Error{
Err: err,
Op: getOp(platform.OpCreateUser),
}
}
return nil
}
// PutUser will put a user without setting an ID.
@ -273,9 +319,12 @@ func (c *Client) uniqueUserName(ctx context.Context, tx *bolt.Tx, u *platform.Us
func (c *Client) UpdateUser(ctx context.Context, id platform.ID, upd platform.UserUpdate) (*platform.User, error) {
var u *platform.User
err := c.db.Update(func(tx *bolt.Tx) error {
usr, err := c.updateUser(ctx, tx, id, upd)
if err != nil {
return err
usr, pe := c.updateUser(ctx, tx, id, upd)
if pe != nil {
return &platform.Error{
Err: pe,
Op: getOp(platform.OpUpdateUser),
}
}
u = usr
return nil
@ -284,7 +333,7 @@ func (c *Client) UpdateUser(ctx context.Context, id platform.ID, upd platform.Us
return u, err
}
func (c *Client) updateUser(ctx context.Context, tx *bolt.Tx, id platform.ID, upd platform.UserUpdate) (*platform.User, error) {
func (c *Client) updateUser(ctx context.Context, tx *bolt.Tx, id platform.ID, upd platform.UserUpdate) (*platform.User, *platform.Error) {
u, err := c.findUserByID(ctx, tx, id)
if err != nil {
return nil, err
@ -294,17 +343,23 @@ func (c *Client) updateUser(ctx context.Context, tx *bolt.Tx, id platform.ID, up
// Users are indexed by name and so the user index must be pruned
// when name is modified.
if err := tx.Bucket(userIndex).Delete(userIndexKey(u.Name)); err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
}
}
u.Name = *upd.Name
}
if err := c.appendUserEventToLog(ctx, tx, u.ID, userUpdatedEvent); err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
}
}
if err := c.putUser(ctx, tx, u); err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
}
}
return u, nil
@ -312,41 +367,64 @@ func (c *Client) updateUser(ctx context.Context, tx *bolt.Tx, id platform.ID, up
// DeleteUser deletes a user and prunes it from the index.
func (c *Client) DeleteUser(ctx context.Context, id platform.ID) error {
return c.db.Update(func(tx *bolt.Tx) error {
if err := c.deleteUsersAuthorizations(ctx, tx, id); err != nil {
return err
err := c.db.Update(func(tx *bolt.Tx) error {
if pe := c.deleteUsersAuthorizations(ctx, tx, id); pe != nil {
return pe
}
return c.deleteUser(ctx, tx, id)
if pe := c.deleteUser(ctx, tx, id); pe != nil {
return pe
}
return nil
})
if err != nil {
return &platform.Error{
Op: getOp(platform.OpDeleteUser),
Err: err,
}
}
return nil
}
func (c *Client) deleteUser(ctx context.Context, tx *bolt.Tx, id platform.ID) error {
u, err := c.findUserByID(ctx, tx, id)
if err != nil {
return err
func (c *Client) deleteUser(ctx context.Context, tx *bolt.Tx, id platform.ID) *platform.Error {
u, pe := c.findUserByID(ctx, tx, id)
if pe != nil {
return pe
}
encodedID, err := id.Encode()
if err != nil {
return err
return &platform.Error{
Err: err,
}
}
if err := tx.Bucket(userIndex).Delete(userIndexKey(u.Name)); err != nil {
return err
return &platform.Error{
Err: err,
}
}
if err := tx.Bucket(userUser).Delete(encodedID); err != nil {
return err
return &platform.Error{
Err: err,
}
}
return c.deleteUserResourceMappings(ctx, tx, platform.UserResourceMappingFilter{
if err := c.deleteUserResourceMappings(ctx, tx, platform.UserResourceMappingFilter{
UserID: id,
})
}); err != nil {
return &platform.Error{
Err: err,
}
}
return nil
}
func (c *Client) deleteUsersAuthorizations(ctx context.Context, tx *bolt.Tx, id platform.ID) error {
func (c *Client) deleteUsersAuthorizations(ctx context.Context, tx *bolt.Tx, id platform.ID) *platform.Error {
authFilter := platform.AuthorizationFilter{
UserID: &id,
}
as, err := c.findAuthorizations(ctx, tx, authFilter)
if err != nil {
return err
return &platform.Error{
Err: err,
}
}
for _, a := range as {
if err := c.deleteAuthorization(ctx, tx, a.ID); err != nil {
@ -356,7 +434,7 @@ func (c *Client) deleteUsersAuthorizations(ctx context.Context, tx *bolt.Tx, id
return nil
}
// GeUserOperationLog retrieves a user operation log.
// GetUserOperationLog retrieves a user operation log.
func (c *Client) GetUserOperationLog(ctx context.Context, id platform.ID, opts platform.FindOptions) ([]*platform.OperationLogEntry, int, error) {
// TODO(desa): might be worthwhile to allocate a slice of size opts.Limit
log := []*platform.OperationLogEntry{}

View File

@ -5,10 +5,11 @@ import (
"testing"
"github.com/influxdata/platform"
bolt "github.com/influxdata/platform/bolt"
platformtesting "github.com/influxdata/platform/testing"
)
func initUserService(f platformtesting.UserFields, t *testing.T) (platform.UserService, func()) {
func initUserService(f platformtesting.UserFields, t *testing.T) (platform.UserService, string, func()) {
c, closeFn, err := NewTestClient()
if err != nil {
t.Fatalf("failed to create new bolt client: %v", err)
@ -20,7 +21,7 @@ func initUserService(f platformtesting.UserFields, t *testing.T) (platform.UserS
t.Fatalf("failed to populate users")
}
}
return c, func() {
return c, bolt.OpPrefix, func() {
defer closeFn()
for _, u := range f.Users {
if err := c.DeleteUser(ctx, u.ID); err != nil {
@ -30,26 +31,6 @@ func initUserService(f platformtesting.UserFields, t *testing.T) (platform.UserS
}
}
func TestUserService_CreateUser(t *testing.T) {
platformtesting.CreateUser(initUserService, t)
}
func TestUserService_FindUserByID(t *testing.T) {
platformtesting.FindUserByID(initUserService, t)
}
func TestUserService_FindUsers(t *testing.T) {
platformtesting.FindUsers(initUserService, t)
}
func TestUserService_DeleteUser(t *testing.T) {
platformtesting.DeleteUser(initUserService, t)
}
func TestUserService_FindUser(t *testing.T) {
platformtesting.FindUser(initUserService, t)
}
func TestUserService_UpdateUser(t *testing.T) {
platformtesting.UpdateUser(initUserService, t)
func TestUserService(t *testing.T) {
platformtesting.UserService(initUserService, t)
}

View File

@ -55,6 +55,8 @@ func main() {
if err := m.Run(ctx, os.Args[1:]...); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
} else if !m.running {
os.Exit(1)
}
<-ctx.Done()
@ -67,8 +69,9 @@ func main() {
// Main represents the main program execution.
type Main struct {
wg sync.WaitGroup
cancel func()
wg sync.WaitGroup
cancel func()
running bool
logLevel string
httpBindAddress string
@ -197,6 +200,7 @@ func (m *Main) Run(ctx context.Context, args ...string) error {
}
func (m *Main) run(ctx context.Context) (err error) {
m.running = true
ctx, m.cancel = context.WithCancel(ctx)
var lvl zapcore.Level

View File

@ -90,7 +90,7 @@ func NewAPIHandler(b *APIBackend) *APIHandler {
h.DashboardHandler.DashboardService = b.DashboardService
h.DashboardHandler.DashboardOperationLogService = b.DashboardOperationLogService
h.ViewHandler = NewViewHandler(b.UserResourceMappingService)
h.ViewHandler = NewViewHandler(b.UserResourceMappingService, b.LabelService)
h.ViewHandler.ViewService = b.ViewService
h.MacroHandler = NewMacroHandler()

View File

@ -516,6 +516,7 @@ func (s *BucketService) FindBucket(ctx context.Context, filter platform.BucketFi
return nil, &platform.Error{
Code: platform.ENotFound,
Op: s.OpPrefix + platform.OpFindBucket,
Msg: "bucket not found",
Err: ErrNotFound,
}
}

View File

@ -630,6 +630,7 @@ paths:
$ref: "#/components/schemas/Error"
/sources:
post:
x-generated: true
tags:
- Sources
summary: Creates a Source
@ -661,6 +662,7 @@ paths:
schema:
$ref: "#/components/schemas/Error"
get:
x-generated: true
tags:
- Sources
summary: Get all sources
@ -685,7 +687,35 @@ paths:
schema:
$ref: "#/components/schemas/Error"
/sources/{sourceID}:
delete:
x-generated: true
tags:
- Sources
summary: Delete a source
parameters:
- in: path
name: sourceID
schema:
type: string
required: true
description: ID of the source
responses:
'204':
description: delete has been accepted
'404':
description: view not found
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
patch:
x-generated: true
tags:
- Sources
summary: Updates a Source
@ -723,6 +753,7 @@ paths:
schema:
$ref: "#/components/schemas/Error"
get:
x-generated: true
tags:
- Sources
summary: Get a source
@ -754,6 +785,7 @@ paths:
$ref: "#/components/schemas/Error"
/sources/{sourceID}/health:
get:
x-generated: true
tags:
- Sources
summary: Get a sources health
@ -823,6 +855,7 @@ paths:
$ref: "#/components/schemas/Error"
/views:
post:
x-generated: true
tags:
- Views
summary: A view contains information about the visual representation of data
@ -854,6 +887,7 @@ paths:
schema:
$ref: "#/components/schemas/Error"
get:
x-generated: true
tags:
- Views
summary: Get all views
@ -878,37 +912,39 @@ paths:
schema:
$ref: "#/components/schemas/Error"
'/views/{viewID}':
get:
tags:
- Views
summary: Get a single View
parameters:
- in: path
name: viewID
schema:
type: string
required: true
description: ID of view to update
responses:
'200':
description: get a single view
content:
application/json:
schema:
$ref: "#/components/schemas/View"
'404':
description: view not found
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
patch:
get:
x-generated: true
tags:
- Views
summary: Get a single View
parameters:
- in: path
name: viewID
schema:
type: string
required: true
description: ID of view to update
responses:
'200':
description: get a single view
content:
application/json:
schema:
$ref: "#/components/schemas/View"
'404':
description: view not found
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
patch:
x-generated: true
tags:
- Views
summary: Update a single view
@ -945,7 +981,8 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
delete:
delete:
x-generated: true
tags:
- Views
summary: Delete a view
@ -973,6 +1010,7 @@ paths:
$ref: "#/components/schemas/Error"
/dashboards:
post:
x-generated: true
tags:
- Dashboards
summary: Create a dashboard
@ -1004,6 +1042,7 @@ paths:
schema:
$ref: "#/components/schemas/Error"
get:
x-generated: true
tags:
- Dashboards
summary: Get all dashboards
@ -1043,47 +1082,49 @@ paths:
schema:
$ref: "#/components/schemas/Error"
'/dashboards/{dashboardID}':
get:
tags:
- Dashboards
summary: Get a single Dashboard
parameters:
- in: path
name: dashboardID
schema:
type: string
required: true
description: ID of dashboard to update
responses:
'200':
description: get a single dashboard
content:
application/json:
schema:
$ref: "#/components/schemas/Dashboard"
'404':
description: dashboard not found
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
patch:
get:
x-generated: true
tags:
- Dashboards
summary: Get a single Dashboard
parameters:
- in: path
name: dashboardID
schema:
type: string
required: true
description: ID of dashboard to update
responses:
'200':
description: get a single dashboard
content:
application/json:
schema:
$ref: "#/components/schemas/Dashboard"
'404':
description: dashboard not found
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
patch:
x-generated: true
tags:
- Dashboards
summary: Update a single dashboard
requestBody:
description: patching of a dashboard
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/Dashboard"
description: patching of a dashboard
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/Dashboard"
parameters:
- in: path
name: dashboardID
@ -1110,7 +1151,8 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
delete:
delete:
x-generated: true
tags:
- Dashboards
summary: Delete a dashboard
@ -1137,18 +1179,19 @@ paths:
schema:
$ref: "#/components/schemas/Error"
'/dashboards/{dashboardID}/cells':
put:
put:
x-generated: true
tags:
- Cells
- Dashboards
summary: Replace a dashboards cells
requestBody:
description: batch replaces all of a dashboards cells (this is used primarily to update the positional information of all of the cells)
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/Cells"
description: batch replaces all of a dashboards cells (this is used primarily to update the positional information of all of the cells)
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/Cells"
parameters:
- in: path
name: dashboardID
@ -1175,7 +1218,8 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
post:
x-generated: true
tags:
- Cells
- Dashboards
@ -1215,6 +1259,7 @@ paths:
$ref: "#/components/schemas/Error"
'/dashboards/{dashboardID}/cells/{cellID}':
patch:
x-generated: true
tags:
- Cells
- Dashboards
@ -1259,6 +1304,7 @@ paths:
schema:
$ref: "#/components/schemas/Error"
delete:
x-generated: true
tags:
- Cells
- Dashboards
@ -2461,6 +2507,7 @@ paths:
$ref: "#/components/schemas/Error"
/tasks:
get:
x-generated: true
tags:
- Tasks
summary: List tasks.
@ -2502,6 +2549,7 @@ paths:
schema:
$ref: "#/components/schemas/Error"
post:
x-generated: true
tags:
- Tasks
summary: Create a new task
@ -2527,6 +2575,7 @@ paths:
$ref: "#/components/schemas/Error"
'/tasks/{taskID}':
get:
x-generated: true
tags:
- Tasks
summary: Retrieve an task
@ -2551,6 +2600,7 @@ paths:
schema:
$ref: "#/components/schemas/Error"
patch:
x-generated: true
tags:
- Tasks
summary: Update a task
@ -2583,6 +2633,7 @@ paths:
schema:
$ref: "#/components/schemas/Error"
delete:
x-generated: true
tags:
- Tasks
summary: Delete a task
@ -4625,12 +4676,16 @@ components:
properties:
self:
type: string
query:
type: string
health:
type: string
buckets:
type: string
id:
type: string
organizationID:
type: string
default:
type: boolean
name:
type: string
type:

View File

@ -96,7 +96,14 @@ func requestMacroID(ctx context.Context) (platform.ID, error) {
}
id, err := platform.IDFromString(urlID)
return *id, err
if err != nil {
return platform.InvalidID(), &platform.Error{
Code: platform.EInvalid,
Err: err,
}
}
return *id, nil
}
func (h *MacroHandler) handleGetMacro(w http.ResponseWriter, r *http.Request) {

View File

@ -159,6 +159,24 @@ func TestMacroService_handleGetMacro(t *testing.T) {
body: ``,
},
},
{
name: "request an invalid macro ID",
args: args{
id: "baz",
},
fields: fields{
&mock.MacroService{
FindMacroByIDF: func(ctx context.Context, id platform.ID) (*platform.Macro, error) {
return nil, nil
},
},
},
wants: wants{
statusCode: 400,
contentType: "application/json",
body: `{"code":"invalid","msg":"An internal error has occurred.","err":"id must have a length of 16 bytes"}`,
},
},
}
for _, tt := range tests {

View File

@ -1,12 +1,34 @@
package http
import (
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/influxdata/platform/toml"
)
// ReadyHandler is a default readiness handler. The default behavior is always ready.
var up = time.Now()
// ReadyHandler is a default readiness handler. The default behaviour is always ready.
func ReadyHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, `{"status": "ready"}`)
var status = struct {
Status string `json:"status"`
Start time.Time `json:"started"`
Up toml.Duration `json:"up"`
}{
Status: "ready",
Start: up,
Up: toml.Duration(time.Since(up)),
}
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
err := enc.Encode(status)
if err != nil {
fmt.Fprintf(w, "Error encoding status data: %v\n", err)
}
}

View File

@ -494,6 +494,12 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Macros"
'400':
description: invalid request
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
default:
description: internal server error
content:
@ -1091,6 +1097,284 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
'/views/{viewID}/labels':
get:
tags:
- Views
summary: list all labels for a view
parameters:
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
responses:
'200':
description: a list of all labels for a view
content:
application/json:
schema:
type: object
properties:
labels:
type: array
items:
type: string
links:
$ref: "#/components/schemas/Links"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
tags:
- Views
summary: add a label to a view
parameters:
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
requestBody:
description: label to add
required: true
content:
application/json:
schema:
type: object
properties:
label:
type: string
responses:
'200':
description: a list of all labels for a view
content:
application/json:
schema:
type: object
properties:
labels:
type: array
items:
type: string
links:
$ref: "#/components/schemas/Links"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'/views/{viewID}/labels/{label}':
delete:
tags:
- Views
summary: delete a label from a view
parameters:
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
- in: path
name: label
schema:
type: string
required: true
description: the label name
responses:
'204':
description: delete has been accepted
'404':
description: view not found
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'/views/{viewID}/members':
get:
tags:
- Users
- Views
summary: List all view members
parameters:
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
responses:
'200':
description: a list of users who have member privileges for a view
content:
application/json:
schema:
$ref: "#/components/schemas/Users"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
tags:
- Users
- Views
summary: Add view member
parameters:
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
requestBody:
description: user to add as member
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/User"
responses:
'201':
description: added to view members
content:
application/json:
schema:
$ref: "#/components/schemas/User"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'/views/{viewID}/members/{userID}':
delete:
tags:
- Users
- Views
summary: removes a member from an view
parameters:
- in: path
name: userID
schema:
type: string
required: true
description: ID of member to remove
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
responses:
'204':
description: member removed
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'/views/{viewID}/owners':
get:
tags:
- Users
- Views
summary: List all view owners
parameters:
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
responses:
'200':
description: a list of users who have owner privileges for a view
content:
application/json:
schema:
$ref: "#/components/schemas/Users"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
tags:
- Users
- Views
summary: Add view owner
parameters:
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
requestBody:
description: user to add as owner
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/User"
responses:
'201':
description: added to view owners
content:
application/json:
schema:
$ref: "#/components/schemas/User"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'/views/{viewID}/owners/{userID}':
delete:
tags:
- Users
- Views
summary: removes an owner from a view
parameters:
- in: path
name: userID
schema:
type: string
required: true
description: ID of owner to remove
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
responses:
'204':
description: owner removed
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/dashboards:
post:
tags:
@ -2512,6 +2796,32 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
delete:
tags:
- Organizations
summary: Delete an organization
parameters:
- in: path
name: orgID
schema:
type: string
required: true
description: ID of organization to delete
responses:
'204':
description: delete has been accepted
'404':
description: organization not found
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'/orgs/{orgID}/labels':
get:
tags:
@ -3557,178 +3867,6 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
'/views/{viewID}/members':
get:
tags:
- Users
- Views
summary: List all view members
parameters:
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
responses:
'200':
description: a list of users who have member privileges for a view
content:
application/json:
schema:
$ref: "#/components/schemas/Users"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
tags:
- Users
- Views
summary: Add view member
parameters:
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
requestBody:
description: user to add as member
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/User"
responses:
'201':
description: added to view members
content:
application/json:
schema:
$ref: "#/components/schemas/User"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'/views/{viewID}/members/{userID}':
delete:
tags:
- Users
- Views
summary: removes a member from an view
parameters:
- in: path
name: userID
schema:
type: string
required: true
description: ID of member to remove
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
responses:
'204':
description: member removed
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'/views/{viewID}/owners':
get:
tags:
- Users
- Views
summary: List all view owners
parameters:
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
responses:
'200':
description: a list of users who have owner privileges for a view
content:
application/json:
schema:
$ref: "#/components/schemas/Users"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
tags:
- Users
- Views
summary: Add view owner
parameters:
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
requestBody:
description: user to add as owner
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/User"
responses:
'201':
description: added to view owners
content:
application/json:
schema:
$ref: "#/components/schemas/User"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'/views/{viewID}/owners/{userID}':
delete:
tags:
- Users
- Views
summary: removes an owner from a view
parameters:
- in: path
name: userID
schema:
type: string
required: true
description: ID of owner to remove
- in: path
name: viewID
schema:
type: string
required: true
description: ID of the view
responses:
'204':
description: owner removed
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
LanguageRequest:

View File

@ -413,6 +413,8 @@ type UserService struct {
Addr string
Token string
InsecureSkipVerify bool
// OpPrefix is the ops of not found error.
OpPrefix string
}
// FindMe returns user information about the owner of the token
@ -436,7 +438,7 @@ func (s *UserService) FindMe(ctx context.Context, id platform.ID) (*platform.Use
defer resp.Body.Close()
if err := CheckError(resp); err != nil {
if err := CheckError(resp, true); err != nil {
return nil, err
}
@ -466,7 +468,7 @@ func (s *UserService) FindUserByID(ctx context.Context, id platform.ID) (*platfo
return nil, err
}
if err := CheckError(resp); err != nil {
if err := CheckError(resp, true); err != nil {
return nil, err
}
@ -483,11 +485,18 @@ func (s *UserService) FindUserByID(ctx context.Context, id platform.ID) (*platfo
func (s *UserService) FindUser(ctx context.Context, filter platform.UserFilter) (*platform.User, error) {
users, n, err := s.FindUsers(ctx, filter)
if err != nil {
return nil, err
return nil, &platform.Error{
Op: s.OpPrefix + platform.OpFindUser,
Err: err,
}
}
if n == 0 {
return nil, ErrNotFound
return nil, &platform.Error{
Code: platform.ENotFound,
Op: s.OpPrefix + platform.OpFindUser,
Msg: "no results found",
}
}
return users[0], nil
@ -564,7 +573,7 @@ func (s *UserService) CreateUser(ctx context.Context, u *platform.User) error {
}
// TODO(jsternberg): Should this check for a 201 explicitly?
if err := CheckError(resp); err != nil {
if err := CheckError(resp, true); err != nil {
return err
}
@ -603,7 +612,7 @@ func (s *UserService) UpdateUser(ctx context.Context, id platform.ID, upd platfo
return nil, err
}
if err := CheckError(resp); err != nil {
if err := CheckError(resp, true); err != nil {
return nil, err
}

View File

@ -10,7 +10,7 @@ import (
platformtesting "github.com/influxdata/platform/testing"
)
func initUserService(f platformtesting.UserFields, t *testing.T) (platform.UserService, func()) {
func initUserService(f platformtesting.UserFields, t *testing.T) (platform.UserService, string, func()) {
t.Helper()
svc := inmem.NewService()
svc.IDGenerator = f.IDGenerator
@ -26,12 +26,13 @@ func initUserService(f platformtesting.UserFields, t *testing.T) (platform.UserS
handler.UserService = svc
server := httptest.NewServer(handler)
client := UserService{
Addr: server.URL,
Addr: server.URL,
OpPrefix: inmem.OpPrefix,
}
done := server.Close
return &client, done
return &client, inmem.OpPrefix, done
}
func TestUserService(t *testing.T) {

View File

@ -17,22 +17,26 @@ type ViewHandler struct {
ViewService platform.ViewService
UserResourceMappingService platform.UserResourceMappingService
LabelService platform.LabelService
}
const (
viewsPath = "/api/v2/views"
viewsIDPath = "/api/v2/views/:id"
viewsIDMembersPath = "/api/v2/views/:id/members"
viewsIDMembersIDPath = "/api/v2/views/:id/members/:userID"
viewsIDOwnersPath = "/api/v2/views/:id/owners"
viewsIDOwnersIDPath = "/api/v2/views/:id/owners:userID"
viewsPath = "/api/v2/views"
viewsIDPath = "/api/v2/views/:id"
viewsIDMembersPath = "/api/v2/views/:id/members"
viewsIDMembersIDPath = "/api/v2/views/:id/members/:userID"
viewsIDOwnersPath = "/api/v2/views/:id/owners"
viewsIDOwnersIDPath = "/api/v2/views/:id/owners/:userID"
viewsIDLabelsPath = "/api/v2/views/:id/labels"
viewsIDLabelsNamePath = "/api/v2/views/:id/labels/:name"
)
// NewViewHandler returns a new instance of ViewHandler.
func NewViewHandler(mappingService platform.UserResourceMappingService) *ViewHandler {
func NewViewHandler(mappingService platform.UserResourceMappingService, labelService platform.LabelService) *ViewHandler {
h := &ViewHandler{
Router: httprouter.New(),
UserResourceMappingService: mappingService,
LabelService: labelService,
}
h.HandlerFunc("POST", viewsPath, h.handlePostViews)
@ -50,6 +54,10 @@ func NewViewHandler(mappingService platform.UserResourceMappingService) *ViewHan
h.HandlerFunc("GET", viewsIDOwnersPath, newGetMembersHandler(h.UserResourceMappingService, platform.Owner))
h.HandlerFunc("DELETE", viewsIDOwnersIDPath, newDeleteMemberHandler(h.UserResourceMappingService, platform.Owner))
h.HandlerFunc("GET", viewsIDLabelsPath, newGetLabelsHandler(h.LabelService))
h.HandlerFunc("POST", viewsIDLabelsPath, newPostLabelHandler(h.LabelService))
h.HandlerFunc("DELETE", viewsIDLabelsNamePath, newDeleteLabelHandler(h.LabelService))
return h
}

View File

@ -129,8 +129,7 @@ func TestService_handleGetViews(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mappingService := mock.NewUserResourceMappingService()
h := NewViewHandler(mappingService)
h := NewViewHandler(mock.NewUserResourceMappingService(), mock.NewLabelService())
h.ViewService = tt.fields.ViewService
r := httptest.NewRequest("GET", "http://any.url", nil)
@ -238,8 +237,7 @@ func TestService_handleGetView(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mappingService := mock.NewUserResourceMappingService()
h := NewViewHandler(mappingService)
h := NewViewHandler(mock.NewUserResourceMappingService(), mock.NewLabelService())
h.ViewService = tt.fields.ViewService
r := httptest.NewRequest("GET", "http://any.url", nil)
@ -344,8 +342,7 @@ func TestService_handlePostViews(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mappingService := mock.NewUserResourceMappingService()
h := NewViewHandler(mappingService)
h := NewViewHandler(mock.NewUserResourceMappingService(), mock.NewLabelService())
h.ViewService = tt.fields.ViewService
b, err := json.Marshal(tt.args.view)
@ -434,8 +431,7 @@ func TestService_handleDeleteView(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mappingService := mock.NewUserResourceMappingService()
h := NewViewHandler(mappingService)
h := NewViewHandler(mock.NewUserResourceMappingService(), mock.NewLabelService())
h.ViewService = tt.fields.ViewService
r := httptest.NewRequest("GET", "http://any.url", nil)
@ -591,8 +587,7 @@ func TestService_handlePatchView(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mappingService := mock.NewUserResourceMappingService()
h := NewViewHandler(mappingService)
h := NewViewHandler(mock.NewUserResourceMappingService(), mock.NewLabelService())
h.ViewService = tt.fields.ViewService
upd := platform.ViewUpdate{}

View File

@ -149,7 +149,7 @@ func (s *Service) FindOrganizations(ctx context.Context, filter platform.Organiz
return orgs, 0, &platform.Error{
Code: platform.ENotFound,
Op: op,
Msg: OpPrefix + platform.OpFindOrganizations,
Msg: errOrganizationNotFound,
}
}

View File

@ -46,17 +46,21 @@ func (s *Service) FindUserByID(ctx context.Context, id platform.ID) (u *platform
var pe *platform.Error
u, pe = s.loadUser(id)
if pe != nil {
err = pe
err = &platform.Error{
Op: OpPrefix + platform.OpFindUserByID,
Err: pe,
}
}
return u, err
}
func (c *Service) findUserByName(ctx context.Context, n string) (*platform.User, error) {
return c.FindUser(ctx, platform.UserFilter{Name: &n})
func (s *Service) findUserByName(ctx context.Context, n string) (*platform.User, error) {
return s.FindUser(ctx, platform.UserFilter{Name: &n})
}
// FindUser returns the first user that matches a filter.
func (s *Service) FindUser(ctx context.Context, filter platform.UserFilter) (*platform.User, error) {
op := OpPrefix + platform.OpFindUser
if filter.Name != nil {
var o *platform.User
@ -75,6 +79,7 @@ func (s *Service) FindUser(ctx context.Context, filter platform.UserFilter) (*pl
if o == nil {
return nil, &platform.Error{
Code: platform.ENotFound,
Op: op,
Msg: "user not found",
}
}
@ -82,14 +87,23 @@ func (s *Service) FindUser(ctx context.Context, filter platform.UserFilter) (*pl
return o, nil
}
return nil, fmt.Errorf("expected filter to contain name")
return nil, &platform.Error{
Code: platform.EInvalid,
Op: op,
Msg: "expected filter to contain name",
}
}
// FindUsers will retrieve a list of users from storage.
func (s *Service) FindUsers(ctx context.Context, filter platform.UserFilter, opt ...platform.FindOptions) ([]*platform.User, int, error) {
op := OpPrefix + platform.OpFindUsers
if filter.ID != nil {
o, err := s.FindUserByID(ctx, *filter.ID)
if err != nil {
return nil, 0, err
return nil, 0, &platform.Error{
Err: err,
Op: op,
}
}
return []*platform.User{o}, 1, nil
@ -97,7 +111,10 @@ func (s *Service) FindUsers(ctx context.Context, filter platform.UserFilter, opt
if filter.Name != nil {
o, err := s.FindUser(ctx, filter)
if err != nil {
return nil, 0, err
return nil, 0, &platform.Error{
Err: err,
Op: op,
}
}
return []*platform.User{o}, 1, nil
@ -117,24 +134,34 @@ func (s *Service) FindUsers(ctx context.Context, filter platform.UserFilter, opt
return orgs, len(orgs), nil
}
// CreateUser will create an user into storage.
func (s *Service) CreateUser(ctx context.Context, u *platform.User) error {
if _, err := s.FindUser(ctx, platform.UserFilter{Name: &u.Name}); err == nil {
return fmt.Errorf("user with name %s already exists", u.Name)
return &platform.Error{
Code: platform.EConflict,
Op: OpPrefix + platform.OpCreateUser,
Msg: fmt.Sprintf("user with name %s already exists", u.Name),
}
}
u.ID = s.IDGenerator.ID()
s.PutUser(ctx, u)
return nil
}
// PutUser put a user into storage.
func (s *Service) PutUser(ctx context.Context, o *platform.User) error {
s.userKV.Store(o.ID.String(), o)
return nil
}
// UpdateUser update a user in storage.
func (s *Service) UpdateUser(ctx context.Context, id platform.ID, upd platform.UserUpdate) (*platform.User, error) {
o, err := s.FindUserByID(ctx, id)
if err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
Op: OpPrefix + platform.OpUpdateUser,
}
}
if upd.Name != nil {
@ -146,9 +173,13 @@ func (s *Service) UpdateUser(ctx context.Context, id platform.ID, upd platform.U
return o, nil
}
// DeleteUser remove a user from storage.
func (s *Service) DeleteUser(ctx context.Context, id platform.ID) error {
if _, err := s.FindUserByID(ctx, id); err != nil {
return err
return &platform.Error{
Err: err,
Op: OpPrefix + platform.OpDeleteUser,
}
}
s.userKV.Delete(id.String())
return nil

View File

@ -8,7 +8,7 @@ import (
platformtesting "github.com/influxdata/platform/testing"
)
func initUserService(f platformtesting.UserFields, t *testing.T) (platform.UserService, func()) {
func initUserService(f platformtesting.UserFields, t *testing.T) (platform.UserService, string, func()) {
s := NewService()
s.IDGenerator = f.IDGenerator
ctx := context.Background()
@ -17,35 +17,10 @@ func initUserService(f platformtesting.UserFields, t *testing.T) (platform.UserS
t.Fatalf("failed to populate users")
}
}
return s, func() {}
return s, OpPrefix, func() {}
}
func TestUserService_CreateUser(t *testing.T) {
func TestUserService(t *testing.T) {
t.Parallel()
platformtesting.CreateUser(initUserService, t)
}
func TestUserService_FindUserByID(t *testing.T) {
t.Parallel()
platformtesting.FindUserByID(initUserService, t)
}
func TestUserService_FindUsers(t *testing.T) {
t.Parallel()
platformtesting.FindUsers(initUserService, t)
}
func TestUserService_DeleteUser(t *testing.T) {
t.Parallel()
platformtesting.DeleteUser(initUserService, t)
}
func TestUserService_FindUser(t *testing.T) {
t.Parallel()
platformtesting.FindUser(initUserService, t)
}
func TestUserService_UpdateUser(t *testing.T) {
t.Parallel()
platformtesting.UpdateUser(initUserService, t)
platformtesting.UserService(initUserService, t)
}

View File

@ -0,0 +1,97 @@
// Generated by tmpl
// https://github.com/benbjohnson/tmpl
//
// DO NOT EDIT!
// Source: arrays.gen.go.tmpl
package gen
import (
"github.com/influxdata/platform/tsdb/cursors"
"github.com/influxdata/platform/tsdb/tsm1"
)
type FloatArray struct {
cursors.FloatArray
}
func NewFloatArrayLen(sz int) *FloatArray {
return &FloatArray{
FloatArray: cursors.FloatArray{
Timestamps: make([]int64, sz),
Values: make([]float64, sz),
},
}
}
func (a *FloatArray) Encode(b []byte) ([]byte, error) {
return tsm1.EncodeFloatArrayBlock(&a.FloatArray, b)
}
type IntegerArray struct {
cursors.IntegerArray
}
func NewIntegerArrayLen(sz int) *IntegerArray {
return &IntegerArray{
IntegerArray: cursors.IntegerArray{
Timestamps: make([]int64, sz),
Values: make([]int64, sz),
},
}
}
func (a *IntegerArray) Encode(b []byte) ([]byte, error) {
return tsm1.EncodeIntegerArrayBlock(&a.IntegerArray, b)
}
type UnsignedArray struct {
cursors.UnsignedArray
}
func NewUnsignedArrayLen(sz int) *UnsignedArray {
return &UnsignedArray{
UnsignedArray: cursors.UnsignedArray{
Timestamps: make([]int64, sz),
Values: make([]uint64, sz),
},
}
}
func (a *UnsignedArray) Encode(b []byte) ([]byte, error) {
return tsm1.EncodeUnsignedArrayBlock(&a.UnsignedArray, b)
}
type StringArray struct {
cursors.StringArray
}
func NewStringArrayLen(sz int) *StringArray {
return &StringArray{
StringArray: cursors.StringArray{
Timestamps: make([]int64, sz),
Values: make([]string, sz),
},
}
}
func (a *StringArray) Encode(b []byte) ([]byte, error) {
return tsm1.EncodeStringArrayBlock(&a.StringArray, b)
}
type BooleanArray struct {
cursors.BooleanArray
}
func NewBooleanArrayLen(sz int) *BooleanArray {
return &BooleanArray{
BooleanArray: cursors.BooleanArray{
Timestamps: make([]int64, sz),
Values: make([]bool, sz),
},
}
}
func (a *BooleanArray) Encode(b []byte) ([]byte, error) {
return tsm1.EncodeBooleanArrayBlock(&a.BooleanArray, b)
}

View File

@ -0,0 +1,26 @@
package gen
import (
"github.com/influxdata/platform/tsdb/tsm1"
"github.com/influxdata/platform/tsdb/cursors"
)
{{range .}}
{{ $typename := print .Name "Array" }}
type {{$typename}} struct {
cursors.{{$typename}}
}
func New{{$typename}}Len(sz int) *{{$typename}} {
return &{{$typename}}{
{{$typename}}: cursors.{{$typename}}{
Timestamps: make([]int64, sz),
Values: make([]{{.Type}}, sz),
},
}
}
func (a *{{$typename}}) Encode(b []byte) ([]byte, error) {
return tsm1.Encode{{$typename}}Block(&a.{{$typename}}, b)
}
{{end}}

3
pkg/data/gen/gen.go Normal file
View File

@ -0,0 +1,3 @@
package gen
//go:generate env GO111MODULE=on go run github.com/benbjohnson/tmpl -data=@types.tmpldata arrays.gen.go.tmpl values_constant.gen.go.tmpl

View File

@ -8,6 +8,10 @@ import (
type Sequence interface {
Next() bool
Value() string
}
type CountableSequence interface {
Sequence
Count() int
}
@ -49,11 +53,11 @@ func (s *CounterByteSequence) update() {
s.val = fmt.Sprintf(s.format, fmt.Sprintf(s.nfmt, s.v))
}
func (s *CounterByteSequence) Count() int { return s.end - s.s }
func (s *CounterByteSequence) Value() string { return s.val }
func (s *CounterByteSequence) Count() int { return s.end - s.s }
type ConstantStringSequence string
func (ConstantStringSequence) Next() bool { return true }
func (s ConstantStringSequence) Next() bool { return true }
func (s ConstantStringSequence) Value() string { return string(s) }
func (ConstantStringSequence) Count() int { return 1 }
func (s ConstantStringSequence) Count() int { return 1 }

View File

@ -0,0 +1,37 @@
package gen
import (
"github.com/influxdata/platform/models"
)
type SeriesGenerator interface {
// Next advances the series generator to the next series key.
Next() bool
// Name returns the name of the measurement.
// The returned value may be modified by a subsequent call to Next.
Name() []byte
// Tags returns the tag set.
// The returned value may be modified by a subsequent call to Next.
Tags() models.Tags
// Field returns the name of the field.
// The returned value may be modified by a subsequent call to Next.
Field() []byte
// ValuesGenerator returns a values sequence for the current series.
ValuesGenerator() ValuesSequence
}
type ValuesSequence interface {
Reset()
Next() bool
Values() Values
}
type Values interface {
MinTime() int64
MaxTime() int64
Encode([]byte) ([]byte, error)
}

View File

@ -16,12 +16,12 @@ type TagsSequence interface {
type TagsValuesSequence struct {
tags models.Tags
vals []Sequence
vals []CountableSequence
n int
max int
}
func NewTagsValuesSequenceKeysValues(keys []string, vals []Sequence) *TagsValuesSequence {
func NewTagsValuesSequenceKeysValues(keys []string, vals []CountableSequence) *TagsValuesSequence {
tm := make(map[string]string, len(keys))
for _, k := range keys {
tm[k] = ""
@ -42,7 +42,7 @@ func NewTagsValuesSequenceKeysValues(keys []string, vals []Sequence) *TagsValues
}
}
func NewTagsValuesSequenceValues(prefix string, vals []Sequence) *TagsValuesSequence {
func NewTagsValuesSequenceValues(prefix string, vals []CountableSequence) *TagsValuesSequence {
keys := make([]string, len(vals))
// max tag width
tw := int(math.Ceil(math.Log10(float64(len(vals)))))
@ -82,7 +82,7 @@ func (s *TagsValuesSequence) Count() int { return s.max }
type keyValues struct {
keys []string
vals []Sequence
vals []CountableSequence
}
func (k keyValues) Len() int { return len(k.keys) }

View File

@ -0,0 +1,27 @@
[
{
"Name":"Float",
"name":"float",
"Type":"float64"
},
{
"Name":"Integer",
"name":"integer",
"Type":"int64"
},
{
"Name":"Unsigned",
"name":"unsigned",
"Type":"uint64"
},
{
"Name":"String",
"name":"string",
"Type":"string"
},
{
"Name":"Boolean",
"name":"boolean",
"Type":"bool"
}
]

8
pkg/data/gen/util.go Normal file
View File

@ -0,0 +1,8 @@
package gen
func min(a, b int) int {
if a < b {
return a
}
return b
}

View File

@ -0,0 +1,308 @@
// Generated by tmpl
// https://github.com/benbjohnson/tmpl
//
// DO NOT EDIT!
// Source: values_constant.gen.go.tmpl
package gen
import (
"time"
"github.com/influxdata/platform/tsdb/cursors"
)
type FloatConstantValuesSequence struct {
vals FloatArray
n int
t int64
state struct {
n int
t int64
d int64
v float64
}
}
func NewFloatConstantValuesSequence(n int, start time.Time, delta time.Duration, v float64) *FloatConstantValuesSequence {
g := &FloatConstantValuesSequence{
vals: *NewFloatArrayLen(cursors.DefaultMaxPointsPerBlock),
}
g.state.n = n
g.state.t = start.UnixNano()
g.state.d = int64(delta)
g.state.v = v
g.Reset()
return g
}
func (g *FloatConstantValuesSequence) Reset() {
g.n = g.state.n
g.t = g.state.t
}
func (g *FloatConstantValuesSequence) Next() bool {
if g.n == 0 {
return false
}
c := min(g.n, cursors.DefaultMaxPointsPerBlock)
g.n -= c
g.vals.Timestamps = g.vals.Timestamps[:c]
g.vals.Values = g.vals.Values[:c]
var (
t = g.t
ts = g.vals.Timestamps
vs = g.vals.Values
d = g.state.d
)
for i := 0; i < len(ts) && i < len(vs); i++ {
ts[i] = g.t
vs[i] = g.state.v
t += d
}
g.t = t
return true
}
func (g *FloatConstantValuesSequence) Values() Values {
return &g.vals
}
type IntegerConstantValuesSequence struct {
vals IntegerArray
n int
t int64
state struct {
n int
t int64
d int64
v int64
}
}
func NewIntegerConstantValuesSequence(n int, start time.Time, delta time.Duration, v int64) *IntegerConstantValuesSequence {
g := &IntegerConstantValuesSequence{
vals: *NewIntegerArrayLen(cursors.DefaultMaxPointsPerBlock),
}
g.state.n = n
g.state.t = start.UnixNano()
g.state.d = int64(delta)
g.state.v = v
g.Reset()
return g
}
func (g *IntegerConstantValuesSequence) Reset() {
g.n = g.state.n
g.t = g.state.t
}
func (g *IntegerConstantValuesSequence) Next() bool {
if g.n == 0 {
return false
}
c := min(g.n, cursors.DefaultMaxPointsPerBlock)
g.n -= c
g.vals.Timestamps = g.vals.Timestamps[:c]
g.vals.Values = g.vals.Values[:c]
var (
t = g.t
ts = g.vals.Timestamps
vs = g.vals.Values
d = g.state.d
)
for i := 0; i < len(ts) && i < len(vs); i++ {
ts[i] = g.t
vs[i] = g.state.v
t += d
}
g.t = t
return true
}
func (g *IntegerConstantValuesSequence) Values() Values {
return &g.vals
}
type UnsignedConstantValuesSequence struct {
vals UnsignedArray
n int
t int64
state struct {
n int
t int64
d int64
v uint64
}
}
func NewUnsignedConstantValuesSequence(n int, start time.Time, delta time.Duration, v uint64) *UnsignedConstantValuesSequence {
g := &UnsignedConstantValuesSequence{
vals: *NewUnsignedArrayLen(cursors.DefaultMaxPointsPerBlock),
}
g.state.n = n
g.state.t = start.UnixNano()
g.state.d = int64(delta)
g.state.v = v
g.Reset()
return g
}
func (g *UnsignedConstantValuesSequence) Reset() {
g.n = g.state.n
g.t = g.state.t
}
func (g *UnsignedConstantValuesSequence) Next() bool {
if g.n == 0 {
return false
}
c := min(g.n, cursors.DefaultMaxPointsPerBlock)
g.n -= c
g.vals.Timestamps = g.vals.Timestamps[:c]
g.vals.Values = g.vals.Values[:c]
var (
t = g.t
ts = g.vals.Timestamps
vs = g.vals.Values
d = g.state.d
)
for i := 0; i < len(ts) && i < len(vs); i++ {
ts[i] = g.t
vs[i] = g.state.v
t += d
}
g.t = t
return true
}
func (g *UnsignedConstantValuesSequence) Values() Values {
return &g.vals
}
type StringConstantValuesSequence struct {
vals StringArray
n int
t int64
state struct {
n int
t int64
d int64
v string
}
}
func NewStringConstantValuesSequence(n int, start time.Time, delta time.Duration, v string) *StringConstantValuesSequence {
g := &StringConstantValuesSequence{
vals: *NewStringArrayLen(cursors.DefaultMaxPointsPerBlock),
}
g.state.n = n
g.state.t = start.UnixNano()
g.state.d = int64(delta)
g.state.v = v
g.Reset()
return g
}
func (g *StringConstantValuesSequence) Reset() {
g.n = g.state.n
g.t = g.state.t
}
func (g *StringConstantValuesSequence) Next() bool {
if g.n == 0 {
return false
}
c := min(g.n, cursors.DefaultMaxPointsPerBlock)
g.n -= c
g.vals.Timestamps = g.vals.Timestamps[:c]
g.vals.Values = g.vals.Values[:c]
var (
t = g.t
ts = g.vals.Timestamps
vs = g.vals.Values
d = g.state.d
)
for i := 0; i < len(ts) && i < len(vs); i++ {
ts[i] = g.t
vs[i] = g.state.v
t += d
}
g.t = t
return true
}
func (g *StringConstantValuesSequence) Values() Values {
return &g.vals
}
type BooleanConstantValuesSequence struct {
vals BooleanArray
n int
t int64
state struct {
n int
t int64
d int64
v bool
}
}
func NewBooleanConstantValuesSequence(n int, start time.Time, delta time.Duration, v bool) *BooleanConstantValuesSequence {
g := &BooleanConstantValuesSequence{
vals: *NewBooleanArrayLen(cursors.DefaultMaxPointsPerBlock),
}
g.state.n = n
g.state.t = start.UnixNano()
g.state.d = int64(delta)
g.state.v = v
g.Reset()
return g
}
func (g *BooleanConstantValuesSequence) Reset() {
g.n = g.state.n
g.t = g.state.t
}
func (g *BooleanConstantValuesSequence) Next() bool {
if g.n == 0 {
return false
}
c := min(g.n, cursors.DefaultMaxPointsPerBlock)
g.n -= c
g.vals.Timestamps = g.vals.Timestamps[:c]
g.vals.Values = g.vals.Values[:c]
var (
t = g.t
ts = g.vals.Timestamps
vs = g.vals.Values
d = g.state.d
)
for i := 0; i < len(ts) && i < len(vs); i++ {
ts[i] = g.t
vs[i] = g.state.v
t += d
}
g.t = t
return true
}
func (g *BooleanConstantValuesSequence) Values() Values {
return &g.vals
}

View File

@ -0,0 +1,68 @@
package gen
import (
"time"
"github.com/influxdata/platform/tsdb/cursors"
)
{{range .}}
type {{.Name}}ConstantValuesSequence struct {
vals {{.Name}}Array
n int
t int64
state struct {
n int
t int64
d int64
v {{.Type}}
}
}
func New{{.Name}}ConstantValuesSequence(n int, start time.Time, delta time.Duration, v {{.Type}}) *{{.Name}}ConstantValuesSequence {
g := &{{.Name}}ConstantValuesSequence{
vals: *New{{.Name}}ArrayLen(cursors.DefaultMaxPointsPerBlock),
}
g.state.n = n
g.state.t = start.UnixNano()
g.state.d = int64(delta)
g.state.v = v
g.Reset()
return g
}
func (g *{{.Name}}ConstantValuesSequence) Reset() {
g.n = g.state.n
g.t = g.state.t
}
func (g *{{.Name}}ConstantValuesSequence) Next() bool {
if g.n == 0 {
return false
}
c := min(g.n, cursors.DefaultMaxPointsPerBlock)
g.n -= c
g.vals.Timestamps = g.vals.Timestamps[:c]
g.vals.Values = g.vals.Values[:c]
var (
t = g.t
ts = g.vals.Timestamps
vs = g.vals.Values
d = g.state.d
)
for i := 0; i < len(ts) && i < len(vs); i++ {
ts[i] = g.t
vs[i] = g.state.v
t += d
}
g.t = t
return true
}
func (g *{{.Name}}ConstantValuesSequence) Values() Values {
return &g.vals
}
{{end}}

View File

@ -0,0 +1,60 @@
package gen
import (
"math/rand"
"time"
"github.com/influxdata/platform/tsdb/cursors"
)
type FloatRandomValuesSequence struct {
buf FloatArray
vals FloatArray
n int
t int64
state struct {
n int
t int64
d int64
scale float64
}
}
func NewFloatRandomValuesSequence(n int, start time.Time, delta time.Duration, scale float64) *FloatRandomValuesSequence {
g := &FloatRandomValuesSequence{
buf: *NewFloatArrayLen(cursors.DefaultMaxPointsPerBlock),
}
g.state.n = n
g.state.t = start.UnixNano()
g.state.d = int64(delta)
g.state.scale = scale
g.Reset()
return g
}
func (g *FloatRandomValuesSequence) Reset() {
g.n = g.state.n
g.t = g.state.t
}
func (g *FloatRandomValuesSequence) Next() bool {
if g.n == 0 {
return false
}
c := min(g.n, cursors.DefaultMaxPointsPerBlock)
g.n -= c
g.vals.Timestamps = g.buf.Timestamps[:0]
g.vals.Values = g.buf.Values[:0]
for i := 0; i < c; i++ {
g.vals.Timestamps = append(g.vals.Timestamps, g.t)
g.vals.Values = append(g.vals.Values, rand.Float64()*g.state.scale)
g.t += g.state.d
}
return true
}
func (g *FloatRandomValuesSequence) Values() Values {
return &g.vals
}

View File

@ -7,7 +7,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/influxdata/platform/models"
"github.com/influxdata/platform/pkg/testing/gen"
"github.com/influxdata/platform/pkg/data/gen"
"github.com/influxdata/platform/storage/reads"
"github.com/influxdata/platform/storage/reads/datatypes"
)
@ -310,7 +310,7 @@ func (s *sliceSeriesCursor) Next() *reads.SeriesRow {
func BenchmarkNewGroupResultSet_GroupBy(b *testing.B) {
card := []int{10, 10, 10}
vals := make([]gen.Sequence, len(card))
vals := make([]gen.CountableSequence, len(card))
for i := range card {
vals[i] = gen.NewCounterByteSequenceCount(card[i])
}

View File

@ -945,7 +945,7 @@ func FindBucket(
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpFindBucket,
Msg: "no results found",
Msg: "bucket not found",
},
},
},

View File

@ -295,7 +295,7 @@ func FindOrganizationByID(
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpFindOrganizationByID,
Msg: "",
Msg: "organization not found",
},
},
},

View File

@ -3,7 +3,6 @@ package testing
import (
"bytes"
"context"
"fmt"
"sort"
"testing"
@ -39,11 +38,11 @@ type UserFields struct {
// UserService tests all the service functions.
func UserService(
init func(UserFields, *testing.T) (platform.UserService, func()), t *testing.T,
init func(UserFields, *testing.T) (platform.UserService, string, func()), t *testing.T,
) {
tests := []struct {
name string
fn func(init func(UserFields, *testing.T) (platform.UserService, func()),
fn func(init func(UserFields, *testing.T) (platform.UserService, string, func()),
t *testing.T)
}{
{
@ -80,7 +79,7 @@ func UserService(
// CreateUser testing
func CreateUser(
init func(UserFields, *testing.T) (platform.UserService, func()),
init func(UserFields, *testing.T) (platform.UserService, string, func()),
t *testing.T,
) {
type args struct {
@ -173,26 +172,22 @@ func CreateUser(
Name: "user1",
},
},
err: fmt.Errorf("user with name user1 already exists"),
err: &platform.Error{
Code: platform.EConflict,
Op: platform.OpCreateUser,
Msg: "user with name user1 already exists",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.TODO()
err := s.CreateUser(ctx, tt.args.user)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
}
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
// Delete only created users - ie., having a not nil ID
if tt.args.user.ID.Valid() {
@ -212,7 +207,7 @@ func CreateUser(
// FindUserByID testing
func FindUserByID(
init func(UserFields, *testing.T) (platform.UserService, func()),
init func(UserFields, *testing.T) (platform.UserService, string, func()),
t *testing.T,
) {
type args struct {
@ -253,24 +248,41 @@ func FindUserByID(
},
},
},
{
name: "find user by id not exists",
fields: UserFields{
Users: []*platform.User{
{
ID: MustIDBase16(userOneID),
Name: "user1",
},
{
ID: MustIDBase16(userTwoID),
Name: "user2",
},
},
},
args: args{
id: MustIDBase16(threeID),
},
wants: wants{
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpFindUserByID,
Msg: "user not found",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.TODO()
user, err := s.FindUserByID(ctx, tt.args.id)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected errors to be equal '%v' got '%v'", tt.wants.err, err)
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
if diff := cmp.Diff(user, tt.wants.user, userCmpOptions...); diff != "" {
t.Errorf("user is different -got/+want\ndiff %s", diff)
@ -281,7 +293,7 @@ func FindUserByID(
// FindUsers testing
func FindUsers(
init func(UserFields, *testing.T) (platform.UserService, func()),
init func(UserFields, *testing.T) (platform.UserService, string, func()),
t *testing.T,
) {
type args struct {
@ -379,11 +391,61 @@ func FindUsers(
},
},
},
{
name: "find user by id not exists",
fields: UserFields{
Users: []*platform.User{
{
ID: MustIDBase16(userOneID),
Name: "abc",
},
{
ID: MustIDBase16(userTwoID),
Name: "xyz",
},
},
},
args: args{
ID: MustIDBase16(threeID),
},
wants: wants{
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpFindUsers,
Msg: "user not found",
},
},
},
{
name: "find user by name not exists",
fields: UserFields{
Users: []*platform.User{
{
ID: MustIDBase16(userOneID),
Name: "abc",
},
{
ID: MustIDBase16(userTwoID),
Name: "xyz",
},
},
},
args: args{
name: "no_exist",
},
wants: wants{
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpFindUsers,
Msg: "user not found",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.TODO()
@ -396,15 +458,7 @@ func FindUsers(
}
users, _, err := s.FindUsers(ctx, filter)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected errors to be equal '%v' got '%v'", tt.wants.err, err)
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
if diff := cmp.Diff(users, tt.wants.users, userCmpOptions...); diff != "" {
t.Errorf("users are different -got/+want\ndiff %s", diff)
@ -415,7 +469,7 @@ func FindUsers(
// DeleteUser testing
func DeleteUser(
init func(UserFields, *testing.T) (platform.UserService, func()),
init func(UserFields, *testing.T) (platform.UserService, string, func()),
t *testing.T,
) {
type args struct {
@ -476,7 +530,11 @@ func DeleteUser(
ID: MustIDBase16(userThreeID),
},
wants: wants{
err: fmt.Errorf("<not found> user not found"),
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpDeleteUser,
Msg: "user not found",
},
users: []*platform.User{
{
Name: "orgA",
@ -493,19 +551,11 @@ func DeleteUser(
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.TODO()
err := s.DeleteUser(ctx, tt.args.ID)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
}
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
filter := platform.UserFilter{}
users, _, err := s.FindUsers(ctx, filter)
@ -521,7 +571,7 @@ func DeleteUser(
// FindUser testing
func FindUser(
init func(UserFields, *testing.T) (platform.UserService, func()),
init func(UserFields, *testing.T) (platform.UserService, string, func()),
t *testing.T,
) {
type args struct {
@ -572,14 +622,18 @@ func FindUser(
name: "abc",
},
wants: wants{
err: fmt.Errorf("<not found> user not found"),
err: &platform.Error{
Code: platform.ENotFound,
Msg: "user not found",
Op: platform.OpFindUser,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.TODO()
filter := platform.UserFilter{}
@ -588,15 +642,7 @@ func FindUser(
}
user, err := s.FindUser(ctx, filter)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
}
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
if diff := cmp.Diff(user, tt.wants.user, userCmpOptions...); diff != "" {
t.Errorf("users are different -got/+want\ndiff %s", diff)
@ -607,7 +653,7 @@ func FindUser(
// UpdateUser testing
func UpdateUser(
init func(UserFields, *testing.T) (platform.UserService, func()),
init func(UserFields, *testing.T) (platform.UserService, string, func()),
t *testing.T,
) {
type args struct {
@ -650,11 +696,37 @@ func UpdateUser(
},
},
},
{
name: "update name with id not exists",
fields: UserFields{
Users: []*platform.User{
{
ID: MustIDBase16(userOneID),
Name: "user1",
},
{
ID: MustIDBase16(userTwoID),
Name: "user2",
},
},
},
args: args{
id: MustIDBase16(threeID),
name: "changed",
},
wants: wants{
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpUpdateUser,
Msg: "user not found",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.TODO()
@ -664,15 +736,7 @@ func UpdateUser(
}
user, err := s.UpdateUser(ctx, tt.args.id, upd)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
}
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
if diff := cmp.Diff(user, tt.wants.user, userCmpOptions...); diff != "" {
t.Errorf("user is different -got/+want\ndiff %s", diff)

View File

@ -21,6 +21,10 @@ func diffErrors(actual, expected error, t *testing.T) {
}
func diffPlatformErrors(name string, actual, expected error, opPrefix string, t *testing.T) {
if expected == nil && actual == nil {
return
}
if expected == nil && actual != nil {
t.Fatalf("%s failed, unexpected error %s", name, actual.Error())
}
@ -29,13 +33,17 @@ func diffPlatformErrors(name string, actual, expected error, opPrefix string, t
t.Fatalf("%s failed, expected error %s but received nil", name, expected.Error())
}
if expected != nil && actual != nil && platform.ErrorCode(expected) != platform.ErrorCode(actual) {
if platform.ErrorCode(expected) != platform.ErrorCode(actual) {
t.Fatalf("%s failed, expected error %q but received error code %q", name, platform.ErrorCode(expected), platform.ErrorCode(actual))
}
if expected != nil && actual != nil && opPrefix+platform.ErrorOp(expected) != platform.ErrorOp(actual) {
if opPrefix+platform.ErrorOp(expected) != platform.ErrorOp(actual) {
t.Fatalf("%s failed, expected error %q but received error op %q", name, opPrefix+platform.ErrorOp(expected), platform.ErrorOp(actual))
}
if platform.ErrorMessage(expected) != platform.ErrorMessage(actual) {
t.Fatalf("%s failed, expected error %q but received error message %q", name, platform.ErrorMessage(expected), platform.ErrorMessage(actual))
}
}
// MustIDBase16 is an helper to ensure a correct ID is built during testing.

View File

@ -3,6 +3,7 @@ package tsi1
import (
"math/rand"
"sync"
"sync/atomic"
"testing"
"time"
@ -152,49 +153,70 @@ func TestTagValueSeriesIDCache_addToSet(t *testing.T) {
if !newSeriesIDSet(20).Equals(ss) {
t.Fatalf("series id set was %v", ss)
}
}
func TestTagValueSeriesIDCache_ConcurrentGetPut(t *testing.T) {
func TestTagValueSeriesIDCache_ConcurrentGetPutDelete(t *testing.T) {
// Exercise concurrent operations against a series ID cache.
// This will catch any likely data races, when run with the race detector.
if testing.Short() {
t.Skip("Skipping long test")
}
a := []string{"a", "b", "c", "d", "e"}
rnd := func() []byte {
return []byte(a[rand.Intn(len(a)-1)])
t.Parallel()
const letters = "abcde"
rnd := func(rng *rand.Rand) []byte {
return []byte{letters[rng.Intn(len(letters)-1)]}
}
cache := TestCache{NewTagValueSeriesIDCache(100)}
done := make(chan struct{})
var wg sync.WaitGroup
var seriesIDCounter int32 // Atomic counter to ensure unique series IDs.
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// Local rng to avoid lock contention.
rng := rand.New(rand.NewSource(rand.Int63()))
for {
select {
case <-done:
return
default:
}
cache.Put(rnd(), rnd(), rnd(), newSeriesIDSet())
nextID := int(atomic.AddInt32(&seriesIDCounter, 1))
cache.Put(rnd(rng), rnd(rng), rnd(rng), newSeriesIDSet(nextID))
}
}()
}
var gets, deletes int32
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// Local rng to avoid lock contention.
rng := rand.New(rand.NewSource(rand.Int63()))
for {
select {
case <-done:
return
default:
}
_ = cache.Get(rnd(), rnd(), rnd())
name, key, value := rnd(rng), rnd(rng), rnd(rng)
if set := cache.Get(name, key, value); set != nil {
ids := set.Slice()
for _, id := range ids {
cache.Delete(name, key, value, tsdb.NewSeriesID(id))
atomic.AddInt32(&deletes, 1)
}
}
atomic.AddInt32(&gets, 1)
}
}()
}
@ -202,6 +224,7 @@ func TestTagValueSeriesIDCache_ConcurrentGetPut(t *testing.T) {
time.Sleep(10 * time.Second)
close(done)
wg.Wait()
t.Logf("Concurrently executed against series ID cache: gets=%d puts=%d deletes=%d", gets, seriesIDCounter, deletes)
}
type TestCache struct {

View File

@ -23,6 +23,8 @@ import (
// Ensure log file can append series.
func TestLogFile_AddSeriesList(t *testing.T) {
t.Parallel()
sfile := MustOpenSeriesFile()
defer sfile.Close()
@ -127,6 +129,8 @@ func TestLogFile_AddSeriesList(t *testing.T) {
}
func TestLogFile_SeriesStoredInOrder(t *testing.T) {
t.Parallel()
sfile := MustOpenSeriesFile()
defer sfile.Close()
@ -189,6 +193,8 @@ func TestLogFile_SeriesStoredInOrder(t *testing.T) {
// Ensure log file can delete an existing measurement.
func TestLogFile_DeleteMeasurement(t *testing.T) {
t.Parallel()
sfile := MustOpenSeriesFile()
defer sfile.Close()
@ -232,6 +238,8 @@ func TestLogFile_DeleteMeasurement(t *testing.T) {
// Ensure log file can recover correctly.
func TestLogFile_Open(t *testing.T) {
t.Parallel()
t.Run("Truncate", func(t *testing.T) {
sfile := MustOpenSeriesFile()
defer sfile.Close()

View File

@ -12,6 +12,8 @@ import (
)
func TestPartition_Open(t *testing.T) {
t.Parallel() // There's a bit of IO in this test.
sfile := MustOpenSeriesFile()
defer sfile.Close()

View File

@ -1554,6 +1554,7 @@ func (e *Engine) fullCompactionStrategy(group CompactionGroup, optimize bool) *c
fast: optimize,
engine: e,
level: 5,
tracker: e.compactionTracker,
}
if optimize {

View File

@ -12,7 +12,7 @@ import {Links} from 'src/types/v2/links'
import {Task, TaskStatus} from 'src/types/v2/tasks'
import {OnboardingStepProps} from 'src/onboarding/containers/OnboardingWizard'
import {ConfigurationState} from 'src/types/v2/dataLoaders'
import {TelegrafPluginInputCpu} from 'src/api'
import {TelegrafPluginInputCpu, TelegrafPluginInputRedis} from 'src/api'
export const links: Links = {
authorizations: '/api/v2/authorizations',
@ -292,6 +292,15 @@ export const telegrafPlugin = {
active: true,
}
export const redisPlugin = {
name: TelegrafPluginInputRedis.NameEnum.Redis,
type: TelegrafPluginInputRedis.TypeEnum.Input,
config: {
servers: [],
password: '',
},
}
export const influxDB2Plugin = {
name: 'influxdb_v2',
type: 'output',

136
ui/package-lock.json generated
View File

@ -1487,7 +1487,7 @@
},
"array-flatten": {
"version": "1.1.1",
"resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
"dev": true
},
@ -5046,27 +5046,27 @@
"dependencies": {
"abbrev": {
"version": "1.1.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true,
"optional": true
},
"ansi-regex": {
"version": "2.1.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
},
"aproba": {
"version": "1.2.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"dev": true,
"optional": true
},
"are-we-there-yet": {
"version": "1.1.4",
"resolved": false,
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
"dev": true,
"optional": true,
@ -5077,13 +5077,13 @@
},
"balanced-match": {
"version": "1.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": false,
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
@ -5093,39 +5093,39 @@
},
"chownr": {
"version": "1.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
"integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=",
"dev": true,
"optional": true
},
"code-point-at": {
"version": "1.1.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true
},
"concat-map": {
"version": "0.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"console-control-strings": {
"version": "1.1.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true
},
"core-util-is": {
"version": "1.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true,
"optional": true
},
"debug": {
"version": "2.6.9",
"resolved": false,
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"optional": true,
@ -5135,28 +5135,28 @@
},
"deep-extend": {
"version": "0.5.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz",
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==",
"dev": true,
"optional": true
},
"delegates": {
"version": "1.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"dev": true,
"optional": true
},
"detect-libc": {
"version": "1.0.3",
"resolved": false,
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"dev": true,
"optional": true
},
"fs-minipass": {
"version": "1.2.5",
"resolved": false,
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"dev": true,
"optional": true,
@ -5166,14 +5166,14 @@
},
"fs.realpath": {
"version": "1.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true,
"optional": true
},
"gauge": {
"version": "2.7.4",
"resolved": false,
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"dev": true,
"optional": true,
@ -5190,7 +5190,7 @@
},
"glob": {
"version": "7.1.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"dev": true,
"optional": true,
@ -5205,14 +5205,14 @@
},
"has-unicode": {
"version": "2.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"dev": true,
"optional": true
},
"iconv-lite": {
"version": "0.4.21",
"resolved": false,
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz",
"integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==",
"dev": true,
"optional": true,
@ -5222,7 +5222,7 @@
},
"ignore-walk": {
"version": "3.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"dev": true,
"optional": true,
@ -5232,7 +5232,7 @@
},
"inflight": {
"version": "1.0.6",
"resolved": false,
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"optional": true,
@ -5243,20 +5243,20 @@
},
"inherits": {
"version": "2.0.3",
"resolved": false,
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
},
"ini": {
"version": "1.3.5",
"resolved": false,
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true,
"requires": {
@ -5265,14 +5265,14 @@
},
"isarray": {
"version": "1.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true,
"optional": true
},
"minimatch": {
"version": "3.0.4",
"resolved": false,
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
@ -5281,13 +5281,13 @@
},
"minimist": {
"version": "0.0.8",
"resolved": false,
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
},
"minipass": {
"version": "2.2.4",
"resolved": false,
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz",
"integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
"dev": true,
"requires": {
@ -5297,7 +5297,7 @@
},
"minizlib": {
"version": "1.1.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz",
"integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==",
"dev": true,
"optional": true,
@ -5307,7 +5307,7 @@
},
"mkdirp": {
"version": "0.5.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
@ -5316,14 +5316,14 @@
},
"ms": {
"version": "2.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true,
"optional": true
},
"needle": {
"version": "2.2.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/needle/-/needle-2.2.0.tgz",
"integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==",
"dev": true,
"optional": true,
@ -5335,7 +5335,7 @@
},
"node-pre-gyp": {
"version": "0.10.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz",
"integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==",
"dev": true,
"optional": true,
@ -5354,7 +5354,7 @@
},
"nopt": {
"version": "4.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"dev": true,
"optional": true,
@ -5365,14 +5365,14 @@
},
"npm-bundled": {
"version": "1.0.3",
"resolved": false,
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz",
"integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==",
"dev": true,
"optional": true
},
"npm-packlist": {
"version": "1.1.10",
"resolved": false,
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.10.tgz",
"integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==",
"dev": true,
"optional": true,
@ -5383,7 +5383,7 @@
},
"npmlog": {
"version": "4.1.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"dev": true,
"optional": true,
@ -5396,20 +5396,20 @@
},
"number-is-nan": {
"version": "1.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true
},
"object-assign": {
"version": "4.1.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true,
"optional": true
},
"once": {
"version": "1.4.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
@ -5418,21 +5418,21 @@
},
"os-homedir": {
"version": "1.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"dev": true,
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true,
"optional": true
},
"osenv": {
"version": "0.1.5",
"resolved": false,
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"dev": true,
"optional": true,
@ -5443,21 +5443,21 @@
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true,
"optional": true
},
"process-nextick-args": {
"version": "2.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"dev": true,
"optional": true
},
"rc": {
"version": "1.2.7",
"resolved": false,
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz",
"integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==",
"dev": true,
"optional": true,
@ -5470,7 +5470,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true,
"optional": true
@ -5479,7 +5479,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": false,
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"optional": true,
@ -5495,7 +5495,7 @@
},
"rimraf": {
"version": "2.6.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
"dev": true,
"optional": true,
@ -5505,48 +5505,48 @@
},
"safe-buffer": {
"version": "5.1.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"dev": true
},
"safer-buffer": {
"version": "2.1.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"optional": true
},
"sax": {
"version": "1.2.4",
"resolved": false,
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"dev": true,
"optional": true
},
"semver": {
"version": "5.5.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
"dev": true,
"optional": true
},
"set-blocking": {
"version": "2.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"dev": true,
"optional": true
},
"signal-exit": {
"version": "3.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true,
"optional": true
},
"string-width": {
"version": "1.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"requires": {
@ -5557,7 +5557,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"optional": true,
@ -5567,7 +5567,7 @@
},
"strip-ansi": {
"version": "3.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@ -5576,14 +5576,14 @@
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true,
"optional": true
},
"tar": {
"version": "4.4.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.1.tgz",
"integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==",
"dev": true,
"optional": true,
@ -5599,14 +5599,14 @@
},
"util-deprecate": {
"version": "1.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true,
"optional": true
},
"wide-align": {
"version": "1.1.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
"integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
"dev": true,
"optional": true,
@ -5616,13 +5616,13 @@
},
"wrappy": {
"version": "1.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
"yallist": {
"version": "3.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
"dev": true
}
@ -9042,7 +9042,7 @@
},
"pako": {
"version": "0.2.9",
"resolved": "http://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
"integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=",
"dev": true
},

View File

@ -2343,12 +2343,6 @@ export namespace Run {
* @interface Source
*/
export interface Source {
/**
*
* @type {boolean}
* @memberof Source
*/
_default?: boolean;
/**
*
* @type {string}
@ -2472,6 +2466,24 @@ export namespace Source {
* @interface SourceLinks
*/
export interface SourceLinks {
/**
*
* @type {string}
* @memberof SourceLinks
*/
buckets?: string;
/**
*
* @type {string}
* @memberof SourceLinks
*/
health?: string;
/**
*
* @type {string}
* @memberof SourceLinks
*/
query?: string;
/**
*
* @type {string}
@ -4769,10 +4781,10 @@ export interface View {
id?: string;
/**
*
* @type {SourceLinks}
* @type {ViewLinks}
* @memberof View
*/
links?: SourceLinks;
links?: ViewLinks;
/**
*
* @type {string}
@ -4787,6 +4799,20 @@ export interface View {
properties?: any;
}
/**
*
* @export
* @interface ViewLinks
*/
export interface ViewLinks {
/**
*
* @type {string}
* @memberof ViewLinks
*/
self?: string;
}
/**
*
* @export
@ -4795,10 +4821,10 @@ export interface View {
export interface Views {
/**
*
* @type {SourceLinks}
* @type {ViewLinks}
* @memberof Views
*/
links?: SourceLinks;
links?: ViewLinks;
/**
*
* @type {Array<View>}
@ -10040,6 +10066,39 @@ export const SourcesApiAxiosParamCreator = function (configuration?: Configurati
options: localVarRequestOptions,
};
},
/**
*
* @summary Delete a source
* @param {string} sourceID ID of the source
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
sourcesSourceIDDelete(sourceID: string, options: any = {}): RequestArgs {
// verify required parameter 'sourceID' is not null or undefined
if (sourceID === null || sourceID === undefined) {
throw new RequiredError('sourceID','Required parameter sourceID was null or undefined when calling sourcesSourceIDDelete.');
}
const localVarPath = `/sources/{sourceID}`
.replace(`{${"sourceID"}}`, encodeURIComponent(String(sourceID)));
const localVarUrlObj = url.parse(localVarPath, true);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = Object.assign({ method: 'DELETE' }, baseOptions, options);
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
localVarUrlObj.query = Object.assign({}, localVarUrlObj.query, localVarQueryParameter, options.query);
// fix override query string Detail: https://stackoverflow.com/a/7517673/1077943
delete localVarUrlObj.search;
localVarRequestOptions.headers = Object.assign({}, localVarHeaderParameter, options.headers);
return {
url: url.format(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @summary Get a source
@ -10201,6 +10260,20 @@ export const SourcesApiFp = function(configuration?: Configuration) {
return axios.request(axiosRequestArgs);
};
},
/**
*
* @summary Delete a source
* @param {string} sourceID ID of the source
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
sourcesSourceIDDelete(sourceID: string, options?: any): (axios?: AxiosInstance, basePath?: string) => AxiosPromise<Response> {
const localVarAxiosArgs = SourcesApiAxiosParamCreator(configuration).sourcesSourceIDDelete(sourceID, options);
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs = Object.assign(localVarAxiosArgs.options, {url: basePath + localVarAxiosArgs.url})
return axios.request(axiosRequestArgs);
};
},
/**
*
* @summary Get a source
@ -10285,6 +10358,16 @@ export const SourcesApiFactory = function (configuration?: Configuration, basePa
sourcesSourceIDBucketsGet(sourceID: string, org: string, options?: any) {
return SourcesApiFp(configuration).sourcesSourceIDBucketsGet(sourceID, org, options)(axios, basePath);
},
/**
*
* @summary Delete a source
* @param {string} sourceID ID of the source
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
sourcesSourceIDDelete(sourceID: string, options?: any) {
return SourcesApiFp(configuration).sourcesSourceIDDelete(sourceID, options)(axios, basePath);
},
/**
*
* @summary Get a source
@ -10364,6 +10447,18 @@ export class SourcesApi extends BaseAPI {
return SourcesApiFp(this.configuration).sourcesSourceIDBucketsGet(sourceID, org, options)(this.axios, this.basePath);
}
/**
*
* @summary Delete a source
* @param {string} sourceID ID of the source
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof SourcesApi
*/
public sourcesSourceIDDelete(sourceID: string, options?: any) {
return SourcesApiFp(this.configuration).sourcesSourceIDDelete(sourceID, options)(this.axios, this.basePath);
}
/**
*
* @summary Get a source

View File

@ -2,7 +2,7 @@
import React, {Component, ChangeEvent, KeyboardEvent} from 'react'
// Components
import {Input, InputType, Radio, ButtonShape} from 'src/clockface'
import {Input, Radio, ButtonShape} from 'src/clockface'
// Styles
import './AutoInput.scss'
@ -26,7 +26,7 @@ interface Props {
interface State {
inputMode: Mode
inputValue: number
inputValue: string
}
@ErrorHandling
@ -79,20 +79,17 @@ export default class AutoInput extends Component<Props, State> {
private get input(): JSX.Element {
const {inputMode, inputValue} = this.state
const {min, max, inputPlaceholder} = this.props
const {inputPlaceholder} = this.props
if (inputMode === Mode.Custom) {
return (
<div className="auto-input--input">
<Input
type={InputType.Number}
min={min}
max={max}
placeholder={inputPlaceholder}
value={`${inputValue}`}
onChange={this.handleInputChange}
onBlur={this.handleInputBlur}
onKeyPress={this.handleInputKeyPress}
onBlur={this.emitValue}
autoFocus={true}
/>
</div>
@ -101,32 +98,41 @@ export default class AutoInput extends Component<Props, State> {
}
private handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
const inputValue = Number(e.target.value)
const {max, min} = this.props
let inputValue = e.target.value
if (Number(inputValue) < min) {
inputValue = String(min)
} else if (Number(inputValue) > max) {
inputValue = String(max)
}
this.setState({inputValue})
}
private handleRadioClick = (inputMode: Mode) => {
const {onChange} = this.props
if (inputMode === Mode.Custom) {
this.setState({inputMode, inputValue: 0})
onChange(null)
} else {
this.setState({inputMode, inputValue: null})
if (inputMode === this.state.inputMode) {
return
}
}
private handleInputBlur = (e: ChangeEvent<HTMLInputElement>) => {
const {onChange} = this.props
const inputValue = Number(e.target.value)
onChange(inputValue)
this.setState({inputMode, inputValue: ''}, this.emitValue)
}
private handleInputKeyPress = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
this.props.onChange(this.state.inputValue)
this.emitValue()
}
}
private emitValue = () => {
const {onChange} = this.props
const {inputValue} = this.state
if (inputValue === '' || isNaN(Number(inputValue))) {
onChange(null)
} else {
onChange(Number(inputValue))
}
}
}

View File

@ -3,44 +3,29 @@
------------------------------------------------------------------------------
*/
$card-select--gutter: 4px;
.grid-sizer {
width: 100%;
display: inline-block;
display: inline-flex;
justify-content: center;
position: relative;
}
.grid-sizer--cells {
height: 100%;
position: relative;
overflow: hidden;
}
.grid-sizer--cells:after {
clear: both;
content: "";
display: block;
}
.grid-sizer--cell {
float: left;
position: relative;
width: calc(100% - #{$card-select--gutter});
height: calc(100% - #{$card-select--gutter});
margin: $card-select--gutter / 2;
}
.grid-sizer--col-2 {
width: calc(50% - #{$card-select--gutter});
padding-bottom: 50%;
}
.grid-sizer--col-3 {
width: calc(33.3333% - #{$card-select--gutter});
padding-bottom: 33.3333%;
}
.grid-sizer--col-4 {
width: calc(25% - #{$card-select--gutter});
padding-bottom: 25%;
}
.grid-sizer--col-5 {
width: calc(20% - #{$card-select--gutter});
padding-bottom: 20%;
}
.grid-sizer--col-6 {
width: calc(16.6667% - #{$card-select--gutter});
padding-bottom: 16.6667%;
}
.grid-sizer--content {

View File

@ -8,68 +8,81 @@ import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
children?: JSX.Element[]
cellWidth?: number
recalculateFlag?: string
width?: number
wait?: number
}
interface State {
columns: number
columnStyle: {
width: string
paddingBottom: string
margin: string
}
}
@ErrorHandling
class GridSizer extends PureComponent<Props, State> {
public static defaultProps: Partial<Props> = {
cellWidth: 150,
recalculateFlag: '',
width: null,
wait: 0,
}
private timeoutID: NodeJS.Timer
private debouncedSizeListener: () => void
private isComponentMounted: boolean
constructor(props) {
super(props)
this.state = {
columns: null,
columnStyle: null,
}
}
public listener = () => {
_.debounce(() => this.setColumns(this.getWidth()), 250)
this.debouncedSizeListener = _.debounce(this.setColumnStyle, 250)
}
public componentDidMount() {
this.isComponentMounted = true
const {width} = this.props
const widthValue = width || this.getWidth()
this.setColumns(widthValue)
this.setColumnStyle()
if (!width) {
window.addEventListener('resize', this.listener, false)
window.addEventListener('resize', this.debouncedSizeListener, false)
}
}
public componentDidUpdate() {
const {width} = this.props
if (width) {
this.setColumns(width)
public componentDidUpdate(prevProps) {
const {recalculateFlag, wait} = this.props
if (prevProps.recalculateFlag !== recalculateFlag) {
this.timeoutID = setTimeout(this.setColumnStyle, wait)
}
}
public componentWillUnmount() {
window.removeEventListener('resize', this.listener, false)
this.isComponentMounted = false
clearInterval(this.timeoutID)
window.removeEventListener('resize', this.debouncedSizeListener, false)
}
public render() {
return (
<div id="grid_sizer" className="grid-sizer">
{this.sizeChildren}
<div className="grid-sizer--cells">{this.sizeChildren}</div>
</div>
)
}
private get sizeChildren() {
const {columns} = this.state
const {columnStyle} = this.state
const {children} = this.props
if (columns) {
if (columnStyle) {
const wrappedChildren = children.map((child, i) => (
<div
key={`grid_cell_${i}`}
className={`grid-sizer--cell grid-sizer--col-${columns}`}
style={columnStyle}
className={`grid-sizer--cell`}
>
<div className="grid-sizer--content">{child}</div>
</div>
@ -82,6 +95,10 @@ class GridSizer extends PureComponent<Props, State> {
}
private getWidth = () => {
if (!this.isComponentMounted) {
return
}
const ele = document.getElementById('grid_sizer')
const computedWidth = window
.getComputedStyle(ele, null)
@ -94,13 +111,24 @@ class GridSizer extends PureComponent<Props, State> {
return widthValue
}
private setColumns = (width: number) => {
const {cellWidth} = this.props
const columns = Math.round(width / cellWidth)
private setColumnStyle = () => {
const {cellWidth, width} = this.props
const actualWidth = width || this.getWidth()
const columns = Math.round(actualWidth / cellWidth)
const columnsPercent = 1 / columns
const calculatedCellWidth = actualWidth * columnsPercent
const gutterWidth = 4
const columnStyle = {
width: `${calculatedCellWidth - gutterWidth}px`,
margin: `${gutterWidth / 2}px`,
paddingBottom: `${calculatedCellWidth - gutterWidth}px`,
}
this.setState({
columns,
})
if (this.isComponentMounted) {
this.setState({
columnStyle,
})
}
}
}

View File

@ -75,6 +75,7 @@
align-items: center;
cursor: pointer;
margin: 0 $ix-marg-a;
flex-shrink: 0;
}
.wizard--progress-button:hover {
@ -84,7 +85,9 @@
}
.wizard--progress-connector {
width: 70px;
min-width: 20px;
width: 100%;
max-width: 70px;
margin: 0 $ix-marg-a;
height: 2px;
background-color: $g7-graphite;

View File

@ -6,6 +6,7 @@
.wizard--full-screen {
min-width: 100%;
min-height: 100%;
max-height: 100%;
padding: $ix-marg-d $ix-marg-e;
z-index: 50;
display: inline-flex;

View File

@ -6,8 +6,7 @@
.wizard--progress-header {
position: relative;
background-color: $g0-obsidian;
width: 80%;
max-width: 1000px;
width: 100%;
height: 50px;
padding: 0 $ix-marg-c;
display: inline-flex;

View File

@ -23,6 +23,7 @@ import IndexList from './components/index_views/IndexList'
import Context from './components/context_menu/Context'
import FormElement from 'src/clockface/components/form_layout/FormElement'
import DraggableResizer from 'src/clockface/components/draggable_resizer/DraggableResizer'
import GridSizer from 'src/clockface/components/grid_sizer/GridSizer'
// Import Types
import {
@ -58,6 +59,7 @@ export {
EmptyState,
Form,
FormElement,
GridSizer,
IndexList,
Input,
InputType,

View File

@ -102,9 +102,8 @@ export enum CEOTabs {
Vis = 'Visualization',
}
export const MAX_TO_LOCALE_STRING_VAL = 20 // 20 is the max input to maximumFractionDigits in spec for "to locale string"
export const MIN_DECIMAL_PLACES = '0'
export const MAX_DECIMAL_PLACES = MAX_TO_LOCALE_STRING_VAL.toString()
export const MIN_DECIMAL_PLACES = 0
export const MAX_DECIMAL_PLACES = 10
// used in importing dashboards and mapping sources
export const DYNAMIC_SOURCE = 'dynamic'

View File

@ -295,10 +295,7 @@ class LogsPage extends Component<Props, State> {
private setCurrentSource = async () => {
if (!this.props.currentSource && this.props.sources.length > 0) {
const source =
this.props.sources.find(src => {
return src.default
}) || this.props.sources[0]
const source = this.props.sources[0]
return await this.props.getSourceAndPopulateBuckets(source.links.self)
}

View File

@ -9,7 +9,8 @@ import {oneline} from 'src/logs/utils/helpers/formatting'
import {QueryConfig} from 'src/types'
import {Filter, LogQuery} from 'src/types/logs'
import {InfluxLanguage, SourceType} from 'src/types/v2'
import {InfluxLanguage} from 'src/types/v2'
import {Source} from 'src/api'
describe('Logs.LogQuery', () => {
let config: QueryConfig
@ -39,7 +40,7 @@ describe('Logs.LogQuery', () => {
const source = {
id: '1',
name: 'foo',
type: SourceType.Self,
type: Source.TypeEnum.Self,
url: 'test.local',
insecureSkipVerify: false,
default: true,

View File

@ -12,8 +12,7 @@
margin: $ix-marg-d;
margin-top: $ix-marg-a;
flex-grow: 1;
width: 80%;
max-width: 1000px;
width: 100%;
}
.wizard-step--container {
@ -26,6 +25,7 @@
text-align: center;
background-color: $g3-castle;
border-radius: $radius;
padding: 20px;
flex: 1 0 100%;
transition: flex 0.4s ease;
transform: translate3d(0, 0, 0);
@ -200,14 +200,8 @@
text-align: center;
}
.wizard-step--grid-container-lg {
max-width: 750px;
.wizard-step--grid-container {
width: 90%;
display: block;
margin: 0 auto;
}
.wizard-step--grid-container-sm {
max-width: 500px;
display: block;
margin: 0 auto;
}
}

View File

@ -27,6 +27,7 @@ interface Props {
notify: NotificationAction
onTabClick: (tabID: string) => void
currentStepIndex: number
handleNewSourceClick: () => void
}
const configStateToTabStatus = (cs: ConfigurationState): TabStatus => {
@ -70,6 +71,7 @@ class OnboardingSideBar extends Component<Props> {
}
private get buttons(): JSX.Element[] {
const {handleNewSourceClick} = this.props
return [
<SideBar.Button
key="Download Config File"
@ -85,6 +87,7 @@ class OnboardingSideBar extends Component<Props> {
titleText="Add New Source"
color={ComponentColor.Default}
icon={IconFont.Plus}
onClick={handleNewSourceClick}
/>,
]
}

View File

@ -11,7 +11,7 @@ import {
ComponentSize,
ComponentStatus,
} from 'src/clockface'
import DataSourceTypeSelector from 'src/onboarding/components/selectionStep/TypeSelector'
import TypeSelector from 'src/onboarding/components/selectionStep/TypeSelector'
import StreamingDataSourceSelector from 'src/onboarding/components/selectionStep/StreamingSelector'
// Actions
@ -107,7 +107,7 @@ class SelectDataSourceStep extends PureComponent<Props, State> {
)
}
return (
<DataSourceTypeSelector
<TypeSelector
onSelectTelegrafPlugin={this.handleSelectTelegrafPlugin}
type={this.props.type}
/>

View File

@ -1,42 +1,83 @@
// Libraries
import React, {PureComponent} from 'react'
import uuid from 'uuid'
// Components
import {ErrorHandling} from 'src/shared/decorators/errors'
import CardSelectCard from 'src/clockface/components/card_select/CardSelectCard'
import GridSizer from 'src/clockface/components/grid_sizer/GridSizer'
import {GridSizer} from 'src/clockface'
// Constants
import {PLUGIN_OPTIONS} from 'src/onboarding/constants/pluginConfigs'
// Types
import {TelegrafPlugin} from 'src/types/v2/dataLoaders'
import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar'
export interface Props {
telegrafPlugins: TelegrafPlugin[]
onToggleTelegrafPlugin: (telegrafPlugin: string, isSelected: boolean) => void
}
interface State {
gridSizerUpdateFlag: string
}
const ANIMATION_LENGTH = 400
@ErrorHandling
class StreamingDataSourcesSelector extends PureComponent<Props> {
class StreamingSelector extends PureComponent<Props, State> {
private scrollMaxHeight = window.innerHeight * 0.45
constructor(props: Props) {
super(props)
this.state = {
gridSizerUpdateFlag: uuid.v4(),
}
}
public componentDidUpdate(prevProps) {
const addFirst =
prevProps.telegrafPlugins.length === 0 &&
this.props.telegrafPlugins.length > 0
const removeLast =
prevProps.telegrafPlugins.length > 0 &&
this.props.telegrafPlugins.length === 0
if (addFirst || removeLast) {
const gridSizerUpdateFlag = uuid.v4()
this.setState({gridSizerUpdateFlag})
}
}
public render() {
const {gridSizerUpdateFlag} = this.state
return (
<div className="wizard-step--grid-container-lg">
<GridSizer>
{PLUGIN_OPTIONS.map(ds => {
return (
<CardSelectCard
key={ds}
id={ds}
name={ds}
label={ds}
checked={this.isCardChecked(ds)}
onClick={this.handleToggle(ds)}
/>
)
})}
</GridSizer>
</div>
<FancyScrollbar
autoHide={false}
autoHeight={true}
maxHeight={this.scrollMaxHeight}
>
<div className="wizard-step--grid-container">
<GridSizer
wait={ANIMATION_LENGTH}
recalculateFlag={gridSizerUpdateFlag}
>
{PLUGIN_OPTIONS.map(ds => {
return (
<CardSelectCard
key={ds}
id={ds}
name={ds}
label={ds}
checked={this.isCardChecked(ds)}
onClick={this.handleToggle(ds)}
/>
)
})}
</GridSizer>
</div>
</FancyScrollbar>
)
}
@ -57,4 +98,4 @@ class StreamingDataSourcesSelector extends PureComponent<Props> {
}
}
export default StreamingDataSourcesSelector
export default StreamingSelector

View File

@ -4,7 +4,7 @@ import React, {PureComponent} from 'react'
// Components
import {ErrorHandling} from 'src/shared/decorators/errors'
import CardSelectCard from 'src/clockface/components/card_select/CardSelectCard'
import GridSizer from 'src/clockface/components/grid_sizer/GridSizer'
import {GridSizer} from 'src/clockface'
// Types
import {DataLoaderType} from 'src/types/v2/dataLoaders'
@ -21,10 +21,10 @@ const DATA_SOURCES_OPTIONS = [
]
@ErrorHandling
class DataSourceTypeSelector extends PureComponent<Props> {
class TypeSelector extends PureComponent<Props> {
public render() {
return (
<div className="wizard-step--grid-container-sm">
<div className="wizard-step--grid-container">
<GridSizer>
{DATA_SOURCES_OPTIONS.map(ds => {
return (
@ -54,4 +54,4 @@ class DataSourceTypeSelector extends PureComponent<Props> {
}
}
export default DataSourceTypeSelector
export default TypeSelector

View File

@ -198,17 +198,17 @@ export const PLUGIN_OPTIONS: TelegrafPluginName[] = [
TelegrafPluginInputDisk.NameEnum.Disk,
TelegrafPluginInputDiskio.NameEnum.Diskio,
TelegrafPluginInputDocker.NameEnum.Docker,
// TelegrafPluginInputFile.NameEnum.File,
TelegrafPluginInputFile.NameEnum.File,
TelegrafPluginInputKernel.NameEnum.Kernel,
TelegrafPluginInputKubernetes.NameEnum.Kubernetes,
TelegrafPluginInputLogParser.NameEnum.Logparser,
// TelegrafPluginInputMem.NameEnum.Mem,
// TelegrafPluginInputNet.NameEnum.Net,
// TelegrafPluginInputNetResponse.NameEnum.NetResponse,
// TelegrafPluginInputNgnix.NameEnum.Ngnix,
// TelegrafPluginInputProcesses.NameEnum.Processes,
// TelegrafPluginInputProcstat.NameEnum.Procstat,
// TelegrafPluginInputPrometheus.NameEnum.Prometheus,
TelegrafPluginInputMem.NameEnum.Mem,
TelegrafPluginInputNet.NameEnum.Net,
TelegrafPluginInputNetResponse.NameEnum.NetResponse,
TelegrafPluginInputNgnix.NameEnum.Ngnix,
TelegrafPluginInputProcesses.NameEnum.Processes,
TelegrafPluginInputProcstat.NameEnum.Procstat,
TelegrafPluginInputPrometheus.NameEnum.Prometheus,
TelegrafPluginInputRedis.NameEnum.Redis,
TelegrafPluginInputSyslog.NameEnum.Syslog,
TelegrafPluginInputSwap.NameEnum.Swap,

View File

@ -121,6 +121,7 @@ class OnboardingWizard extends PureComponent<Props> {
onSaveTelegrafConfig,
setupParams,
notify,
onDecrementCurrentStepIndex,
} = this.props
return (
@ -135,6 +136,7 @@ class OnboardingWizard extends PureComponent<Props> {
title="Selected Sources"
visible={this.sideBarVisible}
currentStepIndex={currentStepIndex}
handleNewSourceClick={onDecrementCurrentStepIndex}
/>
<div className="wizard-step--container">
<OnboardingStepSwitcher

View File

@ -0,0 +1,120 @@
// Libraries
import React, {SFC} from 'react'
interface Props {
width?: number
height?: number
}
const LogoApache: SFC<Props> = ({height, width}) => {
return (
<svg width={width} height={height} viewBox="0 0 49.92 100">
<defs>
<linearGradient
id="apache_c"
x1={-1886.65}
y1={4996.93}
x2={-1872.53}
y2={4980.42}
gradientTransform="scale(1 -1) rotate(65.2 2995.766 3977.41)"
gradientUnits="userSpaceOnUse"
>
<stop offset={0} stopColor="#f69923" />
<stop offset={0.31} stopColor="#f79a23" />
<stop offset={0.84} stopColor="#e97826" />
</linearGradient>
<linearGradient
id="apache_a"
x1={-1991.78}
y1={4989.78}
x2={-1891.01}
y2={4989.78}
gradientTransform="scale(1 -1) rotate(65.2 2995.766 3977.41)"
gradientUnits="userSpaceOnUse"
>
<stop offset={0.32} stopColor="#9e2064" />
<stop offset={0.63} stopColor="#c92037" />
<stop offset={0.75} stopColor="#cd2335" />
<stop offset={1} stopColor="#e97826" />
</linearGradient>
<linearGradient
id="apache_d"
x1={-1991.19}
y1={4998.76}
x2={-1890.42}
y2={4998.76}
xlinkHref="#apache_a"
/>
<linearGradient
id="apache_e"
x1={-1985.53}
y1={4989.27}
x2={-1884.76}
y2={4989.27}
xlinkHref="#apache_a"
/>
<linearGradient
id="apache_f"
x1={-1985.53}
y1={4999.7}
x2={-1884.76}
y2={4999.7}
xlinkHref="#apache_a"
/>
<linearGradient
id="apache_b"
x1={-1979.02}
y1={4988.65}
x2={-1918.97}
y2={4988.65}
gradientTransform="scale(1 -1) rotate(65.2 2995.766 3977.41)"
gradientUnits="userSpaceOnUse"
>
<stop offset={0} stopColor="#282662" />
<stop offset={0.1} stopColor="#662e8d" />
<stop offset={0.79} stopColor="#9f2064" />
<stop offset={0.95} stopColor="#cd2032" />
</linearGradient>
<linearGradient
id="apache_g"
x1={-1978.18}
y1={4998.33}
x2={-1925.22}
y2={4998.33}
xlinkHref="#apache_b"
/>
</defs>
<title>{'logo_apache'}</title>
<path
d="M42.56.48C41 1.4 38.39 4 35.28 7.82l2.85 5.39a71.05 71.05 0 0 1 6.1-7.65 3.45 3.45 0 0 1 .25-.26l-.25.26a64.18 64.18 0 0 0-5.72 7.76A108.36 108.36 0 0 0 49.62 12c1.09-6.18-1.08-9-1.08-9S45.79-1.43 42.56.48z"
fill="url(#apache_c)"
/>
<path
d="M38.51 13.32q-1.26 1.93-2.75 4.42l-.15.26c-.85 1.43-1.76 3-2.71 4.74q-1.23 2.26-2.57 4.81-1.17 2.23-2.4 4.77l9.45-1a11.45 11.45 0 0 0 5.17-4.07c.32-.45.64-.93 1-1.43 1-1.51 1.92-3.18 2.77-4.84s1.54-3.19 2.1-4.62a22.75 22.75 0 0 0 .83-2.5c.17-.65.31-1.28.41-1.87a105.64 105.64 0 0 1-11.15 1.33z"
fill="url(#apache_a)"
/>
<path
d="M29.71 27.15c.87-1.62 1.75-3.21 2.64-4.75s1.85-3.16 2.8-4.66l.16-.27q1.41-2.22 2.83-4.26l-2.86-5.39c-.22.26-.43.53-.65.8-.83 1-1.68 2.13-2.56 3.3-1 1.32-2 2.74-3 4.21s-1.92 2.8-2.89 4.28c-.82 1.26-1.65 2.56-2.47 3.9l-.09.14 3.68 7.35c.79-1.57 1.6-3.12 2.41-4.65z"
fill="url(#apache_d)"
/>
<path
d="M27.87 32.42c-.1.22-.21.44-.32.66-.32.67-.65 1.34-1 2s-.71 1.51-1.08 2.3c-.18.39-.37.79-.55 1.2-.56 1.2-1.12 2.45-1.69 3.74-.71 1.59-1.42 3.23-2.15 5s-1.38 3.3-2.08 5.05-1.36 3.37-2 5.13c-.62 1.57-1.24 3.18-1.87 4.85l-.09.24q-.95 2.47-1.89 5.08v.12l3-.33a1.35 1.35 0 0 0-.18 0c3.5-.46 8.28-3.17 11.37-6.46a27.81 27.81 0 0 0 3.91-5.44 45.77 45.77 0 0 0 2.52-5.24c.7-1.68 1.36-3.51 2-5.49a10.65 10.65 0 0 1-2.82 1l-.56.11-.57.09a13.11 13.11 0 0 0 7-6.83 11.73 11.73 0 0 1-4 1.76 6 6 0 0 1-.72.13h-.18a13.55 13.55 0 0 0 3-1.67c.18-.14.36-.28.53-.43a9.88 9.88 0 0 0 .74-.71c.15-.16.3-.32.44-.49a11.23 11.23 0 0 0 .94-1.29l.27-.44.32-.63a35.043 35.043 0 0 0 1.53-3.52l.13-.37c.11-.35.21-.67.29-.94s.18-.75.22-1a3.77 3.77 0 0 1-.39.26 14.18 14.18 0 0 1-4.15 1.42h-.07l-.42.07h.07l-9.44 1z"
fill="url(#apache_e)"
/>
<path
d="M14.17 61.81q1-2.52 2-5.1t2-5q1-2.49 2.12-5c.74-1.69 1.49-3.39 2.25-5.07s1.52-3.32 2.3-5c.28-.59.57-1.18.85-1.76.49-1 1-2 1.49-3l.09-.16-3.72-7.35c-.06.1-.12.2-.19.3-.86 1.42-1.72 2.86-2.57 4.34s-1.7 3-2.52 4.56c-.69 1.3-1.37 2.61-2 3.94l-.39.8q-1.22 2.51-2.2 4.83c-.75 1.75-1.41 3.43-2 5q-.57 1.57-1 3c-.26.83-.5 1.66-.74 2.48q-.83 2.93-1.43 5.83l3.7 7.55c.5-1.32 1-2.66 1.52-4 .15-.43.27-.81.44-1.19z"
fill="url(#apache_f)"
/>
<path
d="M12.77 68.54c-.49 1.36-1 2.73-1.48 4.14v.06l-.21.6-1.32 3.75c1.1.49 2 1.81 2.81 3.3a6 6 0 0 0-1.94-4.12c5.4.25 10.06-1.12 12.46-5.07a9.11 9.11 0 0 0 .59-1.12c-1.09 1.39-2.45 2-5 1.84 3.76-1.69 5.64-3.3 7.31-6 .39-.63.78-1.32 1.17-2.09a11.77 11.77 0 0 1-11.1 3.6l-3 .33c-.06.24-.2.53-.29.78z"
fill="url(#apache_b)"
/>
<path
d="M8.43 59.84a55.52 55.52 0 0 0-1 7 2 2 0 0 0 0 .25 14.73 14.73 0 0 0-4.29-3.68c2.24 3.24 3.94 6.46 4.19 9.62a9.47 9.47 0 0 1-4.73-.81 9.38 9.38 0 0 0 4 2.45c-1.81.12-3.7 1.36-5.61 2.8 2.79-1.14 5-1.59 6.65-1.22C5.12 83.53 2.56 91.52 0 100a2.24 2.24 0 0 0 1.52-1.47C2 97 5 86.92 9.75 73.67c.14-.37.27-.75.41-1.13l.12-.32c.5-1.39 1-2.8 1.56-4.25.12-.33.24-.66.37-1l-3.73-7.39a2.38 2.38 0 0 0-.05.26z"
fill="url(#apache_g)"
/>
</svg>
)
}
export default LogoApache

View File

@ -0,0 +1,67 @@
// Libraries
import React, {SFC} from 'react'
interface Props {
width?: number
height?: number
}
const LogoConsul: SFC<Props> = ({height, width}) => {
return (
<svg
width={width}
height={height}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 96.81"
>
<style>
{` .consul_a { fill: #8c1c59; }
.consul_a, .consul_b {
fill-rule: evenodd;
}
.consul_b, .consul_c {
fill: #c62a71;
}`}
</style>
<defs />
<path
className="consul_a"
d="M48.23,58.73A10.45,10.45,0,1,1,58.65,48.27,10.42,10.42,0,0,1,48.23,58.73"
/>
<path
className="consul_b"
d="M68.55,53.1a4.82,4.82,0,1,1,4.81-4.81,4.8,4.8,0,0,1-4.81,4.81"
/>
<path
className="consul_b"
d="M86.17,57.64h0a4.69,4.69,0,1,1,.09-.38c0,.12,0,.24-.09.38"
/>
<path
className="consul_b"
d="M82.76,45.23a4.82,4.82,0,1,1,3.57-5.79,5,5,0,0,1,0,1.89,4.62,4.62,0,0,1-3.62,3.9"
/>
<path
className="consul_b"
d="M99.89,57.11a4.47,4.47,0,1,1,.09-.43,1.18,1.18,0,0,0-.09.43"
/>
<path
className="consul_b"
d="M96,45a4.8,4.8,0,1,1,4-5.53,5.16,5.16,0,0,1,0,1.24A4.79,4.79,0,0,1,96,45"
/>
<path
className="consul_b"
d="M92.61,73.78h0a4.85,4.85,0,1,1,.6-2,4.34,4.34,0,0,1-.6,2"
/>
<path
className="consul_b"
d="M90.87,29.53A4.83,4.83,0,1,1,92.71,23a4.52,4.52,0,0,1,.59,2.79,4.83,4.83,0,0,1-2.43,3.79"
/>
<path
className="consul_c"
d="M48.4,96.81A48.35,48.35,0,0,1,0,48.4,48.41,48.41,0,0,1,77.8,9.94l-5.91,7.72A38.7,38.7,0,0,0,21,75.78a38.72,38.72,0,0,0,50.85,3.36l5.91,7.73A48,48,0,0,1,48.4,96.81Z"
/>
</svg>
)
}
export default LogoConsul

View File

@ -0,0 +1,50 @@
// Libraries
import React, {SFC} from 'react'
interface Props {
width?: number
height?: number
}
const LogoCpu: SFC<Props> = ({height, width}) => {
return (
<svg
width={width}
height={height}
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
>
<g id="cpu_icon">
<style>
{`
.cpu_a{fill:#7A65F1;}
.cpu_b{fill:none;stroke:#2C323D;stroke-width:7;stroke-linejoin:round;stroke-miterlimit:10;}
.cpu_c{fill:#2C323D;}
`}
</style>
<path
className="cpu_a"
d="M96,100H4c-2.2,0-4-1.8-4-4V4c0-2.2,1.8-4,4-4h92c2.2,0,4,1.8,4,4v92C100,98.2,98.2,100,96,100z"
/>
<line className="cpu_b" x1="22.2" y1="100" x2="22.2" />
<line className="cpu_b" x1="40.7" y1="100" x2="40.7" />
<line className="cpu_b" x1="59.3" y1="100" x2="59.3" />
<line className="cpu_b" x1="77.8" y1="100" x2="77.8" />
<line className="cpu_b" y1="22.2" x2="100" y2="22.2" />
<line className="cpu_b" y1="40.7" x2="100" y2="40.7" />
<line className="cpu_b" y1="59.3" x2="100" y2="59.3" />
<line className="cpu_b" y1="77.8" x2="100" y2="77.8" />
<g>
<rect x="17" y="17" className="cpu_a" width="66" height="66" />
<path
className="cpu_c"
d="M76,24v52H24V24H76 M88,10H12c-1.1,0-2,0.9-2,2v76c0,1.1,0.9,2,2,2h76c1.1,0,2-0.9,2-2V12 C90,10.9,89.1,10,88,10L88,10z"
/>
</g>
</g>
</svg>
)
}
export default LogoCpu

View File

@ -0,0 +1,92 @@
// Libraries
import React, {SFC} from 'react'
interface Props {
width?: number
height?: number
}
const LogoDocker: SFC<Props> = ({height, width}) => {
return (
<svg
width={width}
height={height}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 69.46"
>
<style>
{`
.docker_a, .docker_b {
fill: #066da5;
}
.docker_a {
fill-rule: evenodd;
}`}
</style>
<defs />
<path
className="docker_a"
d="M97.92,26.44c-2.26-1.51-7.44-2.06-11.43-1.31-.51-3.75-2.61-7-6.41-9.95l-2.19-1.46-1.46,2.19a17,17,0,0,0-2.5,10.47,13.29,13.29,0,0,0,1.93,5.75,15.74,15.74,0,0,1-7.57,1.66H.39l-.14.78c-.64,3.77-.62,15.52,7,24.55C13.05,66,21.75,69.46,33.09,69.46c24.6,0,42.8-11.32,51.32-31.9,3.35.07,10.56,0,14.27-7.06.1-.17.32-.59,1-1.93l.35-.74Z"
/>
<rect className="docker_b" x="44.36" width="10.33" height="9.39" />
<rect
className="docker_b"
x="44.36"
y="11.26"
width="10.33"
height="9.39"
/>
<rect
className="docker_b"
x="32.15"
y="11.26"
width="10.33"
height="9.39"
/>
<rect
className="docker_b"
x="19.95"
y="11.26"
width="10.33"
height="9.39"
/>
<rect
className="docker_b"
x="7.75"
y="22.53"
width="10.33"
height="9.39"
/>
<rect
className="docker_b"
x="19.95"
y="22.53"
width="10.33"
height="9.39"
/>
<rect
className="docker_b"
x="32.15"
y="22.53"
width="10.33"
height="9.39"
/>
<rect
className="docker_b"
x="44.36"
y="22.53"
width="10.33"
height="9.39"
/>
<rect
className="docker_b"
x="56.56"
y="22.53"
width="10.33"
height="9.39"
/>
</svg>
)
}
export default LogoDocker

View File

@ -0,0 +1,80 @@
// Libraries
import React, {SFC} from 'react'
interface Props {
width?: number
height?: number
}
const LogoElastic: SFC<Props> = ({height, width}) => {
return (
<svg
width={width}
height={height}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 99.61"
>
<style>
{`
.elastic_a {
fill: #fff;
}
.elastic_b {
fill: #ffd00a;
}
.elastic_c {
fill: #20b9af;
}
.elastic_d {
fill: #ee5096;
}
.elastic_e {
fill: #12a5df;
}
.elastic_f {
fill: #90c640;
}
.elastic_g {
fill: #05799f;
}`}
</style>
<defs />
<path
className="elastic_a"
d="M100,52.19A19.68,19.68,0,0,0,87,33.63a27.49,27.49,0,0,0,.53-5.38A28.24,28.24,0,0,0,36.36,11.72,15,15,0,0,0,13.12,28.8,20,20,0,0,0,0,47.44,19.69,19.69,0,0,0,13.08,66.05,28.17,28.17,0,0,0,63.63,87.84a14.84,14.84,0,0,0,9.19,3.21A15,15,0,0,0,86.88,70.82,19.94,19.94,0,0,0,100,52.19"
/>
<path
className="elastic_b"
d="M39.32,42.9l21.88,10L83.27,33.53a24.32,24.32,0,0,0,.48-4.85,24.67,24.67,0,0,0-45-13.95L35,33.78Z"
/>
<path
className="elastic_c"
d="M16.67,66.07A25.13,25.13,0,0,0,16.18,71a24.74,24.74,0,0,0,45.2,13.91l3.65-19-4.87-9.29-22-10Z"
/>
<path
className="elastic_d"
d="M16.53,28.2l15,3.55,3.29-17A11.84,11.84,0,0,0,16.53,28.2"
/>
<path
className="elastic_e"
d="M15.23,31.78A16.69,16.69,0,0,0,3.87,47.48,16.52,16.52,0,0,0,14.51,62.93l21-19-3.87-8.26Z"
/>
<path
className="elastic_f"
d="M65.23,84.91A11.82,11.82,0,0,0,83.47,71.42l-15-3.5Z"
/>
<path
className="elastic_g"
d="M68.28,64l16.49,3.86A16.7,16.7,0,0,0,96.13,52.14,16.49,16.49,0,0,0,85.47,36.72L63.9,55.62Z"
/>
</svg>
)
}
export default LogoElastic

View File

@ -0,0 +1,38 @@
// Libraries
import React, {SFC} from 'react'
interface Props {
width?: number
height?: number
}
const LogoEtcd: SFC<Props> = ({height, width}) => {
return (
<svg
width={width}
height={height}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 96.61"
>
<style>{`
.etcd_a {
fill: #419eda;
}`}</style>
<defs />
<path
className="etcd_a"
d="M45.77,43a6.52,6.52,0,1,1-6.52-6.52A6.52,6.52,0,0,1,45.77,43Z"
/>
<path
className="etcd_a"
d="M54.2,43a6.52,6.52,0,1,0,6.52-6.52A6.52,6.52,0,0,0,54.2,43Z"
/>
<path
className="etcd_a"
d="M98.59,50c-.47,0-.94.06-1.44.06a18.79,18.79,0,0,1-8.28-1.93A75.91,75.91,0,0,0,90,33.51a76.15,76.15,0,0,0-9.47-11.23,18.71,18.71,0,0,1,6.68-7.15l1.22-.76-1-1.08A49.92,49.92,0,0,0,70,.58L68.7,0l-.34,1.4A18.74,18.74,0,0,1,63.64,10,76.58,76.58,0,0,0,50,4.35,75.82,75.82,0,0,0,36.35,10a18.71,18.71,0,0,1-4.71-8.56L31.3,0,30,.58A50.43,50.43,0,0,0,12.55,13.29l-1,1.08,1.22.75a18.8,18.8,0,0,1,6.66,7.12A76.39,76.39,0,0,0,10,33.43a76.53,76.53,0,0,0,1.09,14.74,18.73,18.73,0,0,1-8.23,1.91c-.51,0-1,0-1.44,0L0,49.92l.13,1.43A49.62,49.62,0,0,0,6.84,71.87l.73,1.24,1.09-.93A18.73,18.73,0,0,1,17.55,68a75.64,75.64,0,0,0,7.6,12.38,77.51,77.51,0,0,0,14.4,3.54,18.65,18.65,0,0,1-1.2,9.83l-.54,1.33,1.4.31A50.56,50.56,0,0,0,50,96.61l10.79-1.2,1.4-.31-.55-1.33a18.79,18.79,0,0,1-1.19-9.84A77,77,0,0,0,74.81,80.4,76.56,76.56,0,0,0,82.42,68a18.9,18.9,0,0,1,8.92,4.17l1.1.93.73-1.24a49.56,49.56,0,0,0,6.7-20.51l.13-1.43ZM65.43,67.56a58.15,58.15,0,0,1-30.9,0,59.69,59.69,0,0,1-6.61-13.93,59.09,59.09,0,0,1-2.79-15.29,58.8,58.8,0,0,1,11.2-10.66A60,60,0,0,1,50,20.25a60.26,60.26,0,0,1,13.64,7.41A59.14,59.14,0,0,1,74.86,38.4a59.17,59.17,0,0,1-2.81,15.21A59.67,59.67,0,0,1,65.43,67.56Z"
/>
</svg>
)
}
export default LogoEtcd

View File

@ -0,0 +1,93 @@
// Libraries
import React, {SFC} from 'react'
interface Props {
width?: number
height?: number
}
const LogoIis: SFC<Props> = ({height, width}) => {
return (
<svg width={width} height={height} viewBox="0 0 100 100">
<defs>
<linearGradient
id="iis_b"
x1={-514.77}
y1={316.77}
x2={-514.77}
y2={316.81}
gradientTransform="matrix(2493.7 0 0 -2493.7 1283740.92 790028.16)"
gradientUnits="userSpaceOnUse"
>
<stop offset={0} stopColor="#574c4a" />
<stop offset={1} stopColor="#80716d" />
</linearGradient>
<linearGradient
id="iis_c"
x1={-514.92}
y1={316.94}
x2={-514.95}
y2={316.99}
gradientTransform="matrix(1567.75 0 0 -1504.18 807337.55 476821.08)"
gradientUnits="userSpaceOnUse"
>
<stop offset={0} stopColor="#268d83" />
<stop offset={1} stopColor="#2ea19e" />
</linearGradient>
<radialGradient
id="iis_a"
cx={-517.38}
cy={323.85}
r={0.02}
gradientTransform="matrix(181.37 0 0 -181.37 93879.52 58811.53)"
gradientUnits="userSpaceOnUse"
>
<stop offset={0} stopColor="#db7c7c" />
<stop offset={1} stopColor="#c83737" />
</radialGradient>
<radialGradient
id="iis_d"
cx={-519.14}
cy={323.85}
r={0.02}
gradientTransform="matrix(181.36 0 0 -181.36 94206.95 58808.39)"
xlinkHref="#iis_a"
/>
</defs>
<title>{'logo_iis'}</title>
<path
d="M0 67.57V32.43C0 4.05 4.05 0 32.4 0h35.2C96 0 100 4.05 100 32.43v35.14C100 96 96 100 67.6 100H32.4C4.05 100 0 96 0 67.57z"
fill="url(#iis_b)"
/>
<path
d="M21.58 18.85a279.62 279.62 0 0 0-2.34 60.32H34.6c-1.46-7.78-6.7-43.31-2.34-43.43 2.34.37 13 30.15 13 30.15a42.66 42.66 0 0 1 4.72-.3 42.66 42.66 0 0 1 4.72.3s10.68-29.78 13-30.15c4.36.12-.88 35.65-2.34 43.43h15.4a279.62 279.62 0 0 0-2.34-60.32H64.19c-2.7 0-13 18.1-14.19 18.1s-11.48-18.07-14.19-18.1z"
fill="url(#iis_c)"
/>
<path
d="M47 75.53a3.64 3.64 0 1 1-3.64-3.64A3.64 3.64 0 0 1 47 75.53z"
fill="url(#iis_a)"
/>
<path
d="M60.3 75.53a3.64 3.64 0 1 1-3.63-3.64 3.63 3.63 0 0 1 3.63 3.64z"
fill="url(#iis_d)"
/>
<path
d="M77.69 19.88A272.7 272.7 0 0 1 80.39 60c0 11-.67 18.11-.67 18.11H66.79l-1.39 1h15.36a279.62 279.62 0 0 0-2.34-60.32l-.73 1zM37 19.5c4 4.5 11 16.41 12 16.41-2.6-3.28-8.89-13.72-12-16.41zm-5.78 15.2c-4.36.12.88 35.65 2.34 43.43H20.15l-.91 1H34.6c-1.45-7.74-6.65-43-2.41-43.43-.39-.59-.73-1-1-1zm35.48 0c-2.34.37-13 30.14-13 30.14a44.1 44.1 0 0 0-4.7-.29c-1.4 0-2.61.09-3.42.16l-.26 1.18a42.66 42.66 0 0 1 4.72-.3 42.66 42.66 0 0 1 4.72.3s10.6-29.58 13-30.15c-.26-.65-.58-1-1-1z"
style={{
isolation: 'isolate',
}}
opacity={0.1}
/>
<path
d="M21.58 18.85a279.62 279.62 0 0 0-2.34 60.32l.9-1a279.63 279.63 0 0 1 2.48-58.26h14.23a2.21 2.21 0 0 1 1.17.65c-.93-1-1.7-1.69-2.21-1.69zm42.61 0c-2.7 0-13 18.1-14.19 18.1.48.61.88 1 1 1 1.19 0 11.49-18.07 14.19-18.1h12.6l.63-1zM33.24 36.78c3.45 5.19 12 29.11 12 29.11l.25-1.17c-2.12-5.81-10.2-27.61-12.23-27.94zm35.54 0c2.32 5.92-2.07 35.39-3.38 42.39l1.39-1.09c1.78-10.44 6.06-41.19 1.99-41.3z"
style={{
isolation: 'isolate',
}}
fill="#fff"
opacity={0.3}
/>
</svg>
)
}
export default LogoIis

View File

@ -0,0 +1,41 @@
// Libraries
import React, {SFC} from 'react'
interface Props {
width?: number
height?: number
}
const LogoKubernetes: SFC<Props> = ({height, width}) => {
return (
<svg
width={width}
height={height}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 97.03"
>
<style>
{`
.kubernetes_a {
fill: #326ce5;
}
.kubernetes_b {
fill: #fff;
stroke: #fff;
stroke-width: 0.25px;
}`}
</style>
<defs />
<path
className="kubernetes_a"
d="M49.65,0a6.57,6.57,0,0,0-2.54.64L12.34,17.26a6.62,6.62,0,0,0-3.6,4.48L.17,59.05a6.55,6.55,0,0,0,.9,5.06,6.28,6.28,0,0,0,.38.52L25.51,94.55A6.67,6.67,0,0,0,30.71,97H69.3a6.65,6.65,0,0,0,5.19-2.48L98.55,64.62A6.56,6.56,0,0,0,99.83,59L91.24,21.73a6.59,6.59,0,0,0-3.59-4.47L52.88.65A6.66,6.66,0,0,0,49.65,0Z"
/>
<path
className="kubernetes_b"
d="M50,12.71A2.2,2.2,0,0,0,47.92,15v.06c0,.17,0,.38,0,.53a15.23,15.23,0,0,0,.28,2,19,19,0,0,1,.23,3.77,2.31,2.31,0,0,1-.67,1.08l0,.88A25.73,25.73,0,0,0,44,23.9a26.68,26.68,0,0,0-13.6,7.77l-.75-.54A1.63,1.63,0,0,1,28.36,31a19.42,19.42,0,0,1-2.8-2.53A16.86,16.86,0,0,0,24.19,27l-.46-.37a2.5,2.5,0,0,0-1.45-.54,2,2,0,0,0-1.66.73,2.2,2.2,0,0,0,.51,3.07l0,0,.43.35a17.22,17.22,0,0,0,1.73,1,19.64,19.64,0,0,1,3.09,2.17,2.29,2.29,0,0,1,.42,1.2l.67.6A26.91,26.91,0,0,0,23.25,54l-.87.25a2.9,2.9,0,0,1-.9.91,19.74,19.74,0,0,1-3.73.61,17.37,17.37,0,0,0-2,.16l-.56.13h0a2.09,2.09,0,1,0,.93,4.06h.07l.54-.12a16.34,16.34,0,0,0,1.86-.72,19.72,19.72,0,0,1,3.63-1.06,2.22,2.22,0,0,1,1.2.42l.91-.16a27.09,27.09,0,0,0,12,15l-.38.9a2.14,2.14,0,0,1,.18,1.18A20.07,20.07,0,0,1,34.21,79a17.78,17.78,0,0,0-1.12,1.66c-.08.15-.18.39-.26.56A2.09,2.09,0,1,0,36.58,83h0c.08-.16.19-.37.26-.53a18.84,18.84,0,0,0,.6-1.9c.55-1.39.85-2.84,1.61-3.74a1.6,1.6,0,0,1,.89-.44l.48-.85a26.86,26.86,0,0,0,15.63,1.12,27.81,27.81,0,0,0,3.57-1.07c.13.23.38.68.44.8a1.62,1.62,0,0,1,1.07.64,20,20,0,0,1,1.42,3.5,17.5,17.5,0,0,0,.61,1.91c.06.15.18.37.26.53a2.09,2.09,0,1,0,3.76-1.78l-.27-.56a19.21,19.21,0,0,0-1.12-1.66A19.55,19.55,0,0,1,64,75.62a1.69,1.69,0,0,1,.16-1.23c-.07-.09-.24-.6-.34-.84a27.07,27.07,0,0,0,12-15.09l.89.16a1.62,1.62,0,0,1,1.17-.43,19.72,19.72,0,0,1,3.63,1.06,16.34,16.34,0,0,0,1.86.72l.54.12h.07a2.09,2.09,0,1,0,.93-4.06c-.18,0-.43-.11-.61-.14a17.37,17.37,0,0,0-2-.16,19.74,19.74,0,0,1-3.73-.61,2.29,2.29,0,0,1-.9-.91L76.78,54a27.05,27.05,0,0,0-4.34-18.73l.74-.66a1.66,1.66,0,0,1,.39-1.18,19.31,19.31,0,0,1,3.1-2.17,18.41,18.41,0,0,0,1.73-1l.45-.37a2.09,2.09,0,1,0-2.59-3.26L75.8,27a16.86,16.86,0,0,0-1.37,1.46A20.12,20.12,0,0,1,71.62,31a2.3,2.3,0,0,1-1.26.13l-.79.57a27.32,27.32,0,0,0-17.28-8.35c0-.27,0-.78,0-.93a1.68,1.68,0,0,1-.68-1,19.1,19.1,0,0,1,.24-3.77,15.23,15.23,0,0,0,.28-2c0-.17,0-.41,0-.59A2.2,2.2,0,0,0,50,12.71ZM47.39,28.85l-.62,10.91,0,0a1.84,1.84,0,0,1-1.83,1.76,1.81,1.81,0,0,1-1.08-.35h0l-8.95-6.35a21.5,21.5,0,0,1,12.54-6Zm5.22,0a21.55,21.55,0,0,1,12.46,6l-8.89,6.3h0a1.84,1.84,0,0,1-2.92-1.4h0Zm-21,10.08,8.18,7.31v0A1.85,1.85,0,0,1,40,48.81a1.82,1.82,0,0,1-1,.63v0l-10.48,3A21.49,21.49,0,0,1,31.6,38.93Zm36.73,0a21.73,21.73,0,0,1,3.14,13.53l-10.53-3v0a1.83,1.83,0,0,1-1.3-2.18,1.86,1.86,0,0,1,.58-1v0l8.12-7.28Zm-20,7.87h3.35l2.08,2.61L53,52.65,50,54.1l-3-1.45-.74-3.25Zm10.73,8.91a1.53,1.53,0,0,1,.43,0v0l10.84,1.84a21.49,21.49,0,0,1-8.67,10.89L57.45,58.28h0a1.84,1.84,0,0,1,.89-2.38,1.81,1.81,0,0,1,.7-.18Zm-18.19,0a1.85,1.85,0,0,1,1.76,1.43,1.82,1.82,0,0,1-.11,1.13l0,0L38.38,68.4a21.45,21.45,0,0,1-8.64-10.82l10.74-1.82,0,0a2.19,2.19,0,0,1,.36,0Zm9.07,4.41a1.76,1.76,0,0,1,.86.18,1.82,1.82,0,0,1,.82.78h0L57,70.69a21.87,21.87,0,0,1-13.86,0l5.28-9.55h0A1.82,1.82,0,0,1,49.93,60.16Z"
/>
</svg>
)
}
export default LogoKubernetes

View File

@ -0,0 +1,127 @@
// Libraries
import React, {SFC} from 'react'
interface Props {
width?: number
height?: number
}
const LogoMesos: SFC<Props> = ({height, width}) => {
return (
<svg
width={width}
height={height}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 87.83 100"
>
<style>
{`
.mesos_a {
fill: #00445e;
}
.mesos_b {
fill: #00aede;
}`}
</style>
<defs />
<polyline
className="mesos_a"
points="65.28 60.91 65.28 39.09 46.46 50.02 65.28 60.91"
/>
<polyline
className="mesos_b"
points="64.06 36.96 45.21 26.06 45.21 47.89 64.06 36.96"
/>
<polyline
className="mesos_b"
points="42.76 47.89 42.76 26.06 23.89 36.96 42.76 47.89"
/>
<polyline
className="mesos_b"
points="41.54 23.96 22.69 13 22.69 34.86 41.54 23.96"
/>
<polyline
className="mesos_a"
points="64.06 63.01 45.21 52.12 45.21 73.95 64.06 63.01"
/>
<polyline
className="mesos_a"
points="42.76 73.95 42.76 52.12 23.89 63.01 42.76 73.95"
/>
<polyline
className="mesos_b"
points="86.61 50.02 67.73 39.09 67.73 60.91 86.61 50.02"
/>
<polyline
className="mesos_b"
points="20.23 34.86 20.23 13 1.24 23.96 20.23 34.86"
/>
<polyline
className="mesos_a"
points="41.54 76.05 22.69 65.12 22.69 86.97 41.54 76.05"
/>
<polyline
className="mesos_b"
points="20.23 86.97 20.23 65.12 1.24 76.05 20.23 86.97"
/>
<polyline
className="mesos_a"
points="41.54 50.02 22.69 39.09 22.69 60.91 41.54 50.02"
/>
<polyline
className="mesos_b"
points="65.28 34.86 65.28 13 46.46 23.96 65.28 34.86"
/>
<polyline
className="mesos_a"
points="45.21 0 45.21 21.82 64.06 10.9 45.21 0"
/>
<polyline
className="mesos_a"
points="23.89 10.9 42.76 21.82 42.76 0 23.89 10.9"
/>
<polyline
className="mesos_a"
points="64.06 89.07 45.21 78.18 45.21 100 64.06 89.07"
/>
<polyline
className="mesos_a"
points="42.76 100 42.76 78.18 23.89 89.07 42.76 100"
/>
<polyline
className="mesos_b"
points="87.83 73.95 87.83 52.12 69 63.01 87.83 73.95"
/>
<polyline
className="mesos_b"
points="20.23 60.91 20.23 39.05 1.24 50.02 20.23 60.91"
/>
<polyline
className="mesos_b"
points="19.02 63.01 0 52.12 0 73.95 19.02 63.01"
/>
<polyline
className="mesos_b"
points="87.83 47.89 87.83 26.06 69 36.96 87.83 47.89"
/>
<polyline
className="mesos_b"
points="86.61 23.96 67.73 13 67.73 34.86 86.61 23.96"
/>
<polyline
className="mesos_b"
points="86.61 76.05 67.73 65.12 67.73 86.97 86.61 76.05"
/>
<polyline
className="mesos_a"
points="65.28 86.97 65.28 65.12 46.46 76.05 65.28 86.97"
/>
<polyline
className="mesos_b"
points="19.02 36.96 0 26.06 0 47.89 19.02 36.96"
/>
</svg>
)
}
export default LogoMesos

View File

@ -0,0 +1,76 @@
// Libraries
import React, {SFC} from 'react'
interface Props {
width?: number
height?: number
}
const LogoMongodb: SFC<Props> = ({height, width}) => {
return (
<svg width={width} height={height} viewBox="0 0 44.83 100">
<defs>
<linearGradient
id="mongodb_a"
x1={-960.8}
y1={-1260.14}
x2={-992.42}
y2={-1260.35}
gradientTransform="matrix(-.98 -.32 .29 -.88 -566.27 -1364.86)"
gradientUnits="userSpaceOnUse"
>
<stop offset={0.23} stopColor="#999875" />
<stop offset={0.56} stopColor="#9b9977" />
<stop offset={0.68} stopColor="#a09f7e" />
<stop offset={0.77} stopColor="#a9a889" />
<stop offset={0.84} stopColor="#b7b69a" />
<stop offset={0.9} stopColor="#c9c7b0" />
<stop offset={0.95} stopColor="#deddcb" />
<stop offset={0.99} stopColor="#f8f6eb" />
<stop offset={1} stopColor="#fbf9ef" />
</linearGradient>
<linearGradient
id="mongodb_b"
x1={-955.93}
y1={-1204.8}
x2={-1001.42}
y2={-1283.59}
gradientTransform="matrix(-.98 -.32 .29 -.88 -566.27 -1364.86)"
gradientUnits="userSpaceOnUse"
>
<stop offset={0} stopColor="#48a547" />
<stop offset={1} stopColor="#3f9143" />
</linearGradient>
<linearGradient
id="mongodb_c"
x1={-951.77}
y1={-1261.44}
x2={-984.01}
y2={-1239.78}
gradientTransform="matrix(-.98 -.32 .29 -.88 -566.27 -1364.86)"
gradientUnits="userSpaceOnUse"
>
<stop offset={0} stopColor="#41a247" />
<stop offset={0.35} stopColor="#4ba74b" />
<stop offset={0.96} stopColor="#67b554" />
<stop offset={1} stopColor="#69b655" />
</linearGradient>
</defs>
<title>{'logo_mongodb'}</title>
<path
d="M24.7 100l-2.7-.89s.34-13.57-4.55-14.52c-3.23-3.74.49-159.8 12.22-.54 0 0-4 2-4.77 5.44S24.7 100 24.7 100z"
fill="url(#mongodb_a)"
/>
<path
d="M26.15 86.89S49.46 71.54 44 39.66C38.74 16.5 26.32 8.88 25 6a35 35 0 0 1-3-5.73l1 64.54s-2 19.7 3.15 22.08z"
fill="url(#mongodb_b)"
/>
<path
d="M20.66 87.75S-1.21 72.83.05 46.52s16.7-39.26 19.71-41.6C21.7 2.84 21.78 2 21.93 0c1.37 2.93 1.14 43.73 1.29 48.49.56 18.57-1.03 35.75-2.56 39.26z"
fill="url(#mongodb_c)"
/>
</svg>
)
}
export default LogoMongodb

View File

@ -0,0 +1,43 @@
// Libraries
import React, {SFC} from 'react'
interface Props {
width?: number
height?: number
}
const LogoMysql: SFC<Props> = ({height, width}) => {
return (
<svg
width={width}
height={height}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 98.16"
>
<style>
{` .mysql_a {
fill: #687e91;
}
.mysql_a, .mysql_b {
fill-rule: evenodd;
}
.mysql_b {
fill: #00758f;
}`}
</style>
<defs />
<path
className="mysql_a"
d="M22.74,16.82a11,11,0,0,0-2.79.34v.14h.13a22.93,22.93,0,0,0,2.18,2.79c.55,1.09,1,2.17,1.56,3.26l.14-.14a3.81,3.81,0,0,0,1.43-3.4c-.41-.48-.47-.95-.82-1.43-.4-.68-1.29-1-1.83-1.56"
/>
<path
className="mysql_b"
d="M92.11,75.92c-5.44-.13-9.66.41-13.19,1.91-1,.41-2.65.41-2.79,1.7.54.54.61,1.43,1.09,2.18a16,16,0,0,0,3.53,4.15C82.18,86.94,83.61,88,85.11,89c2.65,1.64,5.64,2.59,8.22,4.22,1.5,1,3,2.18,4.5,3.2.74.54,1.21,1.43,2.17,1.76V98c-.48-.61-.61-1.5-1.09-2.18-.68-.68-1.36-1.29-2-2a32.43,32.43,0,0,0-7.07-6.87c-2.18-1.5-6.94-3.54-7.82-6l-.14-.13a27.9,27.9,0,0,0,4.7-1.1c2.3-.61,4.41-.47,6.79-1.08,1.09-.28,2.18-.62,3.27-1V77c-1.23-1.22-2.11-2.86-3.4-4A91.89,91.89,0,0,0,82,64.63c-2.11-1.36-4.83-2.24-7.08-3.4-.81-.41-2.17-.61-2.65-1.29a27.59,27.59,0,0,1-2.79-5.24c-2-3.73-3.88-7.88-5.57-11.83-1.23-2.65-2-5.3-3.47-7.75-7-11.57-14.63-18.57-26.32-25.44-2.52-1.43-5.51-2-8.71-2.79-1.7-.07-3.4-.2-5.1-.27-1.09-.48-2.18-1.77-3.13-2.38C13.28,1.79,3.29-3.52.43,3.49c-1.84,4.42,2.72,8.77,4.28,11a31.2,31.2,0,0,1,3.47,5.1c.48,1.15.61,2.38,1.09,3.6a82,82,0,0,0,3.54,9.12,31.18,31.18,0,0,0,2.51,4.21c.55.75,1.5,1.09,1.7,2.32-.95,1.36-1,3.4-1.56,5.1-2.45,7.68-1.5,17.21,2,22.85,1.09,1.7,3.67,5.44,7.14,4,3.06-1.22,2.38-5.1,3.27-8.5.2-.82.07-1.36.47-1.9v.14c1,1.9,1.91,3.73,2.79,5.64A38.69,38.69,0,0,0,40,75.31c1.62,1.23,2.92,3.33,5,4.08v-.2h-.14a7.51,7.51,0,0,0-1.56-1.36,33.69,33.69,0,0,1-3.54-4.08,87.87,87.87,0,0,1-7.61-12.38C31,59.26,30,57,29.13,54.84c-.41-.81-.41-2-1.09-2.45-1,1.5-2.51,2.79-3.26,4.62-1.29,2.93-1.43,6.53-1.91,10.28-.27.07-.13,0-.27.13-2.17-.54-2.92-2.79-3.74-4.69-2-4.83-2.38-12.58-.61-18.16.47-1.43,2.52-5.92,1.7-7.28-.41-1.29-1.77-2-2.52-3.06A26.58,26.58,0,0,1,15,29.88c-1.64-3.81-2.46-8-4.22-11.84a36.81,36.81,0,0,0-3.4-5.23C6.07,11,4.65,9.68,3.62,7.5c-.33-.75-.81-2-.27-2.78a1.09,1.09,0,0,1,1-.89c.88-.75,3.4.2,4.28.61a34.22,34.22,0,0,1,6.73,3.4c1,.68,2,2,3.2,2.32H20c2.17.47,4.62.13,6.67.74a43.68,43.68,0,0,1,9.79,4.7A60.31,60.31,0,0,1,57.62,38.86c.82,1.56,1.17,3,1.91,4.62,1.43,3.34,3.2,6.74,4.63,10A44.15,44.15,0,0,0,69,62.59c1,1.43,5.1,2.18,6.94,2.93a48.85,48.85,0,0,1,4.69,1.9c2.31,1.43,4.62,3.06,6.8,4.63,1.08.81,4.49,2.51,4.69,3.87"
/>
</svg>
)
}
export default LogoMysql

View File

@ -0,0 +1,39 @@
// Libraries
import React, {SFC} from 'react'
interface Props {
width?: number
height?: number
}
const LogoNginx: SFC<Props> = ({height, width}) => {
return (
<svg
width={width}
height={height}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 87.14 100"
>
<style>
{`
.nginx_a {
fill: #009438;
}
.nginx_b {
fill: #fefefe;
}`}
</style>
<defs />
<path
className="nginx_a"
d="M0,50.07V26.21a2.14,2.14,0,0,1,1.2-2C15,16.21,28.68,8.35,42.37.4A2.09,2.09,0,0,1,44.68.3L86,24.17a2.22,2.22,0,0,1,1.11,2V73.84a2.22,2.22,0,0,1-1.11,2L50.51,96.42c-1.85,1.11-3.8,2.22-5.65,3.23a2.37,2.37,0,0,1-2.49,0C32,93.73,21.74,87.72,11.38,81.8c-3.42-1.94-6.85-4-10.27-5.92A2.08,2.08,0,0,1,0,73.94Z"
/>
<path
className="nginx_b"
d="M29.14,41.19V69a5.75,5.75,0,0,1-5.83,5.83,5.51,5.51,0,0,1-4.72-2.68,4.86,4.86,0,0,1-.74-2.78V30.74a5.59,5.59,0,0,1,3.61-5.27,8.63,8.63,0,0,1,6.11,0,9.8,9.8,0,0,1,4.71,3.23c4,4.81,8,9.62,12,14.43,4.16,5,8.42,10,12.58,15.08a5.25,5.25,0,0,0,.37.46V30.55A5.39,5.39,0,0,1,62.16,25a5.61,5.61,0,0,1,6.39,4.81V69.22A5.16,5.16,0,0,1,65.68,74a8.17,8.17,0,0,1-4.44.83,10.08,10.08,0,0,1-6.11-2.59,35.29,35.29,0,0,1-2.4-2.77C48.19,64,43.66,58.67,39.13,53.22L29.32,41.47C29.32,41.38,29.23,41.28,29.14,41.19Z"
/>
</svg>
)
}
export default LogoNginx

View File

@ -0,0 +1,27 @@
import LogoApache from 'src/onboarding/graphics/LogoApache'
import LogoConsul from 'src/onboarding/graphics/LogoConsul'
import LogoCpu from 'src/onboarding/graphics/LogoCpu'
import LogoDocker from 'src/onboarding/graphics/LogoDocker'
import LogoElastic from 'src/onboarding/graphics/LogoElastic'
import LogoEtcd from 'src/onboarding/graphics/LogoEtcd'
import LogoIis from 'src/onboarding/graphics/LogoIis'
import LogoKubernetes from 'src/onboarding/graphics/LogoKubernetes'
import LogoMesos from 'src/onboarding/graphics/LogoMesos'
import LogoMongodb from 'src/onboarding/graphics/LogoMongodb'
import LogoMysql from 'src/onboarding/graphics/LogoMysql'
import LogoNginx from 'src/onboarding/graphics/LogoNginx'
export {
LogoApache,
LogoConsul,
LogoCpu,
LogoDocker,
LogoElastic,
LogoEtcd,
LogoIis,
LogoKubernetes,
LogoMongodb,
LogoMesos,
LogoMysql,
LogoNginx,
}

View File

@ -8,14 +8,26 @@ import dataLoadersReducer, {
import {
setDataLoadersType,
addTelegrafPlugin,
addConfigValue,
removeConfigValue,
removeTelegrafPlugin,
setActiveTelegrafPlugin,
setTelegrafConfigID,
updateTelegrafPluginConfig,
} from 'src/onboarding/actions/dataLoaders'
// Types
import {TelegrafPluginInputCpu, TelegrafPluginInputDisk} from 'src/api'
import {DataLoaderType, ConfigurationState} from 'src/types/v2/dataLoaders'
import {
TelegrafPluginInputCpu,
TelegrafPluginInputDisk,
TelegrafPluginInputRedis,
} from 'src/api'
import {
DataLoaderType,
ConfigurationState,
TelegrafPlugin,
} from 'src/types/v2/dataLoaders'
import {redisPlugin} from 'mocks/dummyData'
describe('dataLoader reducer', () => {
it('can set a type', () => {
@ -129,4 +141,112 @@ describe('dataLoader reducer', () => {
expect(actual).toEqual(expected)
})
it('can update a plugin config field', () => {
const plugin = {
...redisPlugin,
config: {servers: [], password: ''},
}
const tp: TelegrafPlugin = {
name: TelegrafPluginInputRedis.NameEnum.Redis,
configured: ConfigurationState.Unconfigured,
active: true,
plugin,
}
const actual = dataLoadersReducer(
{...INITIAL_STATE, telegrafPlugins: [tp]},
updateTelegrafPluginConfig(
TelegrafPluginInputRedis.NameEnum.Redis,
'password',
'pa$$w0rd'
)
)
const expected = {
...INITIAL_STATE,
telegrafPlugins: [
{
...tp,
plugin: {
...plugin,
config: {servers: [], password: 'pa$$w0rd'},
},
},
],
}
expect(actual).toEqual(expected)
})
it('can add a plugin config value', () => {
const plugin = {
...redisPlugin,
config: {servers: ['first'], password: ''},
}
const tp: TelegrafPlugin = {
name: TelegrafPluginInputRedis.NameEnum.Redis,
configured: ConfigurationState.Unconfigured,
active: true,
plugin,
}
const actual = dataLoadersReducer(
{...INITIAL_STATE, telegrafPlugins: [tp]},
addConfigValue(
TelegrafPluginInputRedis.NameEnum.Redis,
'servers',
'second'
)
)
const expected = {
...INITIAL_STATE,
telegrafPlugins: [
{
...tp,
plugin: {
...plugin,
config: {servers: ['first', 'second'], password: ''},
},
},
],
}
expect(actual).toEqual(expected)
})
it('can remove a plugin config value', () => {
const plugin = {
...redisPlugin,
config: {servers: ['first', 'second'], password: ''},
}
const tp: TelegrafPlugin = {
name: TelegrafPluginInputRedis.NameEnum.Redis,
configured: ConfigurationState.Unconfigured,
active: true,
plugin,
}
const actual = dataLoadersReducer(
{...INITIAL_STATE, telegrafPlugins: [tp]},
removeConfigValue(
TelegrafPluginInputRedis.NameEnum.Redis,
'servers',
'first'
)
)
const expected = {
...INITIAL_STATE,
telegrafPlugins: [
{
...tp,
plugin: {
...plugin,
config: {servers: ['second'], password: ''},
},
},
],
}
expect(actual).toEqual(expected)
})
})

View File

@ -2,7 +2,10 @@
import _ from 'lodash'
// Utils
import {createNewPlugin} from 'src/onboarding/utils/pluginConfigs'
import {
createNewPlugin,
updateConfigFields,
} from 'src/onboarding/utils/pluginConfigs'
// Types
import {Action} from 'src/onboarding/actions/dataLoaders'
@ -67,10 +70,11 @@ export default (state = INITIAL_STATE, action: Action): DataLoadersState => {
return {
...tp,
plugin: {
...plugin,
[action.payload.field]: action.payload.value,
},
plugin: updateConfigFields(
plugin,
action.payload.field,
action.payload.value
),
}
}
return tp
@ -90,10 +94,11 @@ export default (state = INITIAL_STATE, action: Action): DataLoadersState => {
return {
...tp,
plugin: {
...plugin,
[action.payload.fieldName]: updatedConfigFieldValue,
},
plugin: updateConfigFields(
plugin,
action.payload.fieldName,
updatedConfigFieldValue
),
}
}
return tp
@ -117,10 +122,11 @@ export default (state = INITIAL_STATE, action: Action): DataLoadersState => {
return {
...tp,
plugin: {
...plugin,
[action.payload.fieldName]: filteredConfigFieldValue,
},
plugin: updateConfigFields(
plugin,
action.payload.fieldName,
filteredConfigFieldValue
),
}
}
return tp

View File

@ -14,6 +14,16 @@ export const getConfigFields = (
return telegrafPluginsInfo[pluginName].fields
}
export const updateConfigFields = <T extends Plugin>(
plugin: T,
fieldName: string,
value: string[] | string
): T => {
return Object.assign({}, plugin, {
config: Object.assign({}, plugin.config, {[fieldName]: value}),
})
}
export const createNewPlugin = (name: TelegrafPluginName): Plugin => {
return telegrafPluginsInfo[name].defaults
}

View File

@ -6,17 +6,18 @@ import {executeQuery, ExecuteFluxQueryResult} from 'src/shared/apis/v2/query'
import {parseResponse} from 'src/shared/parsing/flux/response'
// Types
import {SourceType, InfluxLanguage} from 'src/types/v2'
import {InfluxLanguage} from 'src/types/v2'
import {Source} from 'src/api'
export const SEARCH_DURATION = '30d'
export const LIMIT = 200
export async function findBuckets(
url: string,
sourceType: SourceType,
sourceType: Source.TypeEnum,
searchTerm?: string
) {
if (sourceType === SourceType.V1) {
if (sourceType === Source.TypeEnum.V1) {
throw new Error('metaqueries not yet implemented for SourceType.V1')
}
@ -28,11 +29,11 @@ export async function findBuckets(
export async function findMeasurements(
url: string,
sourceType: SourceType,
sourceType: Source.TypeEnum,
bucket: string,
searchTerm: string = ''
): Promise<string[]> {
if (sourceType === SourceType.V1) {
if (sourceType === Source.TypeEnum.V1) {
throw new Error('metaqueries not yet implemented for SourceType.V1')
}
@ -44,12 +45,12 @@ export async function findMeasurements(
export async function findFields(
url: string,
sourceType: SourceType,
sourceType: Source.TypeEnum,
bucket: string,
measurements: string[],
searchTerm: string = ''
): Promise<string[]> {
if (sourceType === SourceType.V1) {
if (sourceType === Source.TypeEnum.V1) {
throw new Error('metaqueries not yet implemented for SourceType.V1')
}

View File

@ -1,119 +1,12 @@
/*
Color Dropdown
------------------------------------------------------------------------------
*/
$color-dropdown--circle: 14px;
$color-dropdown--bar: 104px;
$color-dropdown--bar-height: 10px;
$color-dropdown--left-padding: 11px;
$color-dropdown--name-padding: 20px;
.color-dropdown {
width: 140px;
height: 30px;
position: relative;
}
.color-dropdown.color-dropdown--stretch {
width: 100%;
}
.color-dropdown--toggle {
width: 100%;
position: relative;
}
.color-dropdown--toggle span.caret {
font-style: normal !important;
position: absolute;
top: 50%;
right: 11px;
transform: translateY(-50%);
}
.color-dropdown--menu {
position: absolute;
top: 30px;
left: 0;
z-index: 5;
width: 100%;
border-radius: 4px;
box-shadow: 0 2px 5px 0.6px fade-out($g0-obsidian, 0.7);
@include gradient-h($g0-obsidian, $g2-kevlar);
}
.color-dropdown--item {
@include no-user-select();
width: 100%;
height: 28px;
position: relative;
color: $g11-sidewalk;
transition: color 0.25s ease, background-color 0.25s ease;
display: flex;
align-items: center;
justify-content: flex-start;
}
&:hover {
background-color: $g4-onyx;
color: $g18-cloud;
}
&:hover,
&:hover > * {
cursor: pointer !important;
}
&.active {
background-color: $g3-castle;
color: $g15-platinum;
}
&:first-child {
border-radius: 4px 4px 0 0;
}
&:last-child {
border-radius: 0 0 4px 4px;
}
}
.color-dropdown--swatch,
.color-dropdown--swatches,
.color-dropdown--name {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.color-dropdown--swatch {
width: $color-dropdown--circle;
height: $color-dropdown--circle;
width: 14px;
height: 14px;
border-radius: 50%;
left: $color-dropdown--left-padding;
}
.color-dropdown--swatches {
width: $color-dropdown--bar;
height: $color-dropdown--bar-height;
border-radius: $color-dropdown--bar-height / 2;
left: $color-dropdown--left-padding;
}
.color-dropdown--name {
text-align: left;
right: $color-dropdown--name-padding;
left: $color-dropdown--circle + $color-dropdown--name-padding;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px;
font-weight: 600;
text-transform: capitalize;
.color-dropdown--swatches + & {
left: $color-dropdown--bar + $color-dropdown--name-padding;
}
}
.color-dropdown
.color-dropdown--menu
.fancy-scroll--container
.fancy-scroll--track-v
.fancy-scroll--thumb-v {
@include gradient-v($g9-mountain, $g7-graphite);
}
.color-dropdown--toggle.color-dropdown__disabled {
color: $g7-graphite;
font-style: italic;
cursor: not-allowed;
}
.color-dropdown--toggle.color-dropdown__disabled > .color-dropdown--swatch {
background-color: $g7-graphite !important;
margin-right: 5px;
}

View File

@ -1,16 +1,8 @@
// Libraries
import React, {Component} from 'react'
import classnames from 'classnames'
import React, {SFC} from 'react'
// Components
import {ClickOutside} from 'src/shared/components/ClickOutside'
import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar'
// Utils
import {ErrorHandling} from 'src/shared/decorators/errors'
// Constants
import {DROPDOWN_MENU_MAX_HEIGHT} from 'src/shared/constants/index'
import {Dropdown, ComponentStatus} from 'src/clockface'
// Types
import {ColorLabel} from 'src/types/colors'
@ -23,110 +15,34 @@ interface Props {
onChoose: (colors: ColorLabel) => void
}
interface State {
expanded: boolean
}
const titleCase = (name: string) => `${name[0].toUpperCase()}${name.slice(1)}`
@ErrorHandling
export default class ColorDropdown extends Component<Props, State> {
public static defaultProps: Partial<Props> = {
stretchToFit: false,
disabled: false,
}
const ColorDropdown: SFC<Props> = props => {
const {selected, colors, onChoose, disabled, stretchToFit} = props
public state: State = {expanded: false}
const status = disabled ? ComponentStatus.Disabled : ComponentStatus.Default
const widthPixels = stretchToFit ? null : 200
public render() {
const {expanded} = this.state
const {selected} = this.props
return (
<ClickOutside onClickOutside={this.handleClickOutside}>
<div className={this.dropdownClassNames}>
<div
className={this.buttonClassNames}
onClick={this.handleToggleMenu}
>
return (
<Dropdown
selectedID={selected.name}
onChange={onChoose}
status={status}
widthPixels={widthPixels}
>
{colors.map(color => (
<Dropdown.Item id={color.name} key={color.name} value={color}>
<div className="color-dropdown--item">
<div
className="color-dropdown--swatch"
style={{backgroundColor: selected.hex}}
style={{backgroundColor: color.hex}}
/>
<div className="color-dropdown--name">{selected.name}</div>
<span className="caret" />
<div className="color-dropdown--name">{titleCase(color.name)}</div>
</div>
{expanded && this.renderMenu}
</div>
</ClickOutside>
)
}
private get dropdownClassNames(): string {
const {stretchToFit} = this.props
const {expanded} = this.state
return classnames('color-dropdown', {
open: expanded,
'color-dropdown--stretch': stretchToFit,
})
}
private get buttonClassNames(): string {
const {disabled} = this.props
const {expanded} = this.state
return classnames('btn btn-sm btn-default color-dropdown--toggle', {
active: expanded,
'color-dropdown__disabled': disabled,
})
}
private get renderMenu(): JSX.Element {
const {colors, selected} = this.props
return (
<div className="color-dropdown--menu">
<FancyScrollbar
autoHide={false}
autoHeight={true}
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
>
{colors.map((color, i) => (
<div
className={
color.name === selected.name
? 'color-dropdown--item active'
: 'color-dropdown--item'
}
key={i}
onClick={this.handleColorClick(color)}
>
<span
className="color-dropdown--swatch"
style={{backgroundColor: color.hex}}
/>
<span className="color-dropdown--name">{color.name}</span>
</div>
))}
</FancyScrollbar>
</div>
)
}
private handleToggleMenu = (): void => {
const {disabled} = this.props
if (disabled) {
return
}
this.setState({expanded: !this.state.expanded})
}
private handleClickOutside = (): void => {
this.setState({expanded: false})
}
private handleColorClick = color => (): void => {
this.props.onChoose(color)
this.setState({expanded: false})
}
</Dropdown.Item>
))}
</Dropdown>
)
}
export default ColorDropdown

View File

@ -10,7 +10,7 @@ import {
DEFAULT_VALUE_MIN,
MIN_THRESHOLDS,
} from 'src/shared/constants/thresholds'
import {MAX_TO_LOCALE_STRING_VAL} from 'src/dashboards/constants'
import {MAX_DECIMAL_PLACES} from 'src/dashboards/constants'
import {ErrorHandling} from 'src/shared/decorators/errors'
@ -320,7 +320,7 @@ class Gauge extends Component<Props> {
ctx.textBaseline = 'middle'
ctx.textAlign = 'center'
const valueString = this.valueToString(gaugePosition)
const valueString = this.labelToString(gaugePosition)
const textY = radius
const textContent = `${prefix}${valueString}${suffix}`
@ -328,45 +328,15 @@ class Gauge extends Component<Props> {
}
private labelToString(value: number): string {
const {decimalPlaces} = this.props
const {isEnforced, digits} = this.props.decimalPlaces
let valueString
if (decimalPlaces.isEnforced) {
const digits = Math.min(decimalPlaces.digits, MAX_TO_LOCALE_STRING_VAL)
valueString = value.toLocaleString(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: digits,
})
} else {
valueString = value.toLocaleString(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: MAX_TO_LOCALE_STRING_VAL,
})
if (isEnforced && digits) {
return value.toFixed(Math.min(digits, MAX_DECIMAL_PLACES))
}
return valueString
}
private valueToString(value: number): string {
const {decimalPlaces} = this.props
let valueString
if (decimalPlaces.isEnforced) {
const digits = Math.min(decimalPlaces.digits, MAX_TO_LOCALE_STRING_VAL)
valueString = value.toLocaleString(undefined, {
minimumFractionDigits: digits,
maximumFractionDigits: digits,
})
} else {
valueString = value.toLocaleString(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: MAX_TO_LOCALE_STRING_VAL,
})
}
return valueString
// Ensure the number has at most MAX_DECIMAL_PLACES digits after the
// decimal place
return String(Number(value.toFixed(MAX_DECIMAL_PLACES)))
}
private drawNeedle = (ctx, radius, minValue, maxValue) => {

View File

@ -71,7 +71,7 @@ describe('GaugeChart', () => {
const wrapper = setup({tables})
expect(wrapper.find(Gauge).exists()).toBe(true)
expect(wrapper.find(Gauge).props().gaugePosition).toBe('2')
expect(wrapper.find(Gauge).props().gaugePosition).toBe(2)
})
})
})

View File

@ -44,7 +44,7 @@ class GaugeChart extends PureComponent<Props> {
const {values} = getLastValues(tables)
const lastValue = _.get(values, 0, 0)
return lastValue
return Number(lastValue)
}
}

View File

@ -18,11 +18,11 @@ $time-machine--controls-height: 62px;
width: 100%;
height: 100%;
position: absolute;
background-color: $g3-castle;
}
.time-machine--top {
position: relative;
background-color: $g3-castle;
}
.time-machine--bottom {

View File

@ -55,7 +55,7 @@ class TimeMachineFluxEditor extends PureComponent<Props> {
},
{
render: () => <FluxFunctionsToolbar />,
handlePixels: 10,
handlePixels: 6,
size: 0.25,
},
]

View File

@ -13,6 +13,8 @@
justify-content: space-between;
align-items: flex-start;
border-bottom: 4px solid $g3-castle;
background: $g2-kevlar;
padding: 8px 10px 0 10px;
}
.time-machine-queries--tabs {

View File

@ -29,7 +29,8 @@ import 'src/shared/components/TimeMachineQueryBuilder.scss'
// Types
import {RemoteDataState} from 'src/types'
import {AppState, Source, SourceType, BuilderConfig} from 'src/types/v2'
import {AppState, BuilderConfig} from 'src/types/v2'
import {Source} from 'src/api'
const EMPTY_FIELDS_MESSAGE = 'Select at least one bucket and measurement'
const EMPTY_FUNCTIONS_MESSAGE = 'Select at least one bucket and measurement'
@ -39,7 +40,7 @@ const mergeUnique = (items: string[], selection: string[]) =>
interface StateProps {
queryURL: string
sourceType: SourceType
sourceType: Source.TypeEnum
}
interface DispatchProps {

View File

@ -1,22 +1,41 @@
@import "src/style/modules";
$time-machine-query-tab--lr-padding: 12px;
.time-machine-query-tab {
background: $g2-kevlar;
border-radius: 5px 5px 0 0;
margin-right: 3px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 15px;
padding-right: $time-machine-query-tab--lr-padding;
color: $g10-wolf;
font-weight: 700;
font-size: 13px;
cursor: pointer !important;
@include no-user-select();
&:not(.active):before {
content: "";
height: 60%;
border-left: 1px solid $g3-castle;
padding-right: $time-machine-query-tab--lr-padding;
margin-left: -1px;
}
&:first-child:before {
border-left: none;
}
&:first-child.active {
margin-left: -1px;
}
&.active {
background: $g3-castle;
color: $g16-pearl;
padding-left: $time-machine-query-tab--lr-padding;
}
&:hover:not(.active) {
@ -27,8 +46,9 @@
.time-machine-query-tab--close {
margin-left: 30px;
margin-bottom: 2px;
color: $g7-graphite;
&:hover {
color: $g19-ghost;
color: $g16-pearl;
}
}

View File

@ -6,7 +6,7 @@
@import 'src/style/modules';
.view-options {
padding: $ix-marg-d;
padding: $ix-marg-c $ix-marg-d;
}
.view-options--header {

View File

@ -31,8 +31,8 @@ class DecimalPlacesOption extends PureComponent<Props> {
inputPlaceholder="Enter a number"
onChange={this.handleSetValue}
value={this.value}
min={Number(MIN_DECIMAL_PLACES)}
max={Number(MAX_DECIMAL_PLACES)}
min={MIN_DECIMAL_PLACES}
max={MAX_DECIMAL_PLACES}
/>
</Form.Element>
)

View File

@ -1,7 +1,6 @@
// Libraries
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
import {get} from 'lodash'
// Actions
import {setActiveSource} from 'src/sources/actions'
@ -51,7 +50,6 @@ class SetActiveSource extends PureComponent<Props> {
private resolveActiveSource() {
const {sources, activeSourceID, onSetActiveSource} = this.props
const defaultSourceID = get(sources.find(s => s.default), 'id')
const querySourceID = readQueryParams().sourceID
let resolvedSourceID
@ -60,8 +58,6 @@ class SetActiveSource extends PureComponent<Props> {
resolvedSourceID = activeSourceID
} else if (sources.find(s => s.id === querySourceID)) {
resolvedSourceID = querySourceID
} else if (defaultSourceID) {
resolvedSourceID = defaultSourceID
} else if (sources.length) {
resolvedSourceID = sources[0]
} else {

View File

@ -260,13 +260,31 @@ export const timeMachineReducer = (
case 'SET_PREFIX': {
const {prefix} = action.payload
return setYAxis(state, {prefix})
switch (state.view.properties.type) {
case ViewType.Gauge:
case ViewType.SingleStat:
return setViewProperties(state, {prefix})
case ViewType.XY:
case ViewType.LinePlusSingleStat:
return setYAxis(state, {prefix})
default:
return state
}
}
case 'SET_SUFFIX': {
const {suffix} = action.payload
return setYAxis(state, {suffix})
switch (state.view.properties.type) {
case ViewType.Gauge:
case ViewType.SingleStat:
return setViewProperties(state, {suffix})
case ViewType.XY:
case ViewType.LinePlusSingleStat:
return setYAxis(state, {suffix})
default:
return state
}
}
case 'SET_COLORS': {

View File

@ -10,7 +10,8 @@ import {
} from 'src/sources/apis'
// Types
import {Source, GetState} from 'src/types/v2'
import {GetState} from 'src/types/v2'
import {Source} from 'src/api'
export type Action =
| SetActiveSourceAction
@ -68,12 +69,8 @@ export const removeSource = (sourceID: string): RemoveSourceAction => ({
payload: {sourceID},
})
export const readSources = () => async (
dispatch: Dispatch<Action>,
getState: GetState
) => {
const sourcesLink = getState().links.sources
const sources = await readSourcesAJAX(sourcesLink)
export const readSources = () => async (dispatch: Dispatch<Action>) => {
const sources = await readSourcesAJAX()
dispatch(setSources(sources))
}
@ -91,7 +88,7 @@ export const createSource = (attrs: Partial<Source>) => async (
export const updateSource = (source: Source) => async (
dispatch: Dispatch<Action>
) => {
const updatedSource = await updateSourceAJAX(source)
const updatedSource = await updateSourceAJAX(source.id, source)
dispatch(setSource(updatedSource))
}

View File

@ -1,57 +1,40 @@
import AJAX from 'src/utils/ajax'
import {Source} from 'src/types/v2'
import {Source} from 'src/api'
import {sourcesAPI} from 'src/utils/api'
export const readSources = async (url): Promise<Source[]> => {
const {data} = await AJAX({url})
export const readSources = async (): Promise<Source[]> => {
const {data} = await sourcesAPI.sourcesGet('')
return data.sources
}
export const readSource = async (url: string): Promise<Source> => {
const {data: source} = await AJAX({
url,
})
export const readSource = async (id: string): Promise<Source> => {
const {data} = await sourcesAPI.sourcesSourceIDGet(id)
return source
return data
}
export const createSource = async (
url: string,
org: string,
attributes: Partial<Source>
): Promise<Source> => {
const {data: source} = await AJAX({
url,
method: 'POST',
data: attributes,
})
const {data} = await sourcesAPI.sourcesPost(org, attributes)
return source
return data
}
export const updateSource = async (
newSource: Partial<Source>
id: string,
source: Partial<Source>
): Promise<Source> => {
const {data: source} = await AJAX({
url: newSource.links.self,
method: 'PATCH',
data: newSource,
})
const {data} = await sourcesAPI.sourcesSourceIDPatch(id, source)
return source
return data
}
export function deleteSource(source) {
return AJAX({
url: source.links.self,
method: 'DELETE',
})
export async function deleteSource(source: Source): Promise<void> {
await sourcesAPI.sourcesSourceIDDelete(source.id)
}
export const getSourceHealth = async (url: string): Promise<void> => {
try {
await AJAX({url})
} catch (error) {
console.error(`Unable to contact source ${url}`, error)
throw error
}
export const getSourceHealth = async (id: string): Promise<void> => {
await sourcesAPI.sourcesSourceIDHealthGet(id)
}

View File

@ -26,7 +26,7 @@ import {sourceCreationFailed} from 'src/shared/copy/notifications'
import 'src/sources/components/CreateSourceOverlay.scss'
// Types
import {Source, SourceType} from 'src/types/v2'
import {Source} from 'src/api'
import {RemoteDataState} from 'src/types'
interface DispatchProps {
@ -49,7 +49,7 @@ class CreateSourceOverlay extends PureComponent<Props, State> {
public state: State = {
draftSource: {
name: '',
type: SourceType.V1,
type: Source.TypeEnum.V1,
url: '',
},
creationStatus: RemoteDataState.NotStarted,
@ -94,16 +94,16 @@ class CreateSourceOverlay extends PureComponent<Props, State> {
<Form.Element label="Type">
<Radio>
<Radio.Button
active={draftSource.type === SourceType.V1}
active={draftSource.type === Source.TypeEnum.V1}
onClick={this.handleChangeType}
value={SourceType.V1}
value={Source.TypeEnum.V1}
>
v1
</Radio.Button>
<Radio.Button
active={draftSource.type === SourceType.V2}
active={draftSource.type === Source.TypeEnum.V2}
onClick={this.handleChangeType}
value={SourceType.V2}
value={Source.TypeEnum.V2}
>
v2
</Radio.Button>
@ -144,7 +144,7 @@ class CreateSourceOverlay extends PureComponent<Props, State> {
this.setState({draftSource})
}
private handleChangeType = (type: SourceType) => {
private handleChangeType = (type: Source.TypeEnum) => {
const draftSource = {
...this.state.draftSource,
type,

View File

@ -20,7 +20,7 @@ import 'src/sources/components/SourcesListRow.scss'
// Types
import {AppState} from 'src/types/v2'
import {Source, SourceType} from 'src/types/v2'
import {Source} from 'src/api'
interface StateProps {
activeSourceID: string
@ -43,7 +43,7 @@ const SourcesListRow: SFC<Props> = ({
onSetActiveSource,
onDeleteSource,
}) => {
const canDelete = source.type !== SourceType.Self
const canDelete = source.type !== Source.TypeEnum.Self
const isActiveSource = source.id === activeSourceID
const onButtonClick = () => onSetActiveSource(source.id)
const onDeleteClick = () => onDeleteSource(source.id)

View File

@ -1,7 +1,8 @@
import {Index} from 'react-virtualized'
import {Bucket, Source} from 'src/types/v2'
import {Bucket} from 'src/types/v2'
import {QueryConfig} from 'src/types'
import {Source} from 'src/api'
import {FieldOption, TimeSeriesValue} from 'src/types/v2/dashboards'

View File

@ -1,4 +1,4 @@
import {Source, SourceType} from 'src/types/v2/sources'
import {SourceType} from 'src/types/v2/sources'
import {Bucket, RetentionRule, RetentionRuleTypes} from 'src/types/v2/buckets'
import {RangeState} from 'src/dashboards/reducers/v2/ranges'
import {ViewsState} from 'src/dashboards/reducers/v2/views'
@ -17,7 +17,7 @@ import {
InfluxLanguage,
} from 'src/types/v2/dashboards'
import {Cell, Dashboard} from 'src/api'
import {Cell, Dashboard, Source} from 'src/api'
import {Task} from 'src/types/v2/tasks'
import {Member} from 'src/types/v2/members'
import {Organization} from 'src/types/v2/orgs'

View File

@ -7,6 +7,7 @@ import {
AuthorizationsApi,
ViewsApi,
WriteApi,
SourcesApi,
} from 'src/api'
const basePath = '/api/v2'
@ -19,3 +20,4 @@ export const cellsAPI = new CellsApi({basePath})
export const telegrafsAPI = new TelegrafsApi({basePath})
export const authorizationsAPI = new AuthorizationsApi({basePath})
export const writeAPI = new WriteApi({basePath})
export const sourcesAPI = new SourcesApi({basePath})

10
user.go
View File

@ -8,6 +8,16 @@ type User struct {
Name string `json:"name"`
}
// Ops for user errors and op log.
const (
OpFindUserByID = "FindUserByID"
OpFindUser = "FindUser"
OpFindUsers = "FindUsers"
OpCreateUser = "CreateUser"
OpUpdateUser = "UpdateUser"
OpDeleteUser = "DeleteUser"
)
// UserService represents a service for managing user data.
type UserService interface {