feat: passing `type=basic` returns task metadata without query text (#22728)
parent
de7f052e5a
commit
504f0e4413
|
@ -571,8 +571,19 @@ func decodeGetTasksRequest(ctx context.Context, r *http.Request, orgs influxdb.O
|
||||||
req.filter.Status = &status
|
req.filter.Status = &status
|
||||||
}
|
}
|
||||||
|
|
||||||
// the task api can only create or lookup system tasks.
|
switch typ := qp.Get("type"); typ {
|
||||||
|
case "basic":
|
||||||
|
req.filter.Type = &taskmodel.TaskBasicType
|
||||||
|
case "system":
|
||||||
|
fallthrough
|
||||||
|
case "":
|
||||||
req.filter.Type = &taskmodel.TaskSystemType
|
req.filter.Type = &taskmodel.TaskSystemType
|
||||||
|
default:
|
||||||
|
return nil, &errors2.Error{
|
||||||
|
Code: errors2.EInvalid,
|
||||||
|
Msg: fmt.Sprintf("%q is not a valid task type", typ),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if name := qp.Get("name"); name != "" {
|
if name := qp.Get("name"); name != "" {
|
||||||
req.filter.Name = &name
|
req.filter.Name = &name
|
||||||
|
|
245
kv/task.go
245
kv/task.go
|
@ -36,16 +36,24 @@ var (
|
||||||
|
|
||||||
var _ taskmodel.TaskService = (*Service)(nil)
|
var _ taskmodel.TaskService = (*Service)(nil)
|
||||||
|
|
||||||
type kvTask struct {
|
type matchableTask interface {
|
||||||
|
GetID() platform.ID
|
||||||
|
GetOrgID() platform.ID
|
||||||
|
GetOwnerID() platform.ID
|
||||||
|
GetType() string
|
||||||
|
GetName() string
|
||||||
|
GetStatus() string
|
||||||
|
ToInfluxDB() *taskmodel.Task
|
||||||
|
}
|
||||||
|
|
||||||
|
type basicKvTask struct {
|
||||||
ID platform.ID `json:"id"`
|
ID platform.ID `json:"id"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
OrganizationID platform.ID `json:"orgID"`
|
OrganizationID platform.ID `json:"orgID"`
|
||||||
Organization string `json:"org"`
|
|
||||||
OwnerID platform.ID `json:"ownerID"`
|
OwnerID platform.ID `json:"ownerID"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Flux string `json:"flux"`
|
|
||||||
Every string `json:"every,omitempty"`
|
Every string `json:"every,omitempty"`
|
||||||
Cron string `json:"cron,omitempty"`
|
Cron string `json:"cron,omitempty"`
|
||||||
LastRunStatus string `json:"lastRunStatus,omitempty"`
|
LastRunStatus string `json:"lastRunStatus,omitempty"`
|
||||||
|
@ -55,46 +63,81 @@ type kvTask struct {
|
||||||
LatestScheduled time.Time `json:"latestScheduled,omitempty"`
|
LatestScheduled time.Time `json:"latestScheduled,omitempty"`
|
||||||
LatestSuccess time.Time `json:"latestSuccess,omitempty"`
|
LatestSuccess time.Time `json:"latestSuccess,omitempty"`
|
||||||
LatestFailure time.Time `json:"latestFailure,omitempty"`
|
LatestFailure time.Time `json:"latestFailure,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv basicKvTask) GetID() platform.ID {
|
||||||
|
return kv.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv basicKvTask) GetOrgID() platform.ID {
|
||||||
|
return kv.OrganizationID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv basicKvTask) GetOwnerID() platform.ID {
|
||||||
|
return kv.OwnerID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv basicKvTask) GetType() string {
|
||||||
|
return kv.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv basicKvTask) GetName() string {
|
||||||
|
return kv.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv basicKvTask) GetStatus() string {
|
||||||
|
return kv.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv basicKvTask) ToInfluxDB() *taskmodel.Task {
|
||||||
|
return &taskmodel.Task{
|
||||||
|
ID: kv.ID,
|
||||||
|
Type: kv.Type,
|
||||||
|
OrganizationID: kv.OrganizationID,
|
||||||
|
OwnerID: kv.OwnerID,
|
||||||
|
Name: kv.Name,
|
||||||
|
Description: kv.Description,
|
||||||
|
Status: kv.Status,
|
||||||
|
Every: kv.Every,
|
||||||
|
Cron: kv.Cron,
|
||||||
|
LastRunStatus: kv.LastRunStatus,
|
||||||
|
LastRunError: kv.LastRunError,
|
||||||
|
Offset: kv.Offset.Duration,
|
||||||
|
LatestCompleted: kv.LatestCompleted,
|
||||||
|
LatestScheduled: kv.LatestScheduled,
|
||||||
|
LatestSuccess: kv.LatestSuccess,
|
||||||
|
LatestFailure: kv.LatestFailure,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type kvTask struct {
|
||||||
|
basicKvTask
|
||||||
|
Organization string `json:"org"`
|
||||||
|
Flux string `json:"flux"`
|
||||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||||
UpdatedAt time.Time `json:"updatedAt,omitempty"`
|
UpdatedAt time.Time `json:"updatedAt,omitempty"`
|
||||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func kvToInfluxTask(k *kvTask) *taskmodel.Task {
|
func (kv kvTask) ToInfluxDB() *taskmodel.Task {
|
||||||
return &taskmodel.Task{
|
res := kv.basicKvTask.ToInfluxDB()
|
||||||
ID: k.ID,
|
res.Organization = kv.Organization
|
||||||
Type: k.Type,
|
res.Flux = kv.Flux
|
||||||
OrganizationID: k.OrganizationID,
|
res.CreatedAt = kv.CreatedAt
|
||||||
Organization: k.Organization,
|
res.UpdatedAt = kv.UpdatedAt
|
||||||
OwnerID: k.OwnerID,
|
res.Metadata = kv.Metadata
|
||||||
Name: k.Name,
|
return res
|
||||||
Description: k.Description,
|
|
||||||
Status: k.Status,
|
|
||||||
Flux: k.Flux,
|
|
||||||
Every: k.Every,
|
|
||||||
Cron: k.Cron,
|
|
||||||
LastRunStatus: k.LastRunStatus,
|
|
||||||
LastRunError: k.LastRunError,
|
|
||||||
Offset: k.Offset.Duration,
|
|
||||||
LatestCompleted: k.LatestCompleted,
|
|
||||||
LatestScheduled: k.LatestScheduled,
|
|
||||||
LatestSuccess: k.LatestSuccess,
|
|
||||||
LatestFailure: k.LatestFailure,
|
|
||||||
CreatedAt: k.CreatedAt,
|
|
||||||
UpdatedAt: k.UpdatedAt,
|
|
||||||
Metadata: k.Metadata,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindTaskByID returns a single task
|
// FindTaskByID returns a single task
|
||||||
func (s *Service) FindTaskByID(ctx context.Context, id platform.ID) (*taskmodel.Task, error) {
|
func (s *Service) FindTaskByID(ctx context.Context, id platform.ID) (*taskmodel.Task, error) {
|
||||||
var t *taskmodel.Task
|
var t *taskmodel.Task
|
||||||
err := s.kv.View(ctx, func(tx Tx) error {
|
err := s.kv.View(ctx, func(tx Tx) error {
|
||||||
task, err := s.findTaskByID(ctx, tx, id)
|
task, err := s.findTaskByID(ctx, tx, id, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t = task
|
t = task.ToInfluxDB()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -106,7 +149,7 @@ func (s *Service) FindTaskByID(ctx context.Context, id platform.ID) (*taskmodel.
|
||||||
|
|
||||||
// findTaskByID is an internal method used to do any action with tasks internally
|
// findTaskByID is an internal method used to do any action with tasks internally
|
||||||
// that do not require authorization.
|
// that do not require authorization.
|
||||||
func (s *Service) findTaskByID(ctx context.Context, tx Tx, id platform.ID) (*taskmodel.Task, error) {
|
func (s *Service) findTaskByID(ctx context.Context, tx Tx, id platform.ID, basicOnly bool) (matchableTask, error) {
|
||||||
taskKey, err := taskKey(id)
|
taskKey, err := taskKey(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -124,15 +167,14 @@ func (s *Service) findTaskByID(ctx context.Context, tx Tx, id platform.ID) (*tas
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
kvTask := &kvTask{}
|
var t matchableTask
|
||||||
if err := json.Unmarshal(v, kvTask); err != nil {
|
if basicOnly {
|
||||||
return nil, taskmodel.ErrInternalTaskServiceError(err)
|
t = &basicKvTask{}
|
||||||
|
} else {
|
||||||
|
t = &kvTask{}
|
||||||
}
|
}
|
||||||
|
if err := json.Unmarshal(v, t); err != nil {
|
||||||
t := kvToInfluxTask(kvTask)
|
return nil, taskmodel.ErrInternalTaskServiceError(err)
|
||||||
|
|
||||||
if t.LatestCompleted.IsZero() {
|
|
||||||
t.LatestCompleted = t.CreatedAt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return t, nil
|
return t, nil
|
||||||
|
@ -230,17 +272,21 @@ func (s *Service) findTasksByUser(ctx context.Context, tx Tx, filter taskmodel.T
|
||||||
return nil, 0, taskmodel.ErrUnexpectedTaskBucketErr(err)
|
return nil, 0, taskmodel.ErrUnexpectedTaskBucketErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
matchFn := newTaskMatchFn(filter, nil)
|
matchFn := newTaskMatchFn(filter)
|
||||||
|
|
||||||
for k, v := c.Next(); k != nil; k, v = c.Next() {
|
for k, v := c.Next(); k != nil; k, v = c.Next() {
|
||||||
kvTask := &kvTask{}
|
var task matchableTask
|
||||||
if err := json.Unmarshal(v, kvTask); err != nil {
|
if filter.Type != nil && *filter.Type == taskmodel.TaskBasicType {
|
||||||
|
task = &basicKvTask{}
|
||||||
|
} else {
|
||||||
|
task = &kvTask{}
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(v, task); err != nil {
|
||||||
return nil, 0, taskmodel.ErrInternalTaskServiceError(err)
|
return nil, 0, taskmodel.ErrInternalTaskServiceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t := kvToInfluxTask(kvTask)
|
if matchFn == nil || matchFn(task) {
|
||||||
if matchFn == nil || matchFn(t) {
|
ts = append(ts, task.ToInfluxDB())
|
||||||
ts = append(ts, t)
|
|
||||||
|
|
||||||
if len(ts) >= filter.Limit {
|
if len(ts) >= filter.Limit {
|
||||||
break
|
break
|
||||||
|
@ -298,7 +344,7 @@ func (s *Service) findTasksByOrg(ctx context.Context, tx Tx, orgID platform.ID,
|
||||||
// free cursor resources
|
// free cursor resources
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
matchFn := newTaskMatchFn(filter, nil)
|
matchFn := newTaskMatchFn(filter)
|
||||||
|
|
||||||
for k, v := c.Next(); k != nil; k, v = c.Next() {
|
for k, v := c.Next(); k != nil; k, v = c.Next() {
|
||||||
id, err := platform.IDFromString(string(v))
|
id, err := platform.IDFromString(string(v))
|
||||||
|
@ -306,7 +352,7 @@ func (s *Service) findTasksByOrg(ctx context.Context, tx Tx, orgID platform.ID,
|
||||||
return nil, 0, taskmodel.ErrInvalidTaskID
|
return nil, 0, taskmodel.ErrInvalidTaskID
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := s.findTaskByID(ctx, tx, *id)
|
t, err := s.findTaskByID(ctx, tx, *id, filter.Type != nil && *filter.Type == taskmodel.TaskBasicType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == taskmodel.ErrTaskNotFound {
|
if err == taskmodel.ErrTaskNotFound {
|
||||||
// we might have some crufty index's
|
// we might have some crufty index's
|
||||||
|
@ -317,12 +363,12 @@ func (s *Service) findTasksByOrg(ctx context.Context, tx Tx, orgID platform.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the new task doesn't belong to the org we have looped outside the org filter
|
// If the new task doesn't belong to the org we have looped outside the org filter
|
||||||
if t.OrganizationID != orgID {
|
if t.GetOrgID() != orgID {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if matchFn == nil || matchFn(t) {
|
if matchFn == nil || matchFn(t) {
|
||||||
ts = append(ts, t)
|
ts = append(ts, t.ToInfluxDB())
|
||||||
// Check if we are over running the limit
|
// Check if we are over running the limit
|
||||||
if len(ts) >= filter.Limit {
|
if len(ts) >= filter.Limit {
|
||||||
break
|
break
|
||||||
|
@ -333,61 +379,46 @@ func (s *Service) findTasksByOrg(ctx context.Context, tx Tx, orgID platform.ID,
|
||||||
return ts, len(ts), c.Err()
|
return ts, len(ts), c.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
type taskMatchFn func(*taskmodel.Task) bool
|
type taskMatchFn func(matchableTask) bool
|
||||||
|
|
||||||
// newTaskMatchFn returns a function for validating
|
// newTaskMatchFn returns a function for validating
|
||||||
// a task matches the filter. Will return nil if
|
// a task matches the filter. Will return nil if
|
||||||
// the filter should match all tasks.
|
// the filter should match all tasks.
|
||||||
func newTaskMatchFn(f taskmodel.TaskFilter, org *influxdb.Organization) func(t *taskmodel.Task) bool {
|
func newTaskMatchFn(f taskmodel.TaskFilter) taskMatchFn {
|
||||||
var fn taskMatchFn
|
if f.Type == nil && f.Name == nil && f.Status == nil && f.User == nil {
|
||||||
|
return nil
|
||||||
if org != nil {
|
|
||||||
expected := org.ID
|
|
||||||
prevFn := fn
|
|
||||||
fn = func(t *taskmodel.Task) bool {
|
|
||||||
res := prevFn == nil || prevFn(t)
|
|
||||||
return res && expected == t.OrganizationID
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return func(t matchableTask) bool {
|
||||||
if f.Type != nil {
|
if f.Type != nil {
|
||||||
expected := *f.Type
|
expected := *f.Type
|
||||||
prevFn := fn
|
if expected == taskmodel.TaskBasicType {
|
||||||
fn = func(t *taskmodel.Task) bool {
|
// "basic" type == get "system" type tasks, but without full metadata
|
||||||
res := prevFn == nil || prevFn(t)
|
expected = taskmodel.TaskSystemType
|
||||||
return res &&
|
|
||||||
((expected == taskmodel.TaskSystemType && (t.Type == taskmodel.TaskSystemType || t.Type == "")) || expected == t.Type)
|
|
||||||
}
|
}
|
||||||
|
typ := t.GetType()
|
||||||
|
// Default to "system" for old tasks without a persisted type
|
||||||
|
if typ == "" {
|
||||||
|
typ = taskmodel.TaskSystemType
|
||||||
|
}
|
||||||
|
if expected != typ {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if f.Name != nil && t.GetName() != *f.Name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if f.Status != nil && t.GetStatus() != *f.Status {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if f.User != nil && t.GetOwnerID() != *f.User {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.Name != nil {
|
return true
|
||||||
expected := *f.Name
|
|
||||||
prevFn := fn
|
|
||||||
fn = func(t *taskmodel.Task) bool {
|
|
||||||
res := prevFn == nil || prevFn(t)
|
|
||||||
return res && (expected == t.Name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.Status != nil {
|
|
||||||
prevFn := fn
|
|
||||||
fn = func(t *taskmodel.Task) bool {
|
|
||||||
res := prevFn == nil || prevFn(t)
|
|
||||||
return res && (t.Status == *f.Status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.User != nil {
|
|
||||||
prevFn := fn
|
|
||||||
fn = func(t *taskmodel.Task) bool {
|
|
||||||
res := prevFn == nil || prevFn(t)
|
|
||||||
return res && t.OwnerID == *f.User
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fn
|
|
||||||
}
|
|
||||||
|
|
||||||
// findAllTasks is a subset of the find tasks function. Used for cleanliness.
|
// findAllTasks is a subset of the find tasks function. Used for cleanliness.
|
||||||
// This function should only be executed internally because it doesn't force organization or user filtering.
|
// This function should only be executed internally because it doesn't force organization or user filtering.
|
||||||
// Enforcing filters should be done in a validation layer.
|
// Enforcing filters should be done in a validation layer.
|
||||||
|
@ -420,18 +451,21 @@ func (s *Service) findAllTasks(ctx context.Context, tx Tx, filter taskmodel.Task
|
||||||
// free cursor resources
|
// free cursor resources
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
matchFn := newTaskMatchFn(filter, nil)
|
matchFn := newTaskMatchFn(filter)
|
||||||
|
|
||||||
for k, v := c.Next(); k != nil; k, v = c.Next() {
|
for k, v := c.Next(); k != nil; k, v = c.Next() {
|
||||||
kvTask := &kvTask{}
|
var task matchableTask
|
||||||
if err := json.Unmarshal(v, kvTask); err != nil {
|
if filter.Type != nil && *filter.Type == taskmodel.TaskBasicType {
|
||||||
|
task = &basicKvTask{}
|
||||||
|
} else {
|
||||||
|
task = &kvTask{}
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(v, task); err != nil {
|
||||||
return nil, 0, taskmodel.ErrInternalTaskServiceError(err)
|
return nil, 0, taskmodel.ErrInternalTaskServiceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t := kvToInfluxTask(kvTask)
|
if matchFn == nil || matchFn(task) {
|
||||||
|
ts = append(ts, task.ToInfluxDB())
|
||||||
if matchFn == nil || matchFn(t) {
|
|
||||||
ts = append(ts, t)
|
|
||||||
|
|
||||||
if len(ts) >= filter.Limit {
|
if len(ts) >= filter.Limit {
|
||||||
break
|
break
|
||||||
|
@ -595,10 +629,11 @@ func (s *Service) UpdateTask(ctx context.Context, id platform.ID, upd taskmodel.
|
||||||
|
|
||||||
func (s *Service) updateTask(ctx context.Context, tx Tx, id platform.ID, upd taskmodel.TaskUpdate) (*taskmodel.Task, error) {
|
func (s *Service) updateTask(ctx context.Context, tx Tx, id platform.ID, upd taskmodel.TaskUpdate) (*taskmodel.Task, error) {
|
||||||
// retrieve the task
|
// retrieve the task
|
||||||
task, err := s.findTaskByID(ctx, tx, id)
|
t, err := s.findTaskByID(ctx, tx, id, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
task := t.ToInfluxDB()
|
||||||
|
|
||||||
updatedAt := s.clock.Now().UTC()
|
updatedAt := s.clock.Now().UTC()
|
||||||
|
|
||||||
|
@ -765,13 +800,13 @@ func (s *Service) deleteTask(ctx context.Context, tx Tx, id platform.ID) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieve the task
|
// retrieve the task
|
||||||
task, err := s.findTaskByID(ctx, tx, id)
|
task, err := s.findTaskByID(ctx, tx, id, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the orgs index
|
// remove the orgs index
|
||||||
orgKey, err := taskOrgKey(task.OrganizationID, task.ID)
|
orgKey, err := taskOrgKey(task.GetOrgID(), task.GetID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -781,7 +816,7 @@ func (s *Service) deleteTask(ctx context.Context, tx Tx, id platform.ID) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove latest completed
|
// remove latest completed
|
||||||
lastCompletedKey, err := taskLatestCompletedKey(task.ID)
|
lastCompletedKey, err := taskLatestCompletedKey(task.GetID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -791,13 +826,13 @@ func (s *Service) deleteTask(ctx context.Context, tx Tx, id platform.ID) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the runs
|
// remove the runs
|
||||||
runs, _, err := s.findRuns(ctx, tx, taskmodel.RunFilter{Task: task.ID})
|
runs, _, err := s.findRuns(ctx, tx, taskmodel.RunFilter{Task: task.GetID()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, run := range runs {
|
for _, run := range runs {
|
||||||
key, err := taskRunKey(task.ID, run.ID)
|
key, err := taskRunKey(task.GetID(), run.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -807,7 +842,7 @@ func (s *Service) deleteTask(ctx context.Context, tx Tx, id platform.ID) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// remove the task
|
// remove the task
|
||||||
key, err := taskKey(task.ID)
|
key, err := taskKey(task.GetID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -819,9 +854,9 @@ func (s *Service) deleteTask(ctx context.Context, tx Tx, id platform.ID) error {
|
||||||
uid, _ := icontext.GetUserID(ctx)
|
uid, _ := icontext.GetUserID(ctx)
|
||||||
return s.audit.Log(resource.Change{
|
return s.audit.Log(resource.Change{
|
||||||
Type: resource.Delete,
|
Type: resource.Delete,
|
||||||
ResourceID: task.ID,
|
ResourceID: task.GetID(),
|
||||||
ResourceType: influxdb.TasksResourceType,
|
ResourceType: influxdb.TasksResourceType,
|
||||||
OrganizationID: task.OrganizationID,
|
OrganizationID: task.GetOrgID(),
|
||||||
UserID: uid,
|
UserID: uid,
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,14 +4,12 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/influxdata/influxdb/v2"
|
|
||||||
"github.com/influxdata/influxdb/v2/kit/platform"
|
|
||||||
"github.com/influxdata/influxdb/v2/task/taskmodel"
|
"github.com/influxdata/influxdb/v2/task/taskmodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_newTaskMatchFN(t *testing.T) {
|
func Test_newTaskMatchFN(t *testing.T) {
|
||||||
ct := func(typ string, name string) *taskmodel.Task {
|
ct := func(typ string, name string) *basicKvTask {
|
||||||
return &taskmodel.Task{
|
return &basicKvTask{
|
||||||
Type: typ,
|
Type: typ,
|
||||||
OrganizationID: 1,
|
OrganizationID: 1,
|
||||||
Name: name,
|
Name: name,
|
||||||
|
@ -19,20 +17,12 @@ func Test_newTaskMatchFN(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
NoOrg = platform.ID(0)
|
|
||||||
NoTyp = "-"
|
NoTyp = "-"
|
||||||
NoNam = "-"
|
NoNam = "-"
|
||||||
)
|
)
|
||||||
|
|
||||||
newMatch := func(orgID platform.ID, typ string, name string) taskMatchFn {
|
newMatch := func(typ string, name string) taskMatchFn {
|
||||||
var (
|
var fil taskmodel.TaskFilter
|
||||||
org *influxdb.Organization
|
|
||||||
fil taskmodel.TaskFilter
|
|
||||||
)
|
|
||||||
|
|
||||||
if orgID != NoOrg {
|
|
||||||
org = &influxdb.Organization{ID: orgID}
|
|
||||||
}
|
|
||||||
|
|
||||||
if typ != NoTyp {
|
if typ != NoTyp {
|
||||||
fil.Type = &typ
|
fil.Type = &typ
|
||||||
|
@ -42,12 +32,12 @@ func Test_newTaskMatchFN(t *testing.T) {
|
||||||
fil.Name = &name
|
fil.Name = &name
|
||||||
}
|
}
|
||||||
|
|
||||||
return newTaskMatchFn(fil, org)
|
return newTaskMatchFn(fil)
|
||||||
}
|
}
|
||||||
|
|
||||||
type test struct {
|
type test struct {
|
||||||
name string
|
name string
|
||||||
task *taskmodel.Task
|
task matchableTask
|
||||||
fn taskMatchFn
|
fn taskMatchFn
|
||||||
exp bool
|
exp bool
|
||||||
}
|
}
|
||||||
|
@ -56,48 +46,37 @@ func Test_newTaskMatchFN(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
tests []test
|
tests []test
|
||||||
}{
|
}{
|
||||||
{
|
|
||||||
"match org",
|
|
||||||
[]test{
|
|
||||||
{
|
|
||||||
name: "equal",
|
|
||||||
task: ct(taskmodel.TaskSystemType, "Foo"),
|
|
||||||
fn: newMatch(1, NoTyp, NoNam),
|
|
||||||
exp: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not org",
|
|
||||||
task: ct(taskmodel.TaskSystemType, "Foo"),
|
|
||||||
fn: newMatch(2, NoTyp, NoNam),
|
|
||||||
exp: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"match type",
|
"match type",
|
||||||
[]test{
|
[]test{
|
||||||
{
|
{
|
||||||
name: "empty with system type",
|
name: "empty with system type",
|
||||||
task: ct("", "Foo"),
|
task: ct("", "Foo"),
|
||||||
fn: newMatch(NoOrg, taskmodel.TaskSystemType, NoNam),
|
fn: newMatch(taskmodel.TaskSystemType, NoNam),
|
||||||
exp: true,
|
exp: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "system with system type",
|
name: "system with system type",
|
||||||
task: ct(taskmodel.TaskSystemType, "Foo"),
|
task: ct(taskmodel.TaskSystemType, "Foo"),
|
||||||
fn: newMatch(NoOrg, taskmodel.TaskSystemType, NoNam),
|
fn: newMatch(taskmodel.TaskSystemType, NoNam),
|
||||||
|
exp: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "system with basic type",
|
||||||
|
task: ct(taskmodel.TaskSystemType, "Foo"),
|
||||||
|
fn: newMatch(taskmodel.TaskBasicType, NoNam),
|
||||||
exp: true,
|
exp: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "equal",
|
name: "equal",
|
||||||
task: ct("other type", "Foo"),
|
task: ct("other type", "Foo"),
|
||||||
fn: newMatch(NoOrg, "other type", NoNam),
|
fn: newMatch("other type", NoNam),
|
||||||
exp: true,
|
exp: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "not type",
|
name: "not type",
|
||||||
task: ct(taskmodel.TaskSystemType, "Foo"),
|
task: ct(taskmodel.TaskSystemType, "Foo"),
|
||||||
fn: newMatch(NoOrg, "other type", NoNam),
|
fn: newMatch("other type", NoNam),
|
||||||
exp: false,
|
exp: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -108,88 +87,13 @@ func Test_newTaskMatchFN(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "equal",
|
name: "equal",
|
||||||
task: ct(taskmodel.TaskSystemType, "Foo"),
|
task: ct(taskmodel.TaskSystemType, "Foo"),
|
||||||
fn: newMatch(NoOrg, NoTyp, "Foo"),
|
fn: newMatch(NoTyp, "Foo"),
|
||||||
exp: true,
|
exp: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "not name",
|
name: "not name",
|
||||||
task: ct(taskmodel.TaskSystemType, "Foo"),
|
task: ct(taskmodel.TaskSystemType, "Foo"),
|
||||||
fn: newMatch(NoOrg, NoTyp, "Bar"),
|
fn: newMatch(NoTyp, "Bar"),
|
||||||
exp: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"match org type",
|
|
||||||
[]test{
|
|
||||||
{
|
|
||||||
name: "equal",
|
|
||||||
task: ct(taskmodel.TaskSystemType, "Foo"),
|
|
||||||
fn: newMatch(1, taskmodel.TaskSystemType, NoNam),
|
|
||||||
exp: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not type",
|
|
||||||
task: ct(taskmodel.TaskSystemType, "Foo"),
|
|
||||||
fn: newMatch(1, "wrong type", NoNam),
|
|
||||||
exp: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not org",
|
|
||||||
task: ct(taskmodel.TaskSystemType, "Foo"),
|
|
||||||
fn: newMatch(2, taskmodel.TaskSystemType, NoNam),
|
|
||||||
exp: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not org and type",
|
|
||||||
task: ct("check", "Foo"),
|
|
||||||
fn: newMatch(2, taskmodel.TaskSystemType, NoNam),
|
|
||||||
exp: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"match org name",
|
|
||||||
[]test{
|
|
||||||
{
|
|
||||||
name: "equal",
|
|
||||||
task: ct(taskmodel.TaskSystemType, "Foo"),
|
|
||||||
fn: newMatch(1, NoTyp, "Foo"),
|
|
||||||
exp: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not org",
|
|
||||||
task: ct(taskmodel.TaskSystemType, "Foo"),
|
|
||||||
fn: newMatch(2, NoTyp, "Foo"),
|
|
||||||
exp: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"match org name type",
|
|
||||||
[]test{
|
|
||||||
{
|
|
||||||
name: "equal",
|
|
||||||
task: ct("check", "Foo"),
|
|
||||||
fn: newMatch(1, "check", "Foo"),
|
|
||||||
exp: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not org",
|
|
||||||
task: ct("check", "Foo"),
|
|
||||||
fn: newMatch(2, "check", "Foo"),
|
|
||||||
exp: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not name",
|
|
||||||
task: ct("check", "Foo"),
|
|
||||||
fn: newMatch(1, "check", "Bar"),
|
|
||||||
exp: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not type",
|
|
||||||
task: ct("check", "Foo"),
|
|
||||||
fn: newMatch(1, "other", "Foo"),
|
|
||||||
exp: false,
|
exp: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -208,7 +112,7 @@ func Test_newTaskMatchFN(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("match returns nil for no filter", func(t *testing.T) {
|
t.Run("match returns nil for no filter", func(t *testing.T) {
|
||||||
fn := newTaskMatchFn(taskmodel.TaskFilter{}, nil)
|
fn := newTaskMatchFn(taskmodel.TaskFilter{})
|
||||||
if fn != nil {
|
if fn != nil {
|
||||||
t.Error("expected nil")
|
t.Error("expected nil")
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ declare -r ROOT_DIR=$(dirname ${SCRIPT_DIR})
|
||||||
declare -r STATIC_DIR="$ROOT_DIR/static"
|
declare -r STATIC_DIR="$ROOT_DIR/static"
|
||||||
|
|
||||||
# Pins the swagger that will be downloaded to a specific commit
|
# Pins the swagger that will be downloaded to a specific commit
|
||||||
declare -r OPENAPI_SHA=d6f9073685dfb58e36f20c2ed351cf872ad31a86
|
declare -r OPENAPI_SHA=2f8be204870af493560bd0e5cc3201db9da2e432
|
||||||
|
|
||||||
# Don't do a shallow clone since the commit we want might be several commits
|
# Don't do a shallow clone since the commit we want might be several commits
|
||||||
# back; but do only clone the main branch.
|
# back; but do only clone the main branch.
|
||||||
|
|
|
@ -63,6 +63,10 @@ func TestTaskService(t *testing.T, fn BackendComponentFactory, testCategory ...s
|
||||||
testTaskCRUD(t, sys)
|
testTaskCRUD(t, sys)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("FindTasks basic", func(t *testing.T) {
|
||||||
|
testTaskFindTasksBasic(t, sys)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("FindTasks paging", func(t *testing.T) {
|
t.Run("FindTasks paging", func(t *testing.T) {
|
||||||
testTaskFindTasksPaging(t, sys)
|
testTaskFindTasksPaging(t, sys)
|
||||||
})
|
})
|
||||||
|
@ -496,6 +500,43 @@ func testTaskCRUD(t *testing.T, sys *System) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testTaskFindTasksBasic(t *testing.T, sys *System) {
|
||||||
|
script := `option task = {name: "Task %03d", cron: "* * * * *", concurrency: 100, offset: 10s}
|
||||||
|
|
||||||
|
from(bucket: "b")
|
||||||
|
|> to(bucket: "two", orgID: "000000000000000")`
|
||||||
|
|
||||||
|
cr := creds(t, sys)
|
||||||
|
|
||||||
|
tc := taskmodel.TaskCreate{
|
||||||
|
OrganizationID: cr.OrgID,
|
||||||
|
OwnerID: cr.UserID,
|
||||||
|
Type: taskmodel.TaskSystemType,
|
||||||
|
}
|
||||||
|
|
||||||
|
authorizedCtx := icontext.SetAuthorizer(sys.Ctx, cr.Authorizer())
|
||||||
|
|
||||||
|
created := make([]*taskmodel.Task, 50)
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
tc.Flux = fmt.Sprintf(script, i/10)
|
||||||
|
tc.Description = fmt.Sprintf("Task %d", i)
|
||||||
|
tsk, err := sys.TaskService.CreateTask(authorizedCtx, tc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, tsk.ID.Valid(), "no task ID set")
|
||||||
|
created[i] = tsk
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks, _, err := sys.TaskService.FindTasks(sys.Ctx, taskmodel.TaskFilter{Type: &taskmodel.TaskBasicType})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 50, len(tasks))
|
||||||
|
|
||||||
|
// Basic results should exclude query text, but include other metdata like description.
|
||||||
|
for i, tsk := range tasks {
|
||||||
|
require.Empty(t, tsk.Flux)
|
||||||
|
require.Equal(t, fmt.Sprintf("Task %d", i), tsk.Description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testTaskFindTasksPaging(t *testing.T, sys *System) {
|
func testTaskFindTasksPaging(t *testing.T, sys *System) {
|
||||||
script := `option task = {name: "Task %03d", cron: "* * * * *", concurrency: 100, offset: 10s}
|
script := `option task = {name: "Task %03d", cron: "* * * * *", concurrency: 100, offset: 10s}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,8 @@ const (
|
||||||
var (
|
var (
|
||||||
// TaskSystemType is the type set in tasks' for all crud requests
|
// TaskSystemType is the type set in tasks' for all crud requests
|
||||||
TaskSystemType = "system"
|
TaskSystemType = "system"
|
||||||
|
// TaskBasicType is short-hand used by the UI to request a minimal subset of system task metadata
|
||||||
|
TaskBasicType = "basic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Task is a task. 🎊
|
// Task is a task. 🎊
|
||||||
|
|
Loading…
Reference in New Issue